diff --git a/libs/extension/babel.config.js b/libs/extension/babel.config.js new file mode 100644 index 00000000..c358bb46 --- /dev/null +++ b/libs/extension/babel.config.js @@ -0,0 +1,15 @@ +module.exports = api => { + const isTest = api.env('test'); + console.log('isTest', isTest); + return { + presets: [ + '@babel/preset-env', + '@babel/preset-typescript', + ['@babel/preset-react', { + runtime: 'classic', + 'pragma': 'Horizon.createElement', + 'pragmaFrag': 'Horizon.Fragment', + }]], + plugins: ['@babel/plugin-proposal-class-properties'], + }; +}; \ No newline at end of file diff --git a/libs/extension/package.json b/libs/extension/package.json new file mode 100644 index 00000000..ade577c7 --- /dev/null +++ b/libs/extension/package.json @@ -0,0 +1,35 @@ +{ + "name": "extension", + "version": "1.0.0", + "description": "", + "main": "", + "scripts": { + "build": "webpack --config ./webpack.config.js", + "watch": "webpack --config ./webpack.config.js --watch", + "build-dev": "webpack --config ./webpack.dev.js", + "start": "webpack serve --config ./webpack.dev.js ", + "test": "jest" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "@babel/core": "7.12.3", + "@babel/plugin-proposal-class-properties": "^7.16.7", + "@babel/preset-env": "7.12.1", + "@babel/preset-react": "7.12.1", + "@babel/preset-typescript": "^7.16.7", + "@types/jest": "^27.4.1", + "babel-loader": "8.1.0", + "css-loader": "^6.7.1", + "html-webpack-plugin": "^5.5.0", + "jest": "^27.5.1", + "less": "^4.1.2", + "less-loader": "^10.2.0", + "style-loader": "^3.3.1", + "ts-jest": "^27.1.4", + "webpack": "^5.70.0", + "webpack-cli": "^4.9.2", + "webpack-dev-server": "^4.7.4" + } +} diff --git a/libs/extension/readme.md b/libs/extension/readme.md index d7e98884..ee9ec4cd 100644 --- a/libs/extension/readme.md +++ b/libs/extension/readme.md @@ -2,7 +2,6 @@ devtools_page: devtool主页面 default_popup: 拓展图标点击时弹窗页面 content_scripts: 内容脚本,在项目中负责在页面初始化时调用注入全局变量代码和消息传递 -web_accessible_resources: 注入全局变量代码 ## 打开 panel 页面调试面板的方式 @@ -53,5 +52,12 @@ sequenceDiagram - 每个vNode有唯一的 path 属性,可以作为标识使用 - 通过解析 path 值可以分析出组件树的结构 +## 组件props/state/hook等数据的传输和解析 +将数据格式进行转换后进行传递。对于 props 和 类组件的 state,他们都是对象,可以将对象进行解析然后以 k-v 的形式,树的结构显示。函数组件的 Hooks 是以数组的形式存储在 vNode 的属性中的,每个 hook 的唯一标识符是 hIndex 属性值,在对象展示的时候不能展示该属性值,需要根据 hook 类型展示一个 state/ref/effect 等值。hook 中存储的值也可能不是对象,只是一个简单的字符串,他们的解析和 props/state 的解析同样存在差异。 + + ## 滚动动态渲染 Tree 考虑到组件树可能很大,所以并不适合一次性全部渲染出来,可以通过滚动渲染的方式减少页面 dom 的数量。我们可以把树看成有不同缩进长度的列表,动态渲染滚动列表的实现可以参考谷歌的这篇文章:https://developers.google.com/web/updates/2016/07/infinite-scroller 这样,我们需要的组件树数据可以由树结构转变为数组,可以减少动态渲染时对树结构进行解析时的计算工作。 + +## 测试框架 +jest测试框架不提供浏览器插件的相关 api,我们在封装好相关 api 后需要模拟这些 api 的行为从而展开测试工作。 diff --git a/libs/extension/src/background/index.ts b/libs/extension/src/background/index.ts new file mode 100644 index 00000000..2745c61a --- /dev/null +++ b/libs/extension/src/background/index.ts @@ -0,0 +1,58 @@ +// 多个页面、tab页共享一个 background,需要建立连接池,给每个tab建立连接 +const connections = {}; + +// panel 代码中调用 let backgroundPageConnection = chrome.runtime.connect({...}) 会触发回调函数 +chrome.runtime.onConnect.addListener(function (port) { + + // The original connection event doesn't include the tab ID of the + // DevTools page, so we need to send it explicitly. + function extensionListener(message, sender, sendResponse) { + // 在backgroundPageConnection创建后会发送初始化请求,这样就可以获取tabId,给连接编号 + if (message.name === 'init') { + // 获取 panel 所在 tab 页的tabId + connections[message.tabId] = port; + chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { + chrome.tabs.sendMessage(tabs[0].id, {tag: 'init horizon info'}, function(response) { + console.log(response.farewell); + }); + }); + return; + } + + if (message.name === 'update') { + return; + } + // other message handling + } + + // Listen to messages sent from the DevTools page + port.onMessage.addListener(extensionListener); + + port.onDisconnect.addListener(function (port) { + port.onMessage.removeListener(extensionListener); + + const tabs = Object.keys(connections); + for (let i = 0, len = tabs.length; i < len; i++) { + if (connections[tabs[i]] == port) { + delete connections[tabs[i]]; + break; + } + } + }); +}); + +// 监听来自 content script 的消息,并将消息发送给对应的 devTools page,也就是 panel +chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) { + // Messages from content scripts should have sender.tab set + if (sender.tab) { + const tabId = sender.tab.id; + if (tabId in connections) { + connections[tabId].postMessage(request); + } else { + console.log('Tab not found in connection list.'); + } + } else { + console.log('sender.tab not defined.'); + } + return true; +}); diff --git a/libs/extension/src/components/ComponentInfo.tsx b/libs/extension/src/components/ComponentInfo.tsx index 10a1f637..7d78c814 100644 --- a/libs/extension/src/components/ComponentInfo.tsx +++ b/libs/extension/src/components/ComponentInfo.tsx @@ -18,7 +18,7 @@ type IComponentInfo = { onClickParent: (item: IData) => void; }; -type IAttr = { +export type IAttr = { name: string; type: string; value: string | boolean; diff --git a/libs/extension/src/components/ComponentsInfo.less b/libs/extension/src/components/ComponentsInfo.less index 9a52e2fb..853d4209 100644 --- a/libs/extension/src/components/ComponentsInfo.less +++ b/libs/extension/src/components/ComponentsInfo.less @@ -36,13 +36,8 @@ border-bottom: unset; } - >:first-child { - padding: unset; - } - >div { border-bottom: @divider-style; - padding: 0.5rem } .attrContainer { diff --git a/libs/extension/src/components/ResizeEvent.ts b/libs/extension/src/components/ResizeEvent.ts index 0150d0ad..69f6c1aa 100644 --- a/libs/extension/src/components/ResizeEvent.ts +++ b/libs/extension/src/components/ResizeEvent.ts @@ -60,7 +60,7 @@ export function addResizeListener(element: any, fn: any) { observer.data = 'about:blank'; observer.onload = loadObserver; observer.type = 'text/html'; - observer.__observeElement__ = element; + observer['__observeElement__'] = element; element.__observer__ = observer; element.appendChild(observer); } else { diff --git a/libs/extension/src/components/SizeObserver.tsx b/libs/extension/src/components/SizeObserver.tsx index 3d430093..47348bb6 100644 --- a/libs/extension/src/components/SizeObserver.tsx +++ b/libs/extension/src/components/SizeObserver.tsx @@ -4,8 +4,8 @@ import { addResizeListener, removeResizeListener } from './ResizeEvent'; export function SizeObserver(props) { const { children, ...rest } = props; - const containerRef = useRef(); - const [size, setSize] = useState(); + const containerRef = useRef(); + const [size, setSize] = useState<{width: number, height: number}>(); const notifyChild = (element) => { setSize({ width: element.offsetWidth, diff --git a/libs/extension/src/components/VList.tsx b/libs/extension/src/components/VList.tsx index c932a492..aeee688a 100644 --- a/libs/extension/src/components/VList.tsx +++ b/libs/extension/src/components/VList.tsx @@ -1,3 +1,5 @@ +// TODO:当前的 item 渲染效果较差,每次滚动所有项在数组中的位置都会发生变更。 +// 建议修改成选项增加减少时,未变更项在原数组中位置不变更 import { useState, useRef, useEffect } from 'horizon'; import styles from './VList.less'; @@ -30,7 +32,7 @@ export function VList(props: IProps) { } = props; const [scrollTop, setScrollTop] = useState(data.indexOf(scrollToItem) * itemHeight); const renderInfoRef: { current: renderInfoType } = useRef({ visibleItems: [], skipItemCountBeforeScrollItem: 0 }); - const containerRef = useRef(); + const containerRef = useRef(); useEffect(() => { onRendered(renderInfoRef.current); }); diff --git a/libs/extension/src/components/VTree.tsx b/libs/extension/src/components/VTree.tsx index a286e411..6b990dc1 100644 --- a/libs/extension/src/components/VTree.tsx +++ b/libs/extension/src/components/VTree.tsx @@ -1,7 +1,7 @@ import { useState, useEffect } from 'horizon'; import styles from './VTree.less'; import Triangle from '../svgs/Triangle'; -import { createRegExp } from './../utils'; +import { createRegExp } from '../utils/regExpUtils'; import { SizeObserver } from './SizeObserver'; import { renderInfoType, VList } from './VList'; @@ -102,7 +102,7 @@ function VTree(props: { onRendered: (renderInfo: renderInfoType) => void, collapsedNodes?: IData[], onCollapseNode?: (item: IData[]) => void, - selectItem: IData[], + selectItem: IData, onSelectItem: (item: IData) => void, }) { const { data, highlightValue, scrollToItem, onRendered, onCollapseNode, onSelectItem } = props; diff --git a/libs/extension/src/contentScript/index.ts b/libs/extension/src/contentScript/index.ts new file mode 100644 index 00000000..2236496a --- /dev/null +++ b/libs/extension/src/contentScript/index.ts @@ -0,0 +1,36 @@ +import { injectCode } from '../utils/injectUtils'; + +// 页面的window对象不能直接通过 contentScript 代码修改,只能通过添加 js 代码往页面 window 注入hook +injectCode(chrome.runtime.getURL('/injector.js')); + +// 监听来自页面的信息 +window.addEventListener('message', event => { + // 只监听来自本页面的消息 + if (event.source !== window) { + return; + } + + if (event.data.type && (event.data.type === 'HORIZON_DEV_TOOLS')) { + console.log('Content script received: ' + JSON.stringify(event.data.vNode)); + // 传递给background + chrome.runtime.sendMessage(event.data.vNode, function (response) { + console.log(response); + }); + } +}, false); + + + +// 监听来自background的消息 +chrome.runtime.onMessage.addListener( + function (request, sender, sendResponse) { + console.log(sender.tab ? + 'from a content script:' + sender.tab.url : + 'from the extension'); + if (request.tag === 'init horizon info') { + // 传递消息给页面 + console.log('start pass info to webpage'); + window.postMessage({type: 'HORIZON_DEV_TOOLS', id: 1}, '*'); + } + } +); diff --git a/libs/extension/src/devtools/mockPage/MockClassComponent.tsx b/libs/extension/src/devtools/mockPage/MockClassComponent.tsx new file mode 100644 index 00000000..c59385ed --- /dev/null +++ b/libs/extension/src/devtools/mockPage/MockClassComponent.tsx @@ -0,0 +1,22 @@ +import { Component } from 'horizon'; + +const defaultState = { + name: 'jenny', + boolean: true, +}; + +export default class MockClassComponent extends Component<{fruit: string}, typeof defaultState> { + + state = defaultState; + + render() { + return ( +
+ + {this.state.name} + {this.props?.fruit} +
+ ); + } + +} \ No newline at end of file diff --git a/libs/extension/src/devtools/mockPage/MockFunctionComponent.tsx b/libs/extension/src/devtools/mockPage/MockFunctionComponent.tsx new file mode 100644 index 00000000..48a76e93 --- /dev/null +++ b/libs/extension/src/devtools/mockPage/MockFunctionComponent.tsx @@ -0,0 +1,22 @@ +import { useState, useEffect, useRef, createContext } from 'horizon'; + +const Ctx = createContext(); + +export default function MockFunctionComponent(props) { + const [age, setAge] = useState(0); + const domRef = useRef(); + const objRef = useRef({ str: 'string' }); + + useEffect(() => { }, []); + + return ( +
+ age: {age} + + count: {props.count} +
+
{objRef.current.str}
+ +
+ ); +} \ No newline at end of file diff --git a/libs/extension/src/devtools/mockPage/index.tsx b/libs/extension/src/devtools/mockPage/index.tsx new file mode 100644 index 00000000..9b5332e3 --- /dev/null +++ b/libs/extension/src/devtools/mockPage/index.tsx @@ -0,0 +1,18 @@ +import { render } from 'horizon'; +import MockClassComponent from './MockClassComponent'; +import MockFunctionComponent from './MockFunctionComponent'; + +const root = document.createElement('div'); +document.body.append(root); + +function App() { + return ( +
+ abc + + +
+ ); +} + +render(, root); diff --git a/libs/extension/src/devtools/mockPage/mockPage.html b/libs/extension/src/devtools/mockPage/mockPage.html new file mode 100644 index 00000000..5e6b02c8 --- /dev/null +++ b/libs/extension/src/devtools/mockPage/mockPage.html @@ -0,0 +1,28 @@ + + + + + + Horizon Mock Page + + + + + + + + + diff --git a/libs/extension/src/hooks/FilterTree.ts b/libs/extension/src/hooks/FilterTree.ts index 178b9155..faa789f0 100644 --- a/libs/extension/src/hooks/FilterTree.ts +++ b/libs/extension/src/hooks/FilterTree.ts @@ -13,7 +13,7 @@ // 找到该节点的缩进值,和index值,在data中向上遍历,通过缩进值判断父节点 import { useState, useRef } from 'horizon'; -import { createRegExp } from '../utils'; +import { createRegExp } from '../utils/regExpUtils'; /** * 把节点的父节点从收起节点数组中删除,并返回新的收起节点数组 diff --git a/libs/extension/src/injector/index.ts b/libs/extension/src/injector/index.ts new file mode 100644 index 00000000..178cf608 --- /dev/null +++ b/libs/extension/src/injector/index.ts @@ -0,0 +1,32 @@ +import parseTreeRoot from "../parser/parseVNode"; + +function injectHook() { + if (window.__HORIZON_DEV_HOOK__) { + return; + } + Object.defineProperty(window, '__HORIZON_DEV_HOOK__', { + enumerable: false, + value: { + roots: [], + send: function (vNode: any) { + const result = parseTreeRoot(vNode); + window.postMessage({ + type: 'HORIZON_DEV_TOOLS', vNode: result + }, '*'); + }, + listen: function (id: number) { + window.addEventListener('message', function(event) { + // We only accept messages from ourselves + if (event.source !== window) { + return; + } + + if (event.data.type && (event.data.type === 'HORIZON_DEV_TOOLS') && event.data.id === id) { + console.log('todo'); + } + }); + } + }, + }); +} +injectHook(); diff --git a/libs/extension/src/main/index.ts b/libs/extension/src/main/index.ts new file mode 100644 index 00000000..b81f544c --- /dev/null +++ b/libs/extension/src/main/index.ts @@ -0,0 +1,7 @@ +chrome.devtools.panels.create('Horizon', + '', + 'panel.html', + function(panel) { + + } +); \ No newline at end of file diff --git a/libs/extension/src/main/main.html b/libs/extension/src/main/main.html new file mode 100644 index 00000000..540f18ae --- /dev/null +++ b/libs/extension/src/main/main.html @@ -0,0 +1,12 @@ + + + + + +
+
+

Horizon dev tools!

+
+ + + diff --git a/libs/extension/src/manifest.json b/libs/extension/src/manifest.json new file mode 100644 index 00000000..3db7b374 --- /dev/null +++ b/libs/extension/src/manifest.json @@ -0,0 +1,22 @@ +{ + "name": "Horizon dev tool", + "description": "Horizon chrome dev extension", + "version": "1.0", + "minimum_chrome_version": "10.0", + "manifest_version": 3, + "background": { + "service_worker": "background.js" + }, + "permissions": ["storage", "activeTab", "scripting"], + + "devtools_page": "main.html", + "action": {}, + "content_scripts": [ + { + "matches": [""], + "js": ["contentScript.js"], + "run_at": "document_start" + } + ], + "web_accessible_resources": [] +} diff --git a/libs/extension/src/parser/parseAttr.ts b/libs/extension/src/parser/parseAttr.ts index c52b1891..f710141e 100644 --- a/libs/extension/src/parser/parseAttr.ts +++ b/libs/extension/src/parser/parseAttr.ts @@ -1,12 +1,8 @@ +import { IAttr } from "../components/ComponentInfo"; // 将状态的值解析成固定格式 export function parseAttr(rootAttr: any) { - const result: { - name: string, - type: string, - value: string, - indentation: number - }[] = []; + const result: IAttr[] = []; const indentation = 0; const parseSubAttr = (attr: any, parentIndentation: number, attrName: string) => { const stateType = typeof attr; diff --git a/libs/extension/src/utils/injectUtils.ts b/libs/extension/src/utils/injectUtils.ts new file mode 100644 index 00000000..583eb5ea --- /dev/null +++ b/libs/extension/src/utils/injectUtils.ts @@ -0,0 +1,19 @@ + +function ifNullThrows(v) { + if (v === null) { + throw new Error('received a null'); + } + return v; +} + +// 用于向页面注入脚本 +export function injectCode(src) { + const script = document.createElement('script'); + script.src = src; + script.onload = function () { + // 加载完毕后需要移除 + script.remove(); + }; + + ifNullThrows(document.head || document.documentElement).appendChild(script); +} diff --git a/libs/extension/src/utils.ts b/libs/extension/src/utils/regExpUtils.ts similarity index 100% rename from libs/extension/src/utils.ts rename to libs/extension/src/utils/regExpUtils.ts diff --git a/libs/extension/tsconfig.json b/libs/extension/tsconfig.json new file mode 100644 index 00000000..e2effe5e --- /dev/null +++ b/libs/extension/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "outDir": "./dist/", + "noImplicitAny": false, + "module": "es6", + "target": "es5", + "jsx": "preserve", + "allowJs": true, + "allowSyntheticDefaultImports": true, + "moduleResolution": "Node", + "baseUrl": "./", + "paths": { + "*": ["types/*"] + } + }, + "includes": [ + "./src/index.d.ts", "./src/*/*.ts", "./src/*/*.tsx" + ] +} \ No newline at end of file diff --git a/libs/extension/webpack.config.js b/libs/extension/webpack.config.js new file mode 100644 index 00000000..f712808b --- /dev/null +++ b/libs/extension/webpack.config.js @@ -0,0 +1,50 @@ +const path = require('path'); + +const config = { + entry: { + background: './src/background/index.ts', + main: './src/main/index.ts', + injector: './src/injector/index.ts', + contentScript: './src/contentScript/index.ts', + panel: './src/panel/index.tsx', + }, + output: { + path: path.resolve(__dirname, './build'), + filename: '[name].js', + }, + mode: 'development', + devtool: 'inline-source-map', + module: { + rules: [ + { + test: /\.tsx?$/, + exclude: /node_modules/, + use: [ + { + loader: 'babel-loader', + } + ] + }, + { + test: /\.less/i, + use: [ + 'style-loader', + { + loader: 'css-loader', + options: { + modules: true, + + } + }, + 'less-loader'], + }] + }, + resolve: { + extensions: ['.js', '.ts', '.tsx'], + }, + externals: { + 'horizon': 'Horizon', + }, +}; + +module.exports = config; diff --git a/libs/extension/webpack.dev.js b/libs/extension/webpack.dev.js index 1c468b0c..b3332077 100644 --- a/libs/extension/webpack.dev.js +++ b/libs/extension/webpack.dev.js @@ -1,5 +1,4 @@ const path = require('path'); -const HtmlWebpackPlugin = require('html-webpack-plugin'); const webpack = require('webpack'); // 用于 panel 页面开发 @@ -8,6 +7,7 @@ module.exports = { mode: 'development', entry: { panel: path.join(__dirname, './src/panel/index.tsx'), + mockPage: path.join(__dirname, './src/devtools/mockPage/index.tsx'), }, output: { path: path.join(__dirname, 'dist'), @@ -15,7 +15,7 @@ module.exports = { }, devtool: 'source-map', resolve: { - extensions: ['.ts', '.tsx', '.js'] + extensions: ['.ts', '.tsx', '.js'], }, module: { rules: [ @@ -25,16 +25,6 @@ module.exports = { use: [ { loader: 'babel-loader', - options: { - presets: ['@babel/preset-env', - '@babel/preset-typescript', - ['@babel/preset-react', { - runtime: 'classic', - 'pragma': 'Horizon.createElement', - 'pragmaFrag': 'Horizon.Fragment', - }]], - plugins: ['@babel/plugin-proposal-class-properties'], - } } ] }, @@ -64,10 +54,6 @@ module.exports = { magicHtml: true, }, plugins: [ - new HtmlWebpackPlugin({ - filename: 'panel.html', - template: './src/panel/panel.html' - }), new webpack.DefinePlugin({ 'process.env.NODE_ENV': '"development"', isDev: 'true', diff --git a/libs/horizon/package.json b/libs/horizon/package.json index 388ee500..f3ffb19d 100644 --- a/libs/horizon/package.json +++ b/libs/horizon/package.json @@ -4,7 +4,7 @@ "keywords": [ "horizon" ], - "version": "1.0.0", + "version": "0.0.6", "homepage": "", "bugs": "", "main": "index.js", diff --git a/scripts/gen3rdLib.js b/scripts/gen3rdLib.js index 66b54d69..7e33dfec 100644 --- a/scripts/gen3rdLib.js +++ b/scripts/gen3rdLib.js @@ -23,7 +23,7 @@ const readLib = (lib) => { ejs.renderFile(path.resolve(__dirname, './template.ejs'), { Horizon: readLib(`horizon.${suffix}`), }, null, function(err, result) { - const common3rdLibPath = path.resolve(__dirname, `${libPathPrefix}/common3rdlib.min.js`) + const common3rdLibPath = path.resolve(__dirname, `${libPathPrefix}/horizonCommon3rdlib.min.js`) rimRaf(common3rdLibPath, e => { if (e) { console.log(e) diff --git a/scripts/template.ejs b/scripts/template.ejs index 7ead2d31..3f426b7e 100644 --- a/scripts/template.ejs +++ b/scripts/template.ejs @@ -1,742 +1,764 @@ -if(!window["Horizon"]) { - <%- Horizon %> +<%- Horizon %> + +!function(t,r){"object"==typeof exports&&"object"==typeof module?module.exports=r():"function"==typeof define&&define.amd?define([],r):"object"==typeof exports?exports.ie=r():t.ie=r()}(window,(function(){return function(t){var r={};function e(n){if(r[n])return r[n].exports;var o=r[n]={i:n,l:!1,exports:{}};return t[n].call(o.exports,o,o.exports,e),o.l=!0,o.exports}return e.m=t,e.c=r,e.d=function(t,r,n){e.o(t,r)||Object.defineProperty(t,r,{enumerable:!0,get:n})},e.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},e.t=function(t,r){if(1&r&&(t=e(t)),8&r)return t;if(4&r&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(e.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&r&&"string"!=typeof t)for(var o in t)e.d(n,o,function(r){return t[r]}.bind(null,o));return n},e.n=function(t){var r=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(r,"a",r),r},e.o=function(t,r){return Object.prototype.hasOwnProperty.call(t,r)},e.p="",e(e.s=192)}([function(t,r,e){var n=e(1),o=e(23).f,i=e(25),a=e(17),u=e(114),c=e(90),s=e(73);t.exports=function(t,r){var e,f,l,h,p,v=t.target,g=t.global,d=t.stat;if(e=g?n:d?n[v]||u(v,{}):(n[v]||{}).prototype)for(f in r){if(h=r[f],l=t.noTargetGet?(p=o(e,f))&&p.value:e[f],!s(g?f:v+(d?".":"#")+f,t.forced)&&void 0!==l){if(typeof h==typeof l)continue;c(h,l)}(t.sham||l&&l.sham)&&i(h,"sham",!0),a(e,f,h,t)}}},function(t,r,e){(function(r){var e=function(t){return t&&t.Math==Math&&t};t.exports=e("object"==typeof globalThis&&globalThis)||e("object"==typeof window&&window)||e("object"==typeof self&&self)||e("object"==typeof r&&r)||function(){return this}()||Function("return this")()}).call(this,e(195))},function(t,r){t.exports=function(t){try{return!!t()}catch(t){return!0}}},function(t,r,e){var n=e(68),o=Function.prototype,i=o.bind,a=o.call,u=n&&i.bind(a,a);t.exports=n?function(t){return t&&u(t)}:function(t){return t&&function(){return a.apply(t,arguments)}}},function(t,r,e){var n=e(1),o=e(6),i=n.String,a=n.TypeError;t.exports=function(t){if(o(t))return t;throw a(i(t)+" is not an object")}},function(t,r,e){var n=e(2);t.exports=!n((function(){return 7!=Object.defineProperty({},1,{get:function(){return 7}})[1]}))},function(t,r,e){var n=e(9);t.exports=function(t){return"object"==typeof t?null!==t:n(t)}},function(t,r,e){var n=e(1),o=e(86),i=e(12),a=e(60),u=e(112),c=e(142),s=o("wks"),f=n.Symbol,l=f&&f.for,h=c?f:f&&f.withoutSetter||a;t.exports=function(t){if(!i(s,t)||!u&&"string"!=typeof s[t]){var r="Symbol."+t;u&&i(f,t)?s[t]=f[t]:s[t]=c&&l?l(r):h(r)}return s[t]}},function(t,r,e){var n=e(1),o=e(52),i=n.String;t.exports=function(t){if("Symbol"===o(t))throw TypeError("Cannot convert a Symbol value to a string");return i(t)}},function(t,r){t.exports=function(t){return"function"==typeof t}},function(t,r,e){"use strict";var n,o,i,a=e(128),u=e(5),c=e(1),s=e(9),f=e(6),l=e(12),h=e(52),p=e(70),v=e(25),g=e(17),d=e(13).f,y=e(29),m=e(37),b=e(39),x=e(7),w=e(60),E=c.Int8Array,S=E&&E.prototype,A=c.Uint8ClampedArray,O=A&&A.prototype,R=E&&m(E),T=S&&m(S),I=Object.prototype,M=c.TypeError,j=x("toStringTag"),P=w("TYPED_ARRAY_TAG"),k=w("TYPED_ARRAY_CONSTRUCTOR"),_=a&&!!b&&"Opera"!==h(c.opera),L=!1,N={Int8Array:1,Uint8Array:1,Uint8ClampedArray:1,Int16Array:2,Uint16Array:2,Int32Array:4,Uint32Array:4,Float32Array:4,Float64Array:8},D={BigInt64Array:8,BigUint64Array:8},U=function(t){if(!f(t))return!1;var r=h(t);return l(N,r)||l(D,r)};for(n in N)(i=(o=c[n])&&o.prototype)?v(i,k,o):_=!1;for(n in D)(i=(o=c[n])&&o.prototype)&&v(i,k,o);if((!_||!s(R)||R===Function.prototype)&&(R=function(){throw M("Incorrect invocation")},_))for(n in N)c[n]&&b(c[n],R);if((!_||!T||T===I)&&(T=R.prototype,_))for(n in N)c[n]&&b(c[n].prototype,T);if(_&&m(O)!==T&&b(O,T),u&&!l(T,j))for(n in L=!0,d(T,j,{get:function(){return f(this)?this[P]:void 0}}),N)c[n]&&v(c[n],P,n);t.exports={NATIVE_ARRAY_BUFFER_VIEWS:_,TYPED_ARRAY_CONSTRUCTOR:k,TYPED_ARRAY_TAG:L&&P,aTypedArray:function(t){if(U(t))return t;throw M("Target is not a typed array")},aTypedArrayConstructor:function(t){if(s(t)&&(!b||y(R,t)))return t;throw M(p(t)+" is not a typed array constructor")},exportTypedArrayMethod:function(t,r,e,n){if(u){if(e)for(var o in N){var i=c[o];if(i&&l(i.prototype,t))try{delete i.prototype[t]}catch(e){try{i.prototype[t]=r}catch(t){}}}T[t]&&!e||g(T,t,e?r:_&&S[t]||r,n)}},exportTypedArrayStaticMethod:function(t,r,e){var n,o;if(u){if(b){if(e)for(n in N)if((o=c[n])&&l(o,t))try{delete o[t]}catch(t){}if(R[t]&&!e)return;try{return g(R,t,e?r:_&&R[t]||r)}catch(t){}}for(n in N)!(o=c[n])||o[t]&&!e||g(o,t,r)}},isView:function(t){if(!f(t))return!1;var r=h(t);return"DataView"===r||l(N,r)||l(D,r)},isTypedArray:U,TypedArray:R,TypedArrayPrototype:T}},function(t,r,e){var n=e(68),o=Function.prototype.call;t.exports=n?o.bind(o):function(){return o.apply(o,arguments)}},function(t,r,e){var n=e(3),o=e(14),i=n({}.hasOwnProperty);t.exports=Object.hasOwn||function(t,r){return i(o(t),r)}},function(t,r,e){var n=e(1),o=e(5),i=e(144),a=e(145),u=e(4),c=e(49),s=n.TypeError,f=Object.defineProperty,l=Object.getOwnPropertyDescriptor;r.f=o?a?function(t,r,e){if(u(t),r=c(r),u(e),"function"==typeof t&&"prototype"===r&&"value"in e&&"writable"in e&&!e.writable){var n=l(t,r);n&&n.writable&&(t[r]=e.value,e={configurable:"configurable"in e?e.configurable:n.configurable,enumerable:"enumerable"in e?e.enumerable:n.enumerable,writable:!1})}return f(t,r,e)}:f:function(t,r,e){if(u(t),r=c(r),u(e),i)try{return f(t,r,e)}catch(t){}if("get"in e||"set"in e)throw s("Accessors not supported");return"value"in e&&(t[r]=e.value),t}},function(t,r,e){var n=e(1),o=e(18),i=n.Object;t.exports=function(t){return i(o(t))}},function(t,r,e){var n=e(30);t.exports=function(t){return n(t.length)}},function(t,r,e){var n=e(1),o=e(9),i=function(t){return o(t)?t:void 0};t.exports=function(t,r){return arguments.length<2?i(n[t]):n[t]&&n[t][r]}},function(t,r,e){var n=e(1),o=e(9),i=e(12),a=e(25),u=e(114),c=e(88),s=e(19),f=e(61).CONFIGURABLE,l=s.get,h=s.enforce,p=String(String).split("String");(t.exports=function(t,r,e,c){var s,l=!!c&&!!c.unsafe,v=!!c&&!!c.enumerable,g=!!c&&!!c.noTargetGet,d=c&&void 0!==c.name?c.name:r;o(e)&&("Symbol("===String(d).slice(0,7)&&(d="["+String(d).replace(/^Symbol\(([^)]*)\)/,"$1")+"]"),(!i(e,"name")||f&&e.name!==d)&&a(e,"name",d),(s=h(e)).source||(s.source=p.join("string"==typeof d?d:""))),t!==n?(l?!g&&t[r]&&(v=!0):delete t[r],v?t[r]=e:a(t,r,e)):v?t[r]=e:u(r,e)})(Function.prototype,"toString",(function(){return o(this)&&l(this).source||c(this)}))},function(t,r,e){var n=e(1).TypeError;t.exports=function(t){if(null==t)throw n("Can't call method on "+t);return t}},function(t,r,e){var n,o,i,a=e(146),u=e(1),c=e(3),s=e(6),f=e(25),l=e(12),h=e(113),p=e(89),v=e(71),g=u.TypeError,d=u.WeakMap;if(a||h.state){var y=h.state||(h.state=new d),m=c(y.get),b=c(y.has),x=c(y.set);n=function(t,r){if(b(y,t))throw new g("Object already initialized");return r.facade=t,x(y,t,r),r},o=function(t){return m(y,t)||{}},i=function(t){return b(y,t)}}else{var w=p("state");v[w]=!0,n=function(t,r){if(l(t,w))throw new g("Object already initialized");return r.facade=t,f(t,w,r),r},o=function(t){return l(t,w)?t[w]:{}},i=function(t){return l(t,w)}}t.exports={set:n,get:o,has:i,enforce:function(t){return i(t)?o(t):n(t,{})},getterFor:function(t){return function(r){var e;if(!s(r)||(e=o(r)).type!==t)throw g("Incompatible receiver, "+t+" required");return e}}}},function(t,r){var e=Math.ceil,n=Math.floor;t.exports=function(t){var r=+t;return r!=r||0===r?0:(r>0?n:e)(r)}},function(t,r){t.exports=!1},function(t,r,e){var n=e(38),o=e(3),i=e(69),a=e(14),u=e(15),c=e(77),s=o([].push),f=function(t){var r=1==t,e=2==t,o=3==t,f=4==t,l=6==t,h=7==t,p=5==t||l;return function(v,g,d,y){for(var m,b,x=a(v),w=i(x),E=n(g,d),S=u(w),A=0,O=y||c,R=r?O(v,S):e||h?O(v,0):void 0;S>A;A++)if((p||A in w)&&(b=E(m=w[A],A,x),t))if(r)R[A]=b;else if(b)switch(t){case 3:return!0;case 5:return m;case 6:return A;case 2:s(R,m)}else switch(t){case 4:return!1;case 7:s(R,m)}return l?-1:o||f?f:R}};t.exports={forEach:f(0),map:f(1),filter:f(2),some:f(3),every:f(4),find:f(5),findIndex:f(6),filterReject:f(7)}},function(t,r,e){var n=e(5),o=e(11),i=e(85),a=e(35),u=e(26),c=e(49),s=e(12),f=e(144),l=Object.getOwnPropertyDescriptor;r.f=n?l:function(t,r){if(t=u(t),r=c(r),f)try{return l(t,r)}catch(t){}if(s(t,r))return a(!o(i.f,t,r),t[r])}},function(t,r,e){var n=e(1),o=e(9),i=e(70),a=n.TypeError;t.exports=function(t){if(o(t))return t;throw a(i(t)+" is not a function")}},function(t,r,e){var n=e(5),o=e(13),i=e(35);t.exports=n?function(t,r,e){return o.f(t,r,i(1,e))}:function(t,r,e){return t[r]=e,t}},function(t,r,e){var n=e(69),o=e(18);t.exports=function(t){return n(o(t))}},function(t,r,e){var n=e(150),o=e(12),i=e(149),a=e(13).f;t.exports=function(t){var r=n.Symbol||(n.Symbol={});o(r,t)||a(r,t,{value:i.f(t)})}},function(t,r,e){var n=e(3),o=n({}.toString),i=n("".slice);t.exports=function(t){return i(o(t),8,-1)}},function(t,r,e){var n=e(3);t.exports=n({}.isPrototypeOf)},function(t,r,e){var n=e(20),o=Math.min;t.exports=function(t){return t>0?o(n(t),9007199254740991):0}},function(t,r,e){var n=e(68),o=Function.prototype,i=o.apply,a=o.call;t.exports="object"==typeof Reflect&&Reflect.apply||(n?a.bind(i):function(){return a.apply(i,arguments)})},function(t,r,e){var n,o=e(4),i=e(74),a=e(116),u=e(71),c=e(148),s=e(87),f=e(89),l=f("IE_PROTO"),h=function(){},p=function(t){return"