diff --git a/.changeset/pretty-candles-chew.md b/.changeset/pretty-candles-chew.md new file mode 100644 index 0000000000..8bfa5dc29a --- /dev/null +++ b/.changeset/pretty-candles-chew.md @@ -0,0 +1,8 @@ +--- +'rrweb-snapshot': minor +'rrweb': minor +'rrdom': patch +'@rrweb/types': patch +--- + +Added support for Asset Event and capturing many different types of assets (not just img#src) diff --git a/.changeset/wise-pens-peel.md b/.changeset/wise-pens-peel.md new file mode 100644 index 0000000000..41a603b2e6 --- /dev/null +++ b/.changeset/wise-pens-peel.md @@ -0,0 +1,9 @@ +--- +'rrweb-snapshot': major +'rrweb': major +--- + +`inlineImages` recording option has been deprecated and is now an alias for `captureAssets: { objectURLs: true, origins: true }`. +Please see [asset recording documentation](/docs/recipes/assets.md) for more information. + +The reason we deprecated `inlineImages` is because it modified events after they where already emitted, which could lead to events being saved without the corresponding images. The new `captureAssets` option records assets as a separate event, ensuring that all assets are recorded and events are not modified after they are emitted. diff --git a/.changeset/yellow-vans-protect.md b/.changeset/yellow-vans-protect.md new file mode 100644 index 0000000000..0d2ea4161e --- /dev/null +++ b/.changeset/yellow-vans-protect.md @@ -0,0 +1,8 @@ +--- +'rrweb-snapshot': major +'@rrweb/types': patch +--- + +`NodeType` enum was moved from rrweb-snapshot to @rrweb/types +The following types where moved from rrweb-snapshot to @rrweb/types: `documentNode`, `documentTypeNode`, `attributes`, `legacyAttributes`, `elementNode`, `textNode`, `cdataNode`, `commentNode`, `serializedNode`, `serializedNodeWithId` and `DataURLOptions` +`inlineImage` config option is deprecated and in `rrweb` is an alias for `captureAssets` config option diff --git a/.vscode/rrweb-monorepo.code-workspace b/.vscode/rrweb-monorepo.code-workspace index ee31ea35ee..aa6a759381 100644 --- a/.vscode/rrweb-monorepo.code-workspace +++ b/.vscode/rrweb-monorepo.code-workspace @@ -36,6 +36,7 @@ " rrweb monorepo", "rrweb-player (package)", "@rrweb/types" - ] + ], + "typescript.tsdk": " rrweb monorepo/node_modules/typescript/lib" } } diff --git a/docs/recipes/assets.md b/docs/recipes/assets.md new file mode 100644 index 0000000000..acad0cd418 --- /dev/null +++ b/docs/recipes/assets.md @@ -0,0 +1,40 @@ +# Asset Capture Methods & Configuration in rrweb + +[rrweb](https://rrweb.io/) is a JavaScript library that allows you to record and replay user interactions on your website. It provides various configuration options for capturing assets (such as images) during the recording process. In this document, we will explore the different asset capture methods and their configuration options in rrweb. + +## Inline Images (Deprecated) + +The `inlineImages` configuration option is deprecated and should not be used anymore. The previous implementation had some issues, namely rewriting events that are already emitted which might make you miss the inlined image if the event has already been sent to the server. Currently it just turns on `captureAssets`, so use please directly use the `captureAssets` option to configure asset capture. + +## Asset Capture Configuration + +The `captureAssets` configuration option allows you to customize the asset capture process. It is an object with the following properties: + +- `objectURLs` (default: `true`): This property specifies whether to capture same-origin `blob:` assets using object URLs. Object URLs are created using the `URL.createObjectURL()` method. Setting `objectURLs` to `true` enables the capture of object URLs. + +- `origins` (default: `false`): This property determines which origins to capture assets from. It can have the following values: + - `false` or `[]`: Disables capturing any assets apart from object URLs. + - `true`: Captures assets from all origins. + - `[origin1, origin2, ...]`: Captures assets only from the specified origins. For example, `origins: ['https://s3.example.com/']` captures all assets from the origin `https://s3.example.com/`. + +## TypeScript Type Definition + +Here is the TypeScript type definition for the `recordOptions` object, which includes the asset capture configuration options: + +```typescript +export type recordOptions = { + // Other configuration options... + captureAssets?: { + objectURLs: boolean; + origins: string[] | true | false; + }; + inlineImages?: boolean; // Deprecated, don't use it anymore + // Other configuration options... +}; +``` + +This type definition shows that `captureAssets` is an optional property of the `recordOptions` object. It contains the `objectURLs` and `origins` properties, which have the same meanings as described above. + +## Conclusion + +By configuring the `captureAssets` option in rrweb, you can control how assets like images are captured during the recording process. This allows you to customize which assets are included in the recorded interactions on your website. diff --git a/docs/recipes/assets.zh_CN.md b/docs/recipes/assets.zh_CN.md new file mode 100644 index 0000000000..1aed560b50 --- /dev/null +++ b/docs/recipes/assets.zh_CN.md @@ -0,0 +1,40 @@ +# rrweb 中的资源捕获方法和配置 + +[rrweb](https://rrweb.io/) 是一个 JavaScript 库,允许您记录并重放您网站上的用户互动。它为捕获资产(如图像)提供了各种配置选项。在本文档中,我们将探讨 rrweb 中不同的资源捕获方法及其配置选项。 + +## 内联图像(已弃用) + +`inlineImages` 配置选项已弃用,不应再使用。之前的实现存在一些问题,即重写已经发出的事件,这可能使您错过已经发送到服务器的内联图像。目前它只是开启了 `captureAssets`,所以请直接使用 `captureAssets` 选项来配置资源捕获。 + +## 资源捕获配置 + +`captureAssets` 配置选项允许您自定义资源捕获过程。它是一个具有以下属性的对象: + +- `objectURLs`(默认值:`true`):此属性指定是否使用对象 URL 捕获同源 `blob:` 资源。对象 URL 是使用 `URL.createObjectURL()` 方法创建的。将 `objectURLs` 设置为 `true` 可以启用对象 URL 的捕获。 + +- `origins`(默认值:`false`):此属性确定从哪些来源捕获资源。它可以有以下值: + - `false` 或 `[]`:除了对象 URL 之外,不捕获任何资源。 + - `true`:从所有来源捕获资源。 + - `[origin1, origin2, ...]`:仅从指定的来源捕获资源。例如,`origins: ['https://s3.example.com/']` 从 `https://s3.example.com/` 来源捕获所有资源。 + +## TypeScript 类型定义 + +这是 `recordOptions` 对象的 TypeScript 类型定义,其中包括资源捕获配置选项: + +```typescript +export type recordOptions = { + // 其他配置选项... + captureAssets?: { + objectURLs: boolean; + origins: string[] | true | false; + }; + inlineImages?: boolean; // 已弃用 + // 其他配置选项... +}; +``` + +这种类型定义表明 captureAssets 是 recordOptions 对象的一个可选属性。它包含 objectURLs 和 origins 属性,其含义与上述相同。 + +## 结论 + +通过在 rrweb 中配置 captureAssets 选项,您可以控制在记录过程中如何捕获像图像这样的资源。这允许您 diff --git a/guide.md b/guide.md index 5693b26d29..8565eaded9 100644 --- a/guide.md +++ b/guide.md @@ -135,36 +135,37 @@ 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 | -| ignoreSelector | null | Use a string to configure which selector 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` | -| 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) | -| plugins | [] | load plugins to provide extended record functions. [What is plugins?](./docs/recipes/plugin.md) | -| errorHandler | - | A callback that is called if something inside of rrweb throws an error. The callback receives the error as argument. | +| 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 | +| ignoreSelector | null | Use a string to configure which selector 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` | +| 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 | sets `captureAssets` to { objectURLs: true, origins: true } (deprecated, use `captureAssets` directly instead) | +| captureAssets | { objectURLs: true, origins: false } | Configure the asset (image) capture and generates async asset events.
Refer to the [asset capture documentation](./docs/recipes/assets.md) for more info. | +| 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) | +| errorHandler | - | A callback that is called if something inside of rrweb throws an error. The callback receives the error as argument. | #### Privacy diff --git a/guide.zh_CN.md b/guide.zh_CN.md index dda679006c..c731a1ff1d 100644 --- a/guide.zh_CN.md +++ b/guide.zh_CN.md @@ -131,35 +131,36 @@ 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` | -| 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) | -| plugins | [] | 加载插件以获得额外的录制功能. [什么是插件?](./docs/recipes/plugin.zh_CN.md) | -| errorHandler | - | 一个可以定制化处理错误的回调函数,它的参数是错误对象。如果 rrweb recorder 内部的某些内容抛出错误,则会调用该回调。 | +| 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` | +| recordAfter | 'load' | 如果 document 还没有加载完成,recorder 将会在指定的事件触发后开始录制。可用选项: `DOMContentLoaded`, `load` | +| ~inlineImages~ | false | 将 `captureAssets` 设置为 { objectURLs: true, origins: true }(已弃用,直接使用 `captureAssets` 代替) | +| captureAssets | { objectURLs: true, origins: false } | 配置资源(图像)捕获并生成异步资源事件。
有关更多信息,请参阅[资源捕获文档](./docs/recipes/assets.zh_CN.md) | +| 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/package.json b/package.json index e210dda8ce..b1938f55cc 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "eslint-plugin-tsdoc": "^0.2.17", "markdownlint": "^0.25.1", "markdownlint-cli": "^0.31.1", + "@types/prettier": "2.7.3", "prettier": "2.8.4", "turbo": "^1.2.4", "typescript": "^4.9.5" @@ -41,7 +42,9 @@ "test:watch": "yarn turbo run test:watch", "test:update": "yarn turbo run test:update", "format": "yarn prettier --write '**/*.{ts,md}'", + "format:head": "git diff --name-only HEAD^ |grep '\\.ts\\|\\.md' |xargs yarn prettier --write", "dev": "yarn turbo run dev", + "check-types": "yarn run concurrently --success=all -r -m=1 'yarn workspaces-to-typescript-project-references --check' 'yarn turbo run check-types'", "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'", diff --git a/packages/rrdom-nodejs/package.json b/packages/rrdom-nodejs/package.json index 09a69a7ffd..62b9435d70 100644 --- a/packages/rrdom-nodejs/package.json +++ b/packages/rrdom-nodejs/package.json @@ -29,27 +29,29 @@ "@rollup/plugin-node-resolve": "^13.0.4", "@types/cssom": "^0.4.1", "@types/cssstyle": "^2.2.1", - "@types/jest": "^27.4.1", + "@types/jest": "^29.5.11", "@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", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", + "puppeteer": "^21.6.1", "rollup": "^2.56.3", "rollup-plugin-terser": "^7.0.2", - "rollup-plugin-typescript2": "^0.31.2", + "rollup-plugin-typescript2": "^0.36.0", "rollup-plugin-web-worker-loader": "^1.6.1", - "ts-jest": "^27.1.3" + "ts-jest": "^29.1.1" }, "dependencies": { "cssom": "^0.5.0", "cssstyle": "^2.3.0", "nwsapi": "^2.2.0", "rrdom": "^2.0.0-alpha.13", - "rrweb-snapshot": "^2.0.0-alpha.13" + "rrweb-snapshot": "^2.0.0-alpha.13", + "@rrweb/types": "^2.0.0-alpha.13" }, "browserslist": [ "supports es6-class" diff --git a/packages/rrdom-nodejs/src/document-nodejs.ts b/packages/rrdom-nodejs/src/document-nodejs.ts index 1d13970d34..15a805d6fd 100644 --- a/packages/rrdom-nodejs/src/document-nodejs.ts +++ b/packages/rrdom-nodejs/src/document-nodejs.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -import { NodeType as RRNodeType } from 'rrweb-snapshot'; +import { NodeType as RRNodeType } from '@rrweb/types'; import type { NWSAPI } from 'nwsapi'; import type { CSSStyleDeclaration as CSSStyleDeclarationType } from 'cssstyle'; import { diff --git a/packages/rrdom-nodejs/test/document-nodejs.test.ts b/packages/rrdom-nodejs/test/document-nodejs.test.ts index ba3c6144d8..ac6088165f 100644 --- a/packages/rrdom-nodejs/test/document-nodejs.test.ts +++ b/packages/rrdom-nodejs/test/document-nodejs.test.ts @@ -3,7 +3,7 @@ */ import * as fs from 'fs'; import * as path from 'path'; -import { NodeType as RRNodeType } from 'rrweb-snapshot'; +import { NodeType as RRNodeType } from '@rrweb/types'; import { RRCanvasElement, RRCDATASection, diff --git a/packages/rrdom-nodejs/tsconfig.json b/packages/rrdom-nodejs/tsconfig.json index 0c2f119853..223d62a020 100644 --- a/packages/rrdom-nodejs/tsconfig.json +++ b/packages/rrdom-nodejs/tsconfig.json @@ -3,6 +3,7 @@ "composite": true, "target": "ES6", "module": "commonjs", + "moduleResolution": "Node", "noImplicitAny": true, "strictNullChecks": true, "removeComments": true, @@ -29,6 +30,9 @@ }, { "path": "../rrweb-snapshot" + }, + { + "path": "../types" } ] } diff --git a/packages/rrdom/jest.config.js b/packages/rrdom/jest.config.js index e5841e937d..7893d1b3b9 100644 --- a/packages/rrdom/jest.config.js +++ b/packages/rrdom/jest.config.js @@ -1,5 +1,15 @@ -/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ +/** @type {import('ts-jest/dist/types').JestConfigWithTsJest} */ export default { preset: 'ts-jest', testEnvironment: 'node', + setupFiles: ['./jest.setup.ts'], + /** + * Keeps old (pre-jest 29) snapshot format + * its a bit ugly and harder to read than the new format, + * so we might want to remove this in its own PR + */ + snapshotFormat: { + escapeString: true, + printBasicPrototype: true, + }, }; diff --git a/packages/rrdom/jest.setup.ts b/packages/rrdom/jest.setup.ts new file mode 100644 index 0000000000..7c97c034f2 --- /dev/null +++ b/packages/rrdom/jest.setup.ts @@ -0,0 +1,3 @@ +import { TextEncoder, TextDecoder } from 'util'; + +Object.assign(global, { TextDecoder, TextEncoder }); diff --git a/packages/rrdom/package.json b/packages/rrdom/package.json index 430620782c..3f442ce9a1 100644 --- a/packages/rrdom/package.json +++ b/packages/rrdom/package.json @@ -31,20 +31,22 @@ "url": "https://github.com/rrweb-io/rrweb/issues" }, "devDependencies": { - "@rollup/plugin-commonjs": "^20.0.0", + "@rollup/plugin-commonjs": "^25.0.7", "@rrweb/types": "^2.0.0-alpha.13", - "@types/jest": "^27.4.1", + "@types/jest": "^29.5.11", "@types/puppeteer": "^5.4.4", "@typescript-eslint/eslint-plugin": "^5.23.0", "@typescript-eslint/parser": "^5.23.0", + "@total-typescript/shoehorn": "^0.1.0", "eslint": "^8.15.0", - "jest": "^27.5.1", + "jest": "^29.7.0", + "jest-environment-jsdom": "^30.0.0-alpha.2", "puppeteer": "^17.1.3", - "rollup": "^2.56.3", + "rollup": "^4.9.0", "rollup-plugin-terser": "^7.0.2", - "rollup-plugin-typescript2": "^0.31.2", + "rollup-plugin-typescript2": "^0.36.0", "rollup-plugin-web-worker-loader": "^1.6.1", - "ts-jest": "^27.1.3" + "ts-jest": "^29.1.1" }, "dependencies": { "rrweb-snapshot": "^2.0.0-alpha.13" diff --git a/packages/rrdom/rollup.config.js b/packages/rrdom/rollup.config.js index 5bd346673f..4ab4fd9f18 100644 --- a/packages/rrdom/rollup.config.js +++ b/packages/rrdom/rollup.config.js @@ -3,7 +3,7 @@ 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'; +import pkg from './package.json' assert { type: 'json' }; function toMinPath(path) { return path.replace(/\.js$/, '.min.js'); @@ -18,6 +18,7 @@ const basePlugins = [ typescript({ tsconfigOverride: { compilerOptions: { module: 'ESNext' } }, + cacheRoot: `./node_modules/.cache/rrdom/`, }), ]; diff --git a/packages/rrdom/src/diff.ts b/packages/rrdom/src/diff.ts index 8ccf81f8c6..a870b0553b 100644 --- a/packages/rrdom/src/diff.ts +++ b/packages/rrdom/src/diff.ts @@ -1,15 +1,14 @@ +import type { Mirror as NodeMirror } from 'rrweb-snapshot'; import { NodeType as RRNodeType, - Mirror as NodeMirror, elementNode, -} from 'rrweb-snapshot'; -import type { canvasMutationData, canvasEventWithTime, inputData, scrollData, styleDeclarationData, styleSheetRuleData, + RebuildAssetManagerInterface, } from '@rrweb/types'; import type { IRRCDATASection, @@ -77,6 +76,7 @@ const SVGTagMap: Record = { export type ReplayerHandler = { mirror: NodeMirror; + assetManager?: RebuildAssetManagerInterface; applyCanvas: ( canvasEvent: canvasEventWithTime, canvasMutationData: canvasMutationData, @@ -202,7 +202,7 @@ function diffBeforeUpdatingChildren( * by running `diffProps` on the parent node before `diffChildren` is called, * we can ensure that the correct attributes (and therefore styles) have applied to parent nodes */ - diffProps(oldElement, newRRElement, rrnodeMirror); + diffProps(oldElement, newRRElement, rrnodeMirror, replayer.assetManager); break; } } @@ -311,6 +311,7 @@ function diffProps( oldTree: HTMLElement, newTree: IRRElement, rrnodeMirror: Mirror, + assetManager?: RebuildAssetManagerInterface, ) { const oldAttributes = oldTree.attributes; const newAttributes = newTree.attributes; @@ -330,7 +331,20 @@ function diffProps( } }; } else if (newTree.tagName === 'IFRAME' && name === 'srcdoc') continue; - else oldTree.setAttribute(name, newValue); + else if ( + assetManager && + name.startsWith('rr_captured_') && + newValue && + typeof newValue === 'string' + ) { + // can possibly remove the attribute again if it hasn't loaded yet + assetManager.manageAttribute( + oldTree, + rrnodeMirror.getId(newTree), + name.substring('rr_captured_'.length), + newValue, + ); + } else oldTree.setAttribute(name, newValue); } for (const { name } of Array.from(oldAttributes)) @@ -540,6 +554,9 @@ export function createOrGetNode( case RRNodeType.CDATA: node = document.createCDATASection((rrNode as IRRCDATASection).data); break; + default: + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + throw new Error(`Unknown node type ${rrNode.RRNodeType}`); } if (sn) domMirror.add(node, { ...sn }); diff --git a/packages/rrdom/src/document.ts b/packages/rrdom/src/document.ts index befdadb86d..35487e5116 100644 --- a/packages/rrdom/src/document.ts +++ b/packages/rrdom/src/document.ts @@ -1,4 +1,4 @@ -import { NodeType as RRNodeType } from 'rrweb-snapshot'; +import { NodeType as RRNodeType } from '@rrweb/types'; import { parseCSSText, camelize, toCSSText } from './style'; export interface IRRNode { parentElement: IRRNode | null; diff --git a/packages/rrdom/src/index.ts b/packages/rrdom/src/index.ts index eeddb03e1c..019a16e582 100644 --- a/packages/rrdom/src/index.ts +++ b/packages/rrdom/src/index.ts @@ -1,13 +1,9 @@ -import { - NodeType as RRNodeType, - createMirror as createNodeMirror, -} from 'rrweb-snapshot'; +import { createMirror as createNodeMirror } from 'rrweb-snapshot'; +import type { Mirror as NodeMirror } from 'rrweb-snapshot'; +import { NodeType as RRNodeType } from '@rrweb/types'; import type { - Mirror as NodeMirror, IMirror, serializedNodeWithId, -} from 'rrweb-snapshot'; -import type { canvasMutationData, canvasEventWithTime, inputData, @@ -451,6 +447,8 @@ export function getDefaultSN(node: IRRNode, id: number): serializedNodeWithId { type: node.RRNodeType, textContent: '', }; + default: + throw new Error(`Unknown node type`); } } diff --git a/packages/rrdom/test/diff.test.ts b/packages/rrdom/test/diff.test.ts index fc76f48bd9..81df870c02 100644 --- a/packages/rrdom/test/diff.test.ts +++ b/packages/rrdom/test/diff.test.ts @@ -3,12 +3,8 @@ */ import * as path from 'path'; import * as puppeteer from 'puppeteer'; -import { - NodeType as RRNodeType, - serializedNodeWithId, - createMirror, - Mirror as NodeMirror, -} from 'rrweb-snapshot'; +import { fromPartial } from '@total-typescript/shoehorn'; +import { createMirror, Mirror as NodeMirror } from 'rrweb-snapshot'; import { buildFromDom, getDefaultSN, @@ -27,12 +23,18 @@ import { import type { IRRElement, IRRNode } from '../src/document'; import { Replayer } from 'rrweb'; import type { + serializedNodeWithId, eventWithTime, canvasMutationData, styleDeclarationData, styleSheetRuleData, + RebuildAssetManagerInterface, +} from '@rrweb/types'; +import { + NodeType as RRNodeType, + EventType, + IncrementalSource, } from '@rrweb/types'; -import { EventType, IncrementalSource } from '@rrweb/types'; import { compileTSCode } from './utils'; const elementSn = { @@ -462,6 +464,37 @@ describe('diff algorithm for rrdom', () => { diff(element, rrIframe, replayer); expect(element.getAttribute('srcdoc')).toBe(null); }); + + describe('with asset manager', () => { + let assetManager: RebuildAssetManagerInterface; + beforeEach(() => { + assetManager = fromPartial({ + manageAttribute: jest.fn(), + }); + replayer.assetManager = assetManager; + }); + + it('new properties are managed by asset manager if capturable', () => { + const tagName = 'IMG'; + const node = document.createElement(tagName); + const sn = Object.assign({}, elementSn, { tagName }); + mirror.add(node, sn); + + const rrDocument = new RRDocument(); + const rrNode = rrDocument.createElement(tagName); + const sn2 = Object.assign({}, elementSn, { tagName }); + rrDocument.mirror.add(rrNode, sn2); + + rrNode.attributes = { rr_captured_src: 'image.png', class: 'node' }; + diff(node, rrNode, replayer); + expect(assetManager.manageAttribute).toHaveBeenCalledWith( + node, + mirror.getId(node), + 'src', + 'image.png', + ); + }); + }); }); describe('diff children', () => { diff --git a/packages/rrdom/test/document.test.ts b/packages/rrdom/test/document.test.ts index ad3b02134e..91ccb1ece1 100644 --- a/packages/rrdom/test/document.test.ts +++ b/packages/rrdom/test/document.test.ts @@ -1,7 +1,7 @@ /** * @jest-environment jsdom */ -import { NodeType as RRNodeType } from 'rrweb-snapshot'; +import { NodeType as RRNodeType } from '@rrweb/types'; import { BaseRRDocumentImpl, BaseRRDocumentTypeImpl, diff --git a/packages/rrdom/test/utils.ts b/packages/rrdom/test/utils.ts index da616a64f5..852990f576 100644 --- a/packages/rrdom/test/utils.ts +++ b/packages/rrdom/test/utils.ts @@ -1,7 +1,6 @@ import * as rollup from 'rollup'; -import * as typescript from 'rollup-plugin-typescript2'; +import 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. @@ -11,9 +10,10 @@ export async function compileTSCode(inputFilePath: string) { input: inputFilePath, plugins: [ resolve() as unknown as rollup.Plugin, - _typescript({ + typescript({ tsconfigOverride: { compilerOptions: { module: 'ESNext' } }, clean: true, + cacheRoot: `./node_modules/.cache/rrdom-test/${Date.now()}/`, }) as unknown as rollup.Plugin, ], }); diff --git a/packages/rrdom/test/virtual-dom.test.ts b/packages/rrdom/test/virtual-dom.test.ts index b99aca5ae4..701ec1f207 100644 --- a/packages/rrdom/test/virtual-dom.test.ts +++ b/packages/rrdom/test/virtual-dom.test.ts @@ -5,17 +5,17 @@ import * as fs from 'fs'; import * as path from 'path'; import * as puppeteer from 'puppeteer'; import { JSDOM } from 'jsdom'; +import { Mirror } from 'rrweb-snapshot'; import { cdataNode, commentNode, documentNode, documentTypeNode, elementNode, - Mirror, NodeType, NodeType as RRNodeType, textNode, -} from 'rrweb-snapshot'; +} from '@rrweb/types'; import { buildFromDom, buildFromNode, diff --git a/packages/rrdom/tsconfig.json b/packages/rrdom/tsconfig.json index 450e56e151..4f95e4e374 100644 --- a/packages/rrdom/tsconfig.json +++ b/packages/rrdom/tsconfig.json @@ -3,10 +3,13 @@ "composite": true, "target": "ES6", "module": "commonjs", + "moduleResolution": "Node", "noImplicitAny": true, "strictNullChecks": true, "removeComments": true, "preserveConstEnums": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, "sourceMap": true, "rootDir": "src", "outDir": "build", diff --git a/packages/rrvideo/package.json b/packages/rrvideo/package.json index 4cbd92463d..fcc2288f0a 100644 --- a/packages/rrvideo/package.json +++ b/packages/rrvideo/package.json @@ -21,11 +21,11 @@ "license": "MIT", "devDependencies": { "@types/fs-extra": "11.0.1", - "@types/jest": "^27.4.1", + "@types/jest": "^29.5.11", "@types/minimist": "^1.2.1", "@types/node": "^18.15.11", - "jest": "^27.5.1", - "ts-jest": "^27.1.3", + "jest": "^29.7.0", + "ts-jest": "^29.1.1", "@rrweb/types": "^2.0.0-alpha.13" }, "dependencies": { diff --git a/packages/rrweb-snapshot/jest.config.js b/packages/rrweb-snapshot/jest.config.js index e6cdf180ee..da04b18de6 100644 --- a/packages/rrweb-snapshot/jest.config.js +++ b/packages/rrweb-snapshot/jest.config.js @@ -1,6 +1,16 @@ -/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ +/** @type {import('ts-jest/dist/types').JestConfigWithTsJest} */ export default { preset: 'ts-jest', testEnvironment: 'node', testMatch: ['**/**.test.ts'], + setupFiles: ['./jest.setup.ts'], + /** + * Keeps old (pre-jest 29) snapshot format + * its a bit ugly and harder to read than the new format, + * so we might want to remove this in its own PR + */ + snapshotFormat: { + escapeString: true, + printBasicPrototype: true, + }, }; diff --git a/packages/rrweb-snapshot/jest.setup.ts b/packages/rrweb-snapshot/jest.setup.ts new file mode 100644 index 0000000000..7c97c034f2 --- /dev/null +++ b/packages/rrweb-snapshot/jest.setup.ts @@ -0,0 +1,3 @@ +import { TextEncoder, TextDecoder } from 'util'; + +Object.assign(global, { TextDecoder, TextEncoder }); diff --git a/packages/rrweb-snapshot/package.json b/packages/rrweb-snapshot/package.json index 14eb0fe0d9..69451c02b3 100644 --- a/packages/rrweb-snapshot/package.json +++ b/packages/rrweb-snapshot/package.json @@ -11,8 +11,9 @@ "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", + "dev": "yarn bundle:es-only --watch & yarn typings -w", + "check-types": "tsc --noEmit", + "typings": "tsc -d --declarationDir typings --emitDeclarationOnly", "prepublish": "yarn typings && yarn bundle", "lint": "yarn eslint src" }, @@ -43,21 +44,24 @@ }, "homepage": "https://github.com/rrweb-io/rrweb/tree/master/packages/rrweb-snapshot#readme", "devDependencies": { + "@rrweb/types": "^2.0.0-alpha.11", "@types/chai": "^4.1.4", - "@types/jest": "^27.0.2", - "@types/jsdom": "^20.0.0", + "@types/jest": "^29.5.10", + "@types/jsdom": "^21.1.6", "@types/node": "^18.15.11", "@types/puppeteer": "^1.12.4", "cross-env": "^5.2.0", - "jest": "^27.2.4", + "jest": "^29.7.0", "jest-snapshot": "^23.6.0", - "jsdom": "^16.4.0", + "jest-environment-jsdom": "^29.7.0", + "jsdom": "^23.0.0", "puppeteer": "^17.1.3", - "rollup": "^2.45.2", + "rollup": "^2.47.0", "rollup-plugin-terser": "^7.0.2", - "rollup-plugin-typescript2": "^0.31.2", - "ts-jest": "^27.0.5", + "rollup-plugin-typescript2": "^0.36.0", + "ts-jest": "^29.1.1", "ts-node": "^7.0.1", - "tslib": "^1.9.3" + "tslib": "^2.5.3", + "typescript": "^4.7.3" } } diff --git a/packages/rrweb-snapshot/rollup.config.js b/packages/rrweb-snapshot/rollup.config.js index b13f447864..1e07a43575 100644 --- a/packages/rrweb-snapshot/rollup.config.js +++ b/packages/rrweb-snapshot/rollup.config.js @@ -1,4 +1,5 @@ import typescript from 'rollup-plugin-typescript2'; +import resolve from '@rollup/plugin-node-resolve'; import { terser } from 'rollup-plugin-terser'; import pkg from './package.json'; @@ -10,7 +11,7 @@ let configs = [ // ES module - for building rrweb { input: './src/index.ts', - plugins: [typescript()], + plugins: [resolve(), typescript()], output: [ { format: 'esm', @@ -23,7 +24,7 @@ let extra_configs = [ // browser { input: './src/index.ts', - plugins: [typescript()], + plugins: [resolve(), typescript()], output: [ { name: 'rrwebSnapshot', @@ -34,7 +35,7 @@ let extra_configs = [ }, { input: './src/index.ts', - plugins: [typescript(), terser()], + plugins: [resolve(), typescript(), terser()], output: [ { name: 'rrwebSnapshot', @@ -47,7 +48,7 @@ let extra_configs = [ // CommonJS { input: './src/index.ts', - plugins: [typescript()], + plugins: [resolve(), typescript()], output: [ { format: 'cjs', @@ -58,7 +59,7 @@ let extra_configs = [ // ES module (packed) { input: './src/index.ts', - plugins: [typescript(), terser()], + plugins: [resolve(), typescript(), terser()], output: [ { format: 'esm', diff --git a/packages/rrweb-snapshot/src/index.ts b/packages/rrweb-snapshot/src/index.ts index c9f91a9100..8e00cd4803 100644 --- a/packages/rrweb-snapshot/src/index.ts +++ b/packages/rrweb-snapshot/src/index.ts @@ -8,6 +8,7 @@ import snapshot, { classMatchesRegex, IGNORED_NODE, genId, + getSourcesFromSrcset, } from './snapshot'; import rebuild, { buildNodeWithSN, @@ -32,4 +33,5 @@ export { classMatchesRegex, IGNORED_NODE, genId, + getSourcesFromSrcset, }; diff --git a/packages/rrweb-snapshot/src/rebuild.ts b/packages/rrweb-snapshot/src/rebuild.ts index 7c6ed948e6..64cbc0919e 100644 --- a/packages/rrweb-snapshot/src/rebuild.ts +++ b/packages/rrweb-snapshot/src/rebuild.ts @@ -1,12 +1,12 @@ -import { Rule, Media, NodeWithRules, parse } from './css'; import { + RebuildAssetManagerInterface, serializedNodeWithId, - NodeType, - tagMap, elementNode, - BuildCache, legacyAttributes, -} from './types'; + NodeType, +} from '@rrweb/types'; +import { Rule, Media, NodeWithRules, parse } from './css'; +import { tagMap, BuildCache } from './types'; import { isElement, Mirror, isNodeMetaEqual } from './utils'; const tagMap: tagMap = { @@ -151,6 +151,7 @@ function buildNode( doc: Document; hackCss: boolean; cache: BuildCache; + assetManager?: RebuildAssetManagerInterface; }, ): Node | null { const { doc, hackCss, cache } = options; @@ -282,6 +283,7 @@ function buildNode( 'rrweb-original-srcset', n.attributes.srcset as string, ); + continue; } else { node.setAttribute(name, value.toString()); } @@ -292,6 +294,21 @@ function buildNode( for (const name in specialAttributes) { const value = specialAttributes[name]; + + if ( + name.startsWith('rr_captured_') && + value && + typeof value === 'string' + ) { + options.assetManager?.manageAttribute( + node, + n.id, + name.substring('rr_captured_'.length), + value, + ); + continue; + } + // handle internal attributes if (tagName === 'canvas' && name === 'rr_dataURL') { const image = document.createElement('img'); @@ -405,6 +422,7 @@ export function buildNodeWithSN( */ afterAppend?: (n: Node, id: number) => unknown; cache: BuildCache; + assetManager?: RebuildAssetManagerInterface; }, ): Node | null { const { @@ -414,6 +432,7 @@ export function buildNodeWithSN( hackCss = true, afterAppend, cache, + assetManager, } = options; /** * Add a check to see if the node is already in the mirror. If it is, we can skip the whole process. @@ -428,7 +447,7 @@ export function buildNodeWithSN( // 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 }); + let node = buildNode(n, { doc, hackCss, cache, assetManager }); if (!node) { return null; } @@ -480,6 +499,7 @@ export function buildNodeWithSN( hackCss, afterAppend, cache, + assetManager, }); if (!childNode) { console.warn('Failed to rebuild', childN); @@ -568,6 +588,7 @@ function rebuild( afterAppend?: (n: Node, id: number) => unknown; cache: BuildCache; mirror: Mirror; + assetManager?: RebuildAssetManagerInterface; }, ): Node | null { const { @@ -577,6 +598,7 @@ function rebuild( afterAppend, cache, mirror = new Mirror(), + assetManager, } = options; const node = buildNodeWithSN(n, { doc, @@ -585,6 +607,7 @@ function rebuild( hackCss, afterAppend, cache, + assetManager, }); visit(mirror, (visitedNode) => { if (onVisit) { diff --git a/packages/rrweb-snapshot/src/snapshot.ts b/packages/rrweb-snapshot/src/snapshot.ts index d323e1af8c..d0f93c9ea0 100644 --- a/packages/rrweb-snapshot/src/snapshot.ts +++ b/packages/rrweb-snapshot/src/snapshot.ts @@ -1,19 +1,23 @@ import { - serializedNode, - serializedNodeWithId, - NodeType, - attributes, MaskInputOptions, SlimDOMOptions, - DataURLOptions, MaskTextFn, MaskInputFn, KeepIframeSrcFn, ICanvas, - elementNode, serializedElementNodeWithId, type mediaAttributes, } from './types'; +import { + serializedNode, + serializedNodeWithId, + NodeType, + attributes, + elementNode, + asset, + DataURLOptions, + captureAssetsParam, +} from '@rrweb/types'; import { Mirror, is2DCanvasBlank, @@ -25,6 +29,8 @@ import { getInputType, toLowerCase, extractFileExtension, + isAttributeCapturable, + shouldIgnoreAsset, } from './utils'; let _id = 1; @@ -122,7 +128,11 @@ export function absoluteToStylesheet( const SRCSET_NOT_SPACES = /^[^ \t\n\r\u000c]+/; // Don't use \s, to avoid matching non-breaking space // eslint-disable-next-line no-control-regex const SRCSET_COMMAS_OR_SPACES = /^[, \t\n\r\u000c]+/; -function getAbsoluteSrcsetString(doc: Document, attributeValue: string) { +function parseSrcsetString( + doc: Document, + attributeValue: string, + urlCallback: (doc: Document, url: string) => string, +) { /* run absoluteToDoc over every url in the srcset @@ -159,13 +169,13 @@ function getAbsoluteSrcsetString(doc: Document, attributeValue: string) { let url = collectCharacters(SRCSET_NOT_SPACES); if (url.slice(-1) === ',') { // aside: according to spec more than one comma at the end is a parse error, but we ignore that - url = absoluteToDoc(doc, url.substring(0, url.length - 1)); + url = urlCallback(doc, url.substring(0, url.length - 1)); // the trailing comma splits the srcset, so the interpretion is that // another url will follow, and the descriptor is empty output.push(url); } else { let descriptorsStr = ''; - url = absoluteToDoc(doc, url); + url = urlCallback(doc, url); let inParens = false; // eslint-disable-next-line no-constant-condition while (true) { @@ -196,6 +206,21 @@ function getAbsoluteSrcsetString(doc: Document, attributeValue: string) { return output.join(', '); } +function getAbsoluteSrcsetString(doc: Document, attributeValue: string) { + return parseSrcsetString(doc, attributeValue, (doc, url) => + absoluteToDoc(doc, url), + ); +} + +export function getSourcesFromSrcset(attributeValue: string): string[] { + const urls = new Set(); + parseSrcsetString(document, attributeValue, (_, url) => { + urls.add(url); + return url; + }); + return Array.from(urls); +} + export function absoluteToDoc(doc: Document, attributeValue: string): string { if (!attributeValue || attributeValue.trim() === '') { return attributeValue; @@ -393,11 +418,6 @@ function onceIframeLoaded( iframeEl.addEventListener('load', listener); } -function isStylesheetLoaded(link: HTMLLinkElement) { - if (!link.getAttribute('href')) return true; // nothing to load - return link.sheet !== null; -} - function onceStylesheetLoaded( link: HTMLLinkElement, listener: () => unknown, @@ -440,13 +460,24 @@ function serializeNode( maskTextFn: MaskTextFn | undefined; maskInputFn: MaskInputFn | undefined; dataURLOptions?: DataURLOptions; - inlineImages: boolean; + /** + * @deprecated please use `captureAssets` instead + */ + inlineImages?: boolean; + captureAssets?: captureAssetsParam; recordCanvas: boolean; keepIframeSrcFn: KeepIframeSrcFn; /** * `newlyAddedElement: true` skips scrollTop and scrollLeft check */ newlyAddedElement?: boolean; + /** + * Called when an asset is detected. + * Example of assets: + * - `src` attribute in `img` tags. + * - `srcset` attribute in `img` tags. + */ + onAssetDetected?: (assets: asset[]) => unknown; }, ): serializedNode | false { const { @@ -461,9 +492,14 @@ function serializeNode( maskInputFn, dataURLOptions = {}, inlineImages, + captureAssets = { + objectURLs: true, + origins: false, + }, recordCanvas, keepIframeSrcFn, newlyAddedElement = false, + onAssetDetected, } = options; // Only record root id when document object is not the base document const rootId = getRootId(doc, mirror); @@ -499,10 +535,12 @@ function serializeNode( maskInputFn, dataURLOptions, inlineImages, + captureAssets, recordCanvas, keepIframeSrcFn, newlyAddedElement, rootId, + onAssetDetected, }); case n.TEXT_NODE: return serializeTextNode(n as Text, { @@ -596,7 +634,11 @@ function serializeElementNode( maskInputOptions: MaskInputOptions; maskInputFn: MaskInputFn | undefined; dataURLOptions?: DataURLOptions; - inlineImages: boolean; + /** + * @deprecated please use `captureAssets` instead + */ + inlineImages?: boolean; + captureAssets?: captureAssetsParam; recordCanvas: boolean; keepIframeSrcFn: KeepIframeSrcFn; /** @@ -604,6 +646,13 @@ function serializeElementNode( */ newlyAddedElement?: boolean; rootId: number | undefined; + /** + * Called when an asset is detected. + * Example of assets: + * - `src` attribute in `img` tags. + * - `srcset` attribute in `img` tags. + */ + onAssetDetected?: (assets: asset[]) => unknown; }, ): serializedNode | false { const { @@ -615,24 +664,47 @@ function serializeElementNode( maskInputFn, dataURLOptions = {}, inlineImages, + captureAssets = { + objectURLs: true, + origins: false, + }, recordCanvas, keepIframeSrcFn, newlyAddedElement = false, rootId, + onAssetDetected = false, } = options; const needBlock = _isBlockedElement(n, blockClass, blockSelector); const tagName = getValidTagName(n); let attributes: attributes = {}; + const assets: asset[] = []; const len = n.attributes.length; for (let i = 0; i < len; i++) { const attr = n.attributes[i]; if (!ignoreAttribute(tagName, attr.name, attr.value)) { - attributes[attr.name] = transformAttribute( + const value = transformAttribute( doc, tagName, toLowerCase(attr.name), attr.value, ); + let { name } = attr; + // save assets offline + if ( + value && + typeof value === 'string' && + onAssetDetected && + isAttributeCapturable(n, attr.name) && + !shouldIgnoreAsset(attr.value, captureAssets) + ) { + assets.push({ + element: n, + attr: attr.name, + value, + }); + name = `rr_captured_${name}`; + } + attributes[name] = value; } } // remote css @@ -728,7 +800,8 @@ function serializeElementNode( } } } - // save image offline + + // `inlineImages` is deprecated and will be removed in rrweb 3.x. if (tagName === 'img' && inlineImages) { if (!canvasService) { canvasService = doc.createElement('canvas'); @@ -795,7 +868,11 @@ function serializeElementNode( }; } // iframe - if (tagName === 'iframe' && !keepIframeSrcFn(attributes.src as string)) { + if ( + tagName === 'iframe' && + attributes.src && + !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 @@ -811,6 +888,10 @@ function serializeElementNode( // In case old browsers don't support customElements } + if (assets.length && onAssetDetected) { + onAssetDetected(assets); + } + return { type: NodeType.Element, tagName, @@ -944,7 +1025,11 @@ export function serializeNodeWithId( slimDOMOptions: SlimDOMOptions; dataURLOptions?: DataURLOptions; keepIframeSrcFn?: KeepIframeSrcFn; + /** + * @deprecated please use `captureAssets` instead + */ inlineImages?: boolean; + captureAssets?: captureAssetsParam; recordCanvas?: boolean; preserveWhiteSpace?: boolean; onSerialize?: (n: Node) => unknown; @@ -958,6 +1043,13 @@ export function serializeNodeWithId( node: serializedElementNodeWithId, ) => unknown; stylesheetLoadTimeout?: number; + /** + * Called when an asset is detected. + * Example of assets: + * - `src` attribute in `img` tags. + * - `srcset` attribute in `img` tags. + */ + onAssetDetected?: (assets: asset[]) => unknown; }, ): serializedNodeWithId | null { const { @@ -975,6 +1067,10 @@ export function serializeNodeWithId( slimDOMOptions, dataURLOptions = {}, inlineImages = false, + captureAssets = { + objectURLs: true, + origins: false, + }, recordCanvas = false, onSerialize, onIframeLoad, @@ -983,6 +1079,7 @@ export function serializeNodeWithId( stylesheetLoadTimeout = 5000, keepIframeSrcFn = () => false, newlyAddedElement = false, + onAssetDetected, } = options; let { needsMask } = options; let { preserveWhiteSpace = true } = options; @@ -1013,9 +1110,11 @@ export function serializeNodeWithId( maskInputFn, dataURLOptions, inlineImages, + captureAssets, recordCanvas, keepIframeSrcFn, newlyAddedElement, + onAssetDetected, }); if (!_serializedNode) { // TODO: dev only @@ -1088,6 +1187,7 @@ export function serializeNodeWithId( slimDOMOptions, dataURLOptions, inlineImages, + captureAssets, recordCanvas, preserveWhiteSpace, onSerialize, @@ -1096,6 +1196,7 @@ export function serializeNodeWithId( onStylesheetLoad, stylesheetLoadTimeout, keepIframeSrcFn, + onAssetDetected, }; if ( @@ -1104,6 +1205,13 @@ export function serializeNodeWithId( (serializedNode as elementNode).attributes.value !== undefined ) { // value parameter in DOM reflects the correct value, so ignore childNode + } else if ( + serializedNode.type === NodeType.Element && + serializedNode.tagName === 'iframe' && + (serializedNode as elementNode).attributes.rr_captured_src !== undefined + ) { + // even though we might be able to recurse into it (e.g. browser renders a document with an ) + // we will instead capture it using asset management } else { for (const childN of Array.from(n.childNodes)) { const serializedChildNode = serializeNodeWithId(childN, bypassOptions); @@ -1135,7 +1243,8 @@ export function serializeNodeWithId( if ( serializedNode.type === NodeType.Element && - serializedNode.tagName === 'iframe' + serializedNode.tagName === 'iframe' && + (serializedNode as elementNode).attributes.rr_captured_src === undefined ) { onceIframeLoaded( n as HTMLIFrameElement, @@ -1158,6 +1267,7 @@ export function serializeNodeWithId( slimDOMOptions, dataURLOptions, inlineImages, + captureAssets, recordCanvas, preserveWhiteSpace, onSerialize, @@ -1210,6 +1320,7 @@ export function serializeNodeWithId( slimDOMOptions, dataURLOptions, inlineImages, + captureAssets, recordCanvas, preserveWhiteSpace, onSerialize, @@ -1249,7 +1360,11 @@ function snapshot( maskInputFn?: MaskTextFn; slimDOM?: 'all' | boolean | SlimDOMOptions; dataURLOptions?: DataURLOptions; + /** + * @deprecated please use `captureAssets` instead + */ inlineImages?: boolean; + captureAssets?: captureAssetsParam; recordCanvas?: boolean; preserveWhiteSpace?: boolean; onSerialize?: (n: Node) => unknown; @@ -1264,6 +1379,13 @@ function snapshot( ) => unknown; stylesheetLoadTimeout?: number; keepIframeSrcFn?: KeepIframeSrcFn; + /** + * Called when an asset is detected. + * Example of assets: + * - `src` attribute in `img` tags. + * - `srcset` attribute in `img` tags. + */ + onAssetDetected?: (assets: asset[]) => unknown; }, ): serializedNodeWithId | null { const { @@ -1274,6 +1396,10 @@ function snapshot( maskTextSelector = null, inlineStylesheet = true, inlineImages = false, + captureAssets = { + objectURLs: true, + origins: false, + }, recordCanvas = false, maskAllInputs = false, maskTextFn, @@ -1286,6 +1412,7 @@ function snapshot( iframeLoadTimeout, onStylesheetLoad, stylesheetLoadTimeout, + onAssetDetected, keepIframeSrcFn = () => false, } = options || {}; const maskInputOptions: MaskInputOptions = @@ -1346,6 +1473,7 @@ function snapshot( slimDOMOptions, dataURLOptions, inlineImages, + captureAssets, recordCanvas, preserveWhiteSpace, onSerialize, @@ -1355,6 +1483,7 @@ function snapshot( stylesheetLoadTimeout, keepIframeSrcFn, newlyAddedElement: false, + onAssetDetected, }); } diff --git a/packages/rrweb-snapshot/src/types.ts b/packages/rrweb-snapshot/src/types.ts index 1abfe4d6c0..8e4caea835 100644 --- a/packages/rrweb-snapshot/src/types.ts +++ b/packages/rrweb-snapshot/src/types.ts @@ -1,77 +1,4 @@ -export enum NodeType { - Document, - DocumentType, - Element, - Text, - CDATA, - Comment, -} - -export type documentNode = { - type: NodeType.Document; - childNodes: serializedNodeWithId[]; - compatMode?: string; -}; - -export type documentTypeNode = { - type: NodeType.DocumentType; - name: string; - publicId: string; - systemId: string; -}; - -export type attributes = { - [key: string]: string | number | true | null; -}; -export type legacyAttributes = { - /** - * @deprecated old bug in rrweb was causing these to always be set - * @see https://github.com/rrweb-io/rrweb/pull/651 - */ - selected: false; -}; - -export type elementNode = { - type: NodeType.Element; - tagName: string; - attributes: attributes; - childNodes: serializedNodeWithId[]; - isSVG?: true; - needBlock?: boolean; - // This is a custom element or not. - isCustom?: true; -}; - -export type textNode = { - type: NodeType.Text; - textContent: string; - isStyle?: true; -}; - -export type cdataNode = { - type: NodeType.CDATA; - textContent: ''; -}; - -export type commentNode = { - type: NodeType.Comment; - textContent: string; -}; - -export type serializedNode = ( - | documentNode - | documentTypeNode - | elementNode - | textNode - | cdataNode - | commentNode -) & { - rootId?: number; - isShadowHost?: boolean; - isShadow?: boolean; -}; - -export type serializedNodeWithId = serializedNode & { id: number }; +import { serializedNodeWithId, NodeType } from '@rrweb/types'; export type serializedElementNodeWithId = Extract< serializedNodeWithId, @@ -103,7 +30,9 @@ export type mediaAttributes = { rr_mediaVolume?: number; }; -// @deprecated +/** + * @deprecated + */ export interface INode extends Node { __sn: serializedNodeWithId; } @@ -112,28 +41,6 @@ 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; @@ -171,11 +78,6 @@ export type SlimDOMOptions = Partial<{ headMetaVerification: boolean; }>; -export type DataURLOptions = Partial<{ - type: string; - quality: number; -}>; - export type MaskTextFn = (text: string, element: HTMLElement | null) => string; export type MaskInputFn = (text: string, element: HTMLElement) => string; diff --git a/packages/rrweb-snapshot/src/utils.ts b/packages/rrweb-snapshot/src/utils.ts index 5ccc9082ed..71bda1d5df 100644 --- a/packages/rrweb-snapshot/src/utils.ts +++ b/packages/rrweb-snapshot/src/utils.ts @@ -1,8 +1,6 @@ +import { MaskInputFn, MaskInputOptions, idNodeMap, nodeMetaMap } from './types'; + import { - idNodeMap, - MaskInputFn, - MaskInputOptions, - nodeMetaMap, IMirror, serializedNodeWithId, serializedNode, @@ -11,7 +9,8 @@ import { documentTypeNode, textNode, elementNode, -} from './types'; + captureAssetsParam, +} from '@rrweb/types'; export function isElement(n: Node): n is Element { return n.nodeType === n.ELEMENT_NODE; @@ -351,3 +350,87 @@ export function extractFileExtension( const match = url.pathname.match(regex); return match?.[1] ?? null; } + +export const CAPTURABLE_ELEMENT_ATTRIBUTE_COMBINATIONS = new Map([ + ['IMG', new Set(['src', 'srcset'])], + ['VIDEO', new Set(['src'])], + ['AUDIO', new Set(['src'])], + ['EMBED', new Set(['src'])], + ['SOURCE', new Set(['src'])], + ['TRACK', new Set(['src'])], + ['INPUT', new Set(['src'])], + ['OBJECT', new Set(['src'])], + ['BODY', new Set(['background'])], + ['TABLE', new Set(['background'])], + ['TD', new Set(['background'])], + ['TR', new Set(['background'])], + ['TH', new Set(['background'])], + ['TBODY', new Set(['background'])], + ['THEAD', new Set(['background'])], + ['image', new Set(['href'])], + ['feImage', new Set(['href'])], + ['cursor', new Set(['href'])], +]); + +export function isAttributeCapturable(n: Element, attribute: string): boolean { + if (n.nodeName === 'IFRAME' && attribute == 'src') { + const i = n as HTMLIFrameElement; + if (i.contentDocument && i.contentDocument.contentType) { + return ( + i.contentDocument.contentType.startsWith('image/') || + i.contentDocument.contentType == 'application/pdf' + ); + } else if (i.src) { + // fallback to checking filename + let ipath; + try { + ipath = new URL(i.src).pathname; + } catch (e) { + ipath = i.src.split('?')[0]; + } + return ( + ipath.endsWith('.pdf') || + ipath.endsWith('.jpeg') || + ipath.endsWith('.jpg') || + ipath.endsWith('.gif') || + ipath.endsWith('.png') || + ipath.endsWith('.webp') + ); + } + return false; + } + const acceptedAttributesSet = CAPTURABLE_ELEMENT_ATTRIBUTE_COMBINATIONS.get( + n.nodeName, + ); + if (!acceptedAttributesSet) { + return false; + } + return acceptedAttributesSet.has(attribute); +} + +export function shouldIgnoreAsset( + url: string, + config: captureAssetsParam, +): boolean { + const originsToIgnore = ['data:']; + const urlIsBlob = url.startsWith(`blob:${window.location.origin}/`); + + // Check if url is a blob and we should ignore blobs + if (urlIsBlob) return !config.objectURLs; + + // Check if url matches any ignorable origins + for (const origin of originsToIgnore) { + if (url.startsWith(origin)) return true; + } + + // Check the origins + const captureOrigins = config.origins; + if (typeof captureOrigins === 'boolean') { + return !captureOrigins; + } else if (Array.isArray(captureOrigins)) { + const urlOrigin = new URL(url).origin; + return !captureOrigins.includes(urlOrigin); + } + + return false; +} diff --git a/packages/rrweb-snapshot/test/__snapshots__/integration.test.ts.snap b/packages/rrweb-snapshot/test/__snapshots__/integration.test.ts.snap index f537fc7b29..39c8c49ee1 100644 --- a/packages/rrweb-snapshot/test/__snapshots__/integration.test.ts.snap +++ b/packages/rrweb-snapshot/test/__snapshots__/integration.test.ts.snap @@ -364,6 +364,12 @@ exports[`integration tests [html file]: picture-in-frame.html 1`] = ` " `; +exports[`integration tests [html file]: picture-with-inline-onload.html 1`] = ` +" + \\"This + " +`; + exports[`integration tests [html file]: preload.html 1`] = ` " diff --git a/packages/rrweb-snapshot/test/integration.test.ts b/packages/rrweb-snapshot/test/integration.test.ts index dcc6a3ec0b..989e48cf54 100644 --- a/packages/rrweb-snapshot/test/integration.test.ts +++ b/packages/rrweb-snapshot/test/integration.test.ts @@ -4,10 +4,12 @@ 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-typescript2'; -import * as assert from 'assert'; +import resolve from '@rollup/plugin-node-resolve'; +import typescript from 'rollup-plugin-typescript2'; +import assert from 'assert'; import { waitForRAF } from './utils'; +const _resolve = resolve as unknown as () => rollup.Plugin; const _typescript = typescript as unknown as () => rollup.Plugin; const htmlFolder = path.join(__dirname, 'html'); @@ -74,7 +76,7 @@ describe('integration tests', function (this: ISuite) { const bundle = await rollup.rollup({ input: path.resolve(__dirname, '../src/index.ts'), - plugins: [_typescript()], + plugins: [_resolve(), _typescript()], }); const { output: [{ code: _code }], @@ -262,7 +264,7 @@ iframe.contentDocument.querySelector('center').clientHeight assert(snapshot.includes('data:image/webp;base64,')); }); - it('correctly saves blob:images in iframes offline', async () => { + it('[deprecated] 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', { @@ -277,6 +279,10 @@ iframe.contentDocument.querySelector('center').clientHeight inlineStylesheet: false, onIframeLoad: function(iframe, sn) { window.snapshot = sn; + }, + captureAssets: { + origin: false, + objectURLs: false } })`); await waitForRAF(page); @@ -341,7 +347,7 @@ describe('iframe integration tests', function (this: ISuite) { const bundle = await rollup.rollup({ input: path.resolve(__dirname, '../src/index.ts'), - plugins: [_typescript()], + plugins: [_resolve(), _typescript()], }); const { output: [{ code: _code }], @@ -389,7 +395,7 @@ describe('shadow DOM integration tests', function (this: ISuite) { const bundle = await rollup.rollup({ input: path.resolve(__dirname, '../src/index.ts'), - plugins: [_typescript()], + plugins: [_resolve(), _typescript()], }); const { output: [{ code: _code }], diff --git a/packages/rrweb-snapshot/test/rebuild.test.ts b/packages/rrweb-snapshot/test/rebuild.test.ts index 097ff0989a..8287285596 100644 --- a/packages/rrweb-snapshot/test/rebuild.test.ts +++ b/packages/rrweb-snapshot/test/rebuild.test.ts @@ -8,7 +8,7 @@ import { buildNodeWithSN, createCache, } from '../src/rebuild'; -import { NodeType } from '../src/types'; +import { NodeType } from '@rrweb/types'; import { createMirror, Mirror } from '../src/utils'; function getDuration(hrtime: [number, number]) { diff --git a/packages/rrweb-snapshot/test/snapshot.test.ts b/packages/rrweb-snapshot/test/snapshot.test.ts index aa4bb428ee..f6f4a23c32 100644 --- a/packages/rrweb-snapshot/test/snapshot.test.ts +++ b/packages/rrweb-snapshot/test/snapshot.test.ts @@ -7,7 +7,7 @@ import { serializeNodeWithId, _isBlockedElement, } from '../src/snapshot'; -import { serializedNodeWithId, elementNode } from '../src/types'; +import { serializedNodeWithId, elementNode, asset } from '@rrweb/types'; import { Mirror } from '../src/utils'; describe('absolute url to stylesheet', () => { @@ -205,7 +205,7 @@ describe('scrollTop/scrollLeft', () => { }; it('should serialize scroll positions', () => { - const el = render(`
+ 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; @@ -257,3 +257,110 @@ describe('form', () => { expect(sel?.childNodes).toEqual([]); // shouldn't be stored in childNodes while in transit }); }); + +describe('onAssetDetected callback', () => { + const serializeNode = ( + node: Node, + onAssetDetected: (result: asset[]) => void, + ): 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, + inlineImages: false, + captureAssets: { + objectURLs: true, + origins: ['https://example.com'], + }, + onAssetDetected, + }); + }; + + const render = (html: string): HTMLDivElement => { + document.write(html); + return document.querySelector('div')!; + }; + + it('should detect `src` attribute in image', () => { + const el = render(`
+ +
`); + + const callback = jest.fn(); + serializeNode(el, callback); + expect(callback).toHaveBeenCalledWith([ + { + element: el.querySelector('img'), + attr: 'src', + value: 'https://example.com/image.png', + }, + ]); + }); + + it('should detect `set` attribute in image with ObjectURL', () => { + const el = render(`
+ +
`); + + const callback = jest.fn(); + serializeNode(el, callback); + expect(callback).toHaveBeenCalledWith([ + { + element: el.querySelector('img'), + attr: 'src', + value: 'blob:https://example.com/e81acc2b-f460-4aec-91b3-ce9732b837c4', + }, + ]); + }); + it('should detect `srcset` attribute in image', () => { + const el = render(`
+ +
`); + + // this used to trigger two calls, but now AssetManager is responsible for parsing the args + const callback = jest.fn(); + serializeNode(el, callback); + expect(callback).toHaveBeenCalledWith([ + { + element: el.querySelector('img'), + attr: 'srcset', + value: + 'https://example.com/images/team-photo.jpg, https://example.com/images/team-photo-retina.jpg 2x', + }, + ]); + }); + + it('should detect `src` attribute in two images', () => { + const el = render(`
+ + +
`); + + const callback = jest.fn(); + serializeNode(el, callback); + expect(callback).toBeCalledTimes(2); + expect(callback).toHaveBeenCalledWith([ + { + element: el.querySelectorAll('img')[0], + attr: 'src', + value: 'https://example.com/image.png', + }, + ]); + expect(callback).toHaveBeenCalledWith([ + { + element: el.querySelectorAll('img')[1], + attr: 'src', + value: 'https://example.com/image2.png', + }, + ]); + }); +}); diff --git a/packages/rrweb-snapshot/test/utils.test.ts b/packages/rrweb-snapshot/test/utils.test.ts index afbdda2f42..40e88a5132 100644 --- a/packages/rrweb-snapshot/test/utils.test.ts +++ b/packages/rrweb-snapshot/test/utils.test.ts @@ -1,9 +1,12 @@ /** * @jest-environment jsdom */ -import { NodeType, serializedNode } from '../src/types'; -import { extractFileExtension, isNodeMetaEqual } from '../src/utils'; -import { serializedNodeWithId } from 'rrweb-snapshot'; +import { + extractFileExtension, + isAttributeCapturable, + isNodeMetaEqual, +} from '../src/utils'; +import { NodeType, serializedNode, serializedNodeWithId } from '@rrweb/types'; describe('utils', () => { describe('isNodeMetaEqual()', () => { @@ -198,4 +201,49 @@ describe('utils', () => { expect(extension).toBe('js'); }); }); + + describe('isAttributeCapturable()', () => { + const validAttributeCombinations = [ + ['img', ['src', 'srcset']], + ['video', ['src']], + ['audio', ['src']], + ['embed', ['src']], + ['source', ['src']], + ['track', ['src']], + ['input', ['src']], + ['object', ['src']], + ] as const; + + const invalidAttributeCombinations = [ + ['img', ['href']], + ['script', ['href']], + ['link', ['src']], + ['video', ['href']], + ['audio', ['href']], + ['div', ['src']], + ['source', ['href']], + ['track', ['href']], + ['input', ['href']], + ['iframe', ['href']], + ['object', ['href']], + ] as const; + + validAttributeCombinations.forEach(([tagName, attributes]) => { + const element = document.createElement(tagName); + attributes.forEach((attribute) => { + it(`should correctly identify <${tagName} ${attribute}> as capturable`, () => { + expect(isAttributeCapturable(element, attribute)).toBe(true); + }); + }); + }); + + invalidAttributeCombinations.forEach(([tagName, attributes]) => { + const element = document.createElement(tagName); + attributes.forEach((attribute) => { + it(`should correctly identify <${tagName} ${attribute}> as NOT capturable`, () => { + expect(isAttributeCapturable(element, attribute)).toBe(false); + }); + }); + }); + }); }); diff --git a/packages/rrweb-snapshot/tsconfig.json b/packages/rrweb-snapshot/tsconfig.json index 58577aa6a9..000278dddc 100644 --- a/packages/rrweb-snapshot/tsconfig.json +++ b/packages/rrweb-snapshot/tsconfig.json @@ -4,6 +4,7 @@ "module": "ESNext", "target": "ES6", "moduleResolution": "Node", + "esModuleInterop": true, "noImplicitAny": true, "strictNullChecks": true, "removeComments": true, @@ -14,5 +15,9 @@ }, "exclude": ["test"], "include": ["src"], - "references": [] + "references": [ + { + "path": "../types" + } + ] } diff --git a/packages/rrweb/jest.config.js b/packages/rrweb/jest.config.js index 1d88a78e2b..34742a5fca 100644 --- a/packages/rrweb/jest.config.js +++ b/packages/rrweb/jest.config.js @@ -1,4 +1,4 @@ -/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ +/** @type {import('ts-jest/dist/types').JestConfigWithTsJest} */ export default { preset: 'ts-jest', testEnvironment: 'node', diff --git a/packages/rrweb/package.json b/packages/rrweb/package.json index 9fcf6bb5a6..ba85fd493c 100644 --- a/packages/rrweb/package.json +++ b/packages/rrweb/package.json @@ -5,7 +5,8 @@ "scripts": { "prepare": "npm run prepack", "prepack": "npm run bundle", - "retest": "jest --testPathIgnorePatterns test/benchmark", + "retest": "PUPPETEER_HEADLESS=true yarn retest:headful", + "retest:headful": "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", @@ -69,7 +70,7 @@ "jest-environment-jsdom": "^29.6.0", "jest-image-snapshot": "^6.2.0", "jest-snapshot": "^29.6.2", - "puppeteer": "^20.9.0", + "puppeteer": "^21.7.0", "rollup": "^2.68.0", "rollup-plugin-esbuild": "^4.9.1", "rollup-plugin-postcss": "^3.1.1", diff --git a/packages/rrweb/scripts/stream.js b/packages/rrweb/scripts/stream.js index 39693b1192..5f838908f1 100644 --- a/packages/rrweb/scripts/stream.js +++ b/packages/rrweb/scripts/stream.js @@ -66,7 +66,10 @@ async function injectRecording(frame) { recordCanvas: false, recordCrossOriginIframes: true, collectFonts: true, - inlineImages: true, + captureAssets: { + objectURLs: true, + origins: true, + }, }); })(); }, diff --git a/packages/rrweb/src/plugins/canvas-webrtc/record/index.ts b/packages/rrweb/src/plugins/canvas-webrtc/record/index.ts index 03aad10c9c..7074ac4056 100644 --- a/packages/rrweb/src/plugins/canvas-webrtc/record/index.ts +++ b/packages/rrweb/src/plugins/canvas-webrtc/record/index.ts @@ -1,6 +1,9 @@ -import type { Mirror } from 'rrweb-snapshot'; import SimplePeer from 'simple-peer-light'; -import type { RecordPlugin, ICrossOriginIframeMirror } from '@rrweb/types'; +import type { + RecordPlugin, + ICrossOriginIframeMirror, + IMirror, +} from '@rrweb/types'; import type { WebRTCDataChannel } from '../types'; export const PLUGIN_NAME = 'rrweb/canvas-webrtc@1'; @@ -25,7 +28,7 @@ export type CrossOriginIframeMessageEventContent = { export class RRWebPluginCanvasWebRTCRecord { private peer: SimplePeer.Instance | null = null; - private mirror: Mirror; + private mirror: IMirror; private crossOriginIframeMirror: ICrossOriginIframeMirror; private streamMap: Map = new Map(); private incomingStreams = new Set(); diff --git a/packages/rrweb/src/record/iframe-manager.ts b/packages/rrweb/src/record/iframe-manager.ts index 2d9205c0cb..c82a8e6563 100644 --- a/packages/rrweb/src/record/iframe-manager.ts +++ b/packages/rrweb/src/record/iframe-manager.ts @@ -1,9 +1,12 @@ -import type { Mirror, serializedNodeWithId } from 'rrweb-snapshot'; -import { genId, NodeType } from 'rrweb-snapshot'; +import type { Mirror } from 'rrweb-snapshot'; +import { genId } from 'rrweb-snapshot'; import type { CrossOriginIframeMessageEvent } from '../types'; import CrossOriginIframeMirror from './cross-origin-iframe-mirror'; -import { EventType, IncrementalSource } from '@rrweb/types'; -import type { +import { + EventType, + NodeType, + IncrementalSource, + serializedNodeWithId, eventWithTime, eventWithoutTime, mutationCallBack, diff --git a/packages/rrweb/src/record/index.ts b/packages/rrweb/src/record/index.ts index 7be978199d..2047a289aa 100644 --- a/packages/rrweb/src/record/index.ts +++ b/packages/rrweb/src/record/index.ts @@ -27,6 +27,7 @@ import { scrollCallback, canvasMutationParam, adoptedStyleSheetParam, + assetParam, } from '@rrweb/types'; import type { CrossOriginIframeMessageEventContent } from '../types'; import { IframeManager } from './iframe-manager'; @@ -39,11 +40,13 @@ import { registerErrorHandler, unregisterErrorHandler, } from './error-handler'; +import AssetManager from './observers/asset-manager'; let wrappedEmit!: (e: eventWithoutTime, isCheckout?: boolean) => void; let takeFullSnapshot!: (isCheckout?: boolean) => void; let canvasManager!: CanvasManager; +let assetManager!: AssetManager; let recording = false; const mirror = createMirror(); @@ -80,12 +83,22 @@ function record( userTriggeredOnInput = false, collectFonts = false, inlineImages = false, + captureAssets = { + objectURLs: true, + origins: false, + }, plugins, keepIframeSrcFn = () => false, ignoreCSSAttributes = new Set([]), errorHandler, } = options; + // handle deprecated `inlineImages` option + if (inlineImages) { + captureAssets.objectURLs = true; + captureAssets.origins = true; + } + registerErrorHandler(errorHandler); const inEmittingFrame = recordCrossOriginIframes @@ -258,6 +271,12 @@ function record( }, }); + const wrappedAssetEmit = (p: assetParam) => + wrappedEmit({ + type: EventType.Asset, + data: p, + }); + const wrappedAdoptedStyleSheetEmit = (a: adoptedStyleSheetParam) => wrappedEmit({ type: EventType.IncrementalSnapshot, @@ -306,6 +325,12 @@ function record( dataURLOptions, }); + assetManager = new AssetManager({ + mutationCb: wrappedAssetEmit, + win: window, + captureAssets, + }); + const shadowDomManager = new ShadowDomManager({ mutationCb: wrappedMutationEmit, scrollCb: wrappedScrollEmit, @@ -317,10 +342,10 @@ function record( inlineStylesheet, maskInputOptions, dataURLOptions, + captureAssets, maskTextFn, maskInputFn, recordCanvas, - inlineImages, sampling, slimDOMOptions, iframeManager, @@ -328,6 +353,7 @@ function record( canvasManager, keepIframeSrcFn, processedNodeManager, + assetManager, }, mirror, }); @@ -365,8 +391,8 @@ function record( maskTextFn, slimDOM: slimDOMOptions, dataURLOptions, + captureAssets, recordCanvas, - inlineImages, onSerialize: (n) => { if (isSerializedIframe(n, mirror)) { iframeManager.addIframe(n as HTMLIFrameElement); @@ -385,6 +411,11 @@ function record( onStylesheetLoad: (linkEl, childSn) => { stylesheetManager.attachLinkElement(linkEl, childSn); }, + onAssetDetected: (assets) => { + assets.forEach((asset) => { + assetManager.capture(asset); + }); + }, keepIframeSrcFn, }); @@ -513,7 +544,6 @@ function record( sampling, recordDOM, recordCanvas, - inlineImages, userTriggeredOnInput, collectFonts, doc, @@ -522,6 +552,7 @@ function record( keepIframeSrcFn, blockSelector, slimDOMOptions, + captureAssets, dataURLOptions, mirror, iframeManager, @@ -529,6 +560,7 @@ function record( shadowDomManager, processedNodeManager, canvasManager, + assetManager, ignoreCSSAttributes, plugins: plugins diff --git a/packages/rrweb/src/record/mutation.ts b/packages/rrweb/src/record/mutation.ts index f4267af340..6cfd0baa6b 100644 --- a/packages/rrweb/src/record/mutation.ts +++ b/packages/rrweb/src/record/mutation.ts @@ -180,7 +180,7 @@ export default class MutationBuffer { private maskInputFn: observerParam['maskInputFn']; private keepIframeSrcFn: observerParam['keepIframeSrcFn']; private recordCanvas: observerParam['recordCanvas']; - private inlineImages: observerParam['inlineImages']; + private captureAssets: observerParam['captureAssets']; private slimDOMOptions: observerParam['slimDOMOptions']; private dataURLOptions: observerParam['dataURLOptions']; private doc: observerParam['doc']; @@ -191,6 +191,7 @@ export default class MutationBuffer { private canvasManager: observerParam['canvasManager']; private processedNodeManager: observerParam['processedNodeManager']; private unattachedDoc: HTMLDocument; + private assetManager: observerParam['assetManager']; public init(options: MutationBufferParam) { ( @@ -205,8 +206,8 @@ export default class MutationBuffer { 'maskTextFn', 'maskInputFn', 'keepIframeSrcFn', + 'captureAssets', 'recordCanvas', - 'inlineImages', 'slimDOMOptions', 'dataURLOptions', 'doc', @@ -216,6 +217,7 @@ export default class MutationBuffer { 'shadowDomManager', 'canvasManager', 'processedNodeManager', + 'assetManager', ] as const ).forEach((key) => { // just a type trick, the runtime result is correct @@ -299,6 +301,7 @@ export default class MutationBuffer { if (parentId === -1 || nextId === -1) { return addList.addNode(n); } + const sn = serializeNodeWithId(n, { doc: this.doc, mirror: this.mirror, @@ -314,8 +317,8 @@ export default class MutationBuffer { maskInputFn: this.maskInputFn, slimDOMOptions: this.slimDOMOptions, dataURLOptions: this.dataURLOptions, + captureAssets: this.captureAssets, recordCanvas: this.recordCanvas, - inlineImages: this.inlineImages, onSerialize: (currentN) => { if (isSerializedIframe(currentN, this.mirror)) { this.iframeManager.addIframe(currentN as HTMLIFrameElement); @@ -336,6 +339,11 @@ export default class MutationBuffer { onStylesheetLoad: (link, childSn) => { this.stylesheetManager.attachLinkElement(link, childSn); }, + onAssetDetected: (assets) => { + assets.forEach((asset) => { + this.assetManager.capture(asset); + }); + }, }); if (sn) { adds.push({ @@ -619,12 +627,25 @@ export default class MutationBuffer { if (!ignoreAttribute(target.tagName, attributeName, value)) { // overwrite attribute if the mutations was triggered in same time - item.attributes[attributeName] = transformAttribute( + const transformedValue = transformAttribute( this.doc, toLowerCase(target.tagName), toLowerCase(attributeName), value, ); + if ( + transformedValue && + this.assetManager.isAttributeCapturable(target, attributeName) + ) { + this.assetManager.capture({ + element: target, + attr: attributeName, + value: transformedValue, + }); + attributeName = `rr_captured_${attributeName}`; + } + item.attributes[attributeName] = transformedValue; + if (attributeName === 'style') { if (!this.unattachedDoc) { try { diff --git a/packages/rrweb/src/record/observers/asset-manager.ts b/packages/rrweb/src/record/observers/asset-manager.ts new file mode 100644 index 0000000000..a6edb82d4f --- /dev/null +++ b/packages/rrweb/src/record/observers/asset-manager.ts @@ -0,0 +1,194 @@ +import type { + IWindow, + SerializedCanvasArg, + eventWithTime, + listenerHandler, + asset, +} from '@rrweb/types'; +import type { assetCallback } from '@rrweb/types'; +import { encode } from 'base64-arraybuffer'; + +import { patch } from '../../utils'; + +import type { recordOptions, assetStatus } from '../../types'; +import { + isAttributeCapturable, + getSourcesFromSrcset, + shouldIgnoreAsset, +} from 'rrweb-snapshot'; + +export default class AssetManager { + private urlObjectMap = new Map(); + private capturedURLs = new Set(); + private capturingURLs = new Set(); + private failedURLs = new Set(); + private resetHandlers: listenerHandler[] = []; + private mutationCb: assetCallback; + public readonly config: Exclude< + recordOptions['captureAssets'], + undefined + >; + + public reset() { + this.urlObjectMap.clear(); + this.capturedURLs.clear(); + this.capturingURLs.clear(); + this.failedURLs.clear(); + this.resetHandlers.forEach((h) => h()); + } + + constructor(options: { + mutationCb: assetCallback; + win: IWindow; + captureAssets: Exclude< + recordOptions['captureAssets'], + undefined + >; + }) { + const { win } = options; + + this.mutationCb = options.mutationCb; + this.config = options.captureAssets; + + const urlObjectMap = this.urlObjectMap; + + if (this.config.objectURLs) { + try { + const restoreHandler = patch( + win.URL, + 'createObjectURL', + function (original: (obj: File | Blob | MediaSource) => string) { + return function (obj: File | Blob | MediaSource) { + const url = original.apply(this, [obj]); + urlObjectMap.set(url, obj); + return url; + }; + }, + ); + this.resetHandlers.push(restoreHandler); + } catch { + console.error('failed to patch URL.createObjectURL'); + } + + try { + const restoreHandler = patch( + win.URL, + 'revokeObjectURL', + function (original: (objectURL: string) => void) { + return function (objectURL: string) { + urlObjectMap.delete(objectURL); + return original.apply(this, [objectURL]); + }; + }, + ); + this.resetHandlers.push(restoreHandler); + } catch { + console.error('failed to patch URL.revokeObjectURL'); + } + } + } + + public shouldIgnore(url: string): boolean { + return shouldIgnoreAsset(url, this.config); + } + + public async getURLObject( + url: string, + ): Promise { + const object = this.urlObjectMap.get(url); + if (object) { + return object; + } + + try { + const response = await fetch(url); + const blob = await response.blob(); + return blob; + } catch (e) { + console.warn(`getURLObject failed for ${url}`); + throw e; + } + } + + public capture(asset: asset): assetStatus | assetStatus[] { + if (asset.attr === 'srcset') { + const statuses: assetStatus[] = []; + getSourcesFromSrcset(asset.value).forEach((url) => { + statuses.push(this.captureUrl(url)); + }); + return statuses; + } else { + return this.captureUrl(asset.value); + } + } + + public captureUrl(url: string): assetStatus { + if (this.shouldIgnore(url)) { + console.warn(`snapshot.ts should know to ignore ${url}`); + return { status: 'refused' }; + } + + if (this.capturedURLs.has(url)) { + return { status: 'captured' }; + } else if (this.capturingURLs.has(url)) { + return { status: 'capturing' }; + } else if (this.failedURLs.has(url)) { + return { status: 'error' }; + } + this.capturingURLs.add(url); + void this.getURLObject(url) + .then(async (object) => { + if (object) { + let payload: SerializedCanvasArg; + if (object instanceof File || object instanceof Blob) { + const arrayBuffer = await object.arrayBuffer(); + const base64 = encode(arrayBuffer); // cpu intensive, probably good idea to move all of this to a webworker + + payload = { + rr_type: 'Blob', + type: object.type, + data: [ + { + rr_type: 'ArrayBuffer', + base64, // base64 + }, + ], + }; + + this.capturedURLs.add(url); + this.capturingURLs.delete(url); + + this.mutationCb({ + url, + payload, + }); + } + } + }) + .catch((e: unknown) => { + let message = ''; + if (e instanceof Error) { + message = e.message; + } else if (typeof e === 'string') { + message = e; + } else if (e && typeof e === 'object' && 'toString' in e) { + message = (e as { toString(): string }).toString(); + } + this.mutationCb({ + url, + failed: { + message, + }, + }); + + this.failedURLs.add(url); + this.capturingURLs.delete(url); + }); + + return { status: 'capturing' }; + } + + public isAttributeCapturable(n: Element, attribute: string): boolean { + return isAttributeCapturable(n, attribute); + } +} diff --git a/packages/rrweb/src/record/observers/canvas/canvas-manager.ts b/packages/rrweb/src/record/observers/canvas/canvas-manager.ts index f825877f69..6e367ed970 100644 --- a/packages/rrweb/src/record/observers/canvas/canvas-manager.ts +++ b/packages/rrweb/src/record/observers/canvas/canvas-manager.ts @@ -1,4 +1,4 @@ -import type { ICanvas, Mirror, DataURLOptions } from 'rrweb-snapshot'; +import type { ICanvas, Mirror } from 'rrweb-snapshot'; import type { blockClass, canvasManagerMutationCallback, @@ -8,6 +8,7 @@ import type { IWindow, listenerHandler, CanvasArg, + DataURLOptions, } from '@rrweb/types'; import { isBlocked } from '../../../utils'; import { CanvasContext } from '@rrweb/types'; diff --git a/packages/rrweb/src/record/stylesheet-manager.ts b/packages/rrweb/src/record/stylesheet-manager.ts index 6e0a8077b4..4e4e00221c 100644 --- a/packages/rrweb/src/record/stylesheet-manager.ts +++ b/packages/rrweb/src/record/stylesheet-manager.ts @@ -1,6 +1,7 @@ -import type { elementNode, serializedNodeWithId } from 'rrweb-snapshot'; import { stringifyRule } from 'rrweb-snapshot'; import type { + elementNode, + serializedNodeWithId, adoptedStyleSheetCallback, adoptedStyleSheetParam, attributeMutation, diff --git a/packages/rrweb/src/record/workers/image-bitmap-data-url-worker.ts b/packages/rrweb/src/record/workers/image-bitmap-data-url-worker.ts index 374edfe1b0..b5aed49634 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,6 +1,6 @@ import { encode } from 'base64-arraybuffer'; -import type { DataURLOptions } from 'rrweb-snapshot'; import type { + DataURLOptions, ImageBitmapDataURLWorkerParams, ImageBitmapDataURLWorkerResponse, } from '@rrweb/types'; diff --git a/packages/rrweb/src/replay/asset-manager/index.ts b/packages/rrweb/src/replay/asset-manager/index.ts new file mode 100644 index 0000000000..19a2ff6e57 --- /dev/null +++ b/packages/rrweb/src/replay/asset-manager/index.ts @@ -0,0 +1,227 @@ +import type { + RebuildAssetManagerFinalStatus, + RebuildAssetManagerInterface, + RebuildAssetManagerStatus, + assetEvent, + captureAssetsParam, +} from '@rrweb/types'; +import { deserializeArg } from '../canvas/deserialize-args'; +import { getSourcesFromSrcset } from 'rrweb-snapshot'; +import type { RRElement } from 'rrdom'; +import { updateSrcset } from './update-srcset'; + +export default class AssetManager implements RebuildAssetManagerInterface { + private originalToObjectURLMap: Map = new Map(); + private nodeIdAttributeHijackedMap: Map> = + new Map(); + private loadingURLs: Set = new Set(); + private failedURLs: Set = new Set(); + private callbackMap: Map< + string, + Array<(status: RebuildAssetManagerFinalStatus) => void> + > = new Map(); + private liveMode: boolean; + public allAdded: boolean; + + constructor({ liveMode }: { liveMode: boolean }) { + this.liveMode = liveMode; + this.allAdded = false; + } + + public async add(event: assetEvent) { + const { data } = event; + const { url, payload, failed } = { payload: false, failed: false, ...data }; + if (failed) { + this.failedURLs.add(url); + this.executeCallbacks(url, { status: 'failed' }); + return; + } + this.loadingURLs.add(url); + + // tracks if deserializing did anything, not really needed for AssetManager + const status = { + isUnchanged: true, + }; + + // TODO: extract the logic only needed for assets from deserializeArg + const result = (await deserializeArg(new Map(), null, status)(payload)) as + | Blob + | MediaSource; + + const objectURL = URL.createObjectURL(result); + this.originalToObjectURLMap.set(url, objectURL); + this.loadingURLs.delete(url); + this.executeCallbacks(url, { status: 'loaded', url: objectURL }); + } + + private executeCallbacks( + url: string, + status: RebuildAssetManagerFinalStatus, + ) { + const callbacks = this.callbackMap.get(url); + while (callbacks && callbacks.length > 0) { + const callback = callbacks.pop(); + if (!callback) { + break; + } + callback(status); + } + } + + // TODO: turn this into a true promise that throws if the asset fails to load + public async whenReady(url: string): Promise { + const currentStatus = this.get(url); + if ( + currentStatus.status === 'loaded' || + currentStatus.status === 'failed' + ) { + return currentStatus; + } else if ( + currentStatus.status === 'unknown' && + this.allAdded && + !this.liveMode + ) { + return { + status: 'failed', + }; + } + let resolve: (status: RebuildAssetManagerFinalStatus) => void; + const promise = new Promise((r) => { + resolve = r; + }); + if (!this.callbackMap.has(url)) { + this.callbackMap.set(url, []); + } + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.callbackMap.get(url)!.push(resolve!); + + return promise; + } + + public get(url: string): RebuildAssetManagerStatus { + const result = this.originalToObjectURLMap.get(url); + + if (result) { + return { + status: 'loaded', + url: result, + }; + } + + if (this.loadingURLs.has(url)) { + return { + status: 'loading', + }; + } + + if (this.failedURLs.has(url)) { + return { + status: 'failed', + }; + } + + return { + status: 'unknown', + }; + } + + public async manageAttribute( + node: RRElement | Element, + nodeId: number, + attribute: string, + newValue: string, + ): Promise { + const prevValue = node.getAttribute(attribute); + + const promises: Promise[] = []; + + if (attribute === 'srcset') { + const values = getSourcesFromSrcset(newValue); + let expectedValue: string | null = prevValue; + values.forEach((value) => { + promises.push( + this.whenReady(value).then((status) => { + const isLoaded = status.status === 'loaded'; + if (!isLoaded) { + if (!this.liveMode) { + // failed to load asset, revert to recorded value + node.setAttribute(attribute, newValue); + } + return; // failed to load asset + } + + const attributeUnchanged = + node.getAttribute(attribute) === expectedValue; + + if (!attributeUnchanged) return; // attribute was changed since we started loading the asset + if (!expectedValue) { + // before srcset has been set for the first time + expectedValue = newValue; + } + expectedValue = updateSrcset( + node, + value, + status.url, + expectedValue, + ); + }), + ); + }); + } else { + // In live mode we removes the attribute while it loads so it doesn't show the broken image icon + if (this.liveMode && nodeId > 0) { + let hijackedAttributes = this.nodeIdAttributeHijackedMap.get(nodeId); + if (!hijackedAttributes) { + hijackedAttributes = new Map(); + this.nodeIdAttributeHijackedMap.set(nodeId, hijackedAttributes); + } + hijackedAttributes.set(attribute, newValue); + if (node.tagName === 'IMG' && attribute === 'src') { + // special value to prevent a broken image icon while asset is being loaded + node.setAttribute('src', '//:0'); + } + } + promises.push( + this.whenReady(newValue).then((status) => { + const isLoaded = status.status === 'loaded'; + if (!isLoaded) { + if (!this.liveMode) { + // failed to load asset, revert to recorded value + node.setAttribute(attribute, newValue); + } + return; + } + + const attributeUnchanged = this.liveMode + ? newValue === + this.nodeIdAttributeHijackedMap.get(nodeId)?.get(attribute) + : node.getAttribute(attribute) === prevValue; + + if (!attributeUnchanged) return; // attribute was changed since we started loading the asset + + node.setAttribute(attribute, status.url); + }), + ); + } + + return Promise.all(promises); + } + + public reset(): void { + this.originalToObjectURLMap.forEach((objectURL) => { + URL.revokeObjectURL(objectURL); + }); + this.originalToObjectURLMap.clear(); + this.loadingURLs.clear(); + this.failedURLs.clear(); + this.nodeIdAttributeHijackedMap.clear(); + this.callbackMap.forEach((callbacks) => { + while (callbacks.length > 0) { + const cb = callbacks.pop(); + if (cb) cb({ status: 'reset' }); + } + }); + this.callbackMap.clear(); + this.allAdded = false; + } +} diff --git a/packages/rrweb/src/replay/asset-manager/update-srcset.ts b/packages/rrweb/src/replay/asset-manager/update-srcset.ts new file mode 100644 index 0000000000..6f39cedb85 --- /dev/null +++ b/packages/rrweb/src/replay/asset-manager/update-srcset.ts @@ -0,0 +1,26 @@ +import type { RRElement } from 'rrdom/es'; + +export function updateSrcset( + node: Element | RRElement, + urlToReplace: string, + newURL: string, + expectedValue?: string, +): string | null { + if (typeof expectedValue === 'undefined') { + let srcsetValue = node.getAttribute('srcset'); + if (!srcsetValue) return null; + expectedValue = srcsetValue; + } + + // from https://stackoverflow.com/a/6969486/543604 + const escapedUrlToReplace = urlToReplace.replace( + /[.*+?^${}()|[\]\\]/g, + '\\$&', + ); + const matcher = new RegExp(`(?<=^|[\\s,])${escapedUrlToReplace}(?=[\\s,]|$)`); + const newSrcset = expectedValue.replace(matcher, newURL); + + node.setAttribute('srcset', newSrcset); + + return newSrcset; +} diff --git a/packages/rrweb/src/replay/canvas/2d.ts b/packages/rrweb/src/replay/canvas/2d.ts index 93d852399d..2b95d0c35a 100644 --- a/packages/rrweb/src/replay/canvas/2d.ts +++ b/packages/rrweb/src/replay/canvas/2d.ts @@ -28,7 +28,7 @@ export default async function canvasMutation({ return Promise.all(mutation.args.map(deserializeArg(imageMap, ctx))); }, ); - const args = await Promise.all(mutationArgsPromises); + const args: unknown[][] = await Promise.all(mutationArgsPromises); // step 2 apply all mutations args.forEach((args, index) => { const mutation = mutations[index]; diff --git a/packages/rrweb/src/replay/canvas/deserialize-args.ts b/packages/rrweb/src/replay/canvas/deserialize-args.ts index a690d7986f..7f5d8f98be 100644 --- a/packages/rrweb/src/replay/canvas/deserialize-args.ts +++ b/packages/rrweb/src/replay/canvas/deserialize-args.ts @@ -31,6 +31,21 @@ export function isSerializedArg(arg: unknown): arg is SerializedCanvasArg { return Boolean(arg && typeof arg === 'object' && 'rr_type' in arg); } +type deserializeArgOutput = + | ImageBitmap + | ArrayBuffer + | HTMLImageElement + | Blob + | { + rr_type: string; + index: number; + } + | string + | number + | boolean + | null + | deserializeArgOutput[]; + export function deserializeArg( imageMap: Replayer['imageMap'], ctx: @@ -41,13 +56,17 @@ export function deserializeArg( preload?: { isUnchanged: boolean; }, -): (arg: CanvasArg) => Promise { - return async (arg: CanvasArg): Promise => { +): (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) { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const args = await deserializeArg(imageMap, ctx, preload)(arg.args); + const args = (await deserializeArg( + imageMap, + ctx, + preload, + )(arg.args)) as unknown as Parameters; // eslint-disable-next-line prefer-spread return await createImageBitmap.apply(null, args); } else if ('index' in arg) { @@ -79,9 +98,9 @@ export function deserializeArg( return image; } } else if ('data' in arg && arg.rr_type === 'Blob') { - const blobContents = await Promise.all( + const blobContents = (await Promise.all( arg.data.map(deserializeArg(imageMap, ctx, preload)), - ); + )) as BlobPart[]; const blob = new Blob(blobContents, { type: arg.type, }); diff --git a/packages/rrweb/src/replay/canvas/webgl.ts b/packages/rrweb/src/replay/canvas/webgl.ts index 85ff81f8d6..fec72b4b09 100644 --- a/packages/rrweb/src/replay/canvas/webgl.ts +++ b/packages/rrweb/src/replay/canvas/webgl.ts @@ -88,7 +88,7 @@ export default async function webglMutation({ const args = await Promise.all( mutation.args.map(deserializeArg(imageMap, ctx)), ); - const result = original.apply(ctx, args); + const result = original.apply(ctx, args as unknown[]); saveToWebGLVarMap(ctx, result); // Slows down replay considerably, only use for debugging @@ -96,21 +96,21 @@ export default async function webglMutation({ if (debugMode) { if (mutation.property === 'compileShader') { // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - if (!ctx.getShaderParameter(args[0], ctx.COMPILE_STATUS)) + if (!ctx.getShaderParameter(args[0] as WebGLShader, ctx.COMPILE_STATUS)) console.warn( 'something went wrong in replay', // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - ctx.getShaderInfoLog(args[0]), + ctx.getShaderInfoLog(args[0] as WebGLShader), ); } else if (mutation.property === 'linkProgram') { // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - ctx.validateProgram(args[0]); + ctx.validateProgram(args[0] as WebGLProgram); // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - if (!ctx.getProgramParameter(args[0], ctx.LINK_STATUS)) + if (!ctx.getProgramParameter(args[0] as WebGLProgram, ctx.LINK_STATUS)) console.warn( 'something went wrong in replay', // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - ctx.getProgramInfoLog(args[0]), + ctx.getProgramInfoLog(args[0] as WebGLProgram), ); } const webglError = ctx.getError(); diff --git a/packages/rrweb/src/replay/index.ts b/packages/rrweb/src/replay/index.ts index 89aa66d933..9daa27d72e 100644 --- a/packages/rrweb/src/replay/index.ts +++ b/packages/rrweb/src/replay/index.ts @@ -1,12 +1,10 @@ import { rebuild, buildNodeWithSN, - NodeType, BuildCache, createCache, Mirror, createMirror, - attributes, serializedElementNodeWithId, toLowerCase, } from 'rrweb-snapshot'; @@ -34,6 +32,8 @@ import { Timer } from './timer'; import { createPlayerService, createSpeedService } from './machine'; import type { playerConfig, missingNodeMap } from '../types'; import { + NodeType, + attributes, EventType, IncrementalSource, fullSnapshotEvent, @@ -81,6 +81,7 @@ import './styles/style.css'; import canvasMutation from './canvas'; import { deserializeArg } from './canvas/deserialize-args'; import { MediaManager } from './media'; +import AssetManager from './asset-manager'; const SKIP_TIME_INTERVAL = 5 * 1000; @@ -135,6 +136,9 @@ export class Replayer { private cache: BuildCache = createCache(); private imageMap: Map = new Map(); + + private assetManager: AssetManager; + private canvasEventMap: Map = new Map(); private mirror: Mirror = createMirror(); @@ -195,6 +199,7 @@ export class Replayer { logger: console, }; this.config = Object.assign({}, defaultConfig, config); + this.assetManager = new AssetManager({ liveMode: this.config.liveMode }); this.handleResize = this.handleResize.bind(this); this.getCastFn = this.getCastFn.bind(this); @@ -214,6 +219,7 @@ export class Replayer { if (this.usingVirtualDom) { const replayerHandler: ReplayerHandler = { mirror: this.mirror, + assetManager: this.assetManager, applyCanvas: ( canvasEvent: canvasEventWithTime, canvasMutationData: canvasMutationData, @@ -669,11 +675,12 @@ export class Replayer { }; break; case EventType.Meta: - castFn = () => + castFn = () => { this.emitter.emit(ReplayerEvents.Resize, { width: event.data.width, height: event.data.height, }); + }; break; case EventType.FullSnapshot: castFn = () => { @@ -737,6 +744,11 @@ export class Replayer { } }; break; + case EventType.Asset: + castFn = () => { + void this.assetManager.add(event); + }; + break; default: } const wrappedCastFn = () => { @@ -816,6 +828,8 @@ export class Replayer { } }; + void this.preloadAllAssets(event.timestamp, this.config.liveMode); + /** * Normally rebuilding full snapshot should not be under virtual dom environment. * But if the order of data events has some issues, it might be possible. @@ -832,6 +846,7 @@ export class Replayer { afterAppend, cache: this.cache, mirror: this.mirror, + assetManager: this.assetManager, }); afterAppend(this.iframe.contentDocument, event.data.node.id); @@ -938,6 +953,7 @@ export class Replayer { skipChild: false, afterAppend, cache: this.cache, + assetManager: this.assetManager, }); afterAppend(iframeEl.contentDocument! as Document, mutation.node.id); @@ -1022,16 +1038,31 @@ export class Replayer { } } + /** + * Process all asset events and preload them + */ + private async preloadAllAssets( + timestamp: number, + liveMode: boolean, + ): Promise { + const promises: Promise[] = []; + for (const event of this.service.state.context.events) { + if (event.timestamp <= timestamp) continue; + if (event.type === EventType.Meta && event.timestamp !== timestamp) break; + if (event.type === EventType.Asset) { + promises.push(this.assetManager.add(event)); + } + } + if (!liveMode) { + this.assetManager.allAdded = true; + } + return Promise.all(promises); + } + /** * pause when there are some canvas drawImage args need to be loaded */ 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 ( @@ -1546,6 +1577,7 @@ export class Replayer { skipChild: true, hackCss: true, cache: this.cache, + assetManager: this.assetManager, /** * 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 @@ -1760,6 +1792,7 @@ export class Replayer { skipChild: true, hackCss: true, cache: this.cache, + assetManager: this.assetManager, }); const siblingNode = target.nextSibling; const parentNode = target.parentNode; @@ -1776,6 +1809,7 @@ export class Replayer { // for safe } } + const targetEl = target as Element | RRElement; if (attributeName === 'value' && target.nodeName === 'TEXTAREA') { // this may or may not have an effect on the value property (which is what is displayed) // depending on whether the textarea has been modified by the user yet @@ -1788,11 +1822,15 @@ export class Replayer { if (tn) { textarea.appendChild(tn as TNode); } - } else { - (target as Element | RRElement).setAttribute( - attributeName, + } else if (attributeName.startsWith('rr_captured_') && value) { + void this.assetManager.manageAttribute( + targetEl, + mutation.id, + attributeName.substring('rr_captured_'.length), value, ); + } else { + targetEl.setAttribute(attributeName, value); } } catch (error) { this.warn( diff --git a/packages/rrweb/src/types.ts b/packages/rrweb/src/types.ts index 10ffb62b43..336f69f9fa 100644 --- a/packages/rrweb/src/types.ts +++ b/packages/rrweb/src/types.ts @@ -4,7 +4,6 @@ import type { SlimDOMOptions, MaskInputFn, MaskTextFn, - DataURLOptions, } from 'rrweb-snapshot'; import type { PackFn, UnpackFn } from './packer/base'; import type { IframeManager } from './record/iframe-manager'; @@ -14,6 +13,7 @@ import type { RRNode } from 'rrdom'; import type { CanvasManager } from './record/observers/canvas/canvas-manager'; import type { StylesheetManager } from './record/stylesheet-manager'; import type { + DataURLOptions, addedNodeMutation, blockClass, canvasMutationCallback, @@ -37,8 +37,10 @@ import type { styleDeclarationCallback, styleSheetRuleCallback, viewportResizeCallback, + captureAssetsParam, } from '@rrweb/types'; import type ProcessedNodeManager from './record/processed-node-manager'; +import type AssetManager from './record/observers/asset-manager'; export type recordOptions = { emit?: (e: T, isCheckout?: boolean) => void; @@ -67,7 +69,11 @@ export type recordOptions = { recordAfter?: 'DOMContentLoaded' | 'load'; userTriggeredOnInput?: boolean; collectFonts?: boolean; + /** + * @deprecated please use `captureAssets` instead + */ inlineImages?: boolean; + captureAssets?: captureAssetsParam; plugins?: RecordPlugin[]; // departed, please use sampling options mousemoveWait?: number; @@ -102,8 +108,8 @@ export type observerParam = { fontCb: fontCallback; sampling: SamplingStrategy; recordDOM: boolean; + captureAssets: captureAssetsParam; recordCanvas: boolean; - inlineImages: boolean; userTriggeredOnInput: boolean; collectFonts: boolean; slimDOMOptions: SlimDOMOptions; @@ -115,6 +121,7 @@ export type observerParam = { shadowDomManager: ShadowDomManager; canvasManager: CanvasManager; processedNodeManager: ProcessedNodeManager; + assetManager: AssetManager; ignoreCSSAttributes: Set; plugins: Array<{ observer: ( @@ -139,8 +146,8 @@ export type MutationBufferParam = Pick< | 'maskTextFn' | 'maskInputFn' | 'keepIframeSrcFn' + | 'captureAssets' | 'recordCanvas' - | 'inlineImages' | 'slimDOMOptions' | 'dataURLOptions' | 'doc' @@ -150,6 +157,7 @@ export type MutationBufferParam = Pick< | 'shadowDomManager' | 'canvasManager' | 'processedNodeManager' + | 'assetManager' >; export type ReplayPlugin = { @@ -221,3 +229,7 @@ export type CrossOriginIframeMessageEvent = MessageEvent; export type ErrorHandler = (error: unknown) => void | boolean; + +export type assetStatus = { + status: 'capturing' | 'captured' | 'error' | 'refused'; +}; diff --git a/packages/rrweb/src/utils.ts b/packages/rrweb/src/utils.ts index f426689d2f..7f74c4cc06 100644 --- a/packages/rrweb/src/utils.ts +++ b/packages/rrweb/src/utils.ts @@ -8,8 +8,9 @@ import type { IWindow, DeprecatedMirror, textMutation, + IMirror, } from '@rrweb/types'; -import type { IMirror, Mirror } from 'rrweb-snapshot'; +import type { Mirror } from 'rrweb-snapshot'; import { isShadowRoot, IGNORED_NODE, classMatchesRegex } from 'rrweb-snapshot'; import type { RRNode, RRIFrameElement } from 'rrdom'; diff --git a/packages/rrweb/test/__snapshots__/integration.test.ts.snap b/packages/rrweb/test/__snapshots__/integration.test.ts.snap index ae1aa7bf54..725bf4bc14 100644 --- a/packages/rrweb/test/__snapshots__/integration.test.ts.snap +++ b/packages/rrweb/test/__snapshots__/integration.test.ts.snap @@ -1,5 +1,1050 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`record integration tests [DEPRECATED] should record images inside iframe with blob url 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 with image\\", + \\"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\\": \\"iframe\\", + \\"attributes\\": { + \\"id\\": \\"four\\", + \\"frameborder\\": \\"0\\" + }, + \\"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, + \\"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\\": \\"meta\\", + \\"attributes\\": { + \\"charset\\": \\"UTF-8\\" + }, + \\"childNodes\\": [], + \\"rootId\\": 21, + \\"id\\": 26 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 21, + \\"id\\": 27 + }, + { + \\"type\\": 2, + \\"tagName\\": \\"meta\\", + \\"attributes\\": { + \\"http-equiv\\": \\"X-UA-Compatible\\", + \\"content\\": \\"IE=edge\\" + }, + \\"childNodes\\": [], + \\"rootId\\": 21, + \\"id\\": 28 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\", + \\"rootId\\": 21, + \\"id\\": 29 + }, + { + \\"type\\": 2, + \\"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\\": \\"Image with blob:url\\", + \\"rootId\\": 21, + \\"id\\": 33 + } + ], + \\"rootId\\": 21, + \\"id\\": 32 + }, + { + \\"type\\": 3, + \\"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\\": \\"script\\", + \\"attributes\\": {}, + \\"childNodes\\": [ + { + \\"type\\": 3, + \\"textContent\\": \\"SCRIPT_PLACEHOLDER\\", + \\"rootId\\": 21, + \\"id\\": 39 + } + ], + \\"rootId\\": 21, + \\"id\\": 38 + }, + { + \\"type\\": 3, + \\"textContent\\": \\"\\\\n \\\\n\\\\n\\", + \\"rootId\\": 21, + \\"id\\": 40 + } + ], + \\"rootId\\": 21, + \\"id\\": 36 + } + ], + \\"rootId\\": 21, + \\"id\\": 23 + } + ], + \\"id\\": 21 + } + } + ], + \\"removes\\": [], + \\"texts\\": [], + \\"attributes\\": [], + \\"isAttachIframe\\": true + } + }, + { + \\"type\\": 3, + \\"data\\": { + \\"source\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [], + \\"adds\\": [ + { + \\"parentId\\": 36, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"img\\", + \\"attributes\\": { + \\"rr_captured_src\\": \\"blob:http://localhost:xxxx/...\\" + }, + \\"childNodes\\": [], + \\"rootId\\": 21, + \\"id\\": 41 + } + } + ] + } + }, + { + \\"type\\": 7, + \\"data\\": { + \\"url\\": \\"blob:http://localhost:3030/...\\", + \\"payload\\": { + \\"rr_type\\": \\"Blob\\", + \\"type\\": \\"text/plain\\", + \\"data\\": [ + { + \\"rr_type\\": \\"ArrayBuffer\\", + \\"base64\\": \\"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\\" + } + ] + } + } + } +]" +`; + +exports[`record integration tests [DEPRECATED] should record images inside iframe with blob url after iframe was reloaded 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\\": 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\\": 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\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [], + \\"adds\\": [ + { + \\"parentId\\": 42, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"img\\", + \\"attributes\\": { + \\"rr_captured_src\\": \\"blob:http://localhost:xxxx/...\\" + }, + \\"childNodes\\": [], + \\"rootId\\": 27, + \\"id\\": 47 + } + } + ] + } + }, + { + \\"type\\": 7, + \\"data\\": { + \\"url\\": \\"blob:http://localhost:3030/...\\", + \\"payload\\": { + \\"rr_type\\": \\"Blob\\", + \\"type\\": \\"text/plain\\", + \\"data\\": [ + { + \\"rr_type\\": \\"ArrayBuffer\\", + \\"base64\\": \\"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\\" + } + ] + } + } + } +]" +`; + +exports[`record integration tests [DEPRECATED] should record images with blob url 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\\": { + \\"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\\": 0, + \\"texts\\": [], + \\"attributes\\": [], + \\"removes\\": [], + \\"adds\\": [ + { + \\"parentId\\": 16, + \\"nextId\\": null, + \\"node\\": { + \\"type\\": 2, + \\"tagName\\": \\"img\\", + \\"attributes\\": { + \\"rr_captured_src\\": \\"blob:http://localhost:xxxx/...\\" + }, + \\"childNodes\\": [], + \\"id\\": 24 + } + } + ] + } + }, + { + \\"type\\": 7, + \\"data\\": { + \\"url\\": \\"blob:http://localhost:3030/...\\", + \\"payload\\": { + \\"rr_type\\": \\"Blob\\", + \\"type\\": \\"text/plain\\", + \\"data\\": [ + { + \\"rr_type\\": \\"ArrayBuffer\\", + \\"base64\\": \\"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\\" + } + ] + } + } + } +]" +`; + exports[`record integration tests can correctly serialize a shader and multiple webgl contexts 1`] = ` "[ { @@ -9741,6 +10786,7 @@ exports[`record integration tests should not record input values if dynamically \\"tagName\\": \\"input\\", \\"attributes\\": { \\"id\\": \\"input\\", + \\"size\\": \\"50\\", \\"value\\": \\"**********************\\" }, \\"childNodes\\": [], @@ -12762,8 +13808,7 @@ exports[`record integration tests should record images inside iframe with blob u \\"type\\": 2, \\"tagName\\": \\"img\\", \\"attributes\\": { - \\"src\\": \\"blob:http://localhost:xxxx/...\\", - \\"rr_dataURL\\": \\"data:image/png;base64,...\\" + \\"rr_captured_src\\": \\"blob:http://localhost:xxxx/...\\" }, \\"childNodes\\": [], \\"rootId\\": 21, @@ -12774,37 +13819,19 @@ exports[`record integration tests should record images inside iframe with blob u } }, { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 0, - \\"texts\\": [], - \\"attributes\\": [ - { - \\"id\\": 41, - \\"attributes\\": { - \\"crossorigin\\": \\"anonymous\\" - } - } - ], - \\"removes\\": [], - \\"adds\\": [] - } - }, - { - \\"type\\": 3, + \\"type\\": 7, \\"data\\": { - \\"source\\": 0, - \\"texts\\": [], - \\"attributes\\": [ - { - \\"id\\": 41, - \\"attributes\\": { - \\"crossorigin\\": null + \\"url\\": \\"blob:http://localhost:3030/...\\", + \\"payload\\": { + \\"rr_type\\": \\"Blob\\", + \\"type\\": \\"text/plain\\", + \\"data\\": [ + { + \\"rr_type\\": \\"ArrayBuffer\\", + \\"base64\\": \\"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\\" } - } - ], - \\"removes\\": [], - \\"adds\\": [] + ] + } } } ]" @@ -13230,8 +14257,7 @@ exports[`record integration tests should record images inside iframe with blob u \\"type\\": 2, \\"tagName\\": \\"img\\", \\"attributes\\": { - \\"src\\": \\"blob:http://localhost:xxxx/...\\", - \\"rr_dataURL\\": \\"data:image/png;base64,...\\" + \\"rr_captured_src\\": \\"blob:http://localhost:xxxx/...\\" }, \\"childNodes\\": [], \\"rootId\\": 27, @@ -13242,37 +14268,19 @@ exports[`record integration tests should record images inside iframe with blob u } }, { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 0, - \\"texts\\": [], - \\"attributes\\": [ - { - \\"id\\": 47, - \\"attributes\\": { - \\"crossorigin\\": \\"anonymous\\" - } - } - ], - \\"removes\\": [], - \\"adds\\": [] - } - }, - { - \\"type\\": 3, + \\"type\\": 7, \\"data\\": { - \\"source\\": 0, - \\"texts\\": [], - \\"attributes\\": [ - { - \\"id\\": 47, - \\"attributes\\": { - \\"crossorigin\\": null + \\"url\\": \\"blob:http://localhost:3030/...\\", + \\"payload\\": { + \\"rr_type\\": \\"Blob\\", + \\"type\\": \\"text/plain\\", + \\"data\\": [ + { + \\"rr_type\\": \\"ArrayBuffer\\", + \\"base64\\": \\"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\\" } - } - ], - \\"removes\\": [], - \\"adds\\": [] + ] + } } } ]" @@ -13472,8 +14480,7 @@ exports[`record integration tests should record images with blob url 1`] = ` \\"type\\": 2, \\"tagName\\": \\"img\\", \\"attributes\\": { - \\"src\\": \\"blob:http://localhost:xxxx/...\\", - \\"rr_dataURL\\": \\"data:image/png;base64,...\\" + \\"rr_captured_src\\": \\"blob:http://localhost:xxxx/...\\" }, \\"childNodes\\": [], \\"id\\": 24 @@ -13483,37 +14490,19 @@ exports[`record integration tests should record images with blob url 1`] = ` } }, { - \\"type\\": 3, - \\"data\\": { - \\"source\\": 0, - \\"texts\\": [], - \\"attributes\\": [ - { - \\"id\\": 24, - \\"attributes\\": { - \\"crossorigin\\": \\"anonymous\\" - } - } - ], - \\"removes\\": [], - \\"adds\\": [] - } - }, - { - \\"type\\": 3, + \\"type\\": 7, \\"data\\": { - \\"source\\": 0, - \\"texts\\": [], - \\"attributes\\": [ - { - \\"id\\": 24, - \\"attributes\\": { - \\"crossorigin\\": null + \\"url\\": \\"blob:http://localhost:3030/...\\", + \\"payload\\": { + \\"rr_type\\": \\"Blob\\", + \\"type\\": \\"text/plain\\", + \\"data\\": [ + { + \\"rr_type\\": \\"ArrayBuffer\\", + \\"base64\\": \\"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\\" } - } - ], - \\"removes\\": [], - \\"adds\\": [] + ] + } } } ]" diff --git a/packages/rrweb/test/events/assets-mutation.ts b/packages/rrweb/test/events/assets-mutation.ts new file mode 100644 index 0000000000..db9eabafee --- /dev/null +++ b/packages/rrweb/test/events/assets-mutation.ts @@ -0,0 +1,141 @@ +import { EventType, IncrementalSource, type eventWithTime } from '@rrweb/types'; + +const events: eventWithTime[] = [ + { + type: 4, + data: { + href: '', + width: 1600, + height: 900, + }, + timestamp: 1636379531385, + }, + { + 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: 'assets', 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: 'img', + attributes: { + width: '100', + height: '100', + 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\n', id: 21 }, + ], + id: 14, + }, + ], + id: 3, + }, + ], + id: 1, + }, + initialOffset: { left: 0, top: 0 }, + }, + timestamp: 1636379531389, + }, + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Mutation, + texts: [], + attributes: [ + { + id: 16, + attributes: { + rr_captured_src: 'ftp://example.com/image.png', + }, + }, + ], + removes: [], + adds: [], + }, + timestamp: 1636379531390, + }, + { + type: EventType.Asset, + data: { + url: 'ftp://example.com/image.png', + payload: { + rr_type: 'Blob', + type: 'image/png', + data: [ + { + rr_type: 'ArrayBuffer', + base64: + 'iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAAAXNSR0IArs4c6QAAAWtJREFUeF7t1cEJAEAIxEDtv2gProo8xgpCwuLezI3LGFhBMi0+iCCtHoLEeggiSM1AjMcPESRmIIZjIYLEDMRwLESQmIEYjoUIEjMQw7EQQWIGYjgWIkjMQAzHQgSJGYjhWIggMQMxHAsRJGYghmMhgsQMxHAsRJCYgRiOhQgSMxDDsRBBYgZiOBYiSMxADMdCBIkZiOFYiCAxAzEcCxEkZiCGYyGCxAzEcCxEkJiBGI6FCBIzEMOxEEFiBmI4FiJIzEAMx0IEiRmI4ViIIDEDMRwLESRmIIZjIYLEDMRwLESQmIEYjoUIEjMQw7EQQWIGYjgWIkjMQAzHQgSJGYjhWIggMQMxHAsRJGYghmMhgsQMxHAsRJCYgRiOhQgSMxDDsRBBYgZiOBYiSMxADMdCBIkZiOFYiCAxAzEcCxEkZiCGYyGCxAzEcCxEkJiBGI6FCBIzEMOxEEFiBmI4FiJIzEAMx0IEiRmI4TwVjsedWCiXGAAAAABJRU5ErkJggg==', // base64 + }, + ], + }, + }, + timestamp: 1636379531391, + }, +]; + +export default events; diff --git a/packages/rrweb/test/events/assets-src-changed-before-asset-loaded.ts b/packages/rrweb/test/events/assets-src-changed-before-asset-loaded.ts new file mode 100644 index 0000000000..f4e7c90796 --- /dev/null +++ b/packages/rrweb/test/events/assets-src-changed-before-asset-loaded.ts @@ -0,0 +1,164 @@ +import { EventType, IncrementalSource, type eventWithTime } from '@rrweb/types'; +import { readFileSync } from 'fs'; + +const events: eventWithTime[] = [ + { + type: 4, + data: { + href: '', + width: 1600, + height: 900, + }, + timestamp: 100000000, + }, + { + 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: 'assets', 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: 'img', + attributes: { + width: '100', + height: '100', + style: 'border: 1px solid #000000', + rr_captured_src: 'ftp://example.com/red.png', + }, + 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\n', id: 21 }, + ], + id: 14, + }, + ], + id: 3, + }, + ], + id: 1, + }, + initialOffset: { left: 0, top: 0 }, + }, + timestamp: 100000010, + }, + // 2 change to robot.png + { + type: EventType.IncrementalSnapshot, + data: { + source: IncrementalSource.Mutation, + texts: [], + attributes: [ + { + id: 16, + attributes: { + rr_captured_src: 'ftp://example.com/robot.png', + }, + }, + ], + removes: [], + adds: [], + }, + timestamp: 100000020, + }, + // 3 + { + type: EventType.Asset, + data: { + url: 'ftp://example.com/red.png', + payload: { + rr_type: 'Blob', + type: 'image/png', + data: [ + { + rr_type: 'ArrayBuffer', + base64: + 'iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAAAXNSR0IArs4c6QAAAWtJREFUeF7t1cEJAEAIxEDtv2gProo8xgpCwuLezI3LGFhBMi0+iCCtHoLEeggiSM1AjMcPESRmIIZjIYLEDMRwLESQmIEYjoUIEjMQw7EQQWIGYjgWIkjMQAzHQgSJGYjhWIggMQMxHAsRJGYghmMhgsQMxHAsRJCYgRiOhQgSMxDDsRBBYgZiOBYiSMxADMdCBIkZiOFYiCAxAzEcCxEkZiCGYyGCxAzEcCxEkJiBGI6FCBIzEMOxEEFiBmI4FiJIzEAMx0IEiRmI4ViIIDEDMRwLESRmIIZjIYLEDMRwLESQmIEYjoUIEjMQw7EQQWIGYjgWIkjMQAzHQgSJGYjhWIggMQMxHAsRJGYghmMhgsQMxHAsRJCYgRiOhQgSMxDDsRBBYgZiOBYiSMxADMdCBIkZiOFYiCAxAzEcCxEkZiCGYyGCxAzEcCxEkJiBGI6FCBIzEMOxEEFiBmI4FiJIzEAMx0IEiRmI4TwVjsedWCiXGAAAAABJRU5ErkJggg==', // base64 + }, + ], + }, + }, + timestamp: 100000030, + }, + { + type: EventType.Asset, + data: { + url: 'ftp://example.com/robot.png', + payload: { + rr_type: 'Blob', + type: 'image/png', + data: [ + { + rr_type: 'ArrayBuffer', + base64: readFileSync('test/html/assets/robot.png').toString( + 'base64', + ), + }, + ], + }, + }, + timestamp: 100000040, + }, +]; + +export default events; diff --git a/packages/rrweb/test/events/assets.ts b/packages/rrweb/test/events/assets.ts new file mode 100644 index 0000000000..c345ed380b --- /dev/null +++ b/packages/rrweb/test/events/assets.ts @@ -0,0 +1,124 @@ +import { EventType, type eventWithTime } from '@rrweb/types'; + +const events: eventWithTime[] = [ + { + type: 4, + data: { + href: '', + width: 1600, + height: 900, + }, + timestamp: 1636379531385, + }, + { + 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: 'assets', 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: 'img', + attributes: { + width: '100', + height: '100', + style: 'border: 1px solid #000000', + rr_captured_src: 'ftp://example.com/image.png', + }, + 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\n', id: 21 }, + ], + id: 14, + }, + ], + id: 3, + }, + ], + id: 1, + }, + initialOffset: { left: 0, top: 0 }, + }, + timestamp: 1636379531389, + }, + { + type: EventType.Asset, + data: { + url: 'ftp://example.com/image.png', + payload: { + rr_type: 'Blob', + type: 'image/png', + data: [ + { + rr_type: 'ArrayBuffer', + base64: + 'iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAAAXNSR0IArs4c6QAAAWtJREFUeF7t1cEJAEAIxEDtv2gProo8xgpCwuLezI3LGFhBMi0+iCCtHoLEeggiSM1AjMcPESRmIIZjIYLEDMRwLESQmIEYjoUIEjMQw7EQQWIGYjgWIkjMQAzHQgSJGYjhWIggMQMxHAsRJGYghmMhgsQMxHAsRJCYgRiOhQgSMxDDsRBBYgZiOBYiSMxADMdCBIkZiOFYiCAxAzEcCxEkZiCGYyGCxAzEcCxEkJiBGI6FCBIzEMOxEEFiBmI4FiJIzEAMx0IEiRmI4ViIIDEDMRwLESRmIIZjIYLEDMRwLESQmIEYjoUIEjMQw7EQQWIGYjgWIkjMQAzHQgSJGYjhWIggMQMxHAsRJGYghmMhgsQMxHAsRJCYgRiOhQgSMxDDsRBBYgZiOBYiSMxADMdCBIkZiOFYiCAxAzEcCxEkZiCGYyGCxAzEcCxEkJiBGI6FCBIzEMOxEEFiBmI4FiJIzEAMx0IEiRmI4TwVjsedWCiXGAAAAABJRU5ErkJggg==', // base64 + }, + ], + }, + }, + timestamp: 1636379532355, + }, +]; + +export default events; diff --git a/packages/rrweb/test/html/assets/doc.pdf b/packages/rrweb/test/html/assets/doc.pdf new file mode 100644 index 0000000000..9c79504d6e Binary files /dev/null and b/packages/rrweb/test/html/assets/doc.pdf differ diff --git a/packages/rrweb/test/html/assets/subtitles.vtt b/packages/rrweb/test/html/assets/subtitles.vtt new file mode 100644 index 0000000000..c56d8d687d --- /dev/null +++ b/packages/rrweb/test/html/assets/subtitles.vtt @@ -0,0 +1,16 @@ +WEBVTT + +00:00:00.000 --> 00:00:00.999 line:80% +Hildy! + +00:00:01.000 --> 00:00:01.499 line:80% +How are you? + +00:00:01.500 --> 00:00:02.999 line:80% +Tell me, is the lord of the universe in? + +00:00:03.000 --> 00:00:04.299 line:80% +Yes, he's in - in a bad humor + +00:00:04.300 --> 00:00:06.000 line:80% +Somebody must've stolen the crown jewels diff --git a/packages/rrweb/test/integration.test.ts b/packages/rrweb/test/integration.test.ts index 616b6e91cb..f696d4830f 100644 --- a/packages/rrweb/test/integration.test.ts +++ b/packages/rrweb/test/integration.test.ts @@ -13,8 +13,8 @@ import { ISuite, } from './utils'; import type { recordOptions } from '../src/types'; -import { eventWithTime, EventType, RecordPlugin } from '@rrweb/types'; -import { visitSnapshot, NodeType } from 'rrweb-snapshot'; +import { eventWithTime, NodeType, EventType, RecordPlugin } from '@rrweb/types'; +import { visitSnapshot } from 'rrweb-snapshot'; describe('record integration tests', function (this: ISuite) { jest.setTimeout(10_000); @@ -77,7 +77,7 @@ describe('record integration tests', function (this: ISuite) { x: Math.round(x + width / 2), y: Math.round(y + height / 2), }; - }, span); + }, span!); await page.touchscreen.tap(center.x, center.y); await page.click('a'); @@ -117,17 +117,20 @@ describe('record integration tests', function (this: ISuite) { ta.innerText = 'pre value'; document.body.append(ta); }); + await waitForRAF(page); await page.evaluate(() => { const t = document.querySelector('textarea') as HTMLTextAreaElement; t.innerText = 'ok'; // this mutation should be recorded }); + await waitForRAF(page); await page.evaluate(() => { const t = document.querySelector('textarea') as HTMLTextAreaElement; (t.childNodes[0] as Text).appendData('3'); // this mutation is also valid }); + await waitForRAF(page); await page.type('textarea', '1'); // types (inserts) at index 0, in front of existing text - + await waitForRAF(page); await page.evaluate(() => { const t = document.querySelector('textarea') as HTMLTextAreaElement; // user has typed so childNode content should now be ignored @@ -138,8 +141,9 @@ describe('record integration tests', function (this: ISuite) { // there is nothing explicit in rrweb which enforces this, but this test may protect against // a future change where a mutation on a textarea incorrectly updates the .value }); - + await waitForRAF(page); await page.type('textarea', '2'); // cursor is at index 1 + await waitForRAF(page); const snapshots = (await page.evaluate( 'window.snapshots', @@ -640,6 +644,7 @@ describe('record integration tests', function (this: ISuite) { await page.evaluate(() => { const el = document.createElement('input'); el.id = 'input'; + el.setAttribute('size', '50'); el.value = 'input should be masked'; const nextElement = document.querySelector('#one')!; @@ -831,12 +836,80 @@ describe('record integration tests', function (this: ISuite) { assertSnapshot(snapshots); }); + it('[DEPRECATED] 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, + captureAssets: { objectURLs: false, origins: false }, + }), + ); + 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', + )) as eventWithTime[]; + assertSnapshot(snapshots); + }); + + it('[DEPRECATED] 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, + captureAssets: { objectURLs: false, origins: false }, + }), + ); + await page.waitForResponse(`${serverURL}/html/assets/robot.png`); + await page.waitForTimeout(150); // wait for image to get added + await waitForRAF(page); // wait for image to be captured + + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; + assertSnapshot(snapshots); + }); + + it('[DEPRECATED] 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, + captureAssets: { objectURLs: false, origins: false }, + }), + ); + 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(150); // wait for image to get added + await waitForRAF(page); // wait for image to be captured + + const snapshots = (await page.evaluate( + 'window.snapshots', + )) as eventWithTime[]; + 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 }), + getHtml.call(this, 'image-blob-url.html', { + captureAssets: { objectURLs: true, origins: false }, + }), ); await page.waitForResponse(`${serverURL}/html/assets/robot.png`); await page.waitForSelector('img'); // wait for image to get added @@ -853,10 +926,12 @@ describe('record integration tests', function (this: ISuite) { 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 }), + getHtml.call(this, 'frame-image-blob-url.html', { + captureAssets: { objectURLs: true, origins: false }, + }), ); await page.waitForResponse(`${serverURL}/html/assets/robot.png`); - await page.waitForTimeout(50); // wait for image to get added + await page.waitForTimeout(150); // wait for image to get added await waitForRAF(page); // wait for image to be captured const snapshots = (await page.evaluate( @@ -870,7 +945,9 @@ describe('record integration tests', function (this: ISuite) { page.on('console', (msg) => console.log(msg.text())); await page.goto(`${serverURL}/html`); await page.setContent( - getHtml.call(this, 'frame2.html', { inlineImages: true }), + getHtml.call(this, 'frame2.html', { + captureAssets: { objectURLs: true, origins: false }, + }), ); await page.waitForSelector('iframe'); // wait for iframe to get added await waitForRAF(page); // wait for iframe to load @@ -879,7 +956,7 @@ describe('record integration tests', function (this: ISuite) { 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 page.waitForTimeout(150); // wait for image to get added await waitForRAF(page); // wait for image to be captured const snapshots = (await page.evaluate( diff --git a/packages/rrweb/test/record/asset.test.ts b/packages/rrweb/test/record/asset.test.ts new file mode 100644 index 0000000000..f96dff41c6 --- /dev/null +++ b/packages/rrweb/test/record/asset.test.ts @@ -0,0 +1,693 @@ +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, assetEvent } from '@rrweb/types'; +import { EventType } from '@rrweb/types'; +import { + getServerURL, + launchPuppeteer, + startServer, + 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; + serverB: http.Server; + serverBURL: string; +} + +interface IWindow extends Window { + rrweb: { + record: ( + options: recordOptions, + ) => listenerHandler | undefined; + addCustomEvent(tag: string, payload: T): void; + pack: (e: eventWithTime) => string; + }; + emit: (e: eventWithTime) => undefined; + snapshots: eventWithTime[]; +} +type ExtraOptions = { + captureAssets?: recordOptions['captureAssets']; +}; + +const BASE64_PNG_RECTANGLE = + 'iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAAAXNSR0IArs4c6QAAAWtJREFUeF7t1cEJAEAIxEDtv2gProo8xgpCwuLezI3LGFhBMi0+iCCtHoLEeggiSM1AjMcPESRmIIZjIYLEDMRwLESQmIEYjoUIEjMQw7EQQWIGYjgWIkjMQAzHQgSJGYjhWIggMQMxHAsRJGYghmMhgsQMxHAsRJCYgRiOhQgSMxDDsRBBYgZiOBYiSMxADMdCBIkZiOFYiCAxAzEcCxEkZiCGYyGCxAzEcCxEkJiBGI6FCBIzEMOxEEFiBmI4FiJIzEAMx0IEiRmI4ViIIDEDMRwLESRmIIZjIYLEDMRwLESQmIEYjoUIEjMQw7EQQWIGYjgWIkjMQAzHQgSJGYjhWIggMQMxHAsRJGYghmMhgsQMxHAsRJCYgRiOhQgSMxDDsRBBYgZiOBYiSMxADMdCBIkZiOFYiCAxAzEcCxEkZiCGYyGCxAzEcCxEkJiBGI6FCBIzEMOxEEFiBmI4FiJIzEAMx0IEiRmI4TwVjsedWCiXGAAAAABJRU5ErkJggg=='; + +async function injectRecordScript( + frame: puppeteer.Frame, + options?: ExtraOptions, +) { + await frame.addScriptTag({ + path: path.resolve(__dirname, '../../dist/rrweb-all.js'), + }); + options = options || {}; + await frame.evaluate((options) => { + (window as unknown as IWindow).snapshots = []; + const { record, pack } = (window as unknown as IWindow).rrweb; + const config: recordOptions = { + captureAssets: options.captureAssets, + emit(event) { + (window as unknown as IWindow).snapshots.push(event); + (window as unknown as IWindow).emit(event); + }, + }; + record(config); + }, options); + + for (const child of frame.childFrames()) { + await injectRecordScript(child, options); + } +} + +const setup = function ( + this: ISuite, + content: string, + options?: ExtraOptions, +): 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) + .replace(/\{SERVER_B_URL\}/g, ctx.serverBURL), + ); + // await ctx.page.evaluate(ctx.code); + await waitForRAF(ctx.page); + 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())); + if ( + options?.captureAssets?.origins && + Array.isArray(options.captureAssets.origins) + ) { + options.captureAssets.origins = options.captureAssets.origins.map( + (origin) => origin.replace(/\{SERVER_URL\}/g, ctx.serverURL), + ); + } + await injectRecordScript(ctx.page.mainFrame(), options); + }); + + afterEach(async () => { + await ctx.page.close(); + }); + + afterAll(async () => { + await ctx.browser.close(); + ctx.server.close(); + ctx.serverB.close(); + }); + + return ctx; +}; + +describe('asset caching', function (this: ISuite) { + jest.setTimeout(100_000); + + describe('objectURLs: true with incremental snapshots', function (this: ISuite) { + const ctx: ISuite = setup.call( + this, + ` + + + + + `, + { + captureAssets: { + objectURLs: true, + origins: false, + }, + }, + ); + + it('will emit asset when included as img attribute mutation', async () => { + const url = (await ctx.page.evaluate(() => { + return new Promise((resolve) => { + // create a blob of an image, then create an object URL for the blob + // and append it to the DOM as `src` attribute of an existing image + const img = document.createElement('img'); + document.body.appendChild(img); + + const canvas = document.createElement('canvas'); + canvas.width = 100; + canvas.height = 100; + const context = canvas.getContext('2d')!; + context.fillStyle = 'red'; + context.fillRect(0, 0, 100, 100); + + canvas.toBlob((blob) => { + if (!blob) return; + + const url = URL.createObjectURL(blob); + img.src = url; + resolve(url); + }); + }); + })) as string; + await waitForRAF(ctx.page); + // await ctx.page.waitForTimeout(40_000); + const events = await ctx.page?.evaluate( + () => (window as unknown as IWindow).snapshots, + ); + const expected: assetEvent = { + type: EventType.Asset, + data: { + url, + payload: { + rr_type: 'Blob', + data: [ + { + rr_type: 'ArrayBuffer', + base64: BASE64_PNG_RECTANGLE, // base64 + }, + ], + }, + }, + }; + expect(events[events.length - 1]).toMatchObject(expected); + }); + + it('will emit asset when included with new img', async () => { + const url = (await ctx.page.evaluate(() => { + return new Promise((resolve) => { + // create a blob of an image, then create an object URL for the blob and append it to the DOM as image `src` attribute + const canvas = document.createElement('canvas'); + canvas.width = 100; + canvas.height = 100; + const context = canvas.getContext('2d')!; + context.fillStyle = 'red'; + context.fillRect(0, 0, 100, 100); + + canvas.toBlob((blob) => { + if (!blob) return; + + const url = URL.createObjectURL(blob); + const img = document.createElement('img'); + img.src = url; + document.body.appendChild(img); + resolve(url); + }); + }); + })) as string; + await waitForRAF(ctx.page); + // await ctx.page.waitForTimeout(40_000); + const events = await ctx.page?.evaluate( + () => (window as unknown as IWindow).snapshots, + ); + const expected: assetEvent = { + type: EventType.Asset, + data: { + url, + payload: { + rr_type: 'Blob', + data: [ + { + rr_type: 'ArrayBuffer', + base64: BASE64_PNG_RECTANGLE, // base64 + }, + ], + }, + }, + }; + expect(events[events.length - 1]).toMatchObject(expected); + }); + }); + + describe('objectURLs: true with fullSnapshot', function (this: ISuite) { + const ctx: ISuite = setup.call( + this, + ` + + + + + + + `, + { + captureAssets: { + objectURLs: true, + origins: false, + }, + }, + ); + + it('will emit asset when included with existing img', async () => { + await waitForRAF(ctx.page); + const url = (await ctx.page.evaluate(() => { + return document.querySelector('img')?.src; + })) as string; + await waitForRAF(ctx.page); + + const events = await ctx.page?.evaluate( + () => (window as unknown as IWindow).snapshots, + ); + const expected: assetEvent = { + type: EventType.Asset, + data: { + url, + payload: { + rr_type: 'Blob', + data: [ + { + rr_type: 'ArrayBuffer', + base64: BASE64_PNG_RECTANGLE, // base64 + }, + ], + }, + }, + }; + expect(events[events.length - 1]).toMatchObject(expected); + }); + }); + describe('objectURLs: false', () => { + const ctx: ISuite = setup.call( + this, + ` + + + + + `, + { + captureAssets: { + objectURLs: false, + origins: false, + }, + }, + ); + it("shouldn't capture ObjectURLs when its turned off in config", async () => { + const url = (await ctx.page.evaluate(() => { + return new Promise((resolve) => { + // create a blob of an image, then create an object URL for the blob and append it to the DOM as image `src` attribute + const canvas = document.createElement('canvas'); + canvas.width = 100; + canvas.height = 100; + const context = canvas.getContext('2d')!; + context.fillStyle = 'red'; + context.fillRect(0, 0, 100, 100); + + canvas.toBlob((blob) => { + if (!blob) return; + + const url = URL.createObjectURL(blob); + const img = document.createElement('img'); + img.src = url; + document.body.appendChild(img); + resolve(url); + }); + }); + })) as string; + await waitForRAF(ctx.page); + // await ctx.page.waitForTimeout(40_000); + const events = await ctx.page?.evaluate( + () => (window as unknown as IWindow).snapshots, + ); + + expect(events).not.toContainEqual( + expect.objectContaining({ + type: EventType.Asset, + }), + ); + }); + }); + describe('data urls', () => { + const ctx: ISuite = setup.call( + this, + ` + + + + + + + `, + ); + + it("shouldn't re-capture data:urls", async () => { + const events = await ctx.page?.evaluate( + () => (window as unknown as IWindow).snapshots, + ); + + // expect no event to be emitted with `event.type` === EventType.Asset + expect(events).not.toContainEqual( + expect.objectContaining({ + type: EventType.Asset, + }), + ); + }); + }); + describe('origins: false', () => { + const ctx: ISuite = setup.call( + this, + ` + + + + + + + `, + { + captureAssets: { + origins: false, + objectURLs: false, + }, + }, + ); + + it("shouldn't capture any urls", async () => { + const events = await ctx.page?.evaluate( + () => (window as unknown as IWindow).snapshots, + ); + + // expect no event to be emitted with `event.type` === EventType.Asset + expect(events).not.toContainEqual( + expect.objectContaining({ + type: EventType.Asset, + }), + ); + }); + }); + describe('origins: []', () => { + const ctx: ISuite = setup.call( + this, + ` + + + + + + + `, + { + captureAssets: { + origins: [], + objectURLs: false, + }, + }, + ); + + it("shouldn't capture any urls", async () => { + const events = await ctx.page?.evaluate( + () => (window as unknown as IWindow).snapshots, + ); + + // expect no event to be emitted with `event.type` === EventType.Asset + expect(events).not.toContainEqual( + expect.objectContaining({ + type: EventType.Asset, + }), + ); + }); + }); + describe('origins: true', () => { + const ctx: ISuite = setup.call( + this, + ` + + + + + + + `, + { + captureAssets: { + origins: true, + objectURLs: false, + }, + }, + ); + + it('capture all urls', async () => { + await ctx.page.waitForNetworkIdle({ idleTime: 100 }); + await waitForRAF(ctx.page); + + const events = await ctx.page?.evaluate( + () => (window as unknown as IWindow).snapshots, + ); + + // expect an event to be emitted with `event.type` === EventType.Asset + expect(events).toContainEqual( + expect.objectContaining({ + type: EventType.Asset, + }), + ); + }); + }); + + describe('origins: true with invalid urls', () => { + const ctx: ISuite = setup.call( + this, + ` + + + + + + + + `, + { + captureAssets: { + origins: true, + objectURLs: false, + }, + }, + ); + + it('capture invalid url', async () => { + await waitForRAF(ctx.page); + + const events = await ctx.page?.evaluate( + () => (window as unknown as IWindow).snapshots, + ); + + // expect an event to be emitted with `event.type` === EventType.Asset + expect(events).toContainEqual( + expect.objectContaining({ + type: EventType.Asset, + data: { + url: `failprotocol://example.com/image.png`, + failed: { + message: 'Failed to fetch', + }, + }, + }), + ); + }); + + it('capture url failed due to CORS', async () => { + // Puppeteer has issues with failed requests below 19.8.0 (more info: https://github.com/puppeteer/puppeteer/pull/9883) + // TODO: re-enable next line after upgrading to puppeteer 19.8.0 + // await ctx.page.waitForNetworkIdle({ idleTime: 100 }); + + // TODO: remove next line after upgrading to puppeteer 19.8.0 + await ctx.page.waitForTimeout(500); + + await waitForRAF(ctx.page); + + const events = await ctx.page?.evaluate( + () => (window as unknown as IWindow).snapshots, + ); + + // expect an event to be emitted with `event.type` === EventType.Asset + expect(events).toContainEqual( + expect.objectContaining({ + type: EventType.Asset, + data: { + url: `https://example.com/image.png`, + failed: { + message: 'Failed to fetch', + }, + }, + }), + ); + }); + }); + + describe('origins: ["http://localhost:xxxxx/"]', () => { + const ctx: ISuite = setup.call( + this, + ` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
foobar
+ + + `, + { + captureAssets: { + origins: ['{SERVER_URL}'], + objectURLs: false, + }, + }, + ); + + [ + '{SERVER_URL}/html/assets/robot.png?body', + '{SERVER_URL}/html/assets/robot.png?img', + '{SERVER_URL}/html/assets/1-minute-of-silence.mp3?audio', + '{SERVER_URL}/html/assets/1-minute-of-silence.mp3?video', + '{SERVER_URL}/html/assets/1-minute-of-silence.mp3?source', + '{SERVER_URL}/html/assets/1-minute-of-silence.mp3?embed', + '{SERVER_URL}/html/assets/subtitles.vtt', + '{SERVER_URL}/html/assets/robot.png?1x', + '{SERVER_URL}/html/assets/robot.png?2x', + '{SERVER_URL}/html/assets/robot.png?input-type-image', + '{SERVER_URL}/html/assets/robot.png?iframe', + //'{SERVER_URL}/html/assets/doc.pdf?iframe', + '{SERVER_URL}/html/assets/robot.png?svg', + '{SERVER_URL}/html/assets/robot.png?svg2', + '{SERVER_URL}/html/assets/robot.png?svg3', + '{SERVER_URL}/html/assets/robot.png?table', + '{SERVER_URL}/html/assets/robot.png?td', + ].forEach((u) => { + it(`should capture ${u} with origin defined in config`, async () => { + const url = u.replace(/\{SERVER_URL\}/g, ctx.serverURL); + await ctx.page.waitForNetworkIdle({ idleTime: 100 }); + await waitForRAF(ctx.page); + + const events = await ctx.page?.evaluate( + () => (window as unknown as IWindow).snapshots, + ); + + // expect an event to be emitted with `event.type` === EventType.Asset + expect(events).toContainEqual( + expect.objectContaining({ + type: EventType.Asset, + data: { + url, + payload: expect.any(Object), + }, + }), + ); + }); + }); + + it("shouldn't capture assets with origin not defined in config", async () => { + await ctx.page.waitForNetworkIdle({ idleTime: 100 }); + await waitForRAF(ctx.page); + + const events = await ctx.page?.evaluate( + () => (window as unknown as IWindow).snapshots, + ); + + // expect an event to be emitted with `event.type` === EventType.Asset + expect(events).not.toContainEqual( + expect.objectContaining({ + type: EventType.Asset, + data: { + url: `${ctx.serverBURL}/html/assets/robot.png?img`, + payload: expect.any(Object), + }, + }), + ); + }); + + it("shouldn't capture iframe src assets if srcdoc overrides", async () => { + await ctx.page.waitForNetworkIdle({ idleTime: 100 }); + await waitForRAF(ctx.page); + + const events = await ctx.page?.evaluate( + () => (window as unknown as IWindow).snapshots, + ); + + // expect an event to be emitted with `event.type` === EventType.Asset + expect(events).not.toContainEqual( + expect.objectContaining({ + type: EventType.Asset, + data: { + url: `${ctx.serverBURL}/html/assets/robot.png?should-ignore`, + payload: expect.any(Object), + }, + }), + ); + }); + }); +}); diff --git a/packages/rrweb/test/record/cross-origin-iframes.test.ts b/packages/rrweb/test/record/cross-origin-iframes.test.ts index 1b0d00f2d0..c587fa6d78 100644 --- a/packages/rrweb/test/record/cross-origin-iframes.test.ts +++ b/packages/rrweb/test/record/cross-origin-iframes.test.ts @@ -25,6 +25,8 @@ interface ISuite { events: eventWithTime[]; server: http.Server; serverURL: string; + serverB: http.Server; + serverBURL: string; } interface IWindow extends Window { @@ -77,10 +79,7 @@ const setup = function ( content: string, options?: ExtraOptions, ): ISuite { - const ctx = {} as ISuite & { - serverB: http.Server; - serverBURL: string; - }; + const ctx = {} as ISuite; beforeAll(async () => { ctx.browser = await launchPuppeteer(); @@ -202,14 +201,19 @@ describe('cross origin iframes', function (this: ISuite) { }); it('should replace the existing DOM nodes on iframe navigation with `isAttachIframe`', async () => { - await ctx.page.evaluate((url) => { + const newUrl = `${ctx.serverURL}/html/form.html?2`; + ctx.page.evaluate((url) => { const iframe = document.querySelector('iframe') as HTMLIFrameElement; - iframe.src = `${url}/html/form.html?2`; - }, ctx.serverURL); + iframe.src = url; + }, newUrl); + await ctx.page.waitForFrame((iframe) => iframe.url() === newUrl); + await waitForRAF(ctx.page); // loads iframe await injectRecordScript(ctx.page.mainFrame().childFrames()[0]); // injects script into new iframe + await waitForRAF(ctx.page); // wait till script is loaded + const events: eventWithTime[] = await ctx.page.evaluate( () => (window as unknown as IWindow).snapshots, ); @@ -538,7 +542,7 @@ describe('cross origin iframes', function (this: ISuite) { it('should filter out forwarded cross origin rrweb messages', async () => { const frame = ctx.page.mainFrame().childFrames()[0]; const iframe2URL = `${ctx.serverBURL}/html/blank.html`; - await frame.evaluate((iframe2URL) => { + 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) @@ -550,7 +554,7 @@ describe('cross origin iframes', function (this: ISuite) { }, iframe2URL); // Wait for iframe2 to load - await ctx.page.waitForFrame(iframe2URL); + await ctx.page.waitForFrame((iframe) => iframe.url() === iframe2URL); const iframe2 = frame.childFrames()[0]; // Record iframe2 await injectRecordScript(iframe2); diff --git a/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-loading.png b/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-loading.png new file mode 100644 index 0000000000..8c55d13d1f Binary files /dev/null and b/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-loading.png differ diff --git a/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-on-mutation-should-add-bogus-src-attribute-until-the-asset-is-loaded-so-chrome-doesnt-display-broken-image-icon-1-snap.png b/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-on-mutation-should-add-bogus-src-attribute-until-the-asset-is-loaded-so-chrome-doesnt-display-broken-image-icon-1-snap.png new file mode 100644 index 0000000000..3bbd91056e Binary files /dev/null and b/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-on-mutation-should-add-bogus-src-attribute-until-the-asset-is-loaded-so-chrome-doesnt-display-broken-image-icon-1-snap.png differ diff --git a/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-on-mutation-should-add-bogus-src-attribute-until-the-asset-is-loaded-so-chrome-doesnt-display-broken-image-icon-2-snap.png b/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-on-mutation-should-add-bogus-src-attribute-until-the-asset-is-loaded-so-chrome-doesnt-display-broken-image-icon-2-snap.png new file mode 100644 index 0000000000..3bbd91056e Binary files /dev/null and b/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-on-mutation-should-add-bogus-src-attribute-until-the-asset-is-loaded-so-chrome-doesnt-display-broken-image-icon-2-snap.png differ diff --git a/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-on-mutation-should-wait-with-adding-src-attribute-until-the-asset-is-loaded-1-snap.png b/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-on-mutation-should-wait-with-adding-src-attribute-until-the-asset-is-loaded-1-snap.png new file mode 100644 index 0000000000..3bbd91056e Binary files /dev/null and b/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-on-mutation-should-wait-with-adding-src-attribute-until-the-asset-is-loaded-1-snap.png differ diff --git a/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-should-incorporate-assets-emitted-later-1-snap.png b/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-should-incorporate-assets-emitted-later-1-snap.png new file mode 100644 index 0000000000..3bbd91056e Binary files /dev/null and b/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-should-incorporate-assets-emitted-later-1-snap.png differ diff --git a/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-should-incorporate-assets-streamed-later-1-snap.png b/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-should-incorporate-assets-streamed-later-1-snap.png new file mode 100644 index 0000000000..3bbd91056e Binary files /dev/null and b/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-should-incorporate-assets-streamed-later-1-snap.png differ diff --git a/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-should-list-original-url-in-non-live-mode-when-asset-fails-to-load-1-snap.png b/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-should-list-original-url-in-non-live-mode-when-asset-fails-to-load-1-snap.png new file mode 100644 index 0000000000..44bc0f698c Binary files /dev/null and b/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-should-list-original-url-in-non-live-mode-when-asset-fails-to-load-1-snap.png differ diff --git a/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-should-list-original-url-in-non-live-mode-when-asset-never-gets-loaded-1-snap.png b/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-should-list-original-url-in-non-live-mode-when-asset-never-gets-loaded-1-snap.png new file mode 100644 index 0000000000..44bc0f698c Binary files /dev/null and b/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-should-list-original-url-in-non-live-mode-when-asset-never-gets-loaded-1-snap.png differ diff --git a/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-should-show-the-correct-asset-when-assets-are-loading-while-src-is-changed-in-live-mode-1-snap.png b/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-should-show-the-correct-asset-when-assets-are-loading-while-src-is-changed-in-live-mode-1-snap.png new file mode 100644 index 0000000000..0f6ea7a349 Binary files /dev/null and b/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-should-show-the-correct-asset-when-assets-are-loading-while-src-is-changed-in-live-mode-1-snap.png differ diff --git a/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-should-show-the-loaded-asset-red-square-in-non-live-mode-1-snap.png b/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-should-show-the-loaded-asset-red-square-in-non-live-mode-1-snap.png new file mode 100644 index 0000000000..3bbd91056e Binary files /dev/null and b/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-should-show-the-loaded-asset-red-square-in-non-live-mode-1-snap.png differ diff --git a/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-should-show-the-loaded-asset-robot-in-non-live-mode-1-snap.png b/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-should-show-the-loaded-asset-robot-in-non-live-mode-1-snap.png new file mode 100644 index 0000000000..0f6ea7a349 Binary files /dev/null and b/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-should-show-the-loaded-asset-robot-in-non-live-mode-1-snap.png differ diff --git a/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-should-support-urls-src-modified-via-incremental-mutation-1-snap.png b/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-should-support-urls-src-modified-via-incremental-mutation-1-snap.png new file mode 100644 index 0000000000..3bbd91056e Binary files /dev/null and b/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-should-support-urls-src-modified-via-incremental-mutation-1-snap.png differ diff --git a/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-should-support-urls-src-modified-via-mutation-1-snap.png b/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-should-support-urls-src-modified-via-mutation-1-snap.png new file mode 100644 index 0000000000..3bbd91056e Binary files /dev/null and b/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-should-support-urls-src-modified-via-mutation-1-snap.png differ diff --git a/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-should-wait-with-adding-src-attribute-until-the-asset-is-loaded-2-1-snap.png b/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-should-wait-with-adding-src-attribute-until-the-asset-is-loaded-2-1-snap.png new file mode 100644 index 0000000000..8c55d13d1f Binary files /dev/null and b/packages/rrweb/test/replay/__image_snapshots__/asset-integration-test-ts-replayer-asset-should-wait-with-adding-src-attribute-until-the-asset-is-loaded-2-1-snap.png differ diff --git a/packages/rrweb/test/replay/asset-integration.test.ts b/packages/rrweb/test/replay/asset-integration.test.ts new file mode 100644 index 0000000000..cc3dda84fc --- /dev/null +++ b/packages/rrweb/test/replay/asset-integration.test.ts @@ -0,0 +1,293 @@ +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/assets'; +import mutationEvents from '../events/assets-mutation'; +import assetsChangedEvents from '../events/assets-src-changed-before-asset-loaded'; +import type { assetEvent } from '@rrweb/types'; + +interface ISuite { + code: string; + browser: puppeteer.Browser; + page: puppeteer.Page; +} + +expect.extend({ toMatchImageSnapshot }); + +describe('replayer', function () { + jest.setTimeout(10_000); + + let code: ISuite['code']; + let browser: ISuite['browser']; + let page: ISuite['page']; + + beforeAll(async () => { + browser = await launchPuppeteer(); + + const bundlePath = path.resolve(__dirname, '../../dist/rrweb.js'); + code = fs.readFileSync(bundlePath, 'utf8'); + }); + + beforeEach(async () => { + page = await browser.newPage(); + await page.goto('about:blank'); + // mouse cursor canvas is large and pushes the replayer below the fold + // lets hide it... + await page.addStyleTag({ + content: '.replayer-mouse-tail{display: none !important;}', + }); + await page.evaluate(code); + await page.evaluate(`let events = ${JSON.stringify(events)}`); + await page.evaluate( + `let mutationEvents = ${JSON.stringify(mutationEvents)}`, + ); + await page.evaluate( + `let assetsChangedEvents = ${JSON.stringify(assetsChangedEvents)}`, + ); + + page.on('console', (msg) => console.log('PAGE LOG:', msg.text())); + }); + + afterEach(async () => { + await page.close(); + }); + + afterAll(async () => { + await browser.close(); + }); + + describe('asset', () => { + it('should incorporate assets emitted later', async () => { + await page.evaluate(` + const { Replayer } = rrweb; + const replayer = new Replayer(events, { + }); + replayer.pause(0); + `); + + await waitForRAF(page); + + const image = await page.screenshot(); + expect(image).toMatchImageSnapshot(); + }); + + it('should incorporate assets streamed later', async () => { + await page.evaluate(` + const { Replayer } = rrweb; + window.replayer = new Replayer([], { + liveMode: true, + }); + replayer.startLive(); + window.replayer.addEvent(events[0]); + window.replayer.addEvent(events[1]); + `); + + await waitForRAF(page); + + await page.evaluate(` + window.replayer.addEvent(events[2]); + `); + + await waitForRAF(page); + + const image = await page.screenshot(); + expect(image).toMatchImageSnapshot(); + }); + + it('should support urls src modified via incremental mutation', async () => { + await page.evaluate(` + const { Replayer } = rrweb; + window.replayer = new Replayer([], { + liveMode: true, + }); + replayer.startLive(mutationEvents[0].timestamp); + window.replayer.addEvent(mutationEvents[0]); + window.replayer.addEvent(mutationEvents[1]); + window.replayer.addEvent(mutationEvents[2]); + `); + + await waitForRAF(page); + + await page.evaluate(` + window.replayer.addEvent(mutationEvents[3]); + `); + + await waitForRAF(page); + + const image = await page.screenshot(); + expect(image).toMatchImageSnapshot(); + }); + + it("on mutation should add bogus src attribute until the asset is loaded so chrome doesn't display broken image icon", async () => { + await page.evaluate(` + const { Replayer } = rrweb; + window.replayer = new Replayer([], { + liveMode: true, + }); + replayer.startLive(mutationEvents[0].timestamp); + window.replayer.addEvent(mutationEvents[0]); + window.replayer.addEvent(mutationEvents[1]); + window.replayer.addEvent(mutationEvents[2]); + `); + + await waitForRAF(page); + + const loadingImage = await page.screenshot(); + expect(loadingImage).toMatchImageSnapshot({ + customSnapshotIdentifier: 'asset-integration-test-ts-loading', + failureThreshold: 0.02, + failureThresholdType: 'percent', + }); + + expect( + await page.evaluate( + `document.querySelector('iframe').contentDocument.querySelector('img').getAttribute('src')`, + ), + ).toBe('//:0'); + + await page.evaluate(` + window.replayer.addEvent(mutationEvents[3]); + `); + + await waitForRAF(page); + + const image = await page.screenshot(); + expect(image).toMatchImageSnapshot(); + }); + + it('should wait with adding src attribute until the asset is loaded 2', async () => { + await page.evaluate(` + const { Replayer } = rrweb; + window.replayer = new Replayer([], { + liveMode: true, + }); + replayer.startLive(events[0].timestamp); + window.replayer.addEvent(events[0]); + window.replayer.addEvent(events[1]); + `); + + await waitForRAF(page); + + expect( + await page.evaluate( + `document.querySelector('iframe').contentDocument.querySelector('img').getAttribute('src')`, + ), + ).toBe('//:0'); + + await page.evaluate(` + window.replayer.addEvent(events[2]); + `); + + await waitForRAF(page); + + const image = await page.screenshot(); + expect(image).toMatchImageSnapshot({ + failureThreshold: 0.02, + failureThresholdType: 'percent', + }); + }); + + it('should show the correct asset when assets are loading while src is changed in live mode', async () => { + await page.evaluate(` + const { Replayer } = rrweb; + window.replayer = new Replayer([], { + liveMode: true, + }); + replayer.startLive(assetsChangedEvents[0].timestamp); + window.replayer.addEvent(assetsChangedEvents[0]); + window.replayer.addEvent(assetsChangedEvents[1]); + window.replayer.addEvent(assetsChangedEvents[2]); + window.replayer.addEvent(assetsChangedEvents[3]); + window.replayer.addEvent(assetsChangedEvents[4]); + `); + + await waitForRAF(page); + + const image = await page.screenshot(); + expect(image).toMatchImageSnapshot({ + failureThreshold: 0.04, + failureThresholdType: 'percent', + }); + }); + + it('should show the loaded asset (robot) in non-live mode', async () => { + await page.evaluate(` + const { Replayer } = rrweb; + window.replayer = new Replayer(assetsChangedEvents); + replayer.pause((assetsChangedEvents[2].timestamp - assetsChangedEvents[0].timestamp) + 1); + `); + + await waitForRAF(page); + + const image = await page.screenshot(); + expect(image).toMatchImageSnapshot({ + failureThreshold: 0.04, + failureThresholdType: 'percent', + }); + }); + + it('should show the loaded asset (red square) in non-live mode', async () => { + await page.evaluate(` + const { Replayer } = rrweb; + window.replayer = new Replayer(assetsChangedEvents); + replayer.pause((assetsChangedEvents[1].timestamp - assetsChangedEvents[0].timestamp) + 1); + `); + + await waitForRAF(page); + + const image = await page.screenshot(); + expect(image).toMatchImageSnapshot(); + }); + + it('should list original url in non-live mode when asset never gets loaded', async () => { + await page.evaluate(` + const { Replayer } = rrweb; + window.replayer = new Replayer([assetsChangedEvents[0], assetsChangedEvents[1]]); + replayer.pause(assetsChangedEvents[1].timestamp); + `); + + await waitForRAF(page); + + const image = await page.screenshot(); + expect(image).toMatchImageSnapshot({ + failureThreshold: 30, + }); + }); + + it('should list original url in non-live mode when asset fails to load', async () => { + const failedEvent: assetEvent & { timestamp: number } = { + type: 7, + data: { + url: 'ftp://example.com/red.png', + failed: { + status: 404, + message: 'Not Found', + }, + }, + timestamp: assetsChangedEvents[2].timestamp, + }; + await page.evaluate(` + const { Replayer } = rrweb; + window.replayer = new Replayer([assetsChangedEvents[0], assetsChangedEvents[1], ${JSON.stringify( + failedEvent, + )}]); + replayer.pause(assetsChangedEvents[1].timestamp); + `); + + await waitForRAF(page); + + const image = await page.screenshot(); + expect(image).toMatchImageSnapshot({ + failureThreshold: 30, + }); + + expect( + await page.evaluate( + `document.querySelector('iframe').contentDocument.querySelector('img').getAttribute('src')`, + ), + ).toMatchInlineSnapshot(`"ftp://example.com/red.png"`); + }); + }); +}); diff --git a/packages/rrweb/test/replay/asset-unit.test.ts b/packages/rrweb/test/replay/asset-unit.test.ts new file mode 100644 index 0000000000..00e746448e --- /dev/null +++ b/packages/rrweb/test/replay/asset-unit.test.ts @@ -0,0 +1,445 @@ +/** + * @jest-environment jsdom + */ + +import AssetManager from '../../src/replay/asset-manager'; +import { + EventType, + SerializedBlobArg, + assetEvent, + captureAssetsParam, +} from '@rrweb/types'; +import { updateSrcset } from '../../src/replay/asset-manager/update-srcset'; + +describe('AssetManager', () => { + let assetManager: AssetManager; + let useURLPolyfill = false; + const examplePayload: SerializedBlobArg = { + rr_type: 'Blob', + type: 'image/png', + data: [ + { + rr_type: 'ArrayBuffer', + base64: 'fake-base64-abcd', + }, + ], + }; + + beforeAll(() => { + // https://github.com/jsdom/jsdom/issues/1721 + if (typeof window.URL.createObjectURL === 'undefined') { + useURLPolyfill = true; + window.URL.createObjectURL = () => ''; + } + }); + + beforeEach(() => { + assetManager = new AssetManager({ liveMode: false }); + }); + + afterEach(() => { + jest.restoreAllMocks(); + jest.useRealTimers(); + }); + + afterAll(() => { + if (useURLPolyfill) { + delete (window.URL as any).createObjectURL; + } + }); + + it('should add an asset to the manager', async () => { + const url = 'https://example.com/image.png'; + + const event: assetEvent = { + type: EventType.Asset, + data: { + url, + payload: examplePayload, + }, + }; + const createObjectURLSpy = jest + .spyOn(URL, 'createObjectURL') + .mockReturnValue('objectURL'); + + await assetManager.add(event); + + expect(createObjectURLSpy).toHaveBeenCalledWith(expect.any(Blob)); + expect(assetManager.get(url)).toEqual({ + status: 'loaded', + url: 'objectURL', + }); + }); + + it('should not add a failed asset to the manager', async () => { + const url = 'https://example.com/image.png'; + const event: assetEvent = { + type: EventType.Asset, + data: { url, failed: { message: 'failed to load file' } }, + }; + const createObjectURLSpy = jest.spyOn(URL, 'createObjectURL'); + + await assetManager.add(event); + + expect(createObjectURLSpy).not.toHaveBeenCalled(); + expect(assetManager.get(url)).toEqual({ status: 'failed' }); + }); + + it('should return the correct status for a loading asset', () => { + const url = 'https://example.com/image.png'; + const event: assetEvent = { + type: EventType.Asset, + data: { + url, + payload: examplePayload, + }, + }; + void assetManager.add(event); + + expect(assetManager.get(url)).toEqual({ status: 'loading' }); + }); + + it('should return the correct status for an unknown asset', () => { + const url = 'https://example.com/image.png'; + + expect(assetManager.get(url)).toEqual({ status: 'unknown' }); + }); + + it('should execute hook when an asset is added', async () => { + jest.useFakeTimers(); + const url = 'https://example.com/image.png'; + const event: assetEvent = { + type: EventType.Asset, + data: { + url, + payload: examplePayload, + }, + }; + void assetManager.add(event); + const promise = assetManager.whenReady(url); + + jest.spyOn(URL, 'createObjectURL').mockReturnValue('objectURL'); + + jest.runAllTimers(); + + await expect(promise).resolves.toEqual({ + status: 'loaded', + url: 'objectURL', + }); + }); + + it('should send status reset to callbacks when reset', async () => { + jest.useFakeTimers(); + const url = 'https://example.com/image.png'; + const event: assetEvent = { + type: EventType.Asset, + data: { + url, + payload: examplePayload, + }, + }; + void assetManager.add(event); + const promise = assetManager.whenReady(url); + + assetManager.reset(); + jest.runAllTimers(); + + await expect(promise).resolves.toEqual({ status: 'reset' }); + }); + + it("should be able to modify a node's attribute once asset is loaded", async () => { + const url = 'https://example.com/image.png'; + const event: assetEvent = { + type: EventType.Asset, + data: { + url, + payload: examplePayload, + }, + }; + jest.spyOn(URL, 'createObjectURL').mockReturnValue('objectURL'); + + const element = document.createElement('img'); + + const promise = assetManager.manageAttribute(element, 1, 'src', url); + + await assetManager.add(event); + await promise; + + expect(element.getAttribute('src')).toBe('objectURL'); + }); + + it("should be able to modify a node's attribute for previously loaded assets", async () => { + const url = 'https://example.com/image.png'; + const event: assetEvent = { + type: EventType.Asset, + data: { + url, + payload: examplePayload, + }, + }; + jest.spyOn(URL, 'createObjectURL').mockReturnValue('objectURL'); + await assetManager.add(event); + + const element = document.createElement('img'); + + await assetManager.manageAttribute(element, 1, 'src', url); + + expect(element.getAttribute('src')).toBe('objectURL'); + }); + + it('should be support srcset for previously loaded assets', async () => { + const url = 'https://example.com/image.png'; + const event: assetEvent = { + type: EventType.Asset, + data: { + url, + payload: examplePayload, + }, + }; + jest.spyOn(URL, 'createObjectURL').mockReturnValue('objectURL'); + await assetManager.add(event); + + const element = document.createElement('img'); + + await assetManager.manageAttribute(element, 1, 'srcset', url); + + expect(element.getAttribute('srcset')).toBe('objectURL'); + }); + + it('should be support partial srcset updates for previously loaded assets', async () => { + const url = 'https://example.com/image.png'; + const event: assetEvent = { + type: EventType.Asset, + data: { + url, + payload: examplePayload, + }, + }; + jest.spyOn(URL, 'createObjectURL').mockReturnValue('objectURL'); + await assetManager.add(event); + + const element = document.createElement('img'); + const value = `${url} x2, ${url}?x3 x3`; + + void assetManager.manageAttribute(element, 1, 'srcset', value); + await assetManager.whenReady(url); + + expect(element.getAttribute('srcset')).toBe(`objectURL x2, ${url}?x3 x3`); + }); + + it('should support updating srcset in chunks for every time an asset is loaded', async () => { + const url = 'https://example.com/image.png'; + const url2 = `${url}?x3`; + const element = document.createElement('img'); + + jest + .spyOn(URL, 'createObjectURL') + .mockReturnValueOnce('objectURL1') + .mockReturnValueOnce('objectURL2'); + await assetManager.add({ + type: EventType.Asset, + data: { + url, + payload: examplePayload, + }, + }); + + void assetManager.manageAttribute( + element, + 1, + 'srcset', + `${url} x2, ${url2} x3`, + ); + await assetManager.whenReady(url); + + expect(element.getAttribute('srcset')).toBe(`objectURL1 x2, ${url2} x3`); + + await assetManager.add({ + type: EventType.Asset, + data: { + url: url2, + payload: examplePayload, + }, + }); + + await assetManager.whenReady(url2); + + expect(element.getAttribute('srcset')).toBe(`objectURL1 x2, objectURL2 x3`); + }); + + it('should support svg elements', async () => { + const url = 'https://example.com/image.png'; + const event: assetEvent = { + type: EventType.Asset, + data: { + url, + payload: examplePayload, + }, + }; + jest.spyOn(URL, 'createObjectURL').mockReturnValue('objectURL'); + await assetManager.add(event); + + // create svg element `feImage` + const feImage = document.createElementNS( + 'http://www.w3.org/2000/svg', + 'feImage', + ); + + await assetManager.manageAttribute(feImage, 1, 'href', url); + + expect(feImage.getAttribute('href')).toBe('objectURL'); + }); + + describe('live mode', () => { + beforeEach(() => { + assetManager = new AssetManager({ liveMode: true }); + }); + + it("should remove a node's attribute while asset is being loaded", async () => { + const url = 'https://example.com/image.png'; + const element = document.createElement('embed'); + + void assetManager.manageAttribute(element, 1, 'src', url); + + expect(element.getAttribute('src')).toBeNull(); + }); + + it("should set an image's src attribute to //:0 to prevent a broken image icon while asset is being loaded", async () => { + const url = 'https://example.com/image.png'; + const element = document.createElement('img'); + + void assetManager.manageAttribute(element, 1, 'src', url); + + expect(element.getAttribute('src')).toBe('//:0'); + }); + + it("should be able to modify a node's attribute multiple times", async () => { + const originalUrl = 'https://example.com/original-image.png'; + const newUrl = 'https://example.com/new-image.png'; + const originalAsset: assetEvent = { + type: EventType.Asset, + data: { + url: originalUrl, + payload: examplePayload, + }, + }; + const newAsset: assetEvent = { + type: EventType.Asset, + data: { + url: newUrl, + payload: examplePayload, + }, + }; + let i = 0; + jest + .spyOn(URL, 'createObjectURL') + .mockImplementation(() => `objectURL${(i += 1)}`); + const promises: Promise[] = []; + + const element = document.createElement('img'); + promises.push( + assetManager.manageAttribute(element, 1, 'src', originalUrl), + ); + + promises.push(assetManager.manageAttribute(element, 1, 'src', newUrl)); + + await assetManager.add(newAsset); + await assetManager.add(originalAsset); + + await Promise.all(promises); + expect(element.getAttribute('src')).toBe('objectURL1'); + }); + + it("should be able to modify a node's attribute multiple times 2", async () => { + const originalUrl = 'https://example.com/original-image.png'; + const newUrl = 'https://example.com/new-image.png'; + const originalAsset: assetEvent = { + type: EventType.Asset, + data: { + url: originalUrl, + payload: examplePayload, + }, + }; + const newAsset: assetEvent = { + type: EventType.Asset, + data: { + url: newUrl, + payload: examplePayload, + }, + }; + let i = 0; + jest + .spyOn(URL, 'createObjectURL') + .mockImplementation(() => `objectURL${(i += 1)}`); + const promises: Promise[] = []; + + const element = document.createElement('img'); + promises.push( + assetManager.manageAttribute(element, 1, 'src', originalUrl), + ); + + promises.push(assetManager.manageAttribute(element, 1, 'src', newUrl)); + + await assetManager.add(originalAsset); + await assetManager.add(newAsset); + + await Promise.all(promises); + expect(element.getAttribute('src')).toBe('objectURL2'); + }); + }); + + describe('updateSrcset()', () => { + it('should update srcset attribute', () => { + const element = document.createElement('img'); + element.setAttribute( + 'srcset', + 'https://example.com/image.png x2, https://example.com/image2.png x3', + ); + const oldURL = 'https://example.com/image.png'; + const newURL = 'https://other-url.com/image.png'; + updateSrcset(element, oldURL, newURL); + expect(element.getAttribute('srcset')).toBe( + 'https://other-url.com/image.png x2, https://example.com/image2.png x3', + ); + }); + + it('should update singular srcset attribute', () => { + const element = document.createElement('img'); + element.setAttribute('srcset', 'https://example.com/image.png'); + const oldURL = 'https://example.com/image.png'; + const newURL = 'https://other-url.com/image.png'; + updateSrcset(element, oldURL, newURL); + expect(element.getAttribute('srcset')).toBe( + 'https://other-url.com/image.png', + ); + }); + + it('should update srcset attribute with similar urls', () => { + const element = document.createElement('img'); + element.setAttribute( + 'srcset', + 'https://example.com/image.png x2, https://example.com/image.png?x=3 x3', + ); + const oldURL = 'https://example.com/image.png'; + const newURL = 'https://other-url.com/image.png'; + updateSrcset(element, oldURL, newURL); + expect(element.getAttribute('srcset')).toBe( + 'https://other-url.com/image.png x2, https://example.com/image.png?x=3 x3', + ); + }); + + it('should update srcset attribute with similar urls - second url', () => { + const element = document.createElement('img'); + element.setAttribute( + 'srcset', + 'https://example.com/image.png?x=2 x2, https://example.com/image.png x3', + ); + const oldURL = 'https://example.com/image.png'; + const newURL = 'https://other-url.com/image.png'; + updateSrcset(element, oldURL, newURL); + expect(element.getAttribute('srcset')).toBe( + 'https://example.com/image.png?x=2 x2, https://other-url.com/image.png x3', + ); + }); + }); +}); diff --git a/packages/rrweb/test/replay/deserialize-args.test.ts b/packages/rrweb/test/replay/deserialize-args.test.ts index 1343bd28b0..8d8256ca0a 100644 --- a/packages/rrweb/test/replay/deserialize-args.test.ts +++ b/packages/rrweb/test/replay/deserialize-args.test.ts @@ -153,7 +153,7 @@ describe('deserializeArg', () => { // 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); + expect((deserialized as Blob)?.size).toEqual(expected.size); }); describe('isUnchanged', () => { diff --git a/packages/rrweb/test/utils.ts b/packages/rrweb/test/utils.ts index 30a36abfcb..adaad3ac26 100644 --- a/packages/rrweb/test/utils.ts +++ b/packages/rrweb/test/utils.ts @@ -1,4 +1,4 @@ -import { NodeType } from 'rrweb-snapshot'; +import { NodeType } from '@rrweb/types'; import { EventType, IncrementalSource, @@ -44,13 +44,14 @@ export interface ISuite { events: eventWithTime[]; } -export const startServer = (defaultPort: number = 3030) => +export const startServer = (defaultPort: number = 3031) => new Promise((resolve) => { const mimeType: IMimeType = { '.html': 'text/html', '.js': 'text/javascript', '.css': 'text/css', '.webm': 'video/webm', + '.pdf': 'application/pdf', }; const s = http.createServer((req, res) => { const parsedUrl = url.parse(req.url!); @@ -132,6 +133,9 @@ function stringifySnapshots(snapshots: eventWithTime[]): string { delete (s.data as Optional).x; delete (s.data as Optional).y; } + if (s.type === EventType.Asset) { + s.data.url = s.data.url.replace(/\/[a-f0-9\-]+$/, '/...'); + } if ( s.type === EventType.IncrementalSnapshot && s.data.source === IncrementalSource.Mutation @@ -243,6 +247,7 @@ function stringifySnapshots(snapshots: eventWithTime[]): string { function stripBlobURLsFromAttributes(node: { attributes: { src?: string; + rr_captured_src?: string; }; }) { if ( @@ -255,6 +260,16 @@ function stripBlobURLsFromAttributes(node: { .replace(/[\w-]+$/, '...') .replace(/:[0-9]+\//, ':xxxx/'); } + if ( + 'rr_captured_src' in node.attributes && + node.attributes.rr_captured_src && + typeof node.attributes.rr_captured_src === 'string' && + node.attributes.rr_captured_src.startsWith('blob:') + ) { + node.attributes.rr_captured_src = node.attributes.rr_captured_src + .replace(/[\w-]+$/, '...') + .replace(/:[0-9]+\//, ':xxxx/'); + } } function stringifyDomSnapshot(mhtml: string): string { @@ -701,7 +716,8 @@ export function generateRecordSnippet(options: recordOptions) { recordCanvas: ${options.recordCanvas}, recordAfter: '${options.recordAfter || 'load'}', inlineImages: ${options.inlineImages}, - plugins: ${options.plugins} + plugins: ${options.plugins}, + captureAssets: ${JSON.stringify(options.captureAssets)}, }); `; } diff --git a/packages/types/package.json b/packages/types/package.json index 2d61372168..a5b7ac61a1 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -9,9 +9,10 @@ "@rrweb/types" ], "scripts": { - "dev": "vite", - "build": "tsc -noEmit && vite build", + "dev": "vite build -w & yarn typings -w", + "build": "yarn typings && vite build", "check-types": "tsc -noEmit", + "typings": "tsc -d --declarationDir typings --emitDeclarationOnly", "prepublish": "npm run build", "lint": "yarn eslint src/**/*.ts" }, @@ -27,7 +28,7 @@ "type": "module", "main": "./dist/types.umd.cjs", "module": "./dist/types.js", - "typings": "dist/index.d.ts", + "typings": "typings/index.d.ts", "exports": { ".": { "types": "./dist/index.d.ts", @@ -36,16 +37,13 @@ } }, "files": [ - "build", + "typings", "dist" ], "devDependencies": { "vite": "^3.2.0-beta.2", "vite-plugin-dts": "^1.7.3" }, - "dependencies": { - "rrweb-snapshot": "^2.0.0-alpha.13" - }, "browserslist": [ "supports es6-class" ] diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 0476ce2d68..00b5aca43a 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -1,10 +1,3 @@ -import type { - serializedNodeWithId, - Mirror, - INode, - DataURLOptions, -} from 'rrweb-snapshot'; - export enum EventType { DomContentLoaded, Load, @@ -13,6 +6,7 @@ export enum EventType { Meta, Custom, Plugin, + Asset, } export type domContentLoadedEvent = { @@ -66,6 +60,33 @@ export type pluginEvent = { }; }; +export type captureAssetsParam = { + /** + * Captures object URLs (blobs, files, media sources). + * More info: https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL + */ + objectURLs: boolean; + /** + * Allowlist of origins to capture object URLs from. + * [origin, origin, ...] to capture from specific origins. + * e.g. ['https://example.com', 'https://www.example.com'] + * Set to `true` capture from all origins. + * Set to `false` or `[]` to disable capturing from any origin apart from object URLs. + */ + origins: string[] | true | false; +}; + +export type assetEvent = { + type: EventType.Asset; + data: assetParam; +}; + +export type asset = { + element: HTMLElement; + attr: string; + value: string; +}; + export enum IncrementalSource { Mutation, MouseMove, @@ -170,7 +191,8 @@ export type eventWithoutTime = | incrementalSnapshotEvent | metaEvent | customEvent - | pluginEvent; + | pluginEvent + | assetEvent; /** * @deprecated intended for internal use @@ -254,7 +276,7 @@ export type RecordPlugin = { ) => listenerHandler; eventProcessor?: (event: eventWithTime) => eventWithTime & TExtend; getMirror?: (mirrors: { - nodeMirror: Mirror; + nodeMirror: IMirror; crossOriginIframeMirror: ICrossOriginIframeMirror; crossOriginIframeStyleMirror: ICrossOriginIframeMirror; }) => void; @@ -389,16 +411,18 @@ export enum CanvasContext { WebGL2, } +export type SerializedBlobArg = { + rr_type: 'Blob'; + data: Array; + type?: string; +}; + export type SerializedCanvasArg = | { rr_type: 'ArrayBuffer'; base64: string; // base64 } - | { - rr_type: 'Blob'; - data: Array; - type?: string; - } + | SerializedBlobArg | { rr_type: string; src: string; // url of image @@ -615,6 +639,28 @@ export type customElementParam = { export type customElementCallback = (c: customElementParam) => void; +export type assetParam = + | { + url: string; + payload: SerializedCanvasArg; + } + | { + url: string; + failed: { + status?: number; + message: string; + }; + }; + +export type assetCallback = (d: assetParam) => void; + +/** + * @deprecated + */ +interface INode extends Node { + __sn: serializedNodeWithId; +} + export type DeprecatedMirror = { map: { [key: number]: INode; @@ -704,3 +750,137 @@ export type TakeTypedKeyValues = Pick< Obj, TakeTypeHelper[keyof TakeTypeHelper] >; + +export type RebuildAssetManagerResetStatus = { status: 'reset' }; +export type RebuildAssetManagerUnknownStatus = { status: 'unknown' }; +export type RebuildAssetManagerLoadingStatus = { status: 'loading' }; +export type RebuildAssetManagerLoadedStatus = { status: 'loaded'; url: string }; +export type RebuildAssetManagerFailedStatus = { status: 'failed' }; +export type RebuildAssetManagerFinalStatus = + | RebuildAssetManagerLoadedStatus + | RebuildAssetManagerFailedStatus + | RebuildAssetManagerResetStatus; +export type RebuildAssetManagerStatus = + | RebuildAssetManagerUnknownStatus + | RebuildAssetManagerLoadingStatus + | RebuildAssetManagerFinalStatus; + +export declare abstract class RebuildAssetManagerInterface { + constructor( + playerConfig: { liveMode: boolean }, + assetManagerConfig?: captureAssetsParam | undefined, + ); + abstract add(event: assetEvent): Promise; + abstract get(url: string): RebuildAssetManagerStatus; + abstract whenReady(url: string): Promise; + abstract reset(config?: captureAssetsParam | undefined): void; + abstract manageAttribute( + n: Element, + id: number, + attribute: string, + originalValue: string, + ): void; +} + +export enum NodeType { + Document, + DocumentType, + Element, + Text, + CDATA, + Comment, +} + +export type documentNode = { + type: NodeType.Document; + childNodes: serializedNodeWithId[]; + compatMode?: string; +}; + +export type documentTypeNode = { + type: NodeType.DocumentType; + name: string; + publicId: string; + systemId: string; +}; + +export type attributes = { + [key: string]: string | number | true | null; +}; + +export type legacyAttributes = { + /** + * @deprecated old bug in rrweb was causing these to always be set + * @see https://github.com/rrweb-io/rrweb/pull/651 + */ + selected: false; +}; + +export type elementNode = { + type: NodeType.Element; + tagName: string; + attributes: attributes; + childNodes: serializedNodeWithId[]; + isSVG?: true; + needBlock?: boolean; + // This is a custom element or not. + isCustom?: true; +}; + +export type textNode = { + type: NodeType.Text; + textContent: string; + isStyle?: true; +}; + +export type cdataNode = { + type: NodeType.CDATA; + textContent: ''; +}; + +export type commentNode = { + type: NodeType.Comment; + textContent: string; +}; + +export type serializedNode = ( + | documentNode + | documentTypeNode + | elementNode + | textNode + | cdataNode + | commentNode +) & { + rootId?: number; + isShadowHost?: boolean; + isShadow?: boolean; +}; + +export type serializedNodeWithId = serializedNode & { id: number }; + +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 DataURLOptions = Partial<{ + type: string; + quality: number; +}>; diff --git a/packages/types/tsconfig.json b/packages/types/tsconfig.json index ecde939d12..517fd48488 100644 --- a/packages/types/tsconfig.json +++ b/packages/types/tsconfig.json @@ -18,9 +18,5 @@ "compileOnSave": true, "exclude": ["test"], "include": ["src"], - "references": [ - { - "path": "../rrweb-snapshot" - } - ] + "references": [] } diff --git a/packages/types/vite.config.js b/packages/types/vite.config.js index f84b8f9fde..1e097aa01b 100644 --- a/packages/types/vite.config.js +++ b/packages/types/vite.config.js @@ -1,5 +1,5 @@ import path from 'path'; -import dts from 'vite-plugin-dts'; +// import dts from 'vite-plugin-dts'; // disabled because of https://github.com/qmhc/vite-plugin-dts/issues/193 /** * @type {import('vite').UserConfig} */ @@ -17,5 +17,5 @@ export default { sourcemap: true, }, - plugins: [dts()], + // plugins: [dts()], }; diff --git a/yarn.lock b/yarn.lock index 40f8fd25f3..9a14e50f7e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -51,11 +51,6 @@ dependencies: "@babel/highlight" "^7.22.5" -"@babel/compat-data@^7.15.0": - version "7.15.0" - 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.npmjs.org/@babel/compat-data/-/compat-data-7.16.4.tgz" @@ -71,27 +66,6 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.9.tgz#71cdb00a1ce3a329ce4cbec3a44f9fef35669730" integrity sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ== -"@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" - integrity sha512-pYgXxiwAgQpgM1bNkZsDEq85f0ggXMA5L7c+o3tskGMh2BunCI9QUwB9Z4jpvXUOuMdyGKiGKQiRe11VS6Jzvg== - dependencies: - "@babel/code-frame" "^7.14.5" - "@babel/generator" "^7.15.4" - "@babel/helper-compilation-targets" "^7.15.4" - "@babel/helper-module-transforms" "^7.15.4" - "@babel/helpers" "^7.15.4" - "@babel/parser" "^7.15.5" - "@babel/template" "^7.15.4" - "@babel/traverse" "^7.15.4" - "@babel/types" "^7.15.4" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.1.2" - semver "^6.3.0" - source-map "^0.5.0" - "@babel/core@^7.11.6": version "7.22.9" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.9.tgz#bd96492c68822198f33e8a256061da3cf391f58f" @@ -155,15 +129,6 @@ 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" - integrity sha512-d3itta0tu+UayjEORPNz6e1T3FtvWlP5N4V5M+lhp/CxT4oAA7/NcScnpRyspUMLK6tu9MNHmQHxRykuN2R7hw== - dependencies: - "@babel/types" "^7.15.4" - jsesc "^2.5.1" - source-map "^0.5.0" - "@babel/generator@^7.16.7": version "7.16.7" resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.16.7.tgz" @@ -182,12 +147,12 @@ "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" -"@babel/generator@^7.22.7", "@babel/generator@^7.22.9": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.9.tgz#572ecfa7a31002fa1de2a9d91621fd895da8493d" - integrity sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw== +"@babel/generator@^7.22.7", "@babel/generator@^7.22.9", "@babel/generator@^7.7.2": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.6.tgz#9e1fca4811c77a10580d17d26b57b036133f3c2e" + integrity sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw== dependencies: - "@babel/types" "^7.22.5" + "@babel/types" "^7.23.6" "@jridgewell/gen-mapping" "^0.3.2" "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" @@ -199,16 +164,6 @@ 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" - integrity sha512-rMWPCirulnPSe4d+gwdWXLfAXTTBj8M3guAf5xFQJ0nvFY7tfNAFnWdqaHegHlgDZOCT4qvhF3BYlSJag8yhqQ== - dependencies: - "@babel/compat-data" "^7.15.0" - "@babel/helper-validator-option" "^7.14.5" - browserslist "^4.16.6" - semver "^6.3.0" - "@babel/helper-compilation-targets@^7.16.7": version "7.16.7" resolved "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz" @@ -257,15 +212,6 @@ resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98" integrity sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q== -"@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" - integrity sha512-Z91cOMM4DseLIGOnog+Z8OI6YseR9bua+HpvLAQ2XayUGU+neTtX+97caALaLdyu53I/fjhbeCnWnRH1O3jFOw== - dependencies: - "@babel/helper-get-function-arity" "^7.15.4" - "@babel/template" "^7.15.4" - "@babel/types" "^7.15.4" - "@babel/helper-function-name@^7.16.7": version "7.16.7" resolved "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz" @@ -291,13 +237,6 @@ "@babel/template" "^7.22.5" "@babel/types" "^7.22.5" -"@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" - 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.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz" @@ -305,13 +244,6 @@ dependencies: "@babel/types" "^7.16.7" -"@babel/helper-hoist-variables@^7.15.4": - version "7.15.4" - 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.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz" @@ -333,20 +265,6 @@ dependencies: "@babel/types" "^7.22.5" -"@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" - integrity sha512-cokOMkxC/BTyNP1AlY25HuBWM32iCEsLPI4BHDpJCHHm1FU2E7dKWWIXJgQgSFiu4lp8q3bL1BIKwqkSUviqtA== - dependencies: - "@babel/types" "^7.15.4" - -"@babel/helper-module-imports@^7.15.4": - version "7.15.4" - 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.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz" @@ -368,20 +286,6 @@ dependencies: "@babel/types" "^7.22.5" -"@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" - integrity sha512-ZNqjjQG/AuFfekFTY+7nY4RgBSklgTu970c7Rj3m/JOhIu5KPBUuTA9AY6zaKcUvk4g6EbDXdBnhi35FAssdSw== - dependencies: - "@babel/helper-module-imports" "^7.15.4" - "@babel/helper-replace-supers" "^7.15.4" - "@babel/helper-simple-access" "^7.15.4" - "@babel/helper-split-export-declaration" "^7.15.4" - "@babel/helper-validator-identifier" "^7.15.7" - "@babel/template" "^7.15.4" - "@babel/traverse" "^7.15.4" - "@babel/types" "^7.15.6" - "@babel/helper-module-transforms@^7.16.7": version "7.16.7" resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz" @@ -421,13 +325,6 @@ "@babel/helper-split-export-declaration" "^7.22.6" "@babel/helper-validator-identifier" "^7.22.5" -"@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" - 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.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz" @@ -443,23 +340,6 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== -"@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" - integrity sha512-/ztT6khaXF37MS47fufrKvIsiQkx1LBRvSJNzRqmbyeZnTwU9qBxXYLaaT/6KaxfKhjs2Wy8kG8ZdsFUuWBjzw== - dependencies: - "@babel/helper-member-expression-to-functions" "^7.15.4" - "@babel/helper-optimise-call-expression" "^7.15.4" - "@babel/traverse" "^7.15.4" - "@babel/types" "^7.15.4" - -"@babel/helper-simple-access@^7.15.4": - version "7.15.4" - 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.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz" @@ -481,13 +361,6 @@ dependencies: "@babel/types" "^7.22.5" -"@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" - integrity sha512-HsFqhLDZ08DxCpBdEVtKmywj6PQbwnF6HHybur0MAnkAKnlS6uHkwnmRIkElB2Owpfb4xL4NwDmDLFubueDXsw== - dependencies: - "@babel/types" "^7.15.4" - "@babel/helper-split-export-declaration@^7.16.7": version "7.16.7" resolved "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz" @@ -519,12 +392,17 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== +"@babel/helper-string-parser@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83" + integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ== + "@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" integrity sha512-ZGy6/XQjllhYQrNw/3zfWRwZCTVSiBLZ9DHVZxn9n2gip/7ab8mv2TWlKPIBk26RwedCBoWdjLmn+t9na2Gcow== -"@babel/helper-validator-identifier@^7.14.9", "@babel/helper-validator-identifier@^7.15.7": +"@babel/helper-validator-identifier@^7.14.9": version "7.15.7" 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== @@ -539,16 +417,16 @@ 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-identifier@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== + "@babel/helper-validator-identifier@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ== -"@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" - integrity sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow== - "@babel/helper-validator-option@^7.16.7": version "7.16.7" resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz" @@ -564,15 +442,6 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz#de52000a15a177413c8234fa3a8af4ee8102d0ac" integrity sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw== -"@babel/helpers@^7.15.4": - version "7.15.4" - resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.15.4.tgz" - integrity sha512-V45u6dqEJ3w2rlryYYXf6i9rQ5YMNu4FLS6ngs8ikblhu2VdR1AqAd6aJjBzmf2Qzh6KOLqKHxEN9+TFbAkAVQ== - dependencies: - "@babel/template" "^7.15.4" - "@babel/traverse" "^7.15.4" - "@babel/types" "^7.15.4" - "@babel/helpers@^7.16.7": version "7.16.7" resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.16.7.tgz" @@ -636,7 +505,7 @@ 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": +"@babel/parser@^7.1.0", "@babel/parser@^7.15.4": version "7.15.7" resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.15.7.tgz" integrity sha512-rycZXvQ+xS9QyIcJ9HXeDWf1uxqlbVFAUq0Rq0dbc50Zb/+wUe/ehyfzGfm9KZZF0kBejYgxltBXocP+gKdL2g== @@ -814,15 +683,6 @@ 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" - integrity sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg== - dependencies: - "@babel/code-frame" "^7.14.5" - "@babel/parser" "^7.15.4" - "@babel/types" "^7.15.4" - "@babel/template@^7.16.7": version "7.16.7" resolved "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz" @@ -850,20 +710,14 @@ "@babel/parser" "^7.22.5" "@babel/types" "^7.22.5" -"@babel/traverse@^7.1.0", "@babel/traverse@^7.15.4", "@babel/traverse@^7.7.2": +"@babel/template@^7.3.3": version "7.15.4" - resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.4.tgz" - integrity sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA== + resolved "https://registry.npmjs.org/@babel/template/-/template-7.15.4.tgz" + integrity sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg== dependencies: "@babel/code-frame" "^7.14.5" - "@babel/generator" "^7.15.4" - "@babel/helper-function-name" "^7.15.4" - "@babel/helper-hoist-variables" "^7.15.4" - "@babel/helper-split-export-declaration" "^7.15.4" "@babel/parser" "^7.15.4" "@babel/types" "^7.15.4" - debug "^4.1.0" - globals "^11.1.0" "@babel/traverse@^7.16.7": version "7.16.7" @@ -913,7 +767,7 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.15.4", "@babel/types@^7.15.6", "@babel/types@^7.3.0", "@babel/types@^7.3.3": +"@babel/types@^7.0.0", "@babel/types@^7.15.4", "@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" integrity sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig== @@ -947,6 +801,15 @@ "@babel/helper-validator-identifier" "^7.22.5" to-fast-properties "^2.0.0" +"@babel/types@^7.23.6": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.6.tgz#be33fdb151e1f5a56877d704492c240fc71c7ccd" + integrity sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg== + dependencies: + "@babel/helper-string-parser" "^7.23.4" + "@babel/helper-validator-identifier" "^7.22.20" + 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" @@ -2174,30 +2037,6 @@ 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.npmjs.org/@jest/console/-/console-27.2.4.tgz" - integrity sha512-94znCKynPZpDpYHQ6esRJSc11AmONrVkBOBZiD7S+bSubHhrUfbS95EY5HIOxhm4PQO7cnvZkL3oJcY0oMA+Wg== - dependencies: - "@jest/types" "^27.2.4" - "@types/node" "*" - chalk "^4.0.0" - jest-message-util "^27.2.4" - jest-util "^27.2.4" - slash "^3.0.0" - -"@jest/console@^27.5.1": - version "27.5.1" - resolved "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz" - integrity sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg== - dependencies: - "@jest/types" "^27.5.1" - "@types/node" "*" - chalk "^4.0.0" - jest-message-util "^27.5.1" - jest-util "^27.5.1" - slash "^3.0.0" - "@jest/console@^29.6.2": version "29.6.2" resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.6.2.tgz#bf1d4101347c23e07c029a1b1ae07d550f5cc541" @@ -2210,73 +2049,17 @@ jest-util "^29.6.2" slash "^3.0.0" -"@jest/core@^27.2.4": - version "27.2.4" - resolved "https://registry.npmjs.org/@jest/core/-/core-27.2.4.tgz" - integrity sha512-UNQLyy+rXoojNm2MGlapgzWhZD1CT1zcHZQYeiD0xE7MtJfC19Q6J5D/Lm2l7i4V97T30usKDoEtjI8vKwWcLg== - dependencies: - "@jest/console" "^27.2.4" - "@jest/reporters" "^27.2.4" - "@jest/test-result" "^27.2.4" - "@jest/transform" "^27.2.4" - "@jest/types" "^27.2.4" - "@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.2.4" - jest-config "^27.2.4" - jest-haste-map "^27.2.4" - jest-message-util "^27.2.4" - jest-regex-util "^27.0.6" - jest-resolve "^27.2.4" - jest-resolve-dependencies "^27.2.4" - jest-runner "^27.2.4" - jest-runtime "^27.2.4" - jest-snapshot "^27.2.4" - jest-util "^27.2.4" - jest-validate "^27.2.4" - jest-watcher "^27.2.4" - 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.npmjs.org/@jest/core/-/core-27.5.1.tgz" - integrity sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ== +"@jest/console@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc" + integrity sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg== dependencies: - "@jest/console" "^27.5.1" - "@jest/reporters" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/types" "^29.6.3" "@types/node" "*" - ansi-escapes "^4.2.1" chalk "^4.0.0" - emittery "^0.8.1" - exit "^0.1.2" - graceful-fs "^4.2.9" - jest-changed-files "^27.5.1" - jest-config "^27.5.1" - jest-haste-map "^27.5.1" - jest-message-util "^27.5.1" - jest-regex-util "^27.5.1" - jest-resolve "^27.5.1" - jest-resolve-dependencies "^27.5.1" - jest-runner "^27.5.1" - jest-runtime "^27.5.1" - jest-snapshot "^27.5.1" - jest-util "^27.5.1" - jest-validate "^27.5.1" - jest-watcher "^27.5.1" - micromatch "^4.0.4" - rimraf "^3.0.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" slash "^3.0.0" - strip-ansi "^6.0.0" "@jest/core@^29.6.2": version "29.6.2" @@ -2312,25 +2095,49 @@ slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/environment@^27.2.4": - version "27.2.4" - resolved "https://registry.npmjs.org/@jest/environment/-/environment-27.2.4.tgz" - integrity sha512-wkuui5yr3SSQW0XD0Qm3TATUbL/WE3LDEM3ulC+RCQhMf2yxhci8x7svGkZ4ivJ6Pc94oOzpZ6cdHBAMSYd1ew== +"@jest/core@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f" + integrity sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg== dependencies: - "@jest/fake-timers" "^27.2.4" - "@jest/types" "^27.2.4" + "@jest/console" "^29.7.0" + "@jest/reporters" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" - jest-mock "^27.2.4" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + ci-info "^3.2.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + jest-changed-files "^29.7.0" + jest-config "^29.7.0" + jest-haste-map "^29.7.0" + jest-message-util "^29.7.0" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-resolve-dependencies "^29.7.0" + jest-runner "^29.7.0" + jest-runtime "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" + jest-watcher "^29.7.0" + micromatch "^4.0.4" + pretty-format "^29.7.0" + slash "^3.0.0" + strip-ansi "^6.0.0" -"@jest/environment@^27.5.1": - version "27.5.1" - resolved "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz" - integrity sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA== +"@jest/environment@30.0.0-alpha.2": + version "30.0.0-alpha.2" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-30.0.0-alpha.2.tgz#3ac144fac515f09cd163c09ff40a32dfb2f2440d" + integrity sha512-rlSvTu+VmsTi9rhAVX691FdAGbYJKCS7nB9eImkxvIIfF5ebvQbh8Wzot8lRWB3mEzu9W0vLX3RoUzJXqI5W1w== dependencies: - "@jest/fake-timers" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/fake-timers" "30.0.0-alpha.2" + "@jest/types" "30.0.0-alpha.2" "@types/node" "*" - jest-mock "^27.5.1" + jest-mock "30.0.0-alpha.2" "@jest/environment@^29.6.2": version "29.6.2" @@ -2342,6 +2149,16 @@ "@types/node" "*" jest-mock "^29.6.2" +"@jest/environment@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.7.0.tgz#24d61f54ff1f786f3cd4073b4b94416383baf2a7" + integrity sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw== + dependencies: + "@jest/fake-timers" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + jest-mock "^29.7.0" + "@jest/expect-utils@^29.6.2": version "29.6.2" resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.6.2.tgz#1b97f290d0185d264dd9fdec7567a14a38a90534" @@ -2349,6 +2166,13 @@ dependencies: jest-get-type "^29.4.3" +"@jest/expect-utils@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" + integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA== + dependencies: + jest-get-type "^29.6.3" + "@jest/expect@^29.6.2": version "29.6.2" resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.6.2.tgz#5a2ad58bb345165d9ce0a1845bbf873c480a4b28" @@ -2357,29 +2181,25 @@ expect "^29.6.2" jest-snapshot "^29.6.2" -"@jest/fake-timers@^27.2.4": - version "27.2.4" - resolved "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.2.4.tgz" - integrity sha512-cs/TzvwWUM7kAA6Qm/890SK6JJ2pD5RfDNM3SSEom6BmdyV6OiWP1qf/pqo6ts6xwpcM36oN0wSEzcZWc6/B6w== +"@jest/expect@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.7.0.tgz#76a3edb0cb753b70dfbfe23283510d3d45432bf2" + integrity sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ== dependencies: - "@jest/types" "^27.2.4" - "@sinonjs/fake-timers" "^8.0.1" - "@types/node" "*" - jest-message-util "^27.2.4" - jest-mock "^27.2.4" - jest-util "^27.2.4" + expect "^29.7.0" + jest-snapshot "^29.7.0" -"@jest/fake-timers@^27.5.1": - version "27.5.1" - resolved "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz" - integrity sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ== +"@jest/fake-timers@30.0.0-alpha.2": + version "30.0.0-alpha.2" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-30.0.0-alpha.2.tgz#93f4d65ccf39a97855a22763dbfe27773424c683" + integrity sha512-jngoA5we8/41JzNK0Vq/C4s9cnjzcVufhMWrawF6EEY6N8O9hgDLn2um2R/3XDj85rvZWCl1dp3ca2PTPH0JLw== dependencies: - "@jest/types" "^27.5.1" - "@sinonjs/fake-timers" "^8.0.1" + "@jest/types" "30.0.0-alpha.2" + "@sinonjs/fake-timers" "^11.1.0" "@types/node" "*" - jest-message-util "^27.5.1" - jest-mock "^27.5.1" - jest-util "^27.5.1" + jest-message-util "30.0.0-alpha.2" + jest-mock "30.0.0-alpha.2" + jest-util "30.0.0-alpha.2" "@jest/fake-timers@^29.6.2": version "29.6.2" @@ -2393,23 +2213,17 @@ jest-mock "^29.6.2" jest-util "^29.6.2" -"@jest/globals@^27.2.4": - version "27.2.4" - 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" - "@jest/types" "^27.2.4" - expect "^27.2.4" - -"@jest/globals@^27.5.1": - version "27.5.1" - resolved "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz" - integrity sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q== +"@jest/fake-timers@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565" + integrity sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ== dependencies: - "@jest/environment" "^27.5.1" - "@jest/types" "^27.5.1" - expect "^27.5.1" + "@jest/types" "^29.6.3" + "@sinonjs/fake-timers" "^10.0.2" + "@types/node" "*" + jest-message-util "^29.7.0" + jest-mock "^29.7.0" + jest-util "^29.7.0" "@jest/globals@^29.6.2": version "29.6.2" @@ -2421,77 +2235,56 @@ "@jest/types" "^29.6.1" jest-mock "^29.6.2" -"@jest/reporters@^27.2.4": - version "27.2.4" - resolved "https://registry.npmjs.org/@jest/reporters/-/reporters-27.2.4.tgz" - integrity sha512-LHeSdDnDZkDnJ8kvnjcqV8P1Yv/32yL4d4XfR5gBiy3xGO0onwll1QEbvtW96fIwhx2nejug0GTaEdNDoyr3fQ== +"@jest/globals@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.7.0.tgz#8d9290f9ec47ff772607fa864ca1d5a2efae1d4d" + integrity sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ== dependencies: - "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^27.2.4" - "@jest/test-result" "^27.2.4" - "@jest/transform" "^27.2.4" - "@jest/types" "^27.2.4" - 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 "^4.0.3" - istanbul-lib-report "^3.0.0" - istanbul-lib-source-maps "^4.0.0" - istanbul-reports "^3.0.2" - jest-haste-map "^27.2.4" - jest-resolve "^27.2.4" - jest-util "^27.2.4" - jest-worker "^27.2.4" - 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/environment" "^29.7.0" + "@jest/expect" "^29.7.0" + "@jest/types" "^29.6.3" + jest-mock "^29.7.0" -"@jest/reporters@^27.5.1": - version "27.5.1" - resolved "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz" - integrity sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw== +"@jest/reporters@^29.6.2": + version "29.6.2" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.6.2.tgz#524afe1d76da33d31309c2c4a2c8062d0c48780a" + integrity sha512-sWtijrvIav8LgfJZlrGCdN0nP2EWbakglJY49J1Y5QihcQLfy7ovyxxjJBRXMNltgt4uPtEcFmIMbVshEDfFWw== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/console" "^29.6.2" + "@jest/test-result" "^29.6.2" + "@jest/transform" "^29.6.2" + "@jest/types" "^29.6.1" + "@jridgewell/trace-mapping" "^0.3.18" "@types/node" "*" chalk "^4.0.0" collect-v8-coverage "^1.0.0" exit "^0.1.2" - glob "^7.1.2" + glob "^7.1.3" graceful-fs "^4.2.9" istanbul-lib-coverage "^3.0.0" istanbul-lib-instrument "^5.1.0" istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" istanbul-reports "^3.1.3" - jest-haste-map "^27.5.1" - jest-resolve "^27.5.1" - jest-util "^27.5.1" - jest-worker "^27.5.1" + jest-message-util "^29.6.2" + jest-util "^29.6.2" + jest-worker "^29.6.2" 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" + strip-ansi "^6.0.0" + v8-to-istanbul "^9.0.1" -"@jest/reporters@^29.6.2": - version "29.6.2" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.6.2.tgz#524afe1d76da33d31309c2c4a2c8062d0c48780a" - integrity sha512-sWtijrvIav8LgfJZlrGCdN0nP2EWbakglJY49J1Y5QihcQLfy7ovyxxjJBRXMNltgt4uPtEcFmIMbVshEDfFWw== +"@jest/reporters@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7" + integrity sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^29.6.2" - "@jest/test-result" "^29.6.2" - "@jest/transform" "^29.6.2" - "@jest/types" "^29.6.1" + "@jest/console" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" "@jridgewell/trace-mapping" "^0.3.18" "@types/node" "*" chalk "^4.0.0" @@ -2500,18 +2293,25 @@ glob "^7.1.3" graceful-fs "^4.2.9" istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^5.1.0" + istanbul-lib-instrument "^6.0.0" istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" istanbul-reports "^3.1.3" - jest-message-util "^29.6.2" - jest-util "^29.6.2" - jest-worker "^29.6.2" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + jest-worker "^29.7.0" slash "^3.0.0" string-length "^4.0.1" strip-ansi "^6.0.0" v8-to-istanbul "^9.0.1" +"@jest/schemas@30.0.0-alpha.2": + version "30.0.0-alpha.2" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-30.0.0-alpha.2.tgz#c8cb82e15e324777cc3ae1bc927ed9188b221c2d" + integrity sha512-cmXKHZ2oz0OK1aUg8HR3OT4jAUq+mBLtkOOwFdzmMFKk4gFjGcjevSMN/sLs1daMcXl0TMA1Algh9LVW0+bWwQ== + dependencies: + "@sinclair/typebox" "^0.31.0" + "@jest/schemas@^29.6.0": version "29.6.0" resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.0.tgz#0f4cb2c8e3dca80c135507ba5635a4fd755b0040" @@ -2519,23 +2319,12 @@ dependencies: "@sinclair/typebox" "^0.27.8" -"@jest/source-map@^27.0.6": - version "27.0.6" - 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" - graceful-fs "^4.2.4" - source-map "^0.6.0" - -"@jest/source-map@^27.5.1": - version "27.5.1" - resolved "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz" - integrity sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg== +"@jest/schemas@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" + integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== dependencies: - callsites "^3.0.0" - graceful-fs "^4.2.9" - source-map "^0.6.0" + "@sinclair/typebox" "^0.27.8" "@jest/source-map@^29.6.0": version "29.6.0" @@ -2546,25 +2335,14 @@ callsites "^3.0.0" graceful-fs "^4.2.9" -"@jest/test-result@^27.2.4": - version "27.2.4" - resolved "https://registry.npmjs.org/@jest/test-result/-/test-result-27.2.4.tgz" - integrity sha512-eU+PRo0+lIS01b0dTmMdVZ0TtcRSxEaYquZTRFMQz6CvsehGhx9bRzi9Zdw6VROviJyv7rstU+qAMX5pNBmnfQ== - dependencies: - "@jest/console" "^27.2.4" - "@jest/types" "^27.2.4" - "@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.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz" - integrity sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag== +"@jest/source-map@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4" + integrity sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw== dependencies: - "@jest/console" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/istanbul-lib-coverage" "^2.0.0" - collect-v8-coverage "^1.0.0" + "@jridgewell/trace-mapping" "^0.3.18" + callsites "^3.0.0" + graceful-fs "^4.2.9" "@jest/test-result@^29.6.2": version "29.6.2" @@ -2576,25 +2354,15 @@ "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^27.2.4": - version "27.2.4" - resolved "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.2.4.tgz" - integrity sha512-fpk5eknU3/DXE2QCCG1wv/a468+cfPo3Asu6d6yUtM9LOPh709ubZqrhuUOYfM8hXMrIpIdrv1CdCrWWabX0rQ== - dependencies: - "@jest/test-result" "^27.2.4" - graceful-fs "^4.2.4" - jest-haste-map "^27.2.4" - jest-runtime "^27.2.4" - -"@jest/test-sequencer@^27.5.1": - version "27.5.1" - resolved "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz" - integrity sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ== +"@jest/test-result@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c" + integrity sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA== dependencies: - "@jest/test-result" "^27.5.1" - graceful-fs "^4.2.9" - jest-haste-map "^27.5.1" - jest-runtime "^27.5.1" + "@jest/console" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" "@jest/test-sequencer@^29.6.2": version "29.6.2" @@ -2606,47 +2374,15 @@ jest-haste-map "^29.6.2" slash "^3.0.0" -"@jest/transform@^27.2.4": - version "27.2.4" - resolved "https://registry.npmjs.org/@jest/transform/-/transform-27.2.4.tgz" - integrity sha512-n5FlX2TH0oQGwyVDKPxdJ5nI2sO7TJBFe3u3KaAtt7TOiV4yL+Y+rSFDl+Ic5MpbiA/eqXmLAQxjnBmWgS2rEA== - dependencies: - "@babel/core" "^7.1.0" - "@jest/types" "^27.2.4" - babel-plugin-istanbul "^6.0.0" - 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.2.4" - jest-regex-util "^27.0.6" - jest-util "^27.2.4" - micromatch "^4.0.4" - pirates "^4.0.1" - 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.npmjs.org/@jest/transform/-/transform-27.5.1.tgz" - integrity sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw== +"@jest/test-sequencer@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz#6cef977ce1d39834a3aea887a1726628a6f072ce" + integrity sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw== dependencies: - "@babel/core" "^7.1.0" - "@jest/types" "^27.5.1" - babel-plugin-istanbul "^6.1.1" - chalk "^4.0.0" - convert-source-map "^1.4.0" - fast-json-stable-stringify "^2.0.0" + "@jest/test-result" "^29.7.0" graceful-fs "^4.2.9" - jest-haste-map "^27.5.1" - jest-regex-util "^27.5.1" - jest-util "^27.5.1" - micromatch "^4.0.4" - pirates "^4.0.4" + jest-haste-map "^29.7.0" slash "^3.0.0" - source-map "^0.6.1" - write-file-atomic "^3.0.0" "@jest/transform@^29.6.2": version "29.6.2" @@ -2669,21 +2405,43 @@ slash "^3.0.0" write-file-atomic "^4.0.2" -"@jest/types@^27.2.4": - version "27.2.4" - resolved "https://registry.npmjs.org/@jest/types/-/types-27.2.4.tgz" - integrity sha512-IDO2ezTxeMvQAHxzG/ZvEyA47q0aVfzT95rGFl7bZs/Go0aIucvfDbS2rmnoEdXxlLQhcolmoG/wvL/uKx4tKA== +"@jest/transform@^29.7.0": + version "29.7.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c" + integrity sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw== + dependencies: + "@babel/core" "^7.11.6" + "@jest/types" "^29.6.3" + "@jridgewell/trace-mapping" "^0.3.18" + babel-plugin-istanbul "^6.1.1" + chalk "^4.0.0" + convert-source-map "^2.0.0" + fast-json-stable-stringify "^2.1.0" + graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" + jest-regex-util "^29.6.3" + jest-util "^29.7.0" + micromatch "^4.0.4" + pirates "^4.0.4" + slash "^3.0.0" + write-file-atomic "^4.0.2" + +"@jest/types@30.0.0-alpha.2": + version "30.0.0-alpha.2" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-30.0.0-alpha.2.tgz#32722a6103a9963d531d5b502fb31623830a9417" + integrity sha512-kQ2aDSVtTqrglSgVMe7N11nQtSgy3Q2/Gm1uqDS7eRyD+UG6UFAiWmAQ43YmUkifQE6xtenMTTyuAiznRCMuFw== dependencies: + "@jest/schemas" "30.0.0-alpha.2" "@types/istanbul-lib-coverage" "^2.0.0" "@types/istanbul-reports" "^3.0.0" "@types/node" "*" - "@types/yargs" "^16.0.0" + "@types/yargs" "^17.0.8" chalk "^4.0.0" -"@jest/types@^27.5.1": - version "27.5.1" - resolved "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz" - integrity sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw== +"@jest/types@^27.2.4": + version "27.2.4" + 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" "@types/istanbul-reports" "^3.0.0" @@ -2703,6 +2461,18 @@ "@types/yargs" "^17.0.8" chalk "^4.0.0" +"@jest/types@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" + integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== + dependencies: + "@jest/schemas" "^29.6.3" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + "@jridgewell/gen-mapping@^0.1.0": version "0.1.1" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" @@ -2743,7 +2513,7 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== -"@jridgewell/sourcemap-codec@^1.4.13", "@jridgewell/sourcemap-codec@^1.4.14": +"@jridgewell/sourcemap-codec@^1.4.13", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.4.15": version "1.4.15" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== @@ -2968,21 +2738,34 @@ resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.6.tgz#cee20bd55e68a1720bdab363ecf0c821ded4cd45" integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw== -"@puppeteer/browsers@1.4.6": - version "1.4.6" - resolved "https://registry.yarnpkg.com/@puppeteer/browsers/-/browsers-1.4.6.tgz#1f70fd23d5d2ccce9d29b038e5039d7a1049ca77" - integrity sha512-x4BEjr2SjOPowNeiguzjozQbsc6h437ovD/wu+JpaenxVLm3jkgzHY2xOslMTp50HoTvQreMjiexiGQw1sqZlQ== +"@puppeteer/browsers@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@puppeteer/browsers/-/browsers-1.9.0.tgz#dfd0aad0bdc039572f1b57648f189525d627b7ff" + integrity sha512-QwguOLy44YBGC8vuPP2nmpX4MUN2FzWbsnvZJtiCzecU3lHmVZkaC1tq6rToi9a200m8RzlVtDyxCS0UIDrxUg== dependencies: debug "4.3.4" extract-zip "2.0.1" progress "2.0.3" - proxy-agent "6.3.0" + proxy-agent "6.3.1" tar-fs "3.0.4" unbzip2-stream "1.4.3" - yargs "17.7.1" + yargs "17.7.2" -"@remix-run/router@1.0.2": - version "1.0.2" +"@puppeteer/browsers@1.9.1": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@puppeteer/browsers/-/browsers-1.9.1.tgz#384ee8b09786f0e8f62b1925e4c492424cb549ee" + integrity sha512-PuvK6xZzGhKPvlx3fpfdM2kYY3P/hB1URtK8wA7XUJ6prn6pp22zvJHu48th0SGcHL9SutbPHrFuQgfXTFobWA== + dependencies: + debug "4.3.4" + extract-zip "2.0.1" + progress "2.0.3" + proxy-agent "6.3.1" + tar-fs "3.0.4" + unbzip2-stream "1.4.3" + yargs "17.7.2" + +"@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== @@ -3012,6 +2795,18 @@ magic-string "^0.25.7" resolve "^1.17.0" +"@rollup/plugin-commonjs@^25.0.7": + version "25.0.7" + resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.7.tgz#145cec7589ad952171aeb6a585bbeabd0fd3b4cf" + integrity sha512-nEvcR+LRjEjsaSsc4x3XZfCCvZIaSMenZu/OiwOKGN2UhQpAYI7ru7czFvyWbErlpoGjnSX3D5Ch5FcMA3kRWQ== + dependencies: + "@rollup/pluginutils" "^5.0.1" + commondir "^1.0.1" + estree-walker "^2.0.2" + glob "^8.0.3" + is-reference "1.2.1" + magic-string "^0.30.3" + "@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" @@ -3069,6 +2864,15 @@ estree-walker "^2.0.1" picomatch "^2.2.2" +"@rollup/pluginutils@^5.0.1": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-5.1.0.tgz#7e53eddc8c7f483a4ad0b94afb1f7f5fd3c771e0" + integrity sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g== + dependencies: + "@types/estree" "^1.0.0" + estree-walker "^2.0.2" + picomatch "^2.3.1" + "@rollup/pluginutils@^5.0.2": version "5.0.2" resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-5.0.2.tgz#012b8f53c71e4f6f9cb317e311df1404f56e7a33" @@ -3078,6 +2882,71 @@ estree-walker "^2.0.2" picomatch "^2.3.1" +"@rollup/rollup-android-arm-eabi@4.9.0": + version "4.9.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.0.tgz#0437b27edd7095d0b6d5db99d13af8157d7c58b0" + integrity sha512-+1ge/xmaJpm1KVBuIH38Z94zj9fBD+hp+/5WLaHgyY8XLq1ibxk/zj6dTXaqM2cAbYKq8jYlhHd6k05If1W5xA== + +"@rollup/rollup-android-arm64@4.9.0": + version "4.9.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.0.tgz#d4c14ef9e45d5c46b8d1f611ab8124a611d5be5b" + integrity sha512-im6hUEyQ7ZfoZdNvtwgEJvBWZYauC9KVKq1w58LG2Zfz6zMd8gRrbN+xCVoqA2hv/v6fm9lp5LFGJ3za8EQH3A== + +"@rollup/rollup-darwin-arm64@4.9.0": + version "4.9.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.0.tgz#6f3fdf5712db6b5e3d8f62a86a09cd659dd871f9" + integrity sha512-u7aTMskN6Dmg1lCT0QJ+tINRt+ntUrvVkhbPfFz4bCwRZvjItx2nJtwJnJRlKMMaQCHRjrNqHRDYvE4mBm3DlQ== + +"@rollup/rollup-darwin-x64@4.9.0": + version "4.9.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.0.tgz#626d7786fe7c10b2e8533ad981b4a791fd72b9d0" + integrity sha512-8FvEl3w2ExmpcOmX5RJD0yqXcVSOqAJJUJ29Lca29Ik+3zPS1yFimr2fr5JSZ4Z5gt8/d7WqycpgkX9nocijSw== + +"@rollup/rollup-linux-arm-gnueabihf@4.9.0": + version "4.9.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.0.tgz#57ece7bb1b7659a3ea2ace580a63b8f92b3161f1" + integrity sha512-lHoKYaRwd4gge+IpqJHCY+8Vc3hhdJfU6ukFnnrJasEBUvVlydP8PuwndbWfGkdgSvZhHfSEw6urrlBj0TSSfg== + +"@rollup/rollup-linux-arm64-gnu@4.9.0": + version "4.9.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.0.tgz#345b276b814a5377344adc5780c4dfb7cd0e8ba9" + integrity sha512-JbEPfhndYeWHfOSeh4DOFvNXrj7ls9S/2omijVsao+LBPTPayT1uKcK3dHW3MwDJ7KO11t9m2cVTqXnTKpeaiw== + +"@rollup/rollup-linux-arm64-musl@4.9.0": + version "4.9.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.0.tgz#61cc6516e6e92e2205ea1d0ac30326379b0563c8" + integrity sha512-ahqcSXLlcV2XUBM3/f/C6cRoh7NxYA/W7Yzuv4bDU1YscTFw7ay4LmD7l6OS8EMhTNvcrWGkEettL1Bhjf+B+w== + +"@rollup/rollup-linux-riscv64-gnu@4.9.0": + version "4.9.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.0.tgz#e9add70ddca7bd6f685ec447ae83eb3be552f211" + integrity sha512-uwvOYNtLw8gVtrExKhdFsYHA/kotURUmZYlinH2VcQxNCQJeJXnkmWgw2hI9Xgzhgu7J9QvWiq9TtTVwWMDa+w== + +"@rollup/rollup-linux-x64-gnu@4.9.0": + version "4.9.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.0.tgz#ece153613f0cf2c864dbfc2076c579da8abd51a9" + integrity sha512-m6pkSwcZZD2LCFHZX/zW2aLIISyzWLU3hrLLzQKMI12+OLEzgruTovAxY5sCZJkipklaZqPy/2bEEBNjp+Y7xg== + +"@rollup/rollup-linux-x64-musl@4.9.0": + version "4.9.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.0.tgz#2d2dbdf5fbf2c19d1f3d31b8a7850b57f5799037" + integrity sha512-VFAC1RDRSbU3iOF98X42KaVicAfKf0m0OvIu8dbnqhTe26Kh6Ym9JrDulz7Hbk7/9zGc41JkV02g+p3BivOdAg== + +"@rollup/rollup-win32-arm64-msvc@4.9.0": + version "4.9.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.0.tgz#bf2dbad350376e46cb77fab408bb398ad5f3648d" + integrity sha512-9jPgMvTKXARz4inw6jezMLA2ihDBvgIU9Ml01hjdVpOcMKyxFBJrn83KVQINnbeqDv0+HdO1c09hgZ8N0s820Q== + +"@rollup/rollup-win32-ia32-msvc@4.9.0": + version "4.9.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.0.tgz#5c26b07f74f4054f3ecf202550100496ed2e73f3" + integrity sha512-WE4pT2kTXQN2bAv40Uog0AsV7/s9nT9HBWXAou8+++MBCnY51QS02KYtm6dQxxosKi1VIz/wZIrTQO5UP2EW+Q== + +"@rollup/rollup-win32-x64-msvc@4.9.0": + version "4.9.0" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.0.tgz#4ea610e0c40a07a8afa2977cbf80507f41c2271c" + integrity sha512-aPP5Q5AqNGuT0tnuEkK/g4mnt3ZhheiXrDIiSVIHN9mcN21OyXDVbEMqmXPE7e2OplNLDkcvV+ZoGJa2ZImFgw== + "@rushstack/node-core-library@3.59.0": version "3.59.0" resolved "https://registry.yarnpkg.com/@rushstack/node-core-library/-/node-core-library-3.59.0.tgz#f04db22575a242c30114b4723ba0580b6f2d8c85" @@ -3127,18 +2996,16 @@ resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== +"@sinclair/typebox@^0.31.0": + version "0.31.28" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.31.28.tgz#b68831e7bc7d09daac26968ea32f42bedc968ede" + integrity sha512-/s55Jujywdw/Jpan+vsy6JZs1z2ZTGxTmbZTPiuSL2wz9mfzA2gN1zzaqmvfi4pq+uOt7Du85fkiwv5ymW84aQ== + "@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" - integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ== - dependencies: - type-detect "4.0.8" - "@sinonjs/commons@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.0.tgz#beb434fe875d965265e04722ccfc21df7f755d72" @@ -3153,12 +3020,12 @@ dependencies: "@sinonjs/commons" "^3.0.0" -"@sinonjs/fake-timers@^8.0.1": - version "8.0.1" - resolved "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.0.1.tgz" - integrity sha512-AU7kwFxreVd6OAXcAFlKSmZquiRUU0FvYm44k1Y1QbK7Co4m0aqfGMhjykIeQp/H6rcl+nFmj0zfdUcGVs9Dew== +"@sinonjs/fake-timers@^11.1.0": + version "11.2.2" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-11.2.2.tgz#50063cc3574f4a27bd8453180a04171c85cc9699" + integrity sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw== dependencies: - "@sinonjs/commons" "^1.7.0" + "@sinonjs/commons" "^3.0.0" "@szmarczak/http-timer@^1.1.2": version "1.1.2" @@ -3179,11 +3046,6 @@ 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" - integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== - "@tootallnate/once@2": version "2.0.0" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" @@ -3194,6 +3056,11 @@ resolved "https://registry.yarnpkg.com/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz#db4ecfd499a9765ab24002c3b696d02e6d32a12c" integrity sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA== +"@total-typescript/shoehorn@^0.1.0": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@total-typescript/shoehorn/-/shoehorn-0.1.1.tgz#72d3ba9364faa4f6b8e66c57b7a9094457e3652b" + integrity sha512-XSPcazQsC2Cr7eCiAI+M2bTmMziBvFWYTYMgUDKLbU6i+7m3I2BF5gXF5vKDO8577fONs9CvmTvVa7+nMHMfxg== + "@ts-morph/common@~0.18.0": version "0.18.1" resolved "https://registry.yarnpkg.com/@ts-morph/common/-/common-0.18.1.tgz#ca40c3a62c3f9e17142e0af42633ad63efbae0ec" @@ -3234,7 +3101,7 @@ resolved "https://registry.yarnpkg.com/@types/argparse/-/argparse-1.0.38.tgz#a81fd8606d481f873a3800c6ebae4f1d768a56a9" integrity sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA== -"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": +"@types/babel__core@^7.1.14": version "7.1.16" resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.16.tgz" integrity sha512-EAEHtisTMM+KaKwfWdC3oyllIqswlznXCIVCt7/oRNrh+DhgT4UEBNC/jlADNjvw7UnfbcdkGQcPVZ1xYiLcrQ== @@ -3260,7 +3127,7 @@ "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" -"@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": version "7.14.2" resolved "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.14.2.tgz" integrity sha512-K2waXdXBi2302XUdcHcR1jCeU0LL4TD9HRs/gk0N2Xvrht+G/BfJa4QObBQZfhMdxiCpV3COl5Nfq4uKTeTnJA== @@ -3322,13 +3189,6 @@ "@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" - integrity sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw== - dependencies: - "@types/node" "*" - "@types/graceful-fs@^4.1.3": version "4.1.6" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.6.tgz#e14b2576a1c25026b7f02ede1de3b84c3a1efeae" @@ -3372,7 +3232,7 @@ "@types/pixelmatch" "*" ssim.js "^3.1.1" -"@types/jest@*", "@types/jest@^27.0.2": +"@types/jest@*": version "27.0.2" resolved "https://registry.npmjs.org/@types/jest/-/jest-27.0.2.tgz" integrity sha512-4dRxkS/AFX0c5XW6IPMNOydLn2tEhNhJV7DnYK+0bjoJZ+QTmfucBlihX7aoEsh/ocYtkLC73UbnBXBXIxsULA== @@ -3380,14 +3240,6 @@ jest-diff "^27.0.0" pretty-format "^27.0.0" -"@types/jest@^27.4.1": - version "27.4.1" - 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" - pretty-format "^27.0.0" - "@types/jest@^29.5.0": version "29.5.3" resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.3.tgz#7a35dc0044ffb8b56325c6802a4781a626b05777" @@ -3396,6 +3248,22 @@ expect "^29.0.0" pretty-format "^29.0.0" +"@types/jest@^29.5.10": + version "29.5.10" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.10.tgz#a10fc5bab9e426081c12b2ef73d24d4f0c9b7f50" + integrity sha512-tE4yxKEphEyxj9s4inideLHktW/x6DwesIwWZ9NN1FKf9zbJYsnhBoA9vrHA/IuIOKwPa5PcFBNV4lpMIOEzyQ== + dependencies: + expect "^29.0.0" + pretty-format "^29.0.0" + +"@types/jest@^29.5.11": + version "29.5.11" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.11.tgz#0c13aa0da7d0929f078ab080ae5d4ced80fa2f2c" + integrity sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ== + dependencies: + expect "^29.0.0" + pretty-format "^29.0.0" + "@types/jsdom@^20.0.0": version "20.0.0" resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-20.0.0.tgz#4414fb629465167f8b7b3804b9e067bdd99f1791" @@ -3405,6 +3273,15 @@ "@types/tough-cookie" "*" parse5 "^7.0.0" +"@types/jsdom@^21.1.1", "@types/jsdom@^21.1.6": + version "21.1.6" + resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-21.1.6.tgz#bcbc7b245787ea863f3da1ef19aa1dcfb9271a1b" + integrity sha512-/7kkMsC+/kMs7gAYmmBR9P0vGTnOoLhQhyhQJSlXGI5bzTHp6xdo0TtKWQAsz6pmSAeVqKSbqeyP6hytqr9FDw== + dependencies: + "@types/node" "*" + "@types/tough-cookie" "*" + parse5 "^7.0.0" + "@types/json-schema@^7.0.9": version "7.0.11" resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz" @@ -3481,10 +3358,10 @@ dependencies: "@types/node" "*" -"@types/prettier@^2.1.5": - version "2.4.1" - resolved "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.1.tgz" - integrity sha512-Fo79ojj3vdEZOHg3wR9ksAMRz4P3S5fDB5e/YWZiFnyFQI1WY2Vftu9XoXVVtJfxB7Bpce/QTqWSSntkz2Znrw== +"@types/prettier@2.7.3": + version "2.7.3" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.3.tgz#3e51a17e291d01d17d3fc61422015a933af7a08f" + integrity sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA== "@types/prop-types@*": version "15.7.5" @@ -3853,11 +3730,6 @@ resolved "https://registry.yarnpkg.com/@zag-js/focus-visible/-/focus-visible-0.1.0.tgz#9777bbaff8316d0b3a14a9095631e1494f69dbc7" integrity sha512-PeaBcTmdZWcFf7n1aM+oiOdZc+sy14qi0emPIeUuGMTjbP0xLGrZu43kdpHnWSXy7/r4Ubp/vlg50MCV8+9Isg== -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== - abab@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" @@ -3871,14 +3743,6 @@ accepts@~1.3.7: mime-types "~2.1.24" negotiator "0.6.2" -acorn-globals@^6.0.0: - version "6.0.0" - 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-globals@^7.0.0: version "7.0.1" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-7.0.1.tgz#0dbf05c44fa7c94332914c02066d5beff62c40c3" @@ -3892,27 +3756,17 @@ acorn-jsx@^5.3.1, acorn-jsx@^5.3.2: 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.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz" - integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== - acorn-walk@^8.0.2, acorn-walk@^8.1.1: version "8.2.0" resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== -acorn@^7.1.1: - version "7.4.1" - resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz" - integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== - acorn@^8.1.0, acorn@^8.8.1: version "8.10.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== -acorn@^8.2.4, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.1: +acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.1: version "8.7.1" resolved "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz" integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A== @@ -3993,7 +3847,7 @@ agent-base@6: dependencies: debug "4" -agent-base@^7.0.1, agent-base@^7.0.2, agent-base@^7.1.0: +agent-base@^7.0.2, agent-base@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.0.tgz#536802b76bc0b34aa50195eb2442276d613e3434" integrity sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg== @@ -4280,34 +4134,6 @@ b4a@^1.6.4: resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.6.4.tgz#ef1c1422cae5ce6535ec191baeed7567443f36c9" integrity sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw== -babel-jest@^27.2.4: - version "27.2.4" - resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-27.2.4.tgz" - integrity sha512-f24OmxyWymk5jfgLdlCMu4fTs4ldxFBIdn5sJdhvGC1m08rSkJ5hYbWkNmfBSvE/DjhCVNSHXepxsI6THGfGsg== - dependencies: - "@jest/transform" "^27.2.4" - "@jest/types" "^27.2.4" - "@types/babel__core" "^7.1.14" - babel-plugin-istanbul "^6.0.0" - babel-preset-jest "^27.2.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.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz" - integrity sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg== - dependencies: - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/babel__core" "^7.1.14" - babel-plugin-istanbul "^6.1.1" - babel-preset-jest "^27.5.1" - chalk "^4.0.0" - graceful-fs "^4.2.9" - slash "^3.0.0" - babel-jest@^29.6.2: version "29.6.2" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.6.2.tgz#cada0a59e07f5acaeb11cbae7e3ba92aec9c1126" @@ -4321,16 +4147,18 @@ babel-jest@^29.6.2: graceful-fs "^4.2.9" slash "^3.0.0" -babel-plugin-istanbul@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz" - integrity sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ== +babel-jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5" + integrity sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg== dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@istanbuljs/load-nyc-config" "^1.0.0" - "@istanbuljs/schema" "^0.1.2" - istanbul-lib-instrument "^4.0.0" - test-exclude "^6.0.0" + "@jest/transform" "^29.7.0" + "@types/babel__core" "^7.1.14" + babel-plugin-istanbul "^6.1.1" + babel-preset-jest "^29.6.3" + chalk "^4.0.0" + graceful-fs "^4.2.9" + slash "^3.0.0" babel-plugin-istanbul@^6.1.1: version "6.1.1" @@ -4343,30 +4171,20 @@ babel-plugin-istanbul@^6.1.1: istanbul-lib-instrument "^5.0.4" test-exclude "^6.0.0" -babel-plugin-jest-hoist@^27.2.0: - version "27.2.0" - 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" - "@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.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz" - integrity sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ== +babel-plugin-jest-hoist@^29.5.0: + version "29.5.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz#a97db437936f441ec196990c9738d4b88538618a" + integrity sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w== dependencies: "@babel/template" "^7.3.3" "@babel/types" "^7.3.3" - "@types/babel__core" "^7.0.0" + "@types/babel__core" "^7.1.14" "@types/babel__traverse" "^7.0.6" -babel-plugin-jest-hoist@^29.5.0: - version "29.5.0" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz#a97db437936f441ec196990c9738d4b88538618a" - integrity sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w== +babel-plugin-jest-hoist@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626" + integrity sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg== dependencies: "@babel/template" "^7.3.3" "@babel/types" "^7.3.3" @@ -4400,22 +4218,6 @@ babel-preset-current-node-syntax@^1.0.0: "@babel/plugin-syntax-optional-chaining" "^7.8.3" "@babel/plugin-syntax-top-level-await" "^7.8.3" -babel-preset-jest@^27.2.0: - version "27.2.0" - 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" - babel-preset-current-node-syntax "^1.0.0" - -babel-preset-jest@^27.5.1: - version "27.5.1" - 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" - babel-preset-current-node-syntax "^1.0.0" - babel-preset-jest@^29.5.0: version "29.5.0" resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz#57bc8cc88097af7ff6a5ab59d1cd29d52a5916e2" @@ -4424,6 +4226,14 @@ babel-preset-jest@^29.5.0: babel-plugin-jest-hoist "^29.5.0" babel-preset-current-node-syntax "^1.0.0" +babel-preset-jest@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c" + integrity sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA== + dependencies: + babel-plugin-jest-hoist "^29.6.3" + babel-preset-current-node-syntax "^1.0.0" + babel-runtime@^6.26.0: version "6.26.0" resolved "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz" @@ -4587,11 +4397,6 @@ breakword@^1.0.5: 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" - integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== - browser-resolve@^1.11.3: version "1.11.3" resolved "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz" @@ -4610,17 +4415,6 @@ browserslist@^4.0.0: escalade "^3.1.1" node-releases "^1.1.71" -browserslist@^4.16.6: - version "4.17.3" - 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" - electron-to-chromium "^1.3.857" - escalade "^3.1.1" - node-releases "^1.1.77" - picocolors "^0.2.1" - browserslist@^4.17.5: version "4.19.1" resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz" @@ -4806,7 +4600,7 @@ 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, caniuse-lite@^1.0.30001264, caniuse-lite@^1.0.30001286, caniuse-lite@^1.0.30001400: +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001219, caniuse-lite@^1.0.30001286, 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== @@ -4936,23 +4730,27 @@ chrome-launcher@0.15.0: is-wsl "^2.2.0" lighthouse-logger "^1.0.0" -chromium-bidi@0.4.16: - version "0.4.16" - resolved "https://registry.yarnpkg.com/chromium-bidi/-/chromium-bidi-0.4.16.tgz#8a67bfdf6bb8804efc22765a82859d20724b46ab" - integrity sha512-7ZbXdWERxRxSwo3txsBjjmc/NLxqb1Bk30mRb0BMS4YIaiV6zvKZqL/UAH+DdqcDYayDWk2n/y8klkBDODrPvA== +chromium-bidi@0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/chromium-bidi/-/chromium-bidi-0.5.1.tgz#390c1af350c4887824a33d82190de1cc5c5680fc" + integrity sha512-dcCqOgq9fHKExc2R4JZs/oKbOghWpUNFAJODS8WKRtLhp3avtIH5UDCBrutdqZdh3pARogH8y1ObXm87emwb3g== + dependencies: + mitt "3.0.1" + urlpattern-polyfill "9.0.0" + +chromium-bidi@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/chromium-bidi/-/chromium-bidi-0.5.2.tgz#358b03bb7c53e0f8d0fd77d596ea67ee30f7ff06" + integrity sha512-PbVOSddxgKyj+JByqavWMNqWPCoCaT6XK5Z1EFe168sxnB/BM51LnZEPXSbFcFAJv/+u2B4XNTs9uXxy4GW3cQ== dependencies: - mitt "3.0.0" + mitt "3.0.1" + urlpattern-polyfill "9.0.0" ci-info@^2.0.0: version "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.1: - version "3.2.0" - 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.npmjs.org/ci-info/-/ci-info-3.3.0.tgz" @@ -4963,6 +4761,11 @@ ci-info@^3.7.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== +ci-info@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-4.0.0.tgz#65466f8b280fc019b9f50a5388115d17a63a44f2" + integrity sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg== + cjs-module-lexer@^1.0.0: version "1.2.2" resolved "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz" @@ -5235,18 +5038,18 @@ 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== -convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: +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== + +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" integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== 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== - convert-source-map@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" @@ -5289,14 +5092,14 @@ 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.2.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.2.0.tgz#f7d17c56a590856cd1e7cee98734dca272b0d8fd" - integrity sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ== +cosmiconfig@8.3.6: + version "8.3.6" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.3.6.tgz#060a2b871d66dba6c8538ea1118ba1ac16f5fae3" + integrity sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA== dependencies: - import-fresh "^3.2.1" + import-fresh "^3.3.0" js-yaml "^4.1.0" - parse-json "^5.0.0" + parse-json "^5.2.0" path-type "^4.0.0" cosmiconfig@^5.0.0: @@ -5320,6 +5123,19 @@ cosmiconfig@^7.0.0: path-type "^4.0.0" yaml "^1.10.0" +create-jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320" + integrity sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q== + dependencies: + "@jest/types" "^29.6.3" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + jest-config "^29.7.0" + jest-util "^29.7.0" + prompts "^2.0.1" + create-require@^1.1.0: version "1.1.1" resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz" @@ -5564,7 +5380,12 @@ csso@^4.0.2: dependencies: css-tree "^1.1.2" -cssom@^0.4.4, cssom@^0.5.0, "cssom@https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz": +cssom@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.5.0.tgz#d254fa92cd8b6fbd83811b9fbaed34663cc17c36" + integrity sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw== + +"cssom@https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz": version "0.6.0" resolved "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz#ed298055b97cbddcdeb278f904857629dec5e0e1" @@ -5575,11 +5396,18 @@ cssom@~0.3.6: cssstyle@^2.3.0: version "2.3.0" - resolved "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== dependencies: cssom "~0.3.6" +cssstyle@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-3.0.0.tgz#17ca9c87d26eac764bb8cfd00583cff21ce0277a" + integrity sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg== + dependencies: + rrweb-cssom "^0.6.0" + csstype@^3.0.11, csstype@^3.0.2: version "3.1.1" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.1.tgz#841b532c45c758ee546a11d5bd7b7b473c8c30b9" @@ -5622,15 +5450,6 @@ data-uri-to-buffer@^5.0.1: resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-5.0.1.tgz#db89a9e279c2ffe74f50637a59a32fb23b3e4d7c" integrity sha512-a9l6T1qqDogvvnw0nKlfZzqsyikEBZBClF39V3TFoKhDtGBqHu2HkuomJc02j5zft8zrUaXEuoicLeW54RkzPg== -data-urls@^2.0.0: - version "2.0.0" - 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" - data-urls@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-3.0.2.tgz#9cf24a477ae22bcef5cd5f6f0bfbc1d2d3be9143" @@ -5640,6 +5459,23 @@ data-urls@^3.0.2: whatwg-mimetype "^3.0.0" whatwg-url "^11.0.0" +data-urls@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-4.0.0.tgz#333a454eca6f9a5b7b0f1013ff89074c3f522dd4" + integrity sha512-/mMTei/JXPqvFqQtfyTowxmJVwr2PVAeCcDxyFf6LhoOu/09TX2OX3kb2wzi4DMXcfj4OItwDOnhl5oziPnT6g== + dependencies: + abab "^2.0.6" + whatwg-mimetype "^3.0.0" + whatwg-url "^12.0.0" + +data-urls@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-5.0.0.tgz#2f76906bce1824429ffecb6920f45a0b30f00dde" + integrity sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg== + dependencies: + whatwg-mimetype "^4.0.0" + whatwg-url "^14.0.0" + dataloader@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-1.4.0.tgz#bca11d867f5d3f1b9ed9f737bd15970c65dff5c8" @@ -5694,12 +5530,7 @@ decamelize@^1.1.0, decamelize@^1.2.0: 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.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz" - integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ== - -decimal.js@^10.4.2: +decimal.js@^10.4.2, decimal.js@^10.4.3: version "10.4.3" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== @@ -5711,11 +5542,6 @@ decompress-response@^3.3.0: 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= - dedent@^1.0.0: version "1.5.1" resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.1.tgz#4f3fc94c8b711e9bb2800d185cd6ad20f2a90aff" @@ -5826,31 +5652,26 @@ 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.1147663: - version "0.0.1147663" - resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1147663.tgz#4ec5610b39a6250d1f87e6b9c7e16688ed0ac78e" - integrity sha512-hyWmRrexdhbZ1tcJUGpO95ivbRhWXz++F4Ko+n21AY5PNln2ovoJw+8ZMNDTtip+CNFQfrtLVh/w4009dXO/eQ== - -devtools-protocol@0.0.869402: - version "0.0.869402" - resolved "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.869402.tgz" - integrity sha512-VvlVYY+VDJe639yHs5PHISzdWTLL3Aw8rO4cvUtwvoxFd6FHbE4OpHHcde52M6096uYYazAmd4l0o5VuFRO2WA== +devtools-protocol@0.0.1203626: + version "0.0.1203626" + resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1203626.tgz#4366a4c81a7e0d4fd6924e9182c67f1e5941e820" + integrity sha512-nEzHZteIUZfGCZtTiS1fRpC8UZmsfD1SiyPvaUNvS13dvKf666OAm8YTi0+Ca3n1nLEyu49Cy4+dPWpaHFJk9g== diff-sequences@^27.0.6: version "27.0.6" 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.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz" - integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== - diff-sequences@^29.4.3: version "29.4.3" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2" integrity sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA== +diff-sequences@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" + integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== + diff@^3.1.0, diff@^3.2.0: version "3.5.0" resolved "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz" @@ -5902,13 +5723,6 @@ domelementtype@^2.0.1, domelementtype@^2.2.0: 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.npmjs.org/domexception/-/domexception-2.0.1.tgz" - integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg== - dependencies: - webidl-conversions "^5.0.0" - domexception@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673" @@ -6004,11 +5818,6 @@ electron-to-chromium@^1.3.723: 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.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.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.37.tgz" @@ -6034,11 +5843,6 @@ emittery@^0.13.1: resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== -emittery@^0.8.1: - version "0.8.1" - resolved "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz" - integrity sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg== - emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" @@ -6910,28 +6714,6 @@ expand-range@^1.8.1: dependencies: fill-range "^2.1.0" -expect@^27.2.4: - version "27.2.4" - resolved "https://registry.npmjs.org/expect/-/expect-27.2.4.tgz" - integrity sha512-gOtuonQ8TCnbNNCSw2fhVzRf8EFYDII4nB5NmG4IEV0rbUnW1I5zXvoTntU4iicB/Uh0oZr20NGlOLdJiwsOZA== - dependencies: - "@jest/types" "^27.2.4" - ansi-styles "^5.0.0" - jest-get-type "^27.0.6" - jest-matcher-utils "^27.2.4" - jest-message-util "^27.2.4" - jest-regex-util "^27.0.6" - -expect@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz" - integrity sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw== - dependencies: - "@jest/types" "^27.5.1" - jest-get-type "^27.5.1" - jest-matcher-utils "^27.5.1" - jest-message-util "^27.5.1" - expect@^29.0.0, expect@^29.6.2: version "29.6.2" resolved "https://registry.yarnpkg.com/expect/-/expect-29.6.2.tgz#7b08e83eba18ddc4a2cf62b5f2d1918f5cd84521" @@ -6944,6 +6726,17 @@ expect@^29.0.0, expect@^29.6.2: jest-message-util "^29.6.2" jest-util "^29.6.2" +expect@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" + integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw== + dependencies: + "@jest/expect-utils" "^29.7.0" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + express@^4.16.4: version "4.17.1" resolved "https://registry.npmjs.org/express/-/express-4.17.1.tgz" @@ -7006,7 +6799,7 @@ extglob@^0.3.1: dependencies: is-extglob "^1.0.0" -extract-zip@2.0.1, extract-zip@^2.0.0: +extract-zip@2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz" integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== @@ -7307,15 +7100,6 @@ forever-agent@~0.6.1: 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.npmjs.org/form-data/-/form-data-3.0.1.tgz" - integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== - dependencies: - asynckit "^0.4.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" @@ -7659,7 +7443,7 @@ glob@^6.0.1: 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: +glob@^7.0.3, 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" integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== @@ -7671,6 +7455,17 @@ 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@^8.0.3: + version "8.1.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" + integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^5.0.1" + once "^1.3.0" + glob@~7.2.0: version "7.2.2" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.2.tgz#29deb38e1ef90f132d5958abe9c3ee8e87f3c318" @@ -7780,7 +7575,7 @@ got@^9.6.0: to-readable-stream "^1.0.0" url-parse-lax "^3.0.0" -graceful-fs@^4.1.15, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4: +graceful-fs@^4.1.15, graceful-fs@^4.1.6, graceful-fs@^4.2.0: version "4.2.8" resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz" integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== @@ -7958,13 +7753,6 @@ hsla-regex@^1.0.0: 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.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz" - integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ== - dependencies: - whatwg-encoding "^1.0.5" - html-encoding-sniffer@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9" @@ -7972,6 +7760,13 @@ html-encoding-sniffer@^3.0.0: dependencies: whatwg-encoding "^2.0.0" +html-encoding-sniffer@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz#696df529a7cfd82446369dc5193e590a3735b448" + integrity sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ== + dependencies: + whatwg-encoding "^3.1.1" + html-escaper@^2.0.0: version "2.0.2" resolved "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz" @@ -8014,15 +7809,6 @@ http-errors@~1.7.2: statuses ">= 1.5.0 < 2" toidentifier "1.0.0" -http-proxy-agent@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz" - integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== - dependencies: - "@tootallnate/once" "1" - agent-base "6" - debug "4" - http-proxy-agent@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" @@ -8057,18 +7843,10 @@ https-proxy-agent@5.0.1, https-proxy-agent@^5.0.1: agent-base "6" debug "4" -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@^7.0.0: - version "7.0.1" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.1.tgz#0277e28f13a07d45c663633841e20a40aaafe0ab" - integrity sha512-Eun8zV0kcYS1g19r78osiQLEFIRspRUDd9tIfBCTBPBeMieF/EsJNL8VI3xOIdYRDEkjQnqOYPsZ2DsWsVsFwQ== +https-proxy-agent@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz#e2645b846b90e96c6e6f347fb5b2e41f1590b09b" + integrity sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA== dependencies: agent-base "^7.0.2" debug "4" @@ -8165,7 +7943,7 @@ 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: +import-fresh@3.3.0, import-fresh@^3.0.0, import-fresh@^3.2.1, import-fresh@^3.3.0: version "3.3.0" resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== @@ -8407,13 +8185,6 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" -is-ci@^3.0.0: - version "3.0.0" - 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.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz" @@ -8610,7 +8381,7 @@ is-primitive@^2.0.0: resolved "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz" integrity sha1-IHurkWOEmcB7Kt8kCkGochADRXU= -is-reference@^1.2.1: +is-reference@1.2.1, is-reference@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz" integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ== @@ -8780,16 +8551,6 @@ istanbul-lib-coverage@^3.2.0: 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.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz" - integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== - dependencies: - "@babel/core" "^7.7.5" - "@istanbuljs/schema" "^0.1.2" - istanbul-lib-coverage "^3.0.0" - semver "^6.3.0" - istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: version "5.1.0" resolved "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.1.0.tgz" @@ -8801,6 +8562,17 @@ istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: istanbul-lib-coverage "^3.2.0" semver "^6.3.0" +istanbul-lib-instrument@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz#71e87707e8041428732518c6fb5211761753fbdf" + integrity sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA== + dependencies: + "@babel/core" "^7.12.3" + "@babel/parser" "^7.14.7" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.2.0" + semver "^7.5.4" + istanbul-lib-report@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz" @@ -8819,14 +8591,6 @@ istanbul-lib-source-maps@^4.0.0: istanbul-lib-coverage "^3.0.0" source-map "^0.6.1" -istanbul-reports@^3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz" - integrity sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw== - dependencies: - html-escaper "^2.0.0" - istanbul-lib-report "^3.0.0" - istanbul-reports@^3.1.3: version "3.1.3" resolved "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.3.tgz" @@ -8840,24 +8604,6 @@ jed@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" - integrity sha512-eeO1C1u4ex7pdTroYXezr+rbr957myyVoKGjcY4R1TJi3A+9v+4fu1Iv9J4eLq1bgFyT3O3iRWU9lZsEE7J72Q== - dependencies: - "@jest/types" "^27.2.4" - execa "^5.0.0" - throat "^6.0.1" - -jest-changed-files@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz" - integrity sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw== - dependencies: - "@jest/types" "^27.5.1" - execa "^5.0.0" - throat "^6.0.1" - jest-changed-files@^29.5.0: version "29.5.0" resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.5.0.tgz#e88786dca8bf2aa899ec4af7644e16d9dcf9b23e" @@ -8866,69 +8612,28 @@ jest-changed-files@^29.5.0: execa "^5.0.0" p-limit "^3.1.0" -jest-circus@^27.2.4: - version "27.2.4" - resolved "https://registry.npmjs.org/jest-circus/-/jest-circus-27.2.4.tgz" - integrity sha512-TtheheTElrGjlsY9VxkzUU1qwIx05ItIusMVKnvNkMt4o/PeegLRcjq3Db2Jz0GGdBalJdbzLZBgeulZAJxJWA== +jest-changed-files@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a" + integrity sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w== dependencies: - "@jest/environment" "^27.2.4" - "@jest/test-result" "^27.2.4" - "@jest/types" "^27.2.4" + execa "^5.0.0" + jest-util "^29.7.0" + p-limit "^3.1.0" + +jest-circus@^29.6.2: + version "29.6.2" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.6.2.tgz#1e6ffca60151ac66cad63fce34f443f6b5bb4258" + integrity sha512-G9mN+KOYIUe2sB9kpJkO9Bk18J4dTDArNFPwoZ7WKHKel55eKIS/u2bLthxgojwlf9NLCVQfgzM/WsOVvoC6Fw== + dependencies: + "@jest/environment" "^29.6.2" + "@jest/expect" "^29.6.2" + "@jest/test-result" "^29.6.2" + "@jest/types" "^29.6.1" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" - dedent "^0.7.0" - expect "^27.2.4" - is-generator-fn "^2.0.0" - jest-each "^27.2.4" - jest-matcher-utils "^27.2.4" - jest-message-util "^27.2.4" - jest-runtime "^27.2.4" - jest-snapshot "^27.2.4" - jest-util "^27.2.4" - pretty-format "^27.2.4" - 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.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz" - integrity sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw== - dependencies: - "@jest/environment" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/node" "*" - chalk "^4.0.0" - co "^4.6.0" - dedent "^0.7.0" - expect "^27.5.1" - is-generator-fn "^2.0.0" - jest-each "^27.5.1" - jest-matcher-utils "^27.5.1" - jest-message-util "^27.5.1" - jest-runtime "^27.5.1" - jest-snapshot "^27.5.1" - jest-util "^27.5.1" - pretty-format "^27.5.1" - slash "^3.0.0" - stack-utils "^2.0.3" - throat "^6.0.1" - -jest-circus@^29.6.2: - version "29.6.2" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.6.2.tgz#1e6ffca60151ac66cad63fce34f443f6b5bb4258" - integrity sha512-G9mN+KOYIUe2sB9kpJkO9Bk18J4dTDArNFPwoZ7WKHKel55eKIS/u2bLthxgojwlf9NLCVQfgzM/WsOVvoC6Fw== - dependencies: - "@jest/environment" "^29.6.2" - "@jest/expect" "^29.6.2" - "@jest/test-result" "^29.6.2" - "@jest/types" "^29.6.1" - "@types/node" "*" - chalk "^4.0.0" - co "^4.6.0" - dedent "^1.0.0" + dedent "^1.0.0" is-generator-fn "^2.0.0" jest-each "^29.6.2" jest-matcher-utils "^29.6.2" @@ -8942,41 +8647,31 @@ jest-circus@^29.6.2: slash "^3.0.0" stack-utils "^2.0.3" -jest-cli@^27.2.4: - version "27.2.4" - resolved "https://registry.npmjs.org/jest-cli/-/jest-cli-27.2.4.tgz" - integrity sha512-4kpQQkg74HYLaXo3nzwtg4PYxSLgL7puz1LXHj5Tu85KmlIpxQFjRkXlx4V47CYFFIDoyl3rHA/cXOxUWyMpNg== - dependencies: - "@jest/core" "^27.2.4" - "@jest/test-result" "^27.2.4" - "@jest/types" "^27.2.4" - chalk "^4.0.0" - exit "^0.1.2" - graceful-fs "^4.2.4" - import-local "^3.0.2" - jest-config "^27.2.4" - jest-util "^27.2.4" - jest-validate "^27.2.4" - prompts "^2.0.1" - yargs "^16.2.0" - -jest-cli@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz" - integrity sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw== +jest-circus@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.7.0.tgz#b6817a45fcc835d8b16d5962d0c026473ee3668a" + integrity sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw== dependencies: - "@jest/core" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/environment" "^29.7.0" + "@jest/expect" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" chalk "^4.0.0" - exit "^0.1.2" - graceful-fs "^4.2.9" - import-local "^3.0.2" - jest-config "^27.5.1" - jest-util "^27.5.1" - jest-validate "^27.5.1" - prompts "^2.0.1" - yargs "^16.2.0" + co "^4.6.0" + dedent "^1.0.0" + is-generator-fn "^2.0.0" + jest-each "^29.7.0" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-runtime "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" + p-limit "^3.1.0" + pretty-format "^29.7.0" + pure-rand "^6.0.0" + slash "^3.0.0" + stack-utils "^2.0.3" jest-cli@^29.6.2: version "29.6.2" @@ -8996,62 +8691,22 @@ jest-cli@^29.6.2: prompts "^2.0.1" yargs "^17.3.1" -jest-config@^27.2.4: - version "27.2.4" - resolved "https://registry.npmjs.org/jest-config/-/jest-config-27.2.4.tgz" - integrity sha512-tWy0UxhdzqiKyp4l5Vq4HxLyD+gH5td+GCF3c22/DJ0bYAOsMo+qi2XtbJI6oYMH5JOJQs9nLW/r34nvFCehjA== - dependencies: - "@babel/core" "^7.1.0" - "@jest/test-sequencer" "^27.2.4" - "@jest/types" "^27.2.4" - babel-jest "^27.2.4" - chalk "^4.0.0" - deepmerge "^4.2.2" - glob "^7.1.1" - graceful-fs "^4.2.4" - is-ci "^3.0.0" - jest-circus "^27.2.4" - jest-environment-jsdom "^27.2.4" - jest-environment-node "^27.2.4" - jest-get-type "^27.0.6" - jest-jasmine2 "^27.2.4" - jest-regex-util "^27.0.6" - jest-resolve "^27.2.4" - jest-runner "^27.2.4" - jest-util "^27.2.4" - jest-validate "^27.2.4" - micromatch "^4.0.4" - pretty-format "^27.2.4" - -jest-config@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz" - integrity sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA== +jest-cli@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995" + integrity sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg== dependencies: - "@babel/core" "^7.8.0" - "@jest/test-sequencer" "^27.5.1" - "@jest/types" "^27.5.1" - babel-jest "^27.5.1" + "@jest/core" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" chalk "^4.0.0" - ci-info "^3.2.0" - deepmerge "^4.2.2" - glob "^7.1.1" - graceful-fs "^4.2.9" - jest-circus "^27.5.1" - jest-environment-jsdom "^27.5.1" - jest-environment-node "^27.5.1" - jest-get-type "^27.5.1" - jest-jasmine2 "^27.5.1" - jest-regex-util "^27.5.1" - jest-resolve "^27.5.1" - jest-runner "^27.5.1" - jest-util "^27.5.1" - jest-validate "^27.5.1" - micromatch "^4.0.4" - parse-json "^5.2.0" - pretty-format "^27.5.1" - slash "^3.0.0" - strip-json-comments "^3.1.1" + create-jest "^29.7.0" + exit "^0.1.2" + import-local "^3.0.2" + jest-config "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" + yargs "^17.3.1" jest-config@^29.6.2: version "29.6.2" @@ -9081,6 +8736,34 @@ jest-config@^29.6.2: slash "^3.0.0" strip-json-comments "^3.1.1" +jest-config@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.7.0.tgz#bcbda8806dbcc01b1e316a46bb74085a84b0245f" + integrity sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ== + dependencies: + "@babel/core" "^7.11.6" + "@jest/test-sequencer" "^29.7.0" + "@jest/types" "^29.6.3" + babel-jest "^29.7.0" + chalk "^4.0.0" + ci-info "^3.2.0" + deepmerge "^4.2.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-circus "^29.7.0" + jest-environment-node "^29.7.0" + jest-get-type "^29.6.3" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-runner "^29.7.0" + jest-util "^29.7.0" + jest-validate "^29.7.0" + micromatch "^4.0.4" + parse-json "^5.2.0" + pretty-format "^29.7.0" + slash "^3.0.0" + strip-json-comments "^3.1.1" + jest-diff@^23.6.0: version "23.6.0" resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-23.6.0.tgz" @@ -9091,7 +8774,7 @@ jest-diff@^23.6.0: jest-get-type "^22.1.0" pretty-format "^23.6.0" -jest-diff@^27.0.0, jest-diff@^27.2.4: +jest-diff@^27.0.0: version "27.2.4" resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-27.2.4.tgz" integrity sha512-bLAVlDSCR3gqUPGv+4nzVpEXGsHh98HjUL7Vb2hVyyuBDoQmja8eJb0imUABsuxBeUVmf47taJSAd9nDrwWKEg== @@ -9101,16 +8784,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.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz" - integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw== - dependencies: - chalk "^4.0.0" - diff-sequences "^27.5.1" - jest-get-type "^27.5.1" - pretty-format "^27.5.1" - jest-diff@^29.6.2: version "29.6.2" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.6.2.tgz#c36001e5543e82a0805051d3ceac32e6825c1c46" @@ -9121,19 +8794,15 @@ jest-diff@^29.6.2: jest-get-type "^29.4.3" pretty-format "^29.6.2" -jest-docblock@^27.0.6: - version "27.0.6" - resolved "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.0.6.tgz" - integrity sha512-Fid6dPcjwepTFraz0YxIMCi7dejjJ/KL9FBjPYhBp4Sv1Y9PdhImlKZqYU555BlN4TQKaTc+F2Av1z+anVyGkA== +jest-diff@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" + integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== dependencies: - detect-newline "^3.0.0" - -jest-docblock@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz" - integrity sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ== - dependencies: - detect-newline "^3.0.0" + chalk "^4.0.0" + diff-sequences "^29.6.3" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" jest-docblock@^29.4.3: version "29.4.3" @@ -9142,27 +8811,12 @@ jest-docblock@^29.4.3: dependencies: detect-newline "^3.0.0" -jest-each@^27.2.4: - version "27.2.4" - resolved "https://registry.npmjs.org/jest-each/-/jest-each-27.2.4.tgz" - integrity sha512-w9XVc+0EDBUTJS4xBNJ7N2JCcWItFd006lFjz77OarAQcQ10eFDBMrfDv2GBJMKlXe9aq0HrIIF51AXcZrRJyg== +jest-docblock@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.7.0.tgz#8fddb6adc3cdc955c93e2a87f61cfd350d5d119a" + integrity sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g== dependencies: - "@jest/types" "^27.2.4" - chalk "^4.0.0" - jest-get-type "^27.0.6" - jest-util "^27.2.4" - pretty-format "^27.2.4" - -jest-each@^27.5.1: - version "27.5.1" - 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" - chalk "^4.0.0" - jest-get-type "^27.5.1" - jest-util "^27.5.1" - pretty-format "^27.5.1" + detect-newline "^3.0.0" jest-each@^29.6.2: version "29.6.2" @@ -9175,31 +8829,16 @@ jest-each@^29.6.2: jest-util "^29.6.2" pretty-format "^29.6.2" -jest-environment-jsdom@^27.2.4: - version "27.2.4" - resolved "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.2.4.tgz" - integrity sha512-X70pTXFSypD7AIzKT1mLnDi5hP9w9mdTRcOGOmoDoBrNyNEg4rYm6d4LQWFLc9ps1VnMuDOkFSG0wjSNYGjkng== +jest-each@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1" + integrity sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ== dependencies: - "@jest/environment" "^27.2.4" - "@jest/fake-timers" "^27.2.4" - "@jest/types" "^27.2.4" - "@types/node" "*" - jest-mock "^27.2.4" - jest-util "^27.2.4" - jsdom "^16.6.0" - -jest-environment-jsdom@^27.5.1: - version "27.5.1" - 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" - "@jest/fake-timers" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/node" "*" - jest-mock "^27.5.1" - jest-util "^27.5.1" - jsdom "^16.6.0" + "@jest/types" "^29.6.3" + chalk "^4.0.0" + jest-get-type "^29.6.3" + jest-util "^29.7.0" + pretty-format "^29.7.0" jest-environment-jsdom@^29.6.0: version "29.6.2" @@ -9215,29 +8854,33 @@ jest-environment-jsdom@^29.6.0: jest-util "^29.6.2" jsdom "^20.0.0" -jest-environment-node@^27.2.4: - version "27.2.4" - resolved "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.2.4.tgz" - integrity sha512-ZbVbFSnbzTvhLOIkqh5lcLuGCCFvtG4xTXIRPK99rV2KzQT3kNg16KZwfTnLNlIiWCE8do960eToeDfcqmpSAw== +jest-environment-jsdom@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz#d206fa3551933c3fd519e5dfdb58a0f5139a837f" + integrity sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA== dependencies: - "@jest/environment" "^27.2.4" - "@jest/fake-timers" "^27.2.4" - "@jest/types" "^27.2.4" + "@jest/environment" "^29.7.0" + "@jest/fake-timers" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/jsdom" "^20.0.0" "@types/node" "*" - jest-mock "^27.2.4" - jest-util "^27.2.4" + jest-mock "^29.7.0" + jest-util "^29.7.0" + jsdom "^20.0.0" -jest-environment-node@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz" - integrity sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw== +jest-environment-jsdom@^30.0.0-alpha.2: + version "30.0.0-alpha.2" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-30.0.0-alpha.2.tgz#422e08c0c7fbd769c916ab92d5253b5607f7fba0" + integrity sha512-ZcU+Oo236NX9R4uGfNavhzxr2S2RwRFp8xZtZCbP2CZSYnm+x5YO75WbsQbfaZvNJxUeIBbOBhN1AlAn6sSDBQ== dependencies: - "@jest/environment" "^27.5.1" - "@jest/fake-timers" "^27.5.1" - "@jest/types" "^27.5.1" + "@jest/environment" "30.0.0-alpha.2" + "@jest/fake-timers" "30.0.0-alpha.2" + "@jest/types" "30.0.0-alpha.2" + "@types/jsdom" "^21.1.1" "@types/node" "*" - jest-mock "^27.5.1" - jest-util "^27.5.1" + jest-mock "30.0.0-alpha.2" + jest-util "30.0.0-alpha.2" + jsdom "^22.0.0" jest-environment-node@^29.6.2: version "29.6.2" @@ -9251,6 +8894,18 @@ jest-environment-node@^29.6.2: jest-mock "^29.6.2" jest-util "^29.6.2" +jest-environment-node@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376" + integrity sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/fake-timers" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + jest-mock "^29.7.0" + jest-util "^29.7.0" + jest-get-type@^22.1.0: version "22.4.3" resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-22.4.3.tgz" @@ -9261,70 +8916,49 @@ jest-get-type@^27.0.6: 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.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz" - integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== - jest-get-type@^29.4.3: version "29.4.3" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.3.tgz#1ab7a5207c995161100b5187159ca82dd48b3dd5" integrity sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg== -jest-haste-map@^27.2.4: - version "27.2.4" - resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.2.4.tgz" - integrity sha512-bkJ4bT00T2K+1NZXbRcyKnbJ42I6QBvoDNMTAQQDBhaGNnZreiQKUNqax0e6hLTx7E75pKDeltVu3V1HAdu+YA== - dependencies: - "@jest/types" "^27.2.4" - "@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.0.6" - jest-serializer "^27.0.6" - jest-util "^27.2.4" - jest-worker "^27.2.4" - micromatch "^4.0.4" - walker "^1.0.7" - optionalDependencies: - fsevents "^2.3.2" +jest-get-type@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" + integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== -jest-haste-map@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz" - integrity sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng== +jest-haste-map@^29.6.2: + version "29.6.2" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.6.2.tgz#298c25ea5255cfad8b723179d4295cf3a50a70d1" + integrity sha512-+51XleTDAAysvU8rT6AnS1ZJ+WHVNqhj1k6nTvN2PYP+HjU3kqlaKQ1Lnw3NYW3bm2r8vq82X0Z1nDDHZMzHVA== dependencies: - "@jest/types" "^27.5.1" - "@types/graceful-fs" "^4.1.2" + "@jest/types" "^29.6.1" + "@types/graceful-fs" "^4.1.3" "@types/node" "*" anymatch "^3.0.3" fb-watchman "^2.0.0" graceful-fs "^4.2.9" - jest-regex-util "^27.5.1" - jest-serializer "^27.5.1" - jest-util "^27.5.1" - jest-worker "^27.5.1" + jest-regex-util "^29.4.3" + jest-util "^29.6.2" + jest-worker "^29.6.2" micromatch "^4.0.4" - walker "^1.0.7" + walker "^1.0.8" optionalDependencies: fsevents "^2.3.2" -jest-haste-map@^29.6.2: - version "29.6.2" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.6.2.tgz#298c25ea5255cfad8b723179d4295cf3a50a70d1" - integrity sha512-+51XleTDAAysvU8rT6AnS1ZJ+WHVNqhj1k6nTvN2PYP+HjU3kqlaKQ1Lnw3NYW3bm2r8vq82X0Z1nDDHZMzHVA== +jest-haste-map@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104" + integrity sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA== dependencies: - "@jest/types" "^29.6.1" + "@jest/types" "^29.6.3" "@types/graceful-fs" "^4.1.3" "@types/node" "*" anymatch "^3.0.3" fb-watchman "^2.0.0" graceful-fs "^4.2.9" - jest-regex-util "^29.4.3" - jest-util "^29.6.2" - jest-worker "^29.6.2" + jest-regex-util "^29.6.3" + jest-util "^29.7.0" + jest-worker "^29.7.0" micromatch "^4.0.4" walker "^1.0.8" optionalDependencies: @@ -9344,69 +8978,6 @@ jest-image-snapshot@^6.2.0: rimraf "^2.6.2" ssim.js "^3.1.1" -jest-jasmine2@^27.2.4: - version "27.2.4" - 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" - "@jest/environment" "^27.2.4" - "@jest/source-map" "^27.0.6" - "@jest/test-result" "^27.2.4" - "@jest/types" "^27.2.4" - "@types/node" "*" - chalk "^4.0.0" - co "^4.6.0" - expect "^27.2.4" - is-generator-fn "^2.0.0" - jest-each "^27.2.4" - jest-matcher-utils "^27.2.4" - jest-message-util "^27.2.4" - jest-runtime "^27.2.4" - jest-snapshot "^27.2.4" - jest-util "^27.2.4" - pretty-format "^27.2.4" - throat "^6.0.1" - -jest-jasmine2@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz" - integrity sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ== - dependencies: - "@jest/environment" "^27.5.1" - "@jest/source-map" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/node" "*" - chalk "^4.0.0" - co "^4.6.0" - expect "^27.5.1" - is-generator-fn "^2.0.0" - jest-each "^27.5.1" - jest-matcher-utils "^27.5.1" - jest-message-util "^27.5.1" - jest-runtime "^27.5.1" - jest-snapshot "^27.5.1" - jest-util "^27.5.1" - pretty-format "^27.5.1" - throat "^6.0.1" - -jest-leak-detector@^27.2.4: - version "27.2.4" - 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" - pretty-format "^27.2.4" - -jest-leak-detector@^27.5.1: - version "27.5.1" - 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" - pretty-format "^27.5.1" - jest-leak-detector@^29.6.2: version "29.6.2" resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.6.2.tgz#e2b307fee78cab091c37858a98c7e1d73cdf5b38" @@ -9415,6 +8986,14 @@ jest-leak-detector@^29.6.2: jest-get-type "^29.4.3" pretty-format "^29.6.2" +jest-leak-detector@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz#5b7ec0dadfdfec0ca383dc9aa016d36b5ea4c728" + integrity sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw== + dependencies: + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + jest-matcher-utils@^23.6.0: version "23.6.0" resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-23.6.0.tgz" @@ -9424,26 +9003,6 @@ jest-matcher-utils@^23.6.0: jest-get-type "^22.1.0" pretty-format "^23.6.0" -jest-matcher-utils@^27.0.0, jest-matcher-utils@^27.5.1: - version "27.5.1" - 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" - jest-diff "^27.5.1" - jest-get-type "^27.5.1" - pretty-format "^27.5.1" - -jest-matcher-utils@^27.2.4: - version "27.2.4" - 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" - jest-diff "^27.2.4" - jest-get-type "^27.0.6" - pretty-format "^27.2.4" - jest-matcher-utils@^29.6.2: version "29.6.2" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.6.2.tgz#39de0be2baca7a64eacb27291f0bd834fea3a535" @@ -9454,47 +9013,42 @@ jest-matcher-utils@^29.6.2: jest-get-type "^29.4.3" pretty-format "^29.6.2" -jest-message-util@^23.4.0: - version "23.4.0" - resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-23.4.0.tgz" - integrity sha1-F2EMUJQjSVCNAaPR4L2iwHkIap8= - dependencies: - "@babel/code-frame" "^7.0.0-beta.35" - chalk "^2.0.1" - micromatch "^2.3.11" - slash "^1.0.0" - stack-utils "^1.0.1" - -jest-message-util@^27.2.4: - version "27.2.4" - resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.2.4.tgz" - integrity sha512-wbKT/BNGnBVB9nzi+IoaLkXt6fbSvqUxx+IYY66YFh96J3goY33BAaNG3uPqaw/Sh/FR9YpXGVDfd5DJdbh4nA== +jest-matcher-utils@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" + integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g== dependencies: - "@babel/code-frame" "^7.12.13" - "@jest/types" "^27.2.4" - "@types/stack-utils" "^2.0.0" chalk "^4.0.0" - graceful-fs "^4.2.4" - micromatch "^4.0.4" - pretty-format "^27.2.4" - slash "^3.0.0" - stack-utils "^2.0.3" + jest-diff "^29.7.0" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" -jest-message-util@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz" - integrity sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g== +jest-message-util@30.0.0-alpha.2: + version "30.0.0-alpha.2" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-30.0.0-alpha.2.tgz#67a623aff92884f066aa3c21f54613606b63fc11" + integrity sha512-gAhiwgSIxcXtp6YFxF92abRhc16IJdWT4I318sJ5qo1cRZQPOOeIIUOVXQYeYRiEeo+okBqaY/KXLh5SiE+61A== dependencies: "@babel/code-frame" "^7.12.13" - "@jest/types" "^27.5.1" + "@jest/types" "30.0.0-alpha.2" "@types/stack-utils" "^2.0.0" chalk "^4.0.0" graceful-fs "^4.2.9" micromatch "^4.0.4" - pretty-format "^27.5.1" + pretty-format "30.0.0-alpha.2" slash "^3.0.0" stack-utils "^2.0.3" +jest-message-util@^23.4.0: + version "23.4.0" + resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-23.4.0.tgz" + integrity sha1-F2EMUJQjSVCNAaPR4L2iwHkIap8= + dependencies: + "@babel/code-frame" "^7.0.0-beta.35" + chalk "^2.0.1" + micromatch "^2.3.11" + slash "^1.0.0" + stack-utils "^1.0.1" + jest-message-util@^29.6.2: version "29.6.2" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.6.2.tgz#af7adc2209c552f3f5ae31e77cf0a261f23dc2bb" @@ -9510,21 +9064,29 @@ jest-message-util@^29.6.2: slash "^3.0.0" stack-utils "^2.0.3" -jest-mock@^27.2.4: - version "27.2.4" - resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-27.2.4.tgz" - integrity sha512-iVRU905rutaAoUcrt5Tm1JoHHWi24YabqEGXjPJI4tAyA6wZ7mzDi3GrZ+M7ebgWBqUkZE93GAx1STk7yCMIQA== +jest-message-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3" + integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w== dependencies: - "@jest/types" "^27.2.4" - "@types/node" "*" + "@babel/code-frame" "^7.12.13" + "@jest/types" "^29.6.3" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^29.7.0" + slash "^3.0.0" + stack-utils "^2.0.3" -jest-mock@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz" - integrity sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og== +jest-mock@30.0.0-alpha.2: + version "30.0.0-alpha.2" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-30.0.0-alpha.2.tgz#5cc3a5cc22ce43fa403030bf02a33a282cbedba2" + integrity sha512-jXyAcNg+m42AZ7RMhBMY+zimdyYmv9/Xo7PICUXmYhcJR5Q5fpX9edA8a3zLZTz9+O3I/xxFOpk3ZuuLUfhJoQ== dependencies: - "@jest/types" "^27.5.1" + "@jest/types" "30.0.0-alpha.2" "@types/node" "*" + jest-util "30.0.0-alpha.2" jest-mock@^29.6.2: version "29.6.2" @@ -9535,43 +9097,29 @@ jest-mock@^29.6.2: "@types/node" "*" jest-util "^29.6.2" +jest-mock@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347" + integrity sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + jest-util "^29.7.0" + jest-pnp-resolver@^1.2.2: version "1.2.2" resolved "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz" integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== -jest-regex-util@^27.0.6: - version "27.0.6" - 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.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz" - integrity sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg== - jest-regex-util@^29.4.3: version "29.4.3" resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.4.3.tgz#a42616141e0cae052cfa32c169945d00c0aa0bb8" integrity sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg== -jest-resolve-dependencies@^27.2.4: - version "27.2.4" - resolved "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.2.4.tgz" - integrity sha512-i5s7Uh9B3Q6uwxLpMhNKlgBf6pcemvWaORxsW1zNF/YCY3jd5EftvnGBI+fxVwJ1CBxkVfxqCvm1lpZkbaoGmg== - dependencies: - "@jest/types" "^27.2.4" - jest-regex-util "^27.0.6" - jest-snapshot "^27.2.4" - -jest-resolve-dependencies@^27.5.1: - version "27.5.1" - 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" - jest-regex-util "^27.5.1" - jest-snapshot "^27.5.1" +jest-regex-util@^29.6.3: + version "29.6.3" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52" + integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg== jest-resolve-dependencies@^29.6.2: version "29.6.2" @@ -9581,6 +9129,14 @@ jest-resolve-dependencies@^29.6.2: jest-regex-util "^29.4.3" jest-snapshot "^29.6.2" +jest-resolve-dependencies@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz#1b04f2c095f37fc776ff40803dc92921b1e88428" + integrity sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA== + dependencies: + jest-regex-util "^29.6.3" + jest-snapshot "^29.7.0" + jest-resolve@^23.6.0: version "23.6.0" resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-23.6.0.tgz" @@ -9590,38 +9146,6 @@ jest-resolve@^23.6.0: chalk "^2.0.1" realpath-native "^1.0.0" -jest-resolve@^27.2.4: - version "27.2.4" - 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" - chalk "^4.0.0" - escalade "^3.1.1" - graceful-fs "^4.2.4" - jest-haste-map "^27.2.4" - jest-pnp-resolver "^1.2.2" - jest-util "^27.2.4" - jest-validate "^27.2.4" - resolve "^1.20.0" - slash "^3.0.0" - -jest-resolve@^27.5.1: - version "27.5.1" - 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" - chalk "^4.0.0" - graceful-fs "^4.2.9" - jest-haste-map "^27.5.1" - jest-pnp-resolver "^1.2.2" - jest-util "^27.5.1" - jest-validate "^27.5.1" - resolve "^1.20.0" - resolve.exports "^1.1.0" - slash "^3.0.0" - jest-resolve@^29.6.2: version "29.6.2" resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.6.2.tgz#f18405fe4b50159b7b6d85e81f6a524d22afb838" @@ -9637,60 +9161,20 @@ jest-resolve@^29.6.2: resolve.exports "^2.0.0" slash "^3.0.0" -jest-runner@^27.2.4: - version "27.2.4" - resolved "https://registry.npmjs.org/jest-runner/-/jest-runner-27.2.4.tgz" - integrity sha512-hIo5PPuNUyVDidZS8EetntuuJbQ+4IHWxmHgYZz9FIDbG2wcZjrP6b52uMDjAEQiHAn8yn8ynNe+TL8UuGFYKg== +jest-resolve@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.7.0.tgz#64d6a8992dd26f635ab0c01e5eef4399c6bcbc30" + integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA== dependencies: - "@jest/console" "^27.2.4" - "@jest/environment" "^27.2.4" - "@jest/test-result" "^27.2.4" - "@jest/transform" "^27.2.4" - "@jest/types" "^27.2.4" - "@types/node" "*" chalk "^4.0.0" - emittery "^0.8.1" - exit "^0.1.2" - graceful-fs "^4.2.4" - jest-docblock "^27.0.6" - jest-environment-jsdom "^27.2.4" - jest-environment-node "^27.2.4" - jest-haste-map "^27.2.4" - jest-leak-detector "^27.2.4" - jest-message-util "^27.2.4" - jest-resolve "^27.2.4" - jest-runtime "^27.2.4" - jest-util "^27.2.4" - jest-worker "^27.2.4" - source-map-support "^0.5.6" - throat "^6.0.1" - -jest-runner@^27.5.1: - version "27.5.1" - 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" - "@jest/environment" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" - "@types/node" "*" - chalk "^4.0.0" - emittery "^0.8.1" graceful-fs "^4.2.9" - jest-docblock "^27.5.1" - jest-environment-jsdom "^27.5.1" - jest-environment-node "^27.5.1" - jest-haste-map "^27.5.1" - jest-leak-detector "^27.5.1" - jest-message-util "^27.5.1" - jest-resolve "^27.5.1" - jest-runtime "^27.5.1" - jest-util "^27.5.1" - jest-worker "^27.5.1" - source-map-support "^0.5.6" - throat "^6.0.1" + jest-haste-map "^29.7.0" + jest-pnp-resolver "^1.2.2" + jest-util "^29.7.0" + jest-validate "^29.7.0" + resolve "^1.20.0" + resolve.exports "^2.0.0" + slash "^3.0.0" jest-runner@^29.6.2: version "29.6.2" @@ -9719,66 +9203,32 @@ jest-runner@^29.6.2: p-limit "^3.1.0" source-map-support "0.5.13" -jest-runtime@^27.2.4: - version "27.2.4" - resolved "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.2.4.tgz" - integrity sha512-ICKzzYdjIi70P17MZsLLIgIQFCQmIjMFf+xYww3aUySiUA/QBPUTdUqo5B2eg4HOn9/KkUsV0z6GVgaqAPBJvg== - dependencies: - "@jest/console" "^27.2.4" - "@jest/environment" "^27.2.4" - "@jest/fake-timers" "^27.2.4" - "@jest/globals" "^27.2.4" - "@jest/source-map" "^27.0.6" - "@jest/test-result" "^27.2.4" - "@jest/transform" "^27.2.4" - "@jest/types" "^27.2.4" - "@types/yargs" "^16.0.0" - chalk "^4.0.0" - cjs-module-lexer "^1.0.0" - collect-v8-coverage "^1.0.0" - execa "^5.0.0" - exit "^0.1.2" - glob "^7.1.3" - graceful-fs "^4.2.4" - jest-haste-map "^27.2.4" - jest-message-util "^27.2.4" - jest-mock "^27.2.4" - jest-regex-util "^27.0.6" - jest-resolve "^27.2.4" - jest-snapshot "^27.2.4" - jest-util "^27.2.4" - jest-validate "^27.2.4" - slash "^3.0.0" - strip-bom "^4.0.0" - yargs "^16.2.0" - -jest-runtime@^27.5.1: - version "27.5.1" - 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" - "@jest/fake-timers" "^27.5.1" - "@jest/globals" "^27.5.1" - "@jest/source-map" "^27.5.1" - "@jest/test-result" "^27.5.1" - "@jest/transform" "^27.5.1" - "@jest/types" "^27.5.1" +jest-runner@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.7.0.tgz#809af072d408a53dcfd2e849a4c976d3132f718e" + integrity sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ== + dependencies: + "@jest/console" "^29.7.0" + "@jest/environment" "^29.7.0" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" chalk "^4.0.0" - cjs-module-lexer "^1.0.0" - collect-v8-coverage "^1.0.0" - execa "^5.0.0" - glob "^7.1.3" + emittery "^0.13.1" graceful-fs "^4.2.9" - jest-haste-map "^27.5.1" - jest-message-util "^27.5.1" - jest-mock "^27.5.1" - jest-regex-util "^27.5.1" - jest-resolve "^27.5.1" - jest-snapshot "^27.5.1" - jest-util "^27.5.1" - slash "^3.0.0" - strip-bom "^4.0.0" + jest-docblock "^29.7.0" + jest-environment-node "^29.7.0" + jest-haste-map "^29.7.0" + jest-leak-detector "^29.7.0" + jest-message-util "^29.7.0" + jest-resolve "^29.7.0" + jest-runtime "^29.7.0" + jest-util "^29.7.0" + jest-watcher "^29.7.0" + jest-worker "^29.7.0" + p-limit "^3.1.0" + source-map-support "0.5.13" jest-runtime@^29.6.2: version "29.6.2" @@ -9808,21 +9258,33 @@ jest-runtime@^29.6.2: slash "^3.0.0" strip-bom "^4.0.0" -jest-serializer@^27.0.6: - version "27.0.6" - resolved "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.0.6.tgz" - integrity sha512-PtGdVK9EGC7dsaziskfqaAPib6wTViY3G8E5wz9tLVPhHyiDNTZn/xjZ4khAw+09QkoOVpn7vF5nPSN6dtBexA== - dependencies: - "@types/node" "*" - graceful-fs "^4.2.4" - -jest-serializer@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz" - integrity sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w== - dependencies: +jest-runtime@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.7.0.tgz#efecb3141cf7d3767a3a0cc8f7c9990587d3d817" + integrity sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ== + dependencies: + "@jest/environment" "^29.7.0" + "@jest/fake-timers" "^29.7.0" + "@jest/globals" "^29.7.0" + "@jest/source-map" "^29.6.3" + "@jest/test-result" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" "@types/node" "*" + chalk "^4.0.0" + cjs-module-lexer "^1.0.0" + collect-v8-coverage "^1.0.0" + glob "^7.1.3" graceful-fs "^4.2.9" + jest-haste-map "^29.7.0" + jest-message-util "^29.7.0" + jest-mock "^29.7.0" + jest-regex-util "^29.6.3" + jest-resolve "^29.7.0" + jest-snapshot "^29.7.0" + jest-util "^29.7.0" + slash "^3.0.0" + strip-bom "^4.0.0" jest-snapshot@^23.6.0: version "23.6.0" @@ -9840,64 +9302,6 @@ jest-snapshot@^23.6.0: pretty-format "^23.6.0" semver "^5.5.0" -jest-snapshot@^27.2.4: - version "27.2.4" - 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" - "@babel/generator" "^7.7.2" - "@babel/parser" "^7.7.2" - "@babel/plugin-syntax-typescript" "^7.7.2" - "@babel/traverse" "^7.7.2" - "@babel/types" "^7.0.0" - "@jest/transform" "^27.2.4" - "@jest/types" "^27.2.4" - "@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.2.4" - graceful-fs "^4.2.4" - jest-diff "^27.2.4" - jest-get-type "^27.0.6" - jest-haste-map "^27.2.4" - jest-matcher-utils "^27.2.4" - jest-message-util "^27.2.4" - jest-resolve "^27.2.4" - jest-util "^27.2.4" - natural-compare "^1.4.0" - pretty-format "^27.2.4" - semver "^7.3.2" - -jest-snapshot@^27.5.1: - version "27.5.1" - 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" - "@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.5.1" - "@jest/types" "^27.5.1" - "@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.5.1" - graceful-fs "^4.2.9" - jest-diff "^27.5.1" - jest-get-type "^27.5.1" - jest-haste-map "^27.5.1" - jest-matcher-utils "^27.5.1" - jest-message-util "^27.5.1" - jest-util "^27.5.1" - natural-compare "^1.4.0" - pretty-format "^27.5.1" - semver "^7.3.2" - jest-snapshot@^29.6.2: version "29.6.2" resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.6.2.tgz#9b431b561a83f2bdfe041e1cab8a6becdb01af9c" @@ -9924,29 +9328,43 @@ jest-snapshot@^29.6.2: pretty-format "^29.6.2" semver "^7.5.3" -jest-util@^27.0.0, jest-util@^27.2.4: - version "27.2.4" - resolved "https://registry.npmjs.org/jest-util/-/jest-util-27.2.4.tgz" - integrity sha512-mW++4u+fSvAt3YBWm5IpbmRAceUqa2B++JlUZTiuEt2AmNYn0Yw5oay4cP17TGsMINRNPSGiJ2zNnX60g+VbFg== +jest-snapshot@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5" + integrity sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw== dependencies: - "@jest/types" "^27.2.4" - "@types/node" "*" + "@babel/core" "^7.11.6" + "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-jsx" "^7.7.2" + "@babel/plugin-syntax-typescript" "^7.7.2" + "@babel/types" "^7.3.3" + "@jest/expect-utils" "^29.7.0" + "@jest/transform" "^29.7.0" + "@jest/types" "^29.6.3" + babel-preset-current-node-syntax "^1.0.0" chalk "^4.0.0" - graceful-fs "^4.2.4" - is-ci "^3.0.0" - picomatch "^2.2.3" + expect "^29.7.0" + graceful-fs "^4.2.9" + jest-diff "^29.7.0" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + natural-compare "^1.4.0" + pretty-format "^29.7.0" + semver "^7.5.3" -jest-util@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz" - integrity sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw== +jest-util@30.0.0-alpha.2: + version "30.0.0-alpha.2" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-30.0.0-alpha.2.tgz#1f2cf97bd66d02ca367fae21c594a2c051767dbc" + integrity sha512-W2slmlWItPPD7uOnVy8mkjshUrGCiqwtFUIyDE/wkg+mzp8hSpHOAwyxBvqI+UvO3Vpeuk0AL07DYEpUzMy/4g== dependencies: - "@jest/types" "^27.5.1" + "@jest/types" "30.0.0-alpha.2" "@types/node" "*" chalk "^4.0.0" - ci-info "^3.2.0" + ci-info "^4.0.0" graceful-fs "^4.2.9" - picomatch "^2.2.3" + picomatch "^3.0.0" jest-util@^29.0.0, jest-util@^29.6.2: version "29.6.2" @@ -9960,29 +9378,17 @@ jest-util@^29.0.0, jest-util@^29.6.2: graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-validate@^27.2.4: - version "27.2.4" - resolved "https://registry.npmjs.org/jest-validate/-/jest-validate-27.2.4.tgz" - integrity sha512-VMtbxbkd7LHnIH7PChdDtrluCFRJ4b1YV2YJzNwwsASMWftq/HgqiqjvptBOWyWOtevgO3f14wPxkPcLlVBRog== +jest-util@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc" + integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== dependencies: - "@jest/types" "^27.2.4" - camelcase "^6.2.0" - chalk "^4.0.0" - jest-get-type "^27.0.6" - leven "^3.1.0" - pretty-format "^27.2.4" - -jest-validate@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz" - integrity sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ== - dependencies: - "@jest/types" "^27.5.1" - camelcase "^6.2.0" + "@jest/types" "^29.6.3" + "@types/node" "*" chalk "^4.0.0" - jest-get-type "^27.5.1" - leven "^3.1.0" - pretty-format "^27.5.1" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" jest-validate@^29.6.2: version "29.6.2" @@ -9996,31 +9402,17 @@ jest-validate@^29.6.2: leven "^3.1.0" pretty-format "^29.6.2" -jest-watcher@^27.2.4: - version "27.2.4" - resolved "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.2.4.tgz" - integrity sha512-LXC/0+dKxhK7cfF7reflRYlzDIaQE+fL4ynhKhzg8IMILNMuI4xcjXXfUJady7OR4/TZeMg7X8eHx8uan9vqaQ== +jest-validate@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c" + integrity sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw== dependencies: - "@jest/test-result" "^27.2.4" - "@jest/types" "^27.2.4" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - jest-util "^27.2.4" - string-length "^4.0.1" - -jest-watcher@^27.5.1: - version "27.5.1" - 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" - "@jest/types" "^27.5.1" - "@types/node" "*" - ansi-escapes "^4.2.1" + "@jest/types" "^29.6.3" + camelcase "^6.2.0" chalk "^4.0.0" - jest-util "^27.5.1" - string-length "^4.0.1" + jest-get-type "^29.6.3" + leven "^3.1.0" + pretty-format "^29.7.0" jest-watcher@^29.6.2: version "29.6.2" @@ -10036,6 +9428,20 @@ jest-watcher@^29.6.2: jest-util "^29.6.2" string-length "^4.0.1" +jest-watcher@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.7.0.tgz#7810d30d619c3a62093223ce6bb359ca1b28a2f2" + integrity sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g== + dependencies: + "@jest/test-result" "^29.7.0" + "@jest/types" "^29.6.3" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + emittery "^0.13.1" + jest-util "^29.7.0" + string-length "^4.0.1" + jest-worker@^26.2.1: version "26.6.2" resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz" @@ -10045,24 +9451,6 @@ jest-worker@^26.2.1: merge-stream "^2.0.0" supports-color "^7.0.0" -jest-worker@^27.2.4: - version "27.2.4" - resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-27.2.4.tgz" - integrity sha512-Zq9A2Pw59KkVjBBKD1i3iE2e22oSjXhUKKuAK1HGX8flGwkm6NMozyEYzKd41hXc64dbd/0eWFeEEuxqXyhM+g== - 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.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz" - integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== - dependencies: - "@types/node" "*" - merge-stream "^2.0.0" - supports-color "^8.0.0" - jest-worker@^29.6.2: version "29.6.2" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.6.2.tgz#682fbc4b6856ad0aa122a5403c6d048b83f3fb44" @@ -10073,23 +9461,15 @@ jest-worker@^29.6.2: merge-stream "^2.0.0" supports-color "^8.0.0" -jest@^27.2.4: - version "27.2.4" - resolved "https://registry.npmjs.org/jest/-/jest-27.2.4.tgz" - integrity sha512-h4uqb1EQLfPulWyUFFWv9e9Nn8sCqsJ/j3wk/KCY0p4s4s0ICCfP3iMf6hRf5hEhsDyvyrCgKiZXma63gMz16A== - dependencies: - "@jest/core" "^27.2.4" - import-local "^3.0.2" - jest-cli "^27.2.4" - -jest@^27.5.1: - version "27.5.1" - resolved "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz" - integrity sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ== +jest-worker@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a" + integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw== dependencies: - "@jest/core" "^27.5.1" - import-local "^3.0.2" - jest-cli "^27.5.1" + "@types/node" "*" + jest-util "^29.7.0" + merge-stream "^2.0.0" + supports-color "^8.0.0" jest@^29.6.0: version "29.6.2" @@ -10101,6 +9481,16 @@ jest@^29.6.0: import-local "^3.0.2" jest-cli "^29.6.2" +jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613" + integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== + dependencies: + "@jest/core" "^29.7.0" + "@jest/types" "^29.6.3" + import-local "^3.0.2" + jest-cli "^29.7.0" + jju@~1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/jju/-/jju-1.4.0.tgz#a3abe2718af241a2b2904f84a625970f389ae32a" @@ -10136,72 +9526,6 @@ jsbn@~0.1.0: 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.npmjs.org/jsdom/-/jsdom-16.6.0.tgz" - integrity sha512-Ty1vmF4NHJkolaEmdjtxTfSfkdb8Ywarwf63f+F8/mDD1uLSSWDxDuMiZxiPhwunLrn9LOSVItWj4bLYsLN3Dg== - dependencies: - abab "^2.0.5" - acorn "^8.2.4" - acorn-globals "^6.0.0" - cssom "^0.4.4" - cssstyle "^2.3.0" - data-urls "^2.0.0" - decimal.js "^10.2.1" - domexception "^2.0.1" - escodegen "^2.0.0" - form-data "^3.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 "^8.5.0" - ws "^7.4.5" - xml-name-validator "^3.0.0" - -jsdom@^16.6.0: - version "16.7.0" - resolved "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz" - integrity sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw== - dependencies: - abab "^2.0.5" - acorn "^8.2.4" - acorn-globals "^6.0.0" - cssom "^0.4.4" - cssstyle "^2.3.0" - data-urls "^2.0.0" - decimal.js "^10.2.1" - domexception "^2.0.1" - escodegen "^2.0.0" - form-data "^3.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 "^8.5.0" - ws "^7.4.6" - xml-name-validator "^3.0.0" - jsdom@^20.0.0: version "20.0.3" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-20.0.3.tgz#886a41ba1d4726f67a8858028c99489fed6ad4db" @@ -10234,6 +9558,62 @@ jsdom@^20.0.0: ws "^8.11.0" xml-name-validator "^4.0.0" +jsdom@^22.0.0: + version "22.1.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-22.1.0.tgz#0fca6d1a37fbeb7f4aac93d1090d782c56b611c8" + integrity sha512-/9AVW7xNbsBv6GfWho4TTNjEo9fe6Zhf9O7s0Fhhr3u+awPwAJMKwAMXnkk5vBxflqLW9hTHX/0cs+P3gW+cQw== + dependencies: + abab "^2.0.6" + cssstyle "^3.0.0" + data-urls "^4.0.0" + decimal.js "^10.4.3" + domexception "^4.0.0" + form-data "^4.0.0" + html-encoding-sniffer "^3.0.0" + http-proxy-agent "^5.0.0" + https-proxy-agent "^5.0.1" + is-potential-custom-element-name "^1.0.1" + nwsapi "^2.2.4" + parse5 "^7.1.2" + rrweb-cssom "^0.6.0" + saxes "^6.0.0" + symbol-tree "^3.2.4" + tough-cookie "^4.1.2" + w3c-xmlserializer "^4.0.0" + webidl-conversions "^7.0.0" + whatwg-encoding "^2.0.0" + whatwg-mimetype "^3.0.0" + whatwg-url "^12.0.1" + ws "^8.13.0" + xml-name-validator "^4.0.0" + +jsdom@^23.0.0: + version "23.0.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-23.0.0.tgz#7c8bac82e32e1ac3eef29096ea59d519c72ce4eb" + integrity sha512-cbL/UCtohJguhFC7c2/hgW6BeZCNvP7URQGnx9tSJRYKCdnfbfWOrtuLTMfiB2VxKsx5wPHVsh/J0aBy9lIIhQ== + dependencies: + cssstyle "^3.0.0" + data-urls "^5.0.0" + decimal.js "^10.4.3" + form-data "^4.0.0" + html-encoding-sniffer "^4.0.0" + http-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.2" + is-potential-custom-element-name "^1.0.1" + nwsapi "^2.2.7" + parse5 "^7.1.2" + rrweb-cssom "^0.6.0" + saxes "^6.0.0" + symbol-tree "^3.2.4" + tough-cookie "^4.1.3" + w3c-xmlserializer "^5.0.0" + webidl-conversions "^7.0.0" + whatwg-encoding "^3.1.1" + whatwg-mimetype "^4.0.0" + whatwg-url "^14.0.0" + ws "^8.14.2" + xml-name-validator "^5.0.0" + jsesc@^2.5.1: version "2.5.2" resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz" @@ -10286,13 +9666,6 @@ json-stringify-safe@~5.0.1: 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.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.npmjs.org/json5/-/json5-1.0.1.tgz" @@ -10300,6 +9673,13 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" +json5@^2.1.2: + version "2.2.0" + 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@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" @@ -10618,7 +9998,7 @@ lodash.uniq@^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.21, lodash@^4.17.4, lodash@^4.7.0, lodash@~4.17.15: +lodash@^4.17.21, lodash@^4.17.4, lodash@~4.17.15: version "4.17.21" resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -10696,6 +10076,13 @@ magic-string@^0.27.0: dependencies: "@jridgewell/sourcemap-codec" "^1.4.13" +magic-string@^0.30.3: + version "0.30.5" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.5.tgz#1994d980bd1c8835dc6e78db7cbd4ae4f24746f9" + integrity sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA== + dependencies: + "@jridgewell/sourcemap-codec" "^1.4.15" + 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" @@ -10715,13 +10102,6 @@ makeerror@1.0.12: dependencies: tmpl "1.0.5" -makeerror@1.0.x: - version "1.0.11" - resolved "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz" - integrity sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw= - 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" @@ -10971,7 +10351,7 @@ min-indent@^1.0.0: dependencies: brace-expansion "^1.1.7" -minimatch@^5.1.0: +minimatch@^5.0.1, minimatch@^5.1.0: version "5.1.6" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== @@ -10999,7 +10379,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== -mitt@3.0.0, mitt@^3.0.0: +mitt@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/mitt/-/mitt-3.0.1.tgz#ea36cf0cc30403601ae074c8f77b7092cdab36d1" + integrity sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw== + +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== @@ -11135,7 +10520,7 @@ 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.7, node-fetch@^2.6.1: +node-fetch@2.6.7: version "2.6.7" resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz" integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== @@ -11166,11 +10551,6 @@ node-int64@^0.4.0: 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.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" @@ -11188,11 +10568,6 @@ node-releases@^1.1.71: 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.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.npmjs.org/node-releases/-/node-releases-2.0.1.tgz" @@ -11266,7 +10641,7 @@ nwsapi@^2.2.0: resolved "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz" integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== -nwsapi@^2.2.2: +nwsapi@^2.2.2, nwsapi@^2.2.4, nwsapi@^2.2.7: version "2.2.7" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.7.tgz#738e0707d3128cb750dddcfe90e4610482df0f30" integrity sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ== @@ -11558,19 +10933,19 @@ p-try@^2.0.0: resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== -pac-proxy-agent@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-7.0.0.tgz#db42120c64292685dafaf2bd921e223c56bfb13b" - integrity sha512-t4tRAMx0uphnZrio0S0Jw9zg3oDbz1zVhQ/Vy18FjLfP1XOLNUEjaVxYCYRI6NS+BsMBXKIzV6cTLOkO9AtywA== +pac-proxy-agent@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-7.0.1.tgz#6b9ddc002ec3ff0ba5fdf4a8a21d363bcc612d75" + integrity sha512-ASV8yU4LLKBAjqIPMbrgtaKIvxQri/yh2OpI+S6hVa9JRkUI3Y3NPFbfngDtY7oFtSMD3w31Xns89mDa3Feo5A== dependencies: "@tootallnate/quickjs-emscripten" "^0.23.0" agent-base "^7.0.2" debug "^4.3.4" get-uri "^6.0.1" http-proxy-agent "^7.0.0" - https-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.2" pac-resolver "^7.0.0" - socks-proxy-agent "^8.0.1" + socks-proxy-agent "^8.0.2" pac-resolver@^7.0.0: version "7.0.0" @@ -11638,7 +11013,7 @@ parse5-htmlparser2-tree-adapter@^6.0.1: dependencies: parse5 "^6.0.1" -parse5@6.0.1, parse5@^6.0.1: +parse5@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== @@ -11650,7 +11025,7 @@ parse5@^7.0.0: dependencies: entities "^4.3.0" -parse5@^7.1.1: +parse5@^7.1.1, parse5@^7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32" integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw== @@ -11746,6 +11121,11 @@ picomatch@^2.3.1: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== +picomatch@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-3.0.1.tgz#817033161def55ec9638567a2f3bbc876b3e7516" + integrity sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag== + pify@^2.0.0, pify@^2.3.0: version "2.3.0" resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz" @@ -11807,13 +11187,6 @@ pino@7.9.1: 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" - integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA== - dependencies: - node-modules-regexp "^1.0.0" - pirates@^4.0.4: version "4.0.4" resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.4.tgz" @@ -12299,6 +11672,15 @@ prettier@2.8.4, prettier@^2.7.1: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.4.tgz#34dd2595629bfbb79d344ac4a91ff948694463c3" integrity sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw== +pretty-format@30.0.0-alpha.2: + version "30.0.0-alpha.2" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-30.0.0-alpha.2.tgz#f69024bc3cc0398fa4d86a9b44c32028006e94ea" + integrity sha512-9preHaHWIBEtQOkuN0vpCgfTo8X3vlWmDdCQHA1hSJ5vKNA1EGFr7iEQZDFLdqYe6DeJChdBqi+A+VFV98QGXQ== + dependencies: + "@jest/schemas" "30.0.0-alpha.2" + ansi-styles "^5.0.0" + react-is "^18.0.0" + pretty-format@^23.6.0: version "23.6.0" resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-23.6.0.tgz" @@ -12317,15 +11699,6 @@ pretty-format@^27.0.0, pretty-format@^27.2.4: ansi-styles "^5.0.0" react-is "^17.0.1" -pretty-format@^27.5.1: - version "27.5.1" - 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" - ansi-styles "^5.0.0" - react-is "^17.0.1" - pretty-format@^29.0.0, pretty-format@^29.6.2: version "29.6.2" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.6.2.tgz#3d5829261a8a4d89d8b9769064b29c50ed486a47" @@ -12335,6 +11708,15 @@ pretty-format@^29.0.0, pretty-format@^29.6.2: ansi-styles "^5.0.0" react-is "^18.0.0" +pretty-format@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" + integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== + dependencies: + "@jest/schemas" "^29.6.3" + ansi-styles "^5.0.0" + react-is "^18.0.0" + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" @@ -12345,7 +11727,7 @@ process-warning@^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: +progress@2.0.3: version "2.0.3" resolved "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== @@ -12387,19 +11769,19 @@ proxy-addr@~2.0.5: forwarded "0.2.0" ipaddr.js "1.9.1" -proxy-agent@6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-6.3.0.tgz#72f7bb20eb06049db79f7f86c49342c34f9ba08d" - integrity sha512-0LdR757eTj/JfuU7TL2YCuAZnxWXu3tkJbg4Oq3geW/qFNT/32T0sp2HnZ9O0lMR4q3vwAt0+xCA8SR0WAD0og== +proxy-agent@6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-6.3.1.tgz#40e7b230552cf44fd23ffaf7c59024b692612687" + integrity sha512-Rb5RVBy1iyqOtNl15Cw/llpeLH8bsb37gM1FUfKQ+Wck6xHlbAhWGUFiTRHtkjqGTA5pSHz6+0hrPW/oECihPQ== dependencies: agent-base "^7.0.2" debug "^4.3.4" http-proxy-agent "^7.0.0" - https-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.2" lru-cache "^7.14.1" - pac-proxy-agent "^7.0.0" + pac-proxy-agent "^7.0.1" proxy-from-env "^1.1.0" - socks-proxy-agent "^8.0.1" + socks-proxy-agent "^8.0.2" proxy-from-env@1.1.0, proxy-from-env@^1.1.0: version "1.1.0" @@ -12429,6 +11811,11 @@ 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== +punycode@^2.3.0, punycode@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + pupa@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62" @@ -12436,17 +11823,29 @@ pupa@^2.1.1: dependencies: escape-goat "^2.0.0" -puppeteer-core@20.9.0: - version "20.9.0" - resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-20.9.0.tgz#6f4b420001b64419deab38d398a4d9cd071040e6" - integrity sha512-H9fYZQzMTRrkboEfPmf7m3CLDN6JvbxXA3qTtS+dFt27tR+CsFHzPsT6pzp6lYL6bJbAPaR0HaPO6uSi+F94Pg== +puppeteer-core@21.6.1: + version "21.6.1" + resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-21.6.1.tgz#10eccb4dc3167c8c26bc21122fabb45a9fda9ca7" + integrity sha512-0chaaK/RL9S1U3bsyR4fUeUfoj51vNnjWvXgG6DcsyMjwYNpLcAThv187i1rZCo7QhJP0wZN8plQkjNyrq2h+A== + dependencies: + "@puppeteer/browsers" "1.9.0" + chromium-bidi "0.5.1" + cross-fetch "4.0.0" + debug "4.3.4" + devtools-protocol "0.0.1203626" + ws "8.15.1" + +puppeteer-core@21.7.0: + version "21.7.0" + resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-21.7.0.tgz#c0abb98cbd17dbd7ee317b4257958337fa25d2c7" + integrity sha512-elPYPozrgiM3phSy7VDUJCVWQ07SPnOm78fpSaaSNFoQx5sur/MqhTSro9Wz8lOEjqCykGC6WRkwxDgmqcy1dQ== dependencies: - "@puppeteer/browsers" "1.4.6" - chromium-bidi "0.4.16" + "@puppeteer/browsers" "1.9.1" + chromium-bidi "0.5.2" cross-fetch "4.0.0" debug "4.3.4" - devtools-protocol "0.0.1147663" - ws "8.13.0" + devtools-protocol "0.0.1203626" + ws "8.16.0" puppeteer@^17.1.3: version "17.1.3" @@ -12465,32 +11864,23 @@ puppeteer@^17.1.3: unbzip2-stream "1.4.3" ws "8.8.1" -puppeteer@^20.9.0: - version "20.9.0" - resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-20.9.0.tgz#7bfb9e37deab9728e13b02ea3fb499b5560c79a7" - integrity sha512-kAglT4VZ9fWEGg3oLc4/de+JcONuEJhlh3J6f5R1TLkrY/EHHIHxWXDOzXvaxQCtedmyVXBwg8M+P8YCO/wZjw== +puppeteer@^21.6.1: + version "21.6.1" + resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-21.6.1.tgz#2ec0878906ff90b3a424f19e5eb006592abe25b6" + integrity sha512-O+pbc61oj8ln6m8EJKncrsQFmytgRyFYERtk190PeLbJn5JKpmmynn2p1PiFrlhCitAQXLJ0MOy7F0TeyCRqBg== dependencies: - "@puppeteer/browsers" "1.4.6" - cosmiconfig "8.2.0" - puppeteer-core "20.9.0" + "@puppeteer/browsers" "1.9.0" + cosmiconfig "8.3.6" + puppeteer-core "21.6.1" -puppeteer@^9.1.1: - version "9.1.1" - resolved "https://registry.npmjs.org/puppeteer/-/puppeteer-9.1.1.tgz" - integrity sha512-W+nOulP2tYd/ZG99WuZC/I5ljjQQ7EUw/jQGcIb9eu8mDlZxNY2SgcJXTLG9h5gRvqA3uJOe4hZXYsd3EqioMw== +puppeteer@^21.7.0: + version "21.7.0" + resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-21.7.0.tgz#c4b46ef28a2986f9c536eb086ab47c8dea80e4f9" + integrity sha512-Yy+UUy0b9siJezbhHO/heYUoZQUwyqDK1yOQgblTt0l97tspvDVFkcW9toBlnSvSfkDmMI3Dx9cZL6R8bDArHA== dependencies: - debug "^4.1.0" - devtools-protocol "0.0.869402" - extract-zip "^2.0.0" - https-proxy-agent "^5.0.0" - node-fetch "^2.6.1" - pkg-dir "^4.2.0" - progress "^2.0.1" - proxy-from-env "^1.1.0" - rimraf "^3.0.2" - tar-fs "^2.0.0" - unbzip2-stream "^1.3.3" - ws "^7.2.3" + "@puppeteer/browsers" "1.9.1" + cosmiconfig "8.3.6" + puppeteer-core "21.7.0" pure-rand@^6.0.0: version "6.0.2" @@ -12923,11 +12313,6 @@ resolve-from@^5.0.0: 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.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz" - integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== - resolve.exports@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" @@ -13106,6 +12491,17 @@ rollup-plugin-typescript2@^0.31.2: resolve "^1.20.0" tslib "^2.3.1" +rollup-plugin-typescript2@^0.36.0: + version "0.36.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.36.0.tgz#309564eb70d710412f5901344ca92045e180ed53" + integrity sha512-NB2CSQDxSe9+Oe2ahZbf+B4bh7pHwjV5L+RSYpCu7Q5ROuN94F9b6ioWwKfz3ueL3KTtmX4o2MUH2cgHDIEUsw== + dependencies: + "@rollup/pluginutils" "^4.1.2" + find-cache-dir "^3.3.2" + fs-extra "^10.0.0" + semver "^7.5.4" + tslib "^2.6.2" + rollup-plugin-web-worker-loader@^1.6.1: version "1.6.1" resolved "https://registry.npmjs.org/rollup-plugin-web-worker-loader/-/rollup-plugin-web-worker-loader-1.6.1.tgz" @@ -13118,10 +12514,10 @@ rollup-pluginutils@^2.8.2: dependencies: estree-walker "^0.6.1" -rollup@^2.45.2: - version "2.53.3" - resolved "https://registry.npmjs.org/rollup/-/rollup-2.53.3.tgz" - integrity sha512-79QIGP5DXz5ZHYnCPi3tLz+elOQi6gudp9YINdaJdjG0Yddubo6JRFUM//qCZ0Bap/GJrsUoEBVdSOc4AkMlRA== +rollup@^2.47.0, rollup@^2.79.1: + version "2.79.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.79.1.tgz#bedee8faef7c9f93a2647ac0108748f497f081c7" + integrity sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw== optionalDependencies: fsevents "~2.3.2" @@ -13146,11 +12542,24 @@ rollup@^2.71.1: optionalDependencies: fsevents "~2.3.2" -rollup@^2.79.1: - version "2.79.1" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.79.1.tgz#bedee8faef7c9f93a2647ac0108748f497f081c7" - integrity sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw== +rollup@^4.9.0: + version "4.9.0" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.9.0.tgz#94dff4070f106c1be6b2e88401a49b023c87fa88" + integrity sha512-bUHW/9N21z64gw8s6tP4c88P382Bq/L5uZDowHlHx6s/QWpjJXivIAbEw6LZthgSvlEizZBfLC4OAvWe7aoF7A== optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.9.0" + "@rollup/rollup-android-arm64" "4.9.0" + "@rollup/rollup-darwin-arm64" "4.9.0" + "@rollup/rollup-darwin-x64" "4.9.0" + "@rollup/rollup-linux-arm-gnueabihf" "4.9.0" + "@rollup/rollup-linux-arm64-gnu" "4.9.0" + "@rollup/rollup-linux-arm64-musl" "4.9.0" + "@rollup/rollup-linux-riscv64-gnu" "4.9.0" + "@rollup/rollup-linux-x64-gnu" "4.9.0" + "@rollup/rollup-linux-x64-musl" "4.9.0" + "@rollup/rollup-win32-arm64-msvc" "4.9.0" + "@rollup/rollup-win32-ia32-msvc" "4.9.0" + "@rollup/rollup-win32-x64-msvc" "4.9.0" fsevents "~2.3.2" rollup@~2.78.0: @@ -13160,6 +12569,11 @@ rollup@~2.78.0: optionalDependencies: fsevents "~2.3.2" +rrweb-cssom@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz#ed298055b97cbddcdeb278f904857629dec5e0e1" + integrity sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw== + run-async@^2.4.0: version "2.4.1" resolved "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz" @@ -13257,13 +12671,6 @@ sax@>=0.6.0, sax@~1.2.4: 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.npmjs.org/saxes/-/saxes-5.0.1.tgz" - integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== - dependencies: - xmlchars "^2.2.0" - saxes@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5" @@ -13297,7 +12704,7 @@ semver-match@0.1.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.3.2, semver@^7.3.4, semver@^7.3.5: +semver@7.3.5, 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== @@ -13528,12 +12935,12 @@ smartwrap@^2.0.2: wcwidth "^1.0.1" yargs "^15.1.0" -socks-proxy-agent@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.1.tgz#ffc5859a66dac89b0c4dab90253b96705f3e7120" - integrity sha512-59EjPbbgg8U3x62hhKOFVAmySQUcfRQ4C7Q/D5sEHnZTQRrQlNKINks44DMR1gwXp0p4LaVIeccX2KHTTcHVqQ== +socks-proxy-agent@^8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.2.tgz#5acbd7be7baf18c46a3f293a840109a430a640ad" + integrity sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g== dependencies: - agent-base "^7.0.1" + agent-base "^7.0.2" debug "^4.3.4" socks "^2.7.1" @@ -13601,11 +13008,6 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -source-map@^0.7.3: - version "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.8: version "1.4.8" resolved "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz" @@ -13983,14 +13385,6 @@ supports-color@^8.0.0, supports-color@^8.1.0: dependencies: has-flag "^4.0.0" -supports-hyperlinks@^2.0.0: - version "2.2.0" - 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" - supports-color "^7.0.0" - supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" @@ -14050,7 +13444,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.1.1, tar-fs@^2.0.0: +tar-fs@2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz" integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== @@ -14094,14 +13488,6 @@ term-size@^2.1.0: 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" - integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== - dependencies: - ansi-escapes "^4.2.1" - supports-hyperlinks "^2.0.0" - terser@^5.0.0: version "5.14.2" resolved "https://registry.yarnpkg.com/terser/-/terser-5.14.2.tgz#9ac9f22b06994d736174f4091aa368db896f1c10" @@ -14147,11 +13533,6 @@ thread-stream@^0.13.0: 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" - integrity sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w== - 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" @@ -14186,7 +13567,7 @@ tmp@^0.0.33: dependencies: os-tmpdir "~1.0.2" -tmpl@1.0.5, tmpl@1.0.x: +tmpl@1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz" integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== @@ -14228,16 +13609,7 @@ tosource@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" - 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" - punycode "^2.1.1" - universalify "^0.1.2" - -tough-cookie@^4.1.2: +tough-cookie@^4.1.2, tough-cookie@^4.1.3: version "4.1.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf" integrity sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw== @@ -14255,13 +13627,6 @@ tough-cookie@~2.5.0: psl "^1.1.28" punycode "^2.1.1" -tr46@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz" - integrity sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw== - dependencies: - punycode "^2.1.1" - tr46@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9" @@ -14269,6 +13634,20 @@ tr46@^3.0.0: dependencies: punycode "^2.1.1" +tr46@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-4.1.1.tgz#281a758dcc82aeb4fe38c7dfe4d11a395aac8469" + integrity sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw== + dependencies: + punycode "^2.3.0" + +tr46@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-5.0.0.tgz#3b46d583613ec7283020d79019f1335723801cec" + integrity sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g== + dependencies: + punycode "^2.3.1" + tr46@~0.0.3: version "0.0.3" resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" @@ -14291,34 +13670,6 @@ trim-repeated@^1.0.0: dependencies: escape-string-regexp "^1.0.2" -ts-jest@^27.0.5: - version "27.0.5" - resolved "https://registry.npmjs.org/ts-jest/-/ts-jest-27.0.5.tgz" - integrity sha512-lIJApzfTaSSbtlksfFNHkWOzLJuuSm4faFAfo5kvzOiRAuoN4/eKxVJ2zEAho8aecE04qX6K1pAzfH5QHL1/8w== - dependencies: - bs-logger "0.x" - fast-json-stable-stringify "2.x" - jest-util "^27.0.0" - json5 "2.x" - lodash "4.x" - make-error "1.x" - semver "7.x" - yargs-parser "20.x" - -ts-jest@^27.1.3: - version "27.1.3" - resolved "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.3.tgz" - integrity sha512-6Nlura7s6uM9BVUAoqLH7JHyMXjz8gluryjpPXxr3IxZdAXnU6FhjvVLHFtfd1vsE1p8zD1OJfskkc0jhTSnkA== - dependencies: - bs-logger "0.x" - fast-json-stable-stringify "2.x" - jest-util "^27.0.0" - json5 "2.x" - lodash.memoize "4.x" - make-error "1.x" - semver "7.x" - yargs-parser "20.x" - ts-jest@^29.1.1: version "29.1.1" resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.1.tgz#f58fe62c63caf7bfcc5cc6472082f79180f0815b" @@ -14379,7 +13730,7 @@ tslib@2.4.0, tslib@^2.0.3, tslib@^2.1.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: +tslib@^1.8.1, tslib@^1.9.0: version "1.14.1" resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== @@ -14399,6 +13750,16 @@ tslib@^2.3.1: resolved "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz" integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== +tslib@^2.5.3: + version "2.5.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.3.tgz#24944ba2d990940e6e982c4bea147aba80209913" + integrity sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w== + +tslib@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== + tsutils@^3.21.0: version "3.21.0" resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz" @@ -14638,7 +13999,7 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" -unbzip2-stream@1.4.3, unbzip2-stream@^1.3.3: +unbzip2-stream@1.4.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== @@ -14663,7 +14024,7 @@ unique-string@^2.0.0: dependencies: crypto-random-string "^2.0.0" -universalify@^0.1.0, universalify@^0.1.2: +universalify@^0.1.0: version "0.1.2" resolved "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== @@ -14773,6 +14134,11 @@ url-parse@^1.5.3: querystringify "^2.1.1" requires-port "^1.0.0" +urlpattern-polyfill@9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/urlpattern-polyfill/-/urlpattern-polyfill-9.0.0.tgz#bc7e386bb12fd7898b58d1509df21d3c29ab3460" + integrity sha512-WHN8KDQblxd32odxeIgo83rdVDE2bvdkb86it7bMhYZwWKJz0+O0RK/eZiHYnM+zgt/U7hAHOlCQGfjjvSkw2g== + 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" @@ -14839,15 +14205,6 @@ v8-compile-cache@^2.0.3: 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.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" - convert-source-map "^1.6.0" - source-map "^0.7.3" - v8-to-istanbul@^9.0.1: version "9.1.0" resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz#1b83ed4e397f58c85c266a570fc2558b5feb9265" @@ -14943,20 +14300,6 @@ vite@^3.2.0-beta.2: optionalDependencies: fsevents "~2.3.2" -w3c-hr-time@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz" - integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== - dependencies: - browser-process-hrtime "^1.0.0" - -w3c-xmlserializer@^2.0.0: - version "2.0.0" - 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" - w3c-xmlserializer@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz#aebdc84920d806222936e3cdce408e32488a3073" @@ -14964,12 +14307,12 @@ w3c-xmlserializer@^4.0.0: dependencies: xml-name-validator "^4.0.0" -walker@^1.0.7: - version "1.0.7" - resolved "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz" - integrity sha1-L3+bj9ENZ3JisYqITijRlhjgKPs= +w3c-xmlserializer@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz#f925ba26855158594d907313cedd1476c5967f6c" + integrity sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA== dependencies: - makeerror "1.0.x" + xml-name-validator "^5.0.0" walker@^1.0.8: version "1.0.8" @@ -15039,28 +14382,11 @@ webidl-conversions@^3.0.0: 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.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.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz" - integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== - webidl-conversions@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== -whatwg-encoding@^1.0.5: - version "1.0.5" - resolved "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz" - integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== - dependencies: - iconv-lite "0.4.24" - whatwg-encoding@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz#e7635f597fd87020858626805a2729fa7698ac53" @@ -15068,16 +14394,23 @@ whatwg-encoding@^2.0.0: dependencies: iconv-lite "0.6.3" -whatwg-mimetype@^2.3.0: - version "2.3.0" - resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz" - integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== +whatwg-encoding@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz#d0f4ef769905d426e1688f3e34381a99b60b76e5" + integrity sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ== + dependencies: + iconv-lite "0.6.3" whatwg-mimetype@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q== +whatwg-mimetype@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz#bc1bf94a985dc50388d54a9258ac405c3ca2fc0a" + integrity sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg== + whatwg-url@^11.0.0: version "11.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-11.0.0.tgz#0a849eebb5faf2119b901bb76fd795c2848d4018" @@ -15086,6 +14419,22 @@ whatwg-url@^11.0.0: tr46 "^3.0.0" webidl-conversions "^7.0.0" +whatwg-url@^12.0.0, whatwg-url@^12.0.1: + version "12.0.1" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-12.0.1.tgz#fd7bcc71192e7c3a2a97b9a8d6b094853ed8773c" + integrity sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ== + dependencies: + tr46 "^4.1.1" + webidl-conversions "^7.0.0" + +whatwg-url@^14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-14.0.0.tgz#00baaa7fd198744910c4b1ef68378f2200e4ceb6" + integrity sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw== + dependencies: + tr46 "^5.0.0" + webidl-conversions "^7.0.0" + whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" @@ -15094,15 +14443,6 @@ whatwg-url@^5.0.0: tr46 "~0.0.3" webidl-conversions "^3.0.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== - dependencies: - lodash "^4.7.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" @@ -15238,46 +14578,51 @@ 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.13.0, ws@^8.11.0: - version "8.13.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" - integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== +ws@8.15.1, ws@^8.13.0: + version "8.15.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.15.1.tgz#271ba33a45ca0cc477940f7f200cd7fba7ee1997" + integrity sha512-W5OZiCjXEmk0yZ66ZN82beM5Sz7l7coYxpRkzS+p9PP+ToQry8szKh+61eNktr7EA9DOwvFGhfC605jDHbP6QQ== + +ws@8.16.0: + version "8.16.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.16.0.tgz#d1cd774f36fbc07165066a60e40323eab6446fd4" + integrity sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ== 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" - resolved "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz" - integrity sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A== - -ws@^7.4.3, ws@^7.4.5: +ws@^7.4.3: version "7.5.3" 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.npmjs.org/ws/-/ws-7.5.5.tgz" - integrity sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w== +ws@^8.11.0: + version "8.13.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" + integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== + +ws@^8.14.2: + version "8.14.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.14.2.tgz#6c249a806eb2db7a20d26d51e7709eab7b2e6c7f" + integrity sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g== 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== - xml-name-validator@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== +xml-name-validator@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-5.0.0.tgz#82be9b957f7afdacf961e5980f1bf227c0bf7673" + integrity sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg== + xml2js@~0.4.23: version "0.4.23" resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66" @@ -15326,11 +14671,6 @@ yaml@^1.10.0: resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== -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== - 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" @@ -15339,6 +14679,11 @@ yargs-parser@^18.1.2, yargs-parser@^18.1.3: camelcase "^5.0.0" decamelize "^1.2.0" +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== + yargs-parser@^21.0.0, yargs-parser@^21.0.1, yargs-parser@^21.1.1: version "21.1.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" @@ -15370,10 +14715,10 @@ yargs@17.4.0: y18n "^5.0.5" yargs-parser "^21.0.0" -yargs@17.7.1: - version "17.7.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.1.tgz#34a77645201d1a8fc5213ace787c220eabbd0967" - integrity sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw== +yargs@17.7.2, yargs@^17.3.1: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== dependencies: cliui "^8.0.1" escalade "^3.1.1" @@ -15413,19 +14758,6 @@ yargs@^17.1.1: y18n "^5.0.5" yargs-parser "^21.1.1" -yargs@^17.3.1: - version "17.7.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" - integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== - 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, yauzl@^2.10.0: version "2.10.0" resolved "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz"