diff --git a/package.json b/package.json index d84d7a4..0b9cf13 100644 --- a/package.json +++ b/package.json @@ -32,24 +32,25 @@ "ora": "^8.1.0", "please-upgrade-node": "^3.2.0", "pretty-bytes": "^6.1.1", - "puppeteer": "^23.2.0", + "puppeteer": "^23.5.2", "quick-lru": "^7.0.0", "source-map": "^0.7.4", "stacktrace-parser": "^0.1.10", "table": "^6.8.2" }, "devDependencies": { - "@rollup/plugin-replace": "^5.0.7", + "@rollup/plugin-inject": "^5.0.5", + "@rollup/plugin-replace": "^6.0.1", "@rollup/plugin-strip": "^3.0.4", - "@rollup/plugin-typescript": "^11.1.6", + "@rollup/plugin-typescript": "^12.1.0", "@rollup/plugin-virtual": "^3.0.2", "chai": "^5.1.1", "conventional-changelog-cli": "^5.0.0", "navigo": "^8.11.1", "npm-run-all": "^4.1.5", - "rollup": "^4.21.1", + "rollup": "^4.24.0", "serve": "^14.2.3", - "standard": "^17.1.0", + "standard": "^17.1.2", "wait-for-localhost": "^4.1.0" }, "keywords": [ @@ -75,5 +76,5 @@ "thirdparty" ] }, - "packageManager": "pnpm@9.9.0+sha512.60c18acd138bff695d339be6ad13f7e936eea6745660d4cc4a776d5247c540d0edee1a563695c183a66eb917ef88f2b4feb1fc25f32a7adcadc7aaf3438e99c1" + "packageManager": "pnpm@9.12.1+sha512.e5a7e52a4183a02d5931057f7a0dbff9d5e9ce3161e33fa68ae392125b79282a8a8a470a51dfc8a0ed86221442eb2fb57019b0990ed24fab519bf0e1bc5ccfc4" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5ea372e..4246566 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -33,8 +33,8 @@ importers: specifier: ^6.1.1 version: 6.1.1 puppeteer: - specifier: ^23.2.0 - version: 23.2.0(typescript@5.4.5) + specifier: ^23.5.2 + version: 23.5.2(typescript@5.4.5) quick-lru: specifier: ^7.0.0 version: 7.0.0 @@ -48,18 +48,21 @@ importers: specifier: ^6.8.2 version: 6.8.2 devDependencies: + '@rollup/plugin-inject': + specifier: ^5.0.5 + version: 5.0.5(rollup@4.24.0) '@rollup/plugin-replace': - specifier: ^5.0.7 - version: 5.0.7(rollup@4.21.1) + specifier: ^6.0.1 + version: 6.0.1(rollup@4.24.0) '@rollup/plugin-strip': specifier: ^3.0.4 - version: 3.0.4(rollup@4.21.1) + version: 3.0.4(rollup@4.24.0) '@rollup/plugin-typescript': - specifier: ^11.1.6 - version: 11.1.6(rollup@4.21.1)(tslib@2.6.2)(typescript@5.4.5) + specifier: ^12.1.0 + version: 12.1.0(rollup@4.24.0)(tslib@2.6.2)(typescript@5.4.5) '@rollup/plugin-virtual': specifier: ^3.0.2 - version: 3.0.2(rollup@4.21.1) + version: 3.0.2(rollup@4.24.0) chai: specifier: ^5.1.1 version: 5.1.1 @@ -73,14 +76,14 @@ importers: specifier: ^4.1.5 version: 4.1.5 rollup: - specifier: ^4.21.1 - version: 4.21.1 + specifier: ^4.24.0 + version: 4.24.0 serve: specifier: ^14.2.3 version: 14.2.3 standard: - specifier: ^17.1.0 - version: 17.1.0 + specifier: ^17.1.2 + version: 17.1.2 wait-for-localhost: specifier: ^4.1.0 version: 4.1.0 @@ -132,6 +135,7 @@ packages: '@humanwhocodes/config-array@0.11.14': resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} engines: {node: '>=10.10.0'} + deprecated: Use @eslint/config-array instead '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} @@ -139,6 +143,7 @@ packages: '@humanwhocodes/object-schema@2.0.3': resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} + deprecated: Use @eslint/object-schema instead '@hutson/parse-repository-url@5.0.0': resolution: {integrity: sha512-e5+YUKENATs1JgYHMzTr2MW/NDcXGfYFAuOQU8gJgF/kEh4EqKgfGrfLI67bMD4tbhZVlkigz/9YYwWcbOFthg==} @@ -162,13 +167,22 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@puppeteer/browsers@2.3.1': - resolution: {integrity: sha512-uK7o3hHkK+naEobMSJ+2ySYyXtQkBxIH8Gn4MK9ciePjNV+Pf+PgY/W7iPzn2MTjl3stcYB5AlcTmPYw7AXDwA==} + '@puppeteer/browsers@2.4.0': + resolution: {integrity: sha512-x8J1csfIygOwf6D6qUAZ0ASk3z63zPb7wkNeHRerCMh82qWKUrOgkuP005AJC8lDL6/evtXETGEJVcwykKT4/g==} engines: {node: '>=18'} hasBin: true - '@rollup/plugin-replace@5.0.7': - resolution: {integrity: sha512-PqxSfuorkHz/SPpyngLyg5GCEkOcee9M1bkxiVDr41Pd61mqP1PLOoDPbpl44SB2mQGKwV/In74gqQmGITOhEQ==} + '@rollup/plugin-inject@5.0.5': + resolution: {integrity: sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-replace@6.0.1': + resolution: {integrity: sha512-2sPh9b73dj5IxuMmDAsQWVFT7mR+yoHweBaXG2W/R8vQ+IWZlnaI7BR7J6EguVQUp1hd8Z7XuozpDjEKQAAC2Q==} engines: {node: '>=14.0.0'} peerDependencies: rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 @@ -185,8 +199,8 @@ packages: rollup: optional: true - '@rollup/plugin-typescript@11.1.6': - resolution: {integrity: sha512-R92yOmIACgYdJ7dJ97p4K69I8gg6IEHt8M7dUBxN3W6nrO8uUxX5ixl0yU/N3aZTi8WhPuICvOHXQvF6FaykAA==} + '@rollup/plugin-typescript@12.1.0': + resolution: {integrity: sha512-Kzs8KGJofe7cfTRODsnG1jNGxSvU8gVoNNd7Z/QaY25AYwe2LSSUpx/kPxqF38NYkpR8de3m51r9uwJpDlz6dg==} engines: {node: '>=14.0.0'} peerDependencies: rollup: ^2.14.0||^3.0.0||^4.0.0 @@ -216,83 +230,83 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.21.1': - resolution: {integrity: sha512-2thheikVEuU7ZxFXubPDOtspKn1x0yqaYQwvALVtEcvFhMifPADBrgRPyHV0TF3b+9BgvgjgagVyvA/UqPZHmg==} + '@rollup/rollup-android-arm-eabi@4.24.0': + resolution: {integrity: sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.21.1': - resolution: {integrity: sha512-t1lLYn4V9WgnIFHXy1d2Di/7gyzBWS8G5pQSXdZqfrdCGTwi1VasRMSS81DTYb+avDs/Zz4A6dzERki5oRYz1g==} + '@rollup/rollup-android-arm64@4.24.0': + resolution: {integrity: sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.21.1': - resolution: {integrity: sha512-AH/wNWSEEHvs6t4iJ3RANxW5ZCK3fUnmf0gyMxWCesY1AlUj8jY7GC+rQE4wd3gwmZ9XDOpL0kcFnCjtN7FXlA==} + '@rollup/rollup-darwin-arm64@4.24.0': + resolution: {integrity: sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.21.1': - resolution: {integrity: sha512-dO0BIz/+5ZdkLZrVgQrDdW7m2RkrLwYTh2YMFG9IpBtlC1x1NPNSXkfczhZieOlOLEqgXOFH3wYHB7PmBtf+Bg==} + '@rollup/rollup-darwin-x64@4.24.0': + resolution: {integrity: sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==} cpu: [x64] os: [darwin] - '@rollup/rollup-linux-arm-gnueabihf@4.21.1': - resolution: {integrity: sha512-sWWgdQ1fq+XKrlda8PsMCfut8caFwZBmhYeoehJ05FdI0YZXk6ZyUjWLrIgbR/VgiGycrFKMMgp7eJ69HOF2pQ==} + '@rollup/rollup-linux-arm-gnueabihf@4.24.0': + resolution: {integrity: sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.21.1': - resolution: {integrity: sha512-9OIiSuj5EsYQlmwhmFRA0LRO0dRRjdCVZA3hnmZe1rEwRk11Jy3ECGGq3a7RrVEZ0/pCsYWx8jG3IvcrJ6RCew==} + '@rollup/rollup-linux-arm-musleabihf@4.24.0': + resolution: {integrity: sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.21.1': - resolution: {integrity: sha512-0kuAkRK4MeIUbzQYu63NrJmfoUVicajoRAL1bpwdYIYRcs57iyIV9NLcuyDyDXE2GiZCL4uhKSYAnyWpjZkWow==} + '@rollup/rollup-linux-arm64-gnu@4.24.0': + resolution: {integrity: sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.21.1': - resolution: {integrity: sha512-/6dYC9fZtfEY0vozpc5bx1RP4VrtEOhNQGb0HwvYNwXD1BBbwQ5cKIbUVVU7G2d5WRE90NfB922elN8ASXAJEA==} + '@rollup/rollup-linux-arm64-musl@4.24.0': + resolution: {integrity: sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.21.1': - resolution: {integrity: sha512-ltUWy+sHeAh3YZ91NUsV4Xg3uBXAlscQe8ZOXRCVAKLsivGuJsrkawYPUEyCV3DYa9urgJugMLn8Z3Z/6CeyRQ==} + '@rollup/rollup-linux-powerpc64le-gnu@4.24.0': + resolution: {integrity: sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.21.1': - resolution: {integrity: sha512-BggMndzI7Tlv4/abrgLwa/dxNEMn2gC61DCLrTzw8LkpSKel4o+O+gtjbnkevZ18SKkeN3ihRGPuBxjaetWzWg==} + '@rollup/rollup-linux-riscv64-gnu@4.24.0': + resolution: {integrity: sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.21.1': - resolution: {integrity: sha512-z/9rtlGd/OMv+gb1mNSjElasMf9yXusAxnRDrBaYB+eS1shFm6/4/xDH1SAISO5729fFKUkJ88TkGPRUh8WSAA==} + '@rollup/rollup-linux-s390x-gnu@4.24.0': + resolution: {integrity: sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.21.1': - resolution: {integrity: sha512-kXQVcWqDcDKw0S2E0TmhlTLlUgAmMVqPrJZR+KpH/1ZaZhLSl23GZpQVmawBQGVhyP5WXIsIQ/zqbDBBYmxm5w==} + '@rollup/rollup-linux-x64-gnu@4.24.0': + resolution: {integrity: sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.21.1': - resolution: {integrity: sha512-CbFv/WMQsSdl+bpX6rVbzR4kAjSSBuDgCqb1l4J68UYsQNalz5wOqLGYj4ZI0thGpyX5kc+LLZ9CL+kpqDovZA==} + '@rollup/rollup-linux-x64-musl@4.24.0': + resolution: {integrity: sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.21.1': - resolution: {integrity: sha512-3Q3brDgA86gHXWHklrwdREKIrIbxC0ZgU8lwpj0eEKGBQH+31uPqr0P2v11pn0tSIxHvcdOWxa4j+YvLNx1i6g==} + '@rollup/rollup-win32-arm64-msvc@4.24.0': + resolution: {integrity: sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.21.1': - resolution: {integrity: sha512-tNg+jJcKR3Uwe4L0/wY3Ro0H+u3nrb04+tcq1GSYzBEmKLeOQF2emk1whxlzNqb6MMrQ2JOcQEpuuiPLyRcSIw==} + '@rollup/rollup-win32-ia32-msvc@4.24.0': + resolution: {integrity: sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.21.1': - resolution: {integrity: sha512-xGiIH95H1zU7naUyTKEyOA/I0aexNMUdO9qRv0bLKN3qu25bBdrxZHqA3PTJ24YNN/GdMzG4xkDcd/GvjuhfLg==} + '@rollup/rollup-win32-x64-msvc@4.24.0': + resolution: {integrity: sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==} cpu: [x64] os: [win32] @@ -302,6 +316,9 @@ packages: '@types/estree@1.0.5': resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + '@types/estree@1.0.6': + resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + '@types/json5@0.0.29': resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} @@ -412,11 +429,9 @@ packages: resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} engines: {node: '>= 0.4'} - array.prototype.toreversed@1.1.2: - resolution: {integrity: sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==} - - array.prototype.tosorted@1.1.3: - resolution: {integrity: sha512-/DdH4TiTmOKzyQbp/eadcCVexiCb36xJg7HshYOYJnNZFDj33GEv0P7GxsynpShhq4OLYJzbGcBDkLsDt7MnNg==} + array.prototype.tosorted@1.1.4: + resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} + engines: {node: '>= 0.4'} arraybuffer.prototype.slice@1.0.3: resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} @@ -529,8 +544,8 @@ packages: resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} engines: {node: '>= 16'} - chromium-bidi@0.6.4: - resolution: {integrity: sha512-8zoq6ogmhQQkAKZVKO2ObFTl4uOkqoX1PlKQX3hZQ5E9cbUotcAb7h4pTNVAGGv8Z36PF3CtdOriEp/Rz82JqQ==} + chromium-bidi@0.8.0: + resolution: {integrity: sha512-uJydbGdTw0DEUjhoogGveneJVWX/9YuqkWePzMmkBYwtdAqo5d3J/ovNKFr+/2hWXYmYCr6it8mSSTIj6SS6Ug==} peerDependencies: devtools-protocol: '*' @@ -705,8 +720,8 @@ packages: supports-color: optional: true - debug@4.3.4: - resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + debug@4.3.6: + resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -714,8 +729,8 @@ packages: supports-color: optional: true - debug@4.3.6: - resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==} + debug@4.3.7: + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -746,8 +761,8 @@ packages: resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} engines: {node: '>= 14'} - devtools-protocol@0.0.1330662: - resolution: {integrity: sha512-pzh6YQ8zZfz3iKlCvgzVCu22NdpZ8hNmwU6WnQjNVquh0A9iVosPtNLWDwaWVGyrntQlltPFztTMK5Cg6lfCuw==} + devtools-protocol@0.0.1342118: + resolution: {integrity: sha512-75fMas7PkYNDTmDyb6PRJCH7ILmHLp+BhrZGeMsa4bCh40DTxgCz2NRy5UDzII4C5KuD0oBMZ9vXKhEl6UD/3w==} doctrine@2.1.0: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} @@ -898,11 +913,11 @@ packages: peerDependencies: eslint: ^7.0.0 || ^8.0.0 - eslint-plugin-react@7.34.1: - resolution: {integrity: sha512-N97CxlouPT1AHt8Jn0mhhN2RrADlUAsk1/atcT2KyA/l9Q/E6ll7OIGwNumFmWfZ9skV3XXccYS19h80rHtgkw==} + eslint-plugin-react@7.37.1: + resolution: {integrity: sha512-xwTnwDqzbDRA8uJ7BMxPs/EXRB3i8ZfnOIp8BsxEQkT0nHPp+WWceqGgo6rKb9ctNi8GJLDT4Go5HAWELa/WMg==} engines: {node: '>=4'} peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 eslint-scope@7.2.2: resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} @@ -933,6 +948,7 @@ packages: eslint@8.57.0: resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true espree@9.6.1: @@ -1096,6 +1112,7 @@ packages: glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported globals@13.24.0: resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} @@ -1189,6 +1206,7 @@ packages: inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -1558,10 +1576,6 @@ packages: resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} engines: {node: '>= 0.4'} - object.hasown@1.1.4: - resolution: {integrity: sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==} - engines: {node: '>= 0.4'} - object.values@1.2.0: resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} engines: {node: '>= 0.4'} @@ -1742,12 +1756,12 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - puppeteer-core@23.2.0: - resolution: {integrity: sha512-OFyPp2oolGSesx6ZrpmorE5tCaCKY1Z5e/h8f6sB0NpiezenB72jdWBdOrvBO/bUXyq14XyGJsDRUsv0ZOPdZA==} + puppeteer-core@23.5.2: + resolution: {integrity: sha512-UwPAX29EID8lJmxeL7JT3Gz35D1BHn5o9ZXpBLoR24W7gtUg1dLx7OUPsUTR5Tlxf+1Yeqw9W3qP4uqWThqXgg==} engines: {node: '>=18'} - puppeteer@23.2.0: - resolution: {integrity: sha512-MP7kLOdCfx1BJaGN5sgRo5fTYwAyGrlwWtrNphjKcwv/HO91+m90gbbwpRHbGl0rCvrmylq6vljn+zrjukniVg==} + puppeteer@23.5.2: + resolution: {integrity: sha512-7OOGEIoCjGP9lQ6QHvRSBTO3VRDPvu+YGl6rLCKOfYNMp1Lqc1U+s3lS1JdyR+jee1pZ55sxf+TEKsmqOopO1A==} engines: {node: '>=18'} hasBin: true @@ -1833,10 +1847,11 @@ packages: rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true - rollup@4.21.1: - resolution: {integrity: sha512-ZnYyKvscThhgd3M5+Qt3pmhO4jIRR5RGzaSovB6Q7rGNrK5cUncrtLmcTTJVSdcKXyZjW8X8MB0JMSuH9bcAJg==} + rollup@4.24.0: + resolution: {integrity: sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -1971,8 +1986,8 @@ packages: resolution: {integrity: sha512-VHysfoyxFu/ukT+9v49d4BRXIokFRZuH3z1VRxzFArZdjSCFpro6rEIU3ji7e4AoAtuSfKBkiOmsrDqKW5ZSRw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - standard@17.1.0: - resolution: {integrity: sha512-jaDqlNSzLtWYW4lvQmU0EnxWMUGQiwHasZl5ZEIwx3S/ijZDjZOzs1y1QqKwKs5vqnFpGtizo4NOYX2s0Voq/g==} + standard@17.1.2: + resolution: {integrity: sha512-WLm12WoXveKkvnPnPnaFUUHuOB2cUdAsJ4AiGHL2G0UNMrcRAWY2WriQaV8IQ3oRmYr0AWUbLNr94ekYFAHOrA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} hasBin: true @@ -2003,6 +2018,9 @@ packages: resolution: {integrity: sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==} engines: {node: '>= 0.4'} + string.prototype.repeat@1.0.0: + resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} + string.prototype.trim@1.2.9: resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} engines: {node: '>= 0.4'} @@ -2292,7 +2310,7 @@ snapshots: '@eslint/eslintrc@2.1.4': dependencies: ajv: 6.12.6 - debug: 4.3.4 + debug: 4.3.6 espree: 9.6.1 globals: 13.24.0 ignore: 5.3.1 @@ -2308,7 +2326,7 @@ snapshots: '@humanwhocodes/config-array@0.11.14': dependencies: '@humanwhocodes/object-schema': 2.0.3 - debug: 4.3.4 + debug: 4.3.6 minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -2335,7 +2353,7 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.17.1 - '@puppeteer/browsers@2.3.1': + '@puppeteer/browsers@2.4.0': dependencies: debug: 4.3.6 extract-zip: 2.0.1 @@ -2348,94 +2366,104 @@ snapshots: transitivePeerDependencies: - supports-color - '@rollup/plugin-replace@5.0.7(rollup@4.21.1)': + '@rollup/plugin-inject@5.0.5(rollup@4.24.0)': dependencies: - '@rollup/pluginutils': 5.1.0(rollup@4.21.1) + '@rollup/pluginutils': 5.1.0(rollup@4.24.0) + estree-walker: 2.0.2 magic-string: 0.30.10 optionalDependencies: - rollup: 4.21.1 + rollup: 4.24.0 - '@rollup/plugin-strip@3.0.4(rollup@4.21.1)': + '@rollup/plugin-replace@6.0.1(rollup@4.24.0)': dependencies: - '@rollup/pluginutils': 5.1.0(rollup@4.21.1) + '@rollup/pluginutils': 5.1.0(rollup@4.24.0) + magic-string: 0.30.10 + optionalDependencies: + rollup: 4.24.0 + + '@rollup/plugin-strip@3.0.4(rollup@4.24.0)': + dependencies: + '@rollup/pluginutils': 5.1.0(rollup@4.24.0) estree-walker: 2.0.2 magic-string: 0.30.10 optionalDependencies: - rollup: 4.21.1 + rollup: 4.24.0 - '@rollup/plugin-typescript@11.1.6(rollup@4.21.1)(tslib@2.6.2)(typescript@5.4.5)': + '@rollup/plugin-typescript@12.1.0(rollup@4.24.0)(tslib@2.6.2)(typescript@5.4.5)': dependencies: - '@rollup/pluginutils': 5.1.0(rollup@4.21.1) + '@rollup/pluginutils': 5.1.0(rollup@4.24.0) resolve: 1.22.8 typescript: 5.4.5 optionalDependencies: - rollup: 4.21.1 + rollup: 4.24.0 tslib: 2.6.2 - '@rollup/plugin-virtual@3.0.2(rollup@4.21.1)': + '@rollup/plugin-virtual@3.0.2(rollup@4.24.0)': optionalDependencies: - rollup: 4.21.1 + rollup: 4.24.0 - '@rollup/pluginutils@5.1.0(rollup@4.21.1)': + '@rollup/pluginutils@5.1.0(rollup@4.24.0)': dependencies: '@types/estree': 1.0.5 estree-walker: 2.0.2 picomatch: 2.3.1 optionalDependencies: - rollup: 4.21.1 + rollup: 4.24.0 - '@rollup/rollup-android-arm-eabi@4.21.1': + '@rollup/rollup-android-arm-eabi@4.24.0': optional: true - '@rollup/rollup-android-arm64@4.21.1': + '@rollup/rollup-android-arm64@4.24.0': optional: true - '@rollup/rollup-darwin-arm64@4.21.1': + '@rollup/rollup-darwin-arm64@4.24.0': optional: true - '@rollup/rollup-darwin-x64@4.21.1': + '@rollup/rollup-darwin-x64@4.24.0': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.21.1': + '@rollup/rollup-linux-arm-gnueabihf@4.24.0': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.21.1': + '@rollup/rollup-linux-arm-musleabihf@4.24.0': optional: true - '@rollup/rollup-linux-arm64-gnu@4.21.1': + '@rollup/rollup-linux-arm64-gnu@4.24.0': optional: true - '@rollup/rollup-linux-arm64-musl@4.21.1': + '@rollup/rollup-linux-arm64-musl@4.24.0': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.21.1': + '@rollup/rollup-linux-powerpc64le-gnu@4.24.0': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.21.1': + '@rollup/rollup-linux-riscv64-gnu@4.24.0': optional: true - '@rollup/rollup-linux-s390x-gnu@4.21.1': + '@rollup/rollup-linux-s390x-gnu@4.24.0': optional: true - '@rollup/rollup-linux-x64-gnu@4.21.1': + '@rollup/rollup-linux-x64-gnu@4.24.0': optional: true - '@rollup/rollup-linux-x64-musl@4.21.1': + '@rollup/rollup-linux-x64-musl@4.24.0': optional: true - '@rollup/rollup-win32-arm64-msvc@4.21.1': + '@rollup/rollup-win32-arm64-msvc@4.24.0': optional: true - '@rollup/rollup-win32-ia32-msvc@4.21.1': + '@rollup/rollup-win32-ia32-msvc@4.24.0': optional: true - '@rollup/rollup-win32-x64-msvc@4.21.1': + '@rollup/rollup-win32-x64-msvc@4.24.0': optional: true '@tootallnate/quickjs-emscripten@0.23.0': {} '@types/estree@1.0.5': {} + '@types/estree@1.0.6': {} + '@types/json5@0.0.29': {} '@types/node@20.12.12': @@ -2568,14 +2596,7 @@ snapshots: es-abstract: 1.23.3 es-shim-unscopables: 1.0.2 - array.prototype.toreversed@1.1.2: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-shim-unscopables: 1.0.2 - - array.prototype.tosorted@1.1.3: + array.prototype.tosorted@1.1.4: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 @@ -2666,7 +2687,7 @@ snapshots: builtins@5.1.0: dependencies: - semver: 7.6.2 + semver: 7.6.3 bytes@3.0.0: {} @@ -2711,9 +2732,9 @@ snapshots: check-error@2.1.1: {} - chromium-bidi@0.6.4(devtools-protocol@0.0.1330662): + chromium-bidi@0.8.0(devtools-protocol@0.0.1342118): dependencies: - devtools-protocol: 0.0.1330662 + devtools-protocol: 0.0.1342118 mitt: 3.0.1 urlpattern-polyfill: 10.0.0 zod: 3.23.8 @@ -2908,13 +2929,13 @@ snapshots: dependencies: ms: 2.1.3 - debug@4.3.4: + debug@4.3.6: dependencies: ms: 2.1.2 - debug@4.3.6: + debug@4.3.7: dependencies: - ms: 2.1.2 + ms: 2.1.3 deep-eql@5.0.1: {} @@ -2940,7 +2961,7 @@ snapshots: escodegen: 2.1.0 esprima: 4.0.1 - devtools-protocol@0.0.1330662: {} + devtools-protocol@0.0.1342118: {} doctrine@2.1.0: dependencies: @@ -3078,10 +3099,10 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-config-standard-jsx@11.0.0(eslint-plugin-react@7.34.1(eslint@8.57.0))(eslint@8.57.0): + eslint-config-standard-jsx@11.0.0(eslint-plugin-react@7.37.1(eslint@8.57.0))(eslint@8.57.0): dependencies: eslint: 8.57.0 - eslint-plugin-react: 7.34.1(eslint@8.57.0) + eslint-plugin-react: 7.37.1(eslint@8.57.0) eslint-config-standard@17.1.0(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint-plugin-n@15.7.0(eslint@8.57.0))(eslint-plugin-promise@6.1.1(eslint@8.57.0))(eslint@8.57.0): dependencies: @@ -3148,33 +3169,33 @@ snapshots: is-core-module: 2.13.1 minimatch: 3.1.2 resolve: 1.22.8 - semver: 7.6.2 + semver: 7.6.3 eslint-plugin-promise@6.1.1(eslint@8.57.0): dependencies: eslint: 8.57.0 - eslint-plugin-react@7.34.1(eslint@8.57.0): + eslint-plugin-react@7.37.1(eslint@8.57.0): dependencies: array-includes: 3.1.8 array.prototype.findlast: 1.2.5 array.prototype.flatmap: 1.3.2 - array.prototype.toreversed: 1.1.2 - array.prototype.tosorted: 1.1.3 + array.prototype.tosorted: 1.1.4 doctrine: 2.1.0 es-iterator-helpers: 1.0.19 eslint: 8.57.0 estraverse: 5.3.0 + hasown: 2.0.2 jsx-ast-utils: 3.3.5 minimatch: 3.1.2 object.entries: 1.1.8 object.fromentries: 2.0.8 - object.hasown: 1.1.4 object.values: 1.2.0 prop-types: 15.8.1 resolve: 2.0.0-next.5 semver: 6.3.1 string.prototype.matchall: 4.0.11 + string.prototype.repeat: 1.0.0 eslint-scope@7.2.2: dependencies: @@ -3209,7 +3230,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.4 + debug: 4.3.6 doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.2.2 @@ -3854,12 +3875,6 @@ snapshots: define-properties: 1.2.1 es-abstract: 1.23.3 - object.hasown@1.1.4: - dependencies: - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-object-atoms: 1.0.0 - object.values@1.2.0: dependencies: call-bind: 1.0.7 @@ -4046,12 +4061,12 @@ snapshots: punycode@2.3.1: {} - puppeteer-core@23.2.0: + puppeteer-core@23.5.2: dependencies: - '@puppeteer/browsers': 2.3.1 - chromium-bidi: 0.6.4(devtools-protocol@0.0.1330662) - debug: 4.3.6 - devtools-protocol: 0.0.1330662 + '@puppeteer/browsers': 2.4.0 + chromium-bidi: 0.8.0(devtools-protocol@0.0.1342118) + debug: 4.3.7 + devtools-protocol: 0.0.1342118 typed-query-selector: 2.12.0 ws: 8.18.0 transitivePeerDependencies: @@ -4059,13 +4074,13 @@ snapshots: - supports-color - utf-8-validate - puppeteer@23.2.0(typescript@5.4.5): + puppeteer@23.5.2(typescript@5.4.5): dependencies: - '@puppeteer/browsers': 2.3.1 - chromium-bidi: 0.6.4(devtools-protocol@0.0.1330662) + '@puppeteer/browsers': 2.4.0 + chromium-bidi: 0.8.0(devtools-protocol@0.0.1342118) cosmiconfig: 9.0.0(typescript@5.4.5) - devtools-protocol: 0.0.1330662 - puppeteer-core: 23.2.0 + devtools-protocol: 0.0.1342118 + puppeteer-core: 23.5.2 typed-query-selector: 2.12.0 transitivePeerDependencies: - bufferutil @@ -4167,26 +4182,26 @@ snapshots: dependencies: glob: 7.2.3 - rollup@4.21.1: + rollup@4.24.0: dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.21.1 - '@rollup/rollup-android-arm64': 4.21.1 - '@rollup/rollup-darwin-arm64': 4.21.1 - '@rollup/rollup-darwin-x64': 4.21.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.21.1 - '@rollup/rollup-linux-arm-musleabihf': 4.21.1 - '@rollup/rollup-linux-arm64-gnu': 4.21.1 - '@rollup/rollup-linux-arm64-musl': 4.21.1 - '@rollup/rollup-linux-powerpc64le-gnu': 4.21.1 - '@rollup/rollup-linux-riscv64-gnu': 4.21.1 - '@rollup/rollup-linux-s390x-gnu': 4.21.1 - '@rollup/rollup-linux-x64-gnu': 4.21.1 - '@rollup/rollup-linux-x64-musl': 4.21.1 - '@rollup/rollup-win32-arm64-msvc': 4.21.1 - '@rollup/rollup-win32-ia32-msvc': 4.21.1 - '@rollup/rollup-win32-x64-msvc': 4.21.1 + '@rollup/rollup-android-arm-eabi': 4.24.0 + '@rollup/rollup-android-arm64': 4.24.0 + '@rollup/rollup-darwin-arm64': 4.24.0 + '@rollup/rollup-darwin-x64': 4.24.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.24.0 + '@rollup/rollup-linux-arm-musleabihf': 4.24.0 + '@rollup/rollup-linux-arm64-gnu': 4.24.0 + '@rollup/rollup-linux-arm64-musl': 4.24.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.24.0 + '@rollup/rollup-linux-riscv64-gnu': 4.24.0 + '@rollup/rollup-linux-s390x-gnu': 4.24.0 + '@rollup/rollup-linux-x64-gnu': 4.24.0 + '@rollup/rollup-linux-x64-musl': 4.24.0 + '@rollup/rollup-win32-arm64-msvc': 4.24.0 + '@rollup/rollup-win32-ia32-msvc': 4.24.0 + '@rollup/rollup-win32-x64-msvc': 4.24.0 fsevents: 2.3.3 run-parallel@1.2.0: @@ -4340,15 +4355,15 @@ snapshots: pkg-conf: 3.1.0 xdg-basedir: 4.0.0 - standard@17.1.0: + standard@17.1.2: dependencies: eslint: 8.57.0 eslint-config-standard: 17.1.0(eslint-plugin-import@2.29.1(eslint@8.57.0))(eslint-plugin-n@15.7.0(eslint@8.57.0))(eslint-plugin-promise@6.1.1(eslint@8.57.0))(eslint@8.57.0) - eslint-config-standard-jsx: 11.0.0(eslint-plugin-react@7.34.1(eslint@8.57.0))(eslint@8.57.0) + eslint-config-standard-jsx: 11.0.0(eslint-plugin-react@7.37.1(eslint@8.57.0))(eslint@8.57.0) eslint-plugin-import: 2.29.1(eslint@8.57.0) eslint-plugin-n: 15.7.0(eslint@8.57.0) eslint-plugin-promise: 6.1.1(eslint@8.57.0) - eslint-plugin-react: 7.34.1(eslint@8.57.0) + eslint-plugin-react: 7.37.1(eslint@8.57.0) standard-engine: 15.1.0 version-guard: 1.1.2 transitivePeerDependencies: @@ -4406,6 +4421,11 @@ snapshots: es-abstract: 1.23.3 es-object-atoms: 1.0.0 + string.prototype.repeat@1.0.0: + dependencies: + define-properties: 1.2.1 + es-abstract: 1.23.3 + string.prototype.trim@1.2.9: dependencies: call-bind: 1.0.7 diff --git a/scripts/devtools.rollup.config.js b/scripts/devtools.rollup.config.js index 0694556..8e2e4e9 100644 --- a/scripts/devtools.rollup.config.js +++ b/scripts/devtools.rollup.config.js @@ -2,6 +2,7 @@ import typescript from '@rollup/plugin-typescript' import virtual from '@rollup/plugin-virtual' import replace from '@rollup/plugin-replace' import strip from '@rollup/plugin-strip' +import inject from '@rollup/plugin-inject' // Stub out some modules to reduce bundle size const makeStub = (...names) => names @@ -35,21 +36,65 @@ export default { __entry__: ` export { HeapSnapshotLoader } from './front_end/entrypoints/heap_snapshot_worker/heap_snapshot_worker.ts' export { HeapSnapshotModel } from './front_end/models/heap_snapshot_model/heap_snapshot_model.ts' + `, + // Promise.withResolvers minimal shim + // TODO: remove when we set our minimum Node to v22+ + __promise_with_resolvers__: ` + export const withResolvers = () => { + let resolve + let reject + const promise = new Promise((_resolve, _reject) => { + resolve = _resolve + reject = _reject + }) + return { resolve, reject, promise } + } + `, + // Array.prototype.toSorted minimal shim + // TODO: remove when we set our minimum Node to v22+ + __to_sorted__: ` + export const toSorted = (arr) => { + const res = [...arr] + res.sort() + return res + } ` }), typescript({ compilerOptions: { lib: ['esnext', 'dom', 'dom.iterable', 'webworker', 'webworker.iterable'], - target: 'esnext' + target: 'esnext', + outDir: '../fuite/src/thirdparty/devtools-frontend' }, noEmitOnError: false }), replace({ values: { - 'location.search': '' + 'location.search': '', + // TODO: remove when we set our minimum Node to v22+ + 'Promise.withResolvers': 'PromiseWithResolversPolyfill' }, preventAssignment: true }), + + // TODO: remove when we set our minimum Node to v22+ + replace({ + values: { + 'definition.properties.toSorted()': 'toSortedPolyfill(definition.properties)' + }, + delimiters: ['', ''] + }), + // TODO: remove when we set our minimum Node to v22+ + inject({ + PromiseWithResolversPolyfill: [ + '__promise_with_resolvers__', + 'withResolvers' + ], + toSortedPolyfill: [ + '__to_sorted__', + 'toSorted' + ] + }), strip({ include: ['**/*.js', '**/*.ts'], // remove console.* calls diff --git a/src/metrics/heapsnapshots/analyzeHeapsnapshots.js b/src/metrics/heapsnapshots/analyzeHeapsnapshots.js index 4d0645f..2fba28f 100644 --- a/src/metrics/heapsnapshots/analyzeHeapsnapshots.js +++ b/src/metrics/heapsnapshots/analyzeHeapsnapshots.js @@ -40,9 +40,13 @@ export async function analyzeHeapSnapshots (startSnapshotFilename, endSnapshotFi let startSnapshot = await createHeapSnapshotModel(startSnapshotFilename) const startSnapshotUid = startSnapshot.uid const startStatistics = { ...startSnapshot.getStatistics() } - const aggregatesForDiff = await startSnapshot.aggregatesForDiff() + // For reference see: + // https://github.com/ChromeDevTools/devtools-frontend/blob/898fd09/front_end/panels/profiler/HeapSnapshotDataGrids.ts#L999-L1016 + let interfaceDefinitions = await startSnapshot.interfaceDefinitions() + const aggregatesForDiff = await startSnapshot.aggregatesForDiff(interfaceDefinitions) const startAggregates = startSnapshot.aggregatesWithFilter(new HeapSnapshotModel.NodeFilter()) startSnapshot = undefined // free memory + interfaceDefinitions = undefined // free memory const endSnapshot = await createHeapSnapshotModel(endSnapshotFilename) const endStatistics = { ...endSnapshot.getStatistics() } diff --git a/src/thirdparty/devtools-frontend/index.js b/src/thirdparty/devtools-frontend/index.js index e338829..6b75e69 100644 --- a/src/thirdparty/devtools-frontend/index.js +++ b/src/thirdparty/devtools-frontend/index.js @@ -602,6 +602,12 @@ class FunctionAllocationInfo { } } +const toSorted = (arr) => { + const res = [...arr]; + res.sort(); + return res + }; + // Copyright 2021 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -948,24 +954,6 @@ class Multimap { } } -// Copyright 2023 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -/** - * Returns a new pending promise together with it's resolve and reject functions. - * - * Polyfill for https://github.com/tc39/proposal-promise-with-resolvers. - */ -function promiseWithResolvers() { - let resolve; - let reject; - const promise = new Promise((res, rej) => { - resolve = res; - reject = rej; - }); - return { promise, resolve, reject }; -} - // Copyright (c) 2020 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -1139,6 +1127,12 @@ class BitVectorImpl extends Uint8Array { // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. new URLSearchParams(); +var HostConfigFreestylerExecutionMode; +(function (HostConfigFreestylerExecutionMode) { + HostConfigFreestylerExecutionMode["ALL_SCRIPTS"] = "ALL_SCRIPTS"; + HostConfigFreestylerExecutionMode["SIDE_EFFECT_FREE_SCRIPTS_ONLY"] = "SIDE_EFFECT_FREE_SCRIPTS_ONLY"; + HostConfigFreestylerExecutionMode["NO_SCRIPTS"] = "NO_SCRIPTS"; +})(HostConfigFreestylerExecutionMode || (HostConfigFreestylerExecutionMode = {})); const LOCALES = ['en-GB']; const DEFAULT_LOCALE = 'en-GB'; @@ -1180,16 +1174,22 @@ function serializeUIString(string, values = {}) { const UIStrings$2 = { /** *@description μs is the short form of micro-seconds and the placeholder is a number + * The shortest form or abbreviation of micro-seconds should be used, as there is + * limited room in this UI. *@example {2} PH1 */ fmms: '{PH1} μs', /** *@description ms is the short form of milli-seconds and the placeholder is a decimal number + * The shortest form or abbreviation of milli-seconds should be used, as there is + * limited room in this UI. *@example {2.14} PH1 */ fms: '{PH1} ms', /** *@description s is short for seconds and the placeholder is a decimal number + * The shortest form or abbreviation of seconds should be used, as there is + * limited room in this UI. *@example {2.14} PH1 */ fs: '{PH1} s', @@ -1208,40 +1208,15 @@ const UIStrings$2 = { *@example {2.2} PH1 */ fdays: '{PH1} days', + /** + *@description describes a number of milliseconds (the unit should not abbreviated) + *@example {2.14} PH1 + */ + fmsExpanded: '{PH1} milliseconds', }; const str_$2 = registerUIStrings('core/i18n/time-utilities.ts', UIStrings$2); getLocalizedString.bind(undefined, str_$2); -/* - * Copyright (C) 2011 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -/* eslint-disable rulesdir/use_private_class_members */ class HeapSnapshotEdge { snapshot; edges; @@ -1513,7 +1488,7 @@ class HeapSnapshotNode { throw new Error('Not implemented'); } rawName() { - return this.snapshot.strings[this.nameInternal()]; + return this.snapshot.strings[this.rawNameIndex()]; } isRoot() { return this.nodeIndex === this.snapshot.rootNodeIndex; @@ -1564,7 +1539,7 @@ class HeapSnapshotNode { serialize() { return new Node(this.id(), this.name(), this.distance(), this.nodeIndex, this.retainedSize(), this.selfSize(), this.type()); } - nameInternal() { + rawNameIndex() { const snapshot = this.snapshot; return snapshot.nodes.getValue(this.nodeIndex + snapshot.nodeNameOffset); } @@ -1731,6 +1706,19 @@ class HeapSnapshotProblemReport { const BITMASK_FOR_DOM_LINK_STATE = 3; // The class index is stored in the upper 30 bits of the detachedness field. const SHIFT_FOR_CLASS_INDEX = 2; +// The maximum number of results produced by inferInterfaceDefinitions. +const MAX_INTERFACE_COUNT = 1000; +// After this many properties, inferInterfaceDefinitions can stop adding more +// properties to an interface definition if the name is getting too long. +const MIN_INTERFACE_PROPERTY_COUNT = 1; +// The maximum length of an interface name produced by inferInterfaceDefinitions. +// This limit can be exceeded if the first MIN_INTERFACE_PROPERTY_COUNT property +// names are long. +const MAX_INTERFACE_NAME_LENGTH = 120; +// Each interface definition produced by inferInterfaceDefinitions will match at +// least this many objects. There's no point in defining interfaces which match +// only a single object. +const MIN_OBJECT_COUNT_PER_INTERFACE = 2; class HeapSnapshot { nodes; containmentEdges; @@ -1803,6 +1791,8 @@ class HeapSnapshot { #edgeNamesThatAreNotWeakMaps; detachednessAndClassIndexArray; #essentialEdges; + #interfaceNames; + #interfaceDefinitions; constructor(profile, progress) { this.nodes = profile.nodes; this.containmentEdges = profile.edges; @@ -1824,6 +1814,7 @@ class HeapSnapshot { this.#ignoredNodesInRetainersView = new Set(); this.#ignoredEdgesInRetainersView = new Set(); this.#edgeNamesThatAreNotWeakMaps = createBitVector(this.strings.length); + this.#interfaceNames = new Map(); } initialize() { const meta = this.#metaNode; @@ -1874,8 +1865,6 @@ class HeapSnapshot { this.retainingEdges = new Uint32Array(this.#edgeCount); this.firstRetainerIndex = new Uint32Array(this.nodeCount + 1); this.nodeDistances = new Int32Array(this.nodeCount); - this.firstDominatedNodeIndex = new Uint32Array(this.nodeCount + 1); - this.dominatedNodes = new Uint32Array(this.nodeCount - 1); this.#progress.updateStatus('Building edge indexes…'); this.buildEdgeIndexes(); this.#progress.updateStatus('Building retainers…'); @@ -1886,19 +1875,17 @@ class HeapSnapshot { this.calculateFlags(); this.#progress.updateStatus('Calculating distances…'); this.calculateDistances(/* isForRetainersView=*/ false); - this.#progress.updateStatus('Building postorder index…'); - const result = this.buildPostOrderIndex(); - // Actually it is array that maps node ordinal number to dominator node ordinal number. - this.#progress.updateStatus('Building dominator tree…'); - this.dominatorsTree = this.buildDominatorTree(result.postOrderIndex2NodeOrdinal, result.nodeOrdinal2PostOrderIndex); this.#progress.updateStatus('Calculating shallow sizes…'); this.calculateShallowSizes(); this.#progress.updateStatus('Calculating retained sizes…'); - this.calculateRetainedSizes(result.postOrderIndex2NodeOrdinal); + this.buildDominatorTreeAndCalculateRetainedSizes(); this.#progress.updateStatus('Building dominated nodes…'); + this.firstDominatedNodeIndex = new Uint32Array(this.nodeCount + 1); + this.dominatedNodes = new Uint32Array(this.nodeCount - 1); this.buildDominatedNodes(); this.#progress.updateStatus('Calculating object names…'); this.calculateObjectNames(); + this.applyInterfaceDefinitions(this.inferInterfaceDefinitions()); this.#progress.updateStatus('Calculating statistics…'); this.calculateStatistics(); this.#progress.updateStatus('Calculating samples…'); @@ -2220,12 +2207,16 @@ class HeapSnapshot { } return this.#allocationProfile.serializeAllocationStack(allocationNodeId); } - aggregatesForDiff() { - if (this.#aggregatesForDiffInternal) { - return this.#aggregatesForDiffInternal; + aggregatesForDiff(interfaceDefinitions) { + if (this.#aggregatesForDiffInternal?.interfaceDefinitions === interfaceDefinitions) { + return this.#aggregatesForDiffInternal.aggregates; } + // Temporarily apply the interface definitions from the other snapshot. + const originalInterfaceDefinitions = this.#interfaceDefinitions; + this.applyInterfaceDefinitions(JSON.parse(interfaceDefinitions)); const aggregatesByClassName = this.getAggregatesByClassName(true, 'allObjects'); - this.#aggregatesForDiffInternal = {}; + this.applyInterfaceDefinitions(originalInterfaceDefinitions ?? []); + const result = {}; const node = this.createNode(); for (const className in aggregatesByClassName) { const aggregate = aggregatesByClassName[className]; @@ -2237,9 +2228,10 @@ class HeapSnapshot { ids[i] = node.id(); selfSizes[i] = node.selfSize(); } - this.#aggregatesForDiffInternal[className] = { indexes: indexes, ids: ids, selfSizes: selfSizes }; + result[className] = { indexes, ids, selfSizes }; } - return this.#aggregatesForDiffInternal; + this.#aggregatesForDiffInternal = { interfaceDefinitions, aggregates: result }; + return result; } isUserRoot(_node) { return true; @@ -2351,7 +2343,7 @@ class HeapSnapshot { const nameMatters = nodeType === 'object' || nodeType === 'native'; const value = { count: 1, - distance: distance, + distance, self: selfSize, maxRet: 0, type: nodeType, @@ -2382,7 +2374,7 @@ class HeapSnapshot { } classIndexValues.idxs = classIndexValues.idxs.slice(); } - return { aggregatesByClassName: aggregatesByClassName, aggregatesByClassIndex: aggregates }; + return { aggregatesByClassName, aggregatesByClassIndex: aggregates }; } calculateClassesRetainedSize(aggregates, filter) { const rootNodeIndex = this.rootNodeIndexInternal; @@ -2467,6 +2459,11 @@ class HeapSnapshot { if (edgeType === this.edgeWeakType) { return false; } + const childNodeIndex = this.containmentEdges.getValue(edgeIndex + this.edgeToNodeOffset); + // Ignore self edges. + if (nodeIndex === childNodeIndex) { + return false; + } if (nodeIndex !== this.rootNodeIndex) { // Shortcuts at the root node have special meaning of marking user global objects. if (edgeType === this.edgeShortcutType) { @@ -2475,7 +2472,6 @@ class HeapSnapshot { const flags = userObjectsMapAndFlag ? userObjectsMapAndFlag.map : null; const userObjectFlag = userObjectsMapAndFlag ? userObjectsMapAndFlag.flag : 0; const nodeOrdinal = nodeIndex / this.nodeFieldCount; - const childNodeIndex = this.containmentEdges.getValue(edgeIndex + this.edgeToNodeOffset); const childNodeOrdinal = childNodeIndex / this.nodeFieldCount; const nodeFlag = !flags || (flags[nodeOrdinal] & userObjectFlag); const childNodeFlag = !flags || (flags[childNodeOrdinal] & userObjectFlag); @@ -2487,10 +2483,9 @@ class HeapSnapshot { } return true; } - /** - * The function checks whether the edge should be considered during building - * postorder iterator and dominator tree. - */ + // Returns whether the edge should be considered when building the dominator tree. + // The first call to this function computes essential edges and caches them. + // Subsequent calls just lookup from the cache and are much faster. isEssentialEdge(edgeIndex) { let essentialEdges = this.#essentialEdges; if (!essentialEdges) { @@ -2511,105 +2506,6 @@ class HeapSnapshot { } return essentialEdges.getBit(edgeIndex / this.edgeFieldsCount); } - buildPostOrderIndex() { - const nodeFieldCount = this.nodeFieldCount; - const nodeCount = this.nodeCount; - const rootNodeOrdinal = this.rootNodeIndexInternal / nodeFieldCount; - const edgeFieldsCount = this.edgeFieldsCount; - const edgeToNodeOffset = this.edgeToNodeOffset; - const firstEdgeIndexes = this.firstEdgeIndexes; - const containmentEdges = this.containmentEdges; - const stackNodes = new Uint32Array(nodeCount); - const stackCurrentEdge = new Uint32Array(nodeCount); - const postOrderIndex2NodeOrdinal = new Uint32Array(nodeCount); - const nodeOrdinal2PostOrderIndex = new Uint32Array(nodeCount); - const visited = new Uint8Array(nodeCount); - let postOrderIndex = 0; - let stackTop = 0; - stackNodes[0] = rootNodeOrdinal; - stackCurrentEdge[0] = firstEdgeIndexes[rootNodeOrdinal]; - visited[rootNodeOrdinal] = 1; - let iteration = 0; - while (true) { - ++iteration; - while (stackTop >= 0) { - const nodeOrdinal = stackNodes[stackTop]; - const edgeIndex = stackCurrentEdge[stackTop]; - const edgesEnd = firstEdgeIndexes[nodeOrdinal + 1]; - if (edgeIndex < edgesEnd) { - stackCurrentEdge[stackTop] += edgeFieldsCount; - if (!this.isEssentialEdge(edgeIndex)) { - continue; - } - const childNodeIndex = containmentEdges.getValue(edgeIndex + edgeToNodeOffset); - const childNodeOrdinal = childNodeIndex / nodeFieldCount; - if (visited[childNodeOrdinal]) { - continue; - } - ++stackTop; - stackNodes[stackTop] = childNodeOrdinal; - stackCurrentEdge[stackTop] = firstEdgeIndexes[childNodeOrdinal]; - visited[childNodeOrdinal] = 1; - } - else { - // Done with all the node children - nodeOrdinal2PostOrderIndex[nodeOrdinal] = postOrderIndex; - postOrderIndex2NodeOrdinal[postOrderIndex++] = nodeOrdinal; - --stackTop; - } - } - if (postOrderIndex === nodeCount || iteration > 1) { - break; - } - const errors = new HeapSnapshotProblemReport(`Heap snapshot: ${nodeCount - postOrderIndex} nodes are unreachable from the root. Following nodes have only weak retainers:`); - const dumpNode = this.rootNode(); - // Remove root from the result (last node in the array) and put it at the bottom of the stack so that it is - // visited after all orphan nodes and their subgraphs. - --postOrderIndex; - stackTop = 0; - stackNodes[0] = rootNodeOrdinal; - stackCurrentEdge[0] = firstEdgeIndexes[rootNodeOrdinal + 1]; // no need to reiterate its edges - for (let i = 0; i < nodeCount; ++i) { - if (visited[i] || !this.hasOnlyWeakRetainers(i)) { - continue; - } - // Add all nodes that have only weak retainers to traverse their subgraphs. - stackNodes[++stackTop] = i; - stackCurrentEdge[stackTop] = firstEdgeIndexes[i]; - visited[i] = 1; - dumpNode.nodeIndex = i * nodeFieldCount; - const retainers = []; - for (let it = dumpNode.retainers(); it.hasNext(); it.next()) { - retainers.push(`${it.item().node().name()}@${it.item().node().id()}.${it.item().name()}`); - } - errors.addError(`${dumpNode.name()} @${dumpNode.id()} weak retainers: ${retainers.join(', ')}`); - } - } - // If we already processed all orphan nodes that have only weak retainers and still have some orphans... - if (postOrderIndex !== nodeCount) { - const errors = new HeapSnapshotProblemReport('Still found ' + (nodeCount - postOrderIndex) + ' unreachable nodes in heap snapshot:'); - const dumpNode = this.rootNode(); - // Remove root from the result (last node in the array) and put it at the bottom of the stack so that it is - // visited after all orphan nodes and their subgraphs. - --postOrderIndex; - for (let i = 0; i < nodeCount; ++i) { - if (visited[i]) { - continue; - } - dumpNode.nodeIndex = i * nodeFieldCount; - errors.addError(dumpNode.name() + ' @' + dumpNode.id()); - // Fix it by giving the node a postorder index anyway. - nodeOrdinal2PostOrderIndex[i] = postOrderIndex; - postOrderIndex2NodeOrdinal[postOrderIndex++] = i; - } - nodeOrdinal2PostOrderIndex[rootNodeOrdinal] = postOrderIndex; - postOrderIndex2NodeOrdinal[postOrderIndex++] = rootNodeOrdinal; - } - return { - postOrderIndex2NodeOrdinal: postOrderIndex2NodeOrdinal, - nodeOrdinal2PostOrderIndex: nodeOrdinal2PostOrderIndex, - }; - } hasOnlyWeakRetainers(nodeOrdinal) { const retainingEdges = this.retainingEdges; const beginRetainerIndex = this.firstRetainerIndex[nodeOrdinal]; @@ -2622,148 +2518,195 @@ class HeapSnapshot { } return true; } - // The algorithm is based on the article: - // K. Cooper, T. Harvey and K. Kennedy "A Simple, Fast Dominance Algorithm" - // Softw. Pract. Exper. 4 (2001), pp. 1-10. - buildDominatorTree(postOrderIndex2NodeOrdinal, nodeOrdinal2PostOrderIndex) { + // The algorithm for building the dominator tree is from the paper: + // Thomas Lengauer and Robert Endre Tarjan. 1979. A fast algorithm for finding dominators in a flowgraph. + // ACM Trans. Program. Lang. Syst. 1, 1 (July 1979), 121–141. https://doi.org/10.1145/357062.357071 + buildDominatorTreeAndCalculateRetainedSizes() { + // Preload fields into local variables for better performance. + const nodeCount = this.nodeCount; + const firstEdgeIndexes = this.firstEdgeIndexes; + const edgeFieldsCount = this.edgeFieldsCount; + const containmentEdges = this.containmentEdges; + const edgeToNodeOffset = this.edgeToNodeOffset; const nodeFieldCount = this.nodeFieldCount; const firstRetainerIndex = this.firstRetainerIndex; - const retainingNodes = this.retainingNodes; const retainingEdges = this.retainingEdges; - const edgeFieldsCount = this.edgeFieldsCount; - const edgeToNodeOffset = this.edgeToNodeOffset; - const firstEdgeIndexes = this.firstEdgeIndexes; - const containmentEdges = this.containmentEdges; - const nodesCount = postOrderIndex2NodeOrdinal.length; - const rootPostOrderedIndex = nodesCount - 1; - const noEntry = nodesCount; - const dominators = new Uint32Array(nodesCount); - for (let i = 0; i < rootPostOrderedIndex; ++i) { - dominators[i] = noEntry; - } - dominators[rootPostOrderedIndex] = rootPostOrderedIndex; - // The affected array is used to mark entries which dominators have to be - // recalculated because of changes in their retainers. This is just a - // heuristic to guide the fixpoint algorithm below so that it can visit - // nodes that are more likely to need modification; see crbug.com/361372448 - // for an example where visiting only affected nodes is insufficient. - const affected = createBitVector(nodesCount); - let nodeOrdinal; - // Time wasters are nodes with lots of predecessors which didn't change the - // last time we visited them. We'll avoid marking such nodes as affected. - const timeWasters = createBitVector(nodesCount); - { // Mark the root direct children as affected. - nodeOrdinal = this.rootNodeIndexInternal / nodeFieldCount; - const endEdgeIndex = firstEdgeIndexes[nodeOrdinal + 1]; - for (let edgeIndex = firstEdgeIndexes[nodeOrdinal]; edgeIndex < endEdgeIndex; edgeIndex += edgeFieldsCount) { - if (!this.isEssentialEdge(edgeIndex)) { - continue; - } - const childNodeOrdinal = containmentEdges.getValue(edgeIndex + edgeToNodeOffset) / nodeFieldCount; - affected.setBit(nodeOrdinal2PostOrderIndex[childNodeOrdinal]); - } - } - let changed = true; - let shouldVisitEveryNode = false; - while (changed || !shouldVisitEveryNode) { - // The original Cooper-Harvey-Kennedy algorithm visits every node on every traversal, - // but that is far too expensive for the graph shapes encountered in heap snapshots. - // Instead, we use the `affected` bitvector to guide iteration most of the time, and - // only do a full traversal if the previous bitvector-based traversal found nothing - // to change. The order in which nodes are visited doesn't matter for correctness. - shouldVisitEveryNode = !changed; - function getPrevious(postOrderIndex) { - return shouldVisitEveryNode ? postOrderIndex - 1 : affected.previous(postOrderIndex); - } - changed = false; - for (let postOrderIndex = getPrevious(rootPostOrderedIndex); postOrderIndex >= 0; postOrderIndex = getPrevious(postOrderIndex)) { - affected.clearBit(postOrderIndex); - // If dominator of the entry has already been set to root, - // then it can't propagate any further. - if (dominators[postOrderIndex] === rootPostOrderedIndex) { - continue; + const retainingNodes = this.retainingNodes; + const rootNodeOrdinal = this.rootNodeIndexInternal / nodeFieldCount; + const isEssentialEdge = this.isEssentialEdge.bind(this); + const hasOnlyWeakRetainers = this.hasOnlyWeakRetainers.bind(this); + // The Lengauer-Tarjan algorithm expects vectors to be numbered from 1 to n + // and uses 0 as an invalid value, so use 1-indexing for all the arrays. + // Convert between ordinals and vertex numbers by adding/subtracting 1. + const arrayLength = nodeCount + 1; + const parent = new Uint32Array(arrayLength); + const ancestor = new Uint32Array(arrayLength); + const vertex = new Uint32Array(arrayLength); + const label = new Uint32Array(arrayLength); + const semi = new Uint32Array(arrayLength); + const bucket = new Array(arrayLength); + let n = 0; + // Iterative DFS since the recursive version can cause stack overflows. + // Use an array to keep track of the next edge index to be examined for each node. + const nextEdgeIndex = new Uint32Array(arrayLength); + const dfs = (root) => { + const rootOrdinal = root - 1; + nextEdgeIndex[rootOrdinal] = firstEdgeIndexes[rootOrdinal]; + let v = root; + while (v !== 0) { + // First process v if not done already. + if (semi[v] === 0) { + semi[v] = ++n; + vertex[n] = label[v] = v; } - nodeOrdinal = postOrderIndex2NodeOrdinal[postOrderIndex]; - let newDominatorIndex = noEntry; - const beginRetainerIndex = firstRetainerIndex[nodeOrdinal]; - const endRetainerIndex = firstRetainerIndex[nodeOrdinal + 1]; - const edgeCount = endRetainerIndex - beginRetainerIndex; - let orphanNode = true; - for (let retainerIndex = beginRetainerIndex; retainerIndex < endRetainerIndex; ++retainerIndex) { - const retainerEdgeIndex = retainingEdges[retainerIndex]; - const retainerNodeIndex = retainingNodes[retainerIndex]; - if (!this.isEssentialEdge(retainerEdgeIndex)) { + // The next node to process is the first unprocessed successor w of v, + // or parent[v] if all of v's successors have already been processed. + let vNext = parent[v]; + const vOrdinal = v - 1; + for (; nextEdgeIndex[vOrdinal] < firstEdgeIndexes[vOrdinal + 1]; nextEdgeIndex[vOrdinal] += edgeFieldsCount) { + const edgeIndex = nextEdgeIndex[vOrdinal]; + if (!isEssentialEdge(edgeIndex)) { continue; } - orphanNode = false; - const retainerNodeOrdinal = retainerNodeIndex / nodeFieldCount; - let retainerPostOrderIndex = nodeOrdinal2PostOrderIndex[retainerNodeOrdinal]; - if (dominators[retainerPostOrderIndex] !== noEntry) { - if (newDominatorIndex === noEntry) { - newDominatorIndex = retainerPostOrderIndex; - } - else { - while (retainerPostOrderIndex !== newDominatorIndex) { - while (retainerPostOrderIndex < newDominatorIndex) { - retainerPostOrderIndex = dominators[retainerPostOrderIndex]; - } - while (newDominatorIndex < retainerPostOrderIndex) { - newDominatorIndex = dominators[newDominatorIndex]; - } - } - } - // If item has already reached the root, it doesn't make sense - // to check other retainers. - if (newDominatorIndex === rootPostOrderedIndex) { - break; - } + const wOrdinal = containmentEdges.getValue(edgeIndex + edgeToNodeOffset) / nodeFieldCount; + const w = wOrdinal + 1; + if (semi[w] === 0) { + parent[w] = v; + nextEdgeIndex[wOrdinal] = firstEdgeIndexes[wOrdinal]; + vNext = w; + break; } } - // Make root dominator of orphans. - if (orphanNode) { - newDominatorIndex = rootPostOrderedIndex; + v = vNext; + } + }; + // Iterative version since the recursive version can cause stack overflows. + // Preallocate a stack since compress() is called several times. + // The stack cannot grow larger than the number of nodes since we walk up + // the tree represented by the ancestor array. + const compressionStack = new Uint32Array(arrayLength); + const compress = (v) => { + let stackPointer = 0; + while (ancestor[ancestor[v]] !== 0) { + compressionStack[++stackPointer] = v; + v = ancestor[v]; + } + while (stackPointer > 0) { + const w = compressionStack[stackPointer--]; + if (semi[label[ancestor[w]]] < semi[label[w]]) { + label[w] = label[ancestor[w]]; } - if (newDominatorIndex !== noEntry && dominators[postOrderIndex] !== newDominatorIndex) { - timeWasters.clearBit(postOrderIndex); // It's not a waste of time if we changed something. - dominators[postOrderIndex] = newDominatorIndex; - changed = true; - nodeOrdinal = postOrderIndex2NodeOrdinal[postOrderIndex]; - const beginEdgeToNodeFieldIndex = firstEdgeIndexes[nodeOrdinal] + edgeToNodeOffset; - const endEdgeToNodeFieldIndex = firstEdgeIndexes[nodeOrdinal + 1]; - for (let toNodeFieldIndex = beginEdgeToNodeFieldIndex; toNodeFieldIndex < endEdgeToNodeFieldIndex; toNodeFieldIndex += edgeFieldsCount) { - const childNodeOrdinal = containmentEdges.getValue(toNodeFieldIndex) / nodeFieldCount; - const childPostOrderIndex = nodeOrdinal2PostOrderIndex[childNodeOrdinal]; - // Mark the child node as affected, unless it's unlikely to be beneficial. - if (childPostOrderIndex !== postOrderIndex && !timeWasters.getBit(childPostOrderIndex)) { - affected.setBit(childPostOrderIndex); - } - } + ancestor[w] = ancestor[ancestor[w]]; + } + }; + // Simple versions of eval and link from the paper. + const evaluate = (v) => { + if (ancestor[v] === 0) { + return v; + } + compress(v); + return label[v]; + }; + const link = (v, w) => { + ancestor[w] = v; + }; + // Algorithm begins here. The variable names are as per the paper. + const r = rootNodeOrdinal + 1; + n = 0; + const dom = new Uint32Array(arrayLength); + // First perform DFS from the root. + dfs(r); + // Then perform DFS from orphan nodes (ones with only weak retainers) if any. + if (n < nodeCount) { + const errors = new HeapSnapshotProblemReport(`Heap snapshot: ${nodeCount - n} nodes are unreachable from the root.`); + errors.addError('The following nodes have only weak retainers:'); + const dumpNode = this.rootNode(); + for (let v = 1; v <= nodeCount; v++) { + const vOrdinal = v - 1; + if (semi[v] === 0 && hasOnlyWeakRetainers(vOrdinal)) { + dumpNode.nodeIndex = vOrdinal * nodeFieldCount; + errors.addError(`${dumpNode.name()} @${dumpNode.id()}`); + parent[v] = r; + dfs(v); + } + } + } + // If there are unreachable nodes still, visit them individually from the root. + // This can happen when there is a clique of nodes retained by one another. + if (n < nodeCount) { + const errors = new HeapSnapshotProblemReport(`Heap snapshot: Still found ${nodeCount - n} unreachable nodes:`); + const dumpNode = this.rootNode(); + for (let v = 1; v <= nodeCount; v++) { + if (semi[v] === 0) { + const vOrdinal = v - 1; + dumpNode.nodeIndex = vOrdinal * nodeFieldCount; + errors.addError(`${dumpNode.name()} @${dumpNode.id()}`); + parent[v] = r; + semi[v] = ++n; + vertex[n] = label[v] = v; } - else if (edgeCount > 1000) { - timeWasters.setBit(postOrderIndex); + } + } + // Main loop. Process the vertices in decreasing order by DFS number. + for (let i = n; i >= 2; --i) { + const w = vertex[i]; + // Iterate over all predecessors v of w. + const wOrdinal = w - 1; + let isOrphanNode = true; + for (let retainerIndex = firstRetainerIndex[wOrdinal]; retainerIndex < firstRetainerIndex[wOrdinal + 1]; retainerIndex++) { + if (!isEssentialEdge(retainingEdges[retainerIndex])) { + continue; + } + isOrphanNode = false; + const vOrdinal = retainingNodes[retainerIndex] / nodeFieldCount; + const v = vOrdinal + 1; + const u = evaluate(v); + if (semi[u] < semi[w]) { + semi[w] = semi[u]; + } + } + if (isOrphanNode) { + // We treat orphan nodes as having a single predecessor - the root. + // semi[r] is always less than any semi[w] so set it unconditionally. + semi[w] = semi[r]; + } + if (bucket[vertex[semi[w]]] === undefined) { + bucket[vertex[semi[w]]] = new Set(); + } + bucket[vertex[semi[w]]].add(w); + link(parent[w], w); + // Process all vertices v in bucket(parent(w)). + if (bucket[parent[w]] !== undefined) { + for (const v of bucket[parent[w]]) { + const u = evaluate(v); + dom[v] = semi[u] < semi[v] ? u : parent[w]; } + bucket[parent[w]].clear(); } } - const dominatorsTree = new Uint32Array(nodesCount); - for (let postOrderIndex = 0, l = dominators.length; postOrderIndex < l; ++postOrderIndex) { - nodeOrdinal = postOrderIndex2NodeOrdinal[postOrderIndex]; - dominatorsTree[nodeOrdinal] = postOrderIndex2NodeOrdinal[dominators[postOrderIndex]]; + // Final step. Fill in the immediate dominators not explicitly computed above. + // Unlike the paper, we consider the root to be its own dominator and + // set dom[0] to r to propagate the root as the dominator of unreachable nodes. + dom[0] = dom[r] = r; + for (let i = 2; i <= n; i++) { + const w = vertex[i]; + if (dom[w] !== vertex[semi[w]]) { + dom[w] = dom[dom[w]]; + } } - return dominatorsTree; - } - calculateRetainedSizes(postOrderIndex2NodeOrdinal) { - const nodeCount = this.nodeCount; + // Algorithm ends here. + // Transform the dominators into an ordinal-indexed array and populate the self sizes. const nodes = this.nodes; const nodeSelfSizeOffset = this.nodeSelfSizeOffset; - const nodeFieldCount = this.nodeFieldCount; - const dominatorsTree = this.dominatorsTree; + const dominatorsTree = this.dominatorsTree = new Uint32Array(nodeCount); const retainedSizes = this.retainedSizes; - for (let nodeOrdinal = 0; nodeOrdinal < nodeCount; ++nodeOrdinal) { + for (let nodeOrdinal = 0; nodeOrdinal < nodeCount; nodeOrdinal++) { + dominatorsTree[nodeOrdinal] = dom[nodeOrdinal + 1] - 1; retainedSizes[nodeOrdinal] = nodes.getValue(nodeOrdinal * nodeFieldCount + nodeSelfSizeOffset); } - // Propagate retained sizes for each node excluding root. - for (let postOrderIndex = 0; postOrderIndex < nodeCount - 1; ++postOrderIndex) { - const nodeOrdinal = postOrderIndex2NodeOrdinal[postOrderIndex]; + // Then propagate up the retained sizes for each traversed node excluding the root. + for (let i = n; i > 1; i--) { + const nodeOrdinal = vertex[i] - 1; const dominatorOrdinal = dominatorsTree[nodeOrdinal]; retainedSizes[dominatorOrdinal] += retainedSizes[nodeOrdinal]; } @@ -2883,6 +2826,177 @@ class HeapSnapshot { node.nodeIndex = node.nextNodeIndex(); } } + interfaceDefinitions() { + return JSON.stringify(this.#interfaceDefinitions ?? []); + } + isPlainJSObject(node) { + return node.rawType() === this.nodeObjectType && node.rawName() === 'Object'; + } + inferInterfaceDefinitions() { + const { edgePropertyType } = this; + // A map from interface names to their definitions. + const candidates = new Map(); + for (let it = this.allNodes(); it.hasNext(); it.next()) { + const node = it.item(); + if (!this.isPlainJSObject(node)) { + continue; + } + let interfaceName = '{'; + const properties = []; + for (let edgeIt = node.edges(); edgeIt.hasNext(); edgeIt.next()) { + const edge = edgeIt.item(); + const edgeName = edge.name(); + if (edge.rawType() !== edgePropertyType || edgeName === '__proto__') { + continue; + } + const formattedEdgeName = JSHeapSnapshotNode.formatPropertyName(edgeName); + if (interfaceName.length > MIN_INTERFACE_PROPERTY_COUNT && + interfaceName.length + formattedEdgeName.length > MAX_INTERFACE_NAME_LENGTH) { + break; // The interface name is getting too long. + } + if (interfaceName.length !== 1) { + interfaceName += ', '; + } + interfaceName += formattedEdgeName; + properties.push(edgeName); + } + // The empty interface is not a very meaningful, and can be sort of misleading + // since someone might incorrectly interpret it as objects with no properties. + if (properties.length === 0) { + continue; + } + interfaceName += '}'; + const candidate = candidates.get(interfaceName); + if (candidate) { + ++candidate.count; + } + else { + candidates.set(interfaceName, { name: interfaceName, properties, count: 1 }); + } + } + // Next, sort the candidates and select the most popular ones. It's possible that + // some candidates represent the same properties in different orders, but that's + // okay: by sorting here, we ensure that the most popular ordering appears first + // in the result list, and the rules for applying interface definitions will prefer + // the first matching definition if multiple matches contain the same properties. + const sortedCandidates = Array.from(candidates.values()); + sortedCandidates.sort((a, b) => b.count - a.count); + const result = []; + const maxResultSize = Math.min(sortedCandidates.length, MAX_INTERFACE_COUNT); + for (let i = 0; i < maxResultSize; ++i) { + const candidate = sortedCandidates[i]; + if (candidate.count < MIN_OBJECT_COUNT_PER_INTERFACE) { + break; + } + result.push(candidate); + } + return result; + } + applyInterfaceDefinitions(definitions) { + const { edgePropertyType } = this; + this.#interfaceDefinitions = definitions; + // Any computed aggregate data will be wrong after recategorization, so clear it. + this.#aggregates = {}; + this.#aggregatesSortedFlags = {}; + function selectBetterMatch(a, b) { + if (!b || a.propertyCount > b.propertyCount) { + return a; + } + if (b.propertyCount > a.propertyCount) { + return b; + } + return a.index <= b.index ? a : b; + } + // The root node of the tree. + const propertyTree = { + next: new Map(), + matchInfo: null, + greatestNext: null, + }; + // Build up the property tree. + for (let interfaceIndex = 0; interfaceIndex < definitions.length; ++interfaceIndex) { + const definition = definitions[interfaceIndex]; + const properties = toSorted(definition.properties); + let currentNode = propertyTree; + for (const property of properties) { + const nextMap = currentNode.next; + let nextNode = nextMap.get(property); + if (!nextNode) { + nextNode = { + next: new Map(), + matchInfo: null, + greatestNext: null, + }; + nextMap.set(property, nextNode); + if (currentNode.greatestNext === null || currentNode.greatestNext < property) { + currentNode.greatestNext = property; + } + } + currentNode = nextNode; + } + // Only set matchInfo on this node if it wasn't already set, to ensure that + // interfaces defined earlier in the list have priority. + if (!currentNode.matchInfo) { + currentNode.matchInfo = { + name: definition.name, + propertyCount: properties.length, + index: interfaceIndex, + }; + } + } + // The fallback match for objects which don't match any defined interface. + const initialMatch = { + name: 'Object', + propertyCount: 0, + index: Infinity, + }; + // Iterate all nodes and check whether they match a named interface, using + // the tree constructed above. Then update the class name for each node. + for (let it = this.allNodes(); it.hasNext(); it.next()) { + const node = it.item(); + if (!this.isPlainJSObject(node)) { + continue; + } + // Collect and sort the properties of this object. + const properties = []; + for (let edgeIt = node.edges(); edgeIt.hasNext(); edgeIt.next()) { + const edge = edgeIt.item(); + if (edge.rawType() === edgePropertyType) { + properties.push(edge.name()); + } + } + properties.sort(); + // We may explore multiple possible paths through the tree, so this set tracks + // all states that match with the properties iterated thus far. + const states = new Set(); + states.add(propertyTree); + // This variable represents the best match found thus far. We start by checking + // whether there is an interface definition for the empty object. + let match = selectBetterMatch(initialMatch, propertyTree.matchInfo); + // Traverse the tree to find any matches. + for (const property of properties) { + // Iterate only the states that already exist, not the ones added during the loop below. + for (const currentState of Array.from(states.keys())) { + if (currentState.greatestNext === null || property >= currentState.greatestNext) { + // No further transitions are possible from this state. + states.delete(currentState); + } + const nextState = currentState.next.get(property); + if (nextState) { + states.add(nextState); + match = selectBetterMatch(match, nextState.matchInfo); + } + } + } + // Update the node's class name accordingly. + let classIndex = match === initialMatch ? node.rawNameIndex() : this.#interfaceNames.get(match.name); + if (classIndex === undefined) { + classIndex = this.addString(match.name); + this.#interfaceNames.set(match.name, classIndex); + } + node.setClassIndex(classIndex); + } + } /** * Iterates children of a node. */ @@ -3567,7 +3681,7 @@ class JSHeapSnapshot extends HeapSnapshot { constructor(profile, progress) { super(profile, progress); this.nodeFlags = { - // bit flags + // bit flags in 8-bit value canBeQueried: 1, detachedDOMTreeNode: 2, pageObject: 4, // The idea is to track separately the objects owned by the page and the objects owned by debugger. @@ -3594,7 +3708,7 @@ class JSHeapSnapshot extends HeapSnapshot { return filter; } calculateFlags() { - this.flags = new Uint32Array(this.nodeCount); + this.flags = new Uint8Array(this.nodeCount); this.markDetachedDOMTreeNodes(); this.markQueriableHeapObjects(); this.markPageOwnedNodes(); @@ -4225,6 +4339,16 @@ class JSHeapSnapshotRetainerEdge extends HeapSnapshotRetainerEdge { } } +const withResolvers = () => { + let resolve; + let reject; + const promise = new Promise((_resolve, _reject) => { + resolve = _resolve; + reject = _reject; + }); + return { resolve, reject, promise } + }; + /* * Copyright (C) 2013 Google Inc. All rights reserved. * @@ -4477,35 +4601,6 @@ const UIStrings = { const str_ = registerUIStrings('core/common/SettingRegistration.ts', UIStrings); getLocalizedString.bind(undefined, str_); -/* - * Copyright (C) 2012 Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ class HeapSnapshotLoader { #progress; #buffer; @@ -4615,7 +4710,7 @@ class HeapSnapshotLoader { if (this.#buffer.length > 0) { return Promise.resolve(this.#buffer.shift()); } - const { promise, resolve } = promiseWithResolvers(); + const { promise, resolve } = withResolvers(); this.#dataCallback = resolve; return promise; }