From cb7a5887adcf1f9aec0f60db8068544e364641ec Mon Sep 17 00:00:00 2001 From: Rob Walch Date: Tue, 8 Jul 2025 10:27:00 -0700 Subject: [PATCH 01/52] Apply prettier@3.6 formatting --- docs/design.md | 2 -- tools/mp4-inspect.js | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/design.md b/docs/design.md index 5e2c8244cad..be372055927 100644 --- a/docs/design.md +++ b/docs/design.md @@ -64,7 +64,6 @@ design idea is pretty simple : - [src/controller/id3-track-controller.ts][] - in charge of creating the id3 metadata text track and adding cues to that track in response to the FRAG_PARSING_METADATA event. the raw id3 data is base64 encoded and stored in the cue's text property. - [src/controller/level-controller.ts][] - - handling quality level set/get ((re)loading stream manifest/switching levels) - in charge of scheduling playlist (re)loading - monitors fragment and key loading errors. Performs fragment hunt by switching between primary and backup streams and down-shifting a level till `fragLoadingMaxRetry` limit is reached. @@ -83,7 +82,6 @@ design idea is pretty simple : **Retry Recommendations** By not having multiple renditions, recovery logic will not be able to add extra value to your platform. In order to have good results for dual constraint media hunt, specify big enough limits for fragments and levels retries. - - Level: don't use total retry less than `3 - 4` - Fragment: don't use total retry less than `4 - 6` - Implement short burst retries (i.e. small retry delay `0.5 - 4` seconds), and when library returns fatal error switch to a different CDN diff --git a/tools/mp4-inspect.js b/tools/mp4-inspect.js index 4dad9167e6f..9672cc8654f 100644 --- a/tools/mp4-inspect.js +++ b/tools/mp4-inspect.js @@ -663,7 +663,8 @@ var mp4toJSON = function (data) { while (i < data.byteLength) { // parse box data - (size = view.getUint32(i)), (type = parseType(data.subarray(i + 4, i + 8))); + ((size = view.getUint32(i)), + (type = parseType(data.subarray(i + 4, i + 8)))); end = size > 1 ? i + size : data.byteLength; // parse type-specific data From e6d30b051d71b957004315f60310faefbdc73871 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 8 Jul 2025 17:28:22 +0000 Subject: [PATCH 02/52] chore(deps): update dependency prettier to v3.6.2 --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index a538965a2ee..af52b3e0d8c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -64,7 +64,7 @@ "mocha": "11.7.1", "node-fetch": "3.3.2", "npm-run-all2": "8.0.4", - "prettier": "3.5.3", + "prettier": "3.6.2", "promise-polyfill": "8.3.0", "rollup": "4.44.1", "rollup-plugin-istanbul": "5.0.0", @@ -11655,9 +11655,9 @@ } }, "node_modules/prettier": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", - "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" @@ -22926,9 +22926,9 @@ "dev": true }, "prettier": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", - "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true }, "printable-characters": { diff --git a/package.json b/package.json index 4b1cb1ff7db..c356e936aee 100644 --- a/package.json +++ b/package.json @@ -122,7 +122,7 @@ "mocha": "11.7.1", "node-fetch": "3.3.2", "npm-run-all2": "8.0.4", - "prettier": "3.5.3", + "prettier": "3.6.2", "promise-polyfill": "8.3.0", "rollup": "4.44.1", "rollup-plugin-istanbul": "5.0.0", From 4bd1e43b4a27c51d54d41b01fa99f2fed3677e64 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 8 Jul 2025 17:44:03 +0000 Subject: [PATCH 03/52] chore(deps): update node.js to v22.17.0 --- .node-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.node-version b/.node-version index 5b540673a82..fc37597bccd 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -22.16.0 +22.17.0 From 85aa67bd2ac7a61afbb46413ac5bded1e2061982 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 8 Jul 2025 18:43:50 +0000 Subject: [PATCH 04/52] chore(config): migrate config renovate.json --- renovate.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/renovate.json b/renovate.json index 21919c957eb..82b98f68735 100644 --- a/renovate.json +++ b/renovate.json @@ -1,5 +1,5 @@ { - "extends": ["config:base"], + "extends": ["config:recommended"], "labels": ["dependencies", "skip-change-log"], "prHourlyLimit": 0, "prConcurrentLimit": 0, @@ -14,8 +14,8 @@ }, "packageRules": [ { - "matchPackagePatterns": ["*"], - "rangeStrategy": "bump" + "rangeStrategy": "bump", + "matchPackageNames": ["*"] }, { "matchDepTypes": ["devDependencies"], From 083a51ae053278ff05afb8e27f3fc2afb0599856 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 9 Jul 2025 00:04:39 +0000 Subject: [PATCH 05/52] chore(deps): update dependency chromedriver to v138.0.1 --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index af52b3e0d8c..d1c3d7041ae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,7 +36,7 @@ "babel-plugin-transform-remove-console": "6.9.4", "chai": "4.5.0", "chart.js": "2.9.4", - "chromedriver": "138.0.0", + "chromedriver": "138.0.1", "doctoc": "2.2.1", "es-check": "9.1.4", "eslint": "8.57.1", @@ -5509,9 +5509,9 @@ } }, "node_modules/chromedriver": { - "version": "138.0.0", - "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-138.0.0.tgz", - "integrity": "sha512-bJ/DNm5Y0TbqM71ARaAohTWVwcQ2SsWciYC5Q9Ul7DC/oTxm6B1vI2h6WscFCOOi49ul4tXZVjA/LOruljjmjA==", + "version": "138.0.1", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-138.0.1.tgz", + "integrity": "sha512-QS/Z1qB2OpKsPUlJjkfKmpf9lGw6ObB0dX5+dP3M0gdtbu80TUnS+EjXbtu6YkbMfr2/Qt8IfOONrawNW1GwhA==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -18427,9 +18427,9 @@ "peer": true }, "chromedriver": { - "version": "138.0.0", - "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-138.0.0.tgz", - "integrity": "sha512-bJ/DNm5Y0TbqM71ARaAohTWVwcQ2SsWciYC5Q9Ul7DC/oTxm6B1vI2h6WscFCOOi49ul4tXZVjA/LOruljjmjA==", + "version": "138.0.1", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-138.0.1.tgz", + "integrity": "sha512-QS/Z1qB2OpKsPUlJjkfKmpf9lGw6ObB0dX5+dP3M0gdtbu80TUnS+EjXbtu6YkbMfr2/Qt8IfOONrawNW1GwhA==", "dev": true, "requires": { "@testim/chrome-version": "^1.1.4", diff --git a/package.json b/package.json index c356e936aee..9e140046585 100644 --- a/package.json +++ b/package.json @@ -94,7 +94,7 @@ "babel-plugin-transform-remove-console": "6.9.4", "chai": "4.5.0", "chart.js": "2.9.4", - "chromedriver": "138.0.0", + "chromedriver": "138.0.1", "doctoc": "2.2.1", "es-check": "9.1.4", "eslint": "8.57.1", From 93f78e470352aef6d8a43c8eacf112814a12a360 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 10 Jul 2025 11:10:34 +0000 Subject: [PATCH 06/52] chore(deps): update dependency wrangler to v4.23.0 --- package-lock.json | 30 +++++++++++++++--------------- package.json | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index d1c3d7041ae..54b5b1db2c3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -75,7 +75,7 @@ "sinon-chai": "3.7.0", "typescript": "5.8.3", "url-toolkit": "2.2.5", - "wrangler": "4.22.0" + "wrangler": "4.23.0" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -10387,9 +10387,9 @@ } }, "node_modules/miniflare": { - "version": "4.20250617.4", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20250617.4.tgz", - "integrity": "sha512-IAoApFKxOJlaaFkym5ETstVX3qWzVt3xyqCDj6vSSTgEH3zxZJ5417jZGg8iQfMHosKCcQH1doPPqqnOZm/yrw==", + "version": "4.20250617.5", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20250617.5.tgz", + "integrity": "sha512-Qqn30jR6dCjXaKVizT6vH4KOb+GyLccoxLNOJEfu63yBPn8eoXa7PrdiSGTmjs2RY8/tr7eTO8Wu/Yr14k0xVA==", "dev": true, "dependencies": { "@cspotcode/source-map-support": "0.8.1", @@ -14410,16 +14410,16 @@ "dev": true }, "node_modules/wrangler": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.22.0.tgz", - "integrity": "sha512-m8qVO3YxhUTII+4U889G/f5UuLSvMkUkCNatupV2f/SJ+iqaWtP1QbuQII8bs2J/O4rqxsz46Wu2S50u7tKB5Q==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.23.0.tgz", + "integrity": "sha512-JSeDt3IwA4TEmg/V3tRblImPjdxynBt9PUVO/acQJ83XGlMMSwswDKL1FuwvbFzgX6+JXc3GMHeu7r8AQIxw9w==", "dev": true, "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", "@cloudflare/unenv-preset": "2.3.3", "blake3-wasm": "2.1.5", "esbuild": "0.25.4", - "miniflare": "4.20250617.4", + "miniflare": "4.20250617.5", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.17", "workerd": "1.20250617.0" @@ -21974,9 +21974,9 @@ "dev": true }, "miniflare": { - "version": "4.20250617.4", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20250617.4.tgz", - "integrity": "sha512-IAoApFKxOJlaaFkym5ETstVX3qWzVt3xyqCDj6vSSTgEH3zxZJ5417jZGg8iQfMHosKCcQH1doPPqqnOZm/yrw==", + "version": "4.20250617.5", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20250617.5.tgz", + "integrity": "sha512-Qqn30jR6dCjXaKVizT6vH4KOb+GyLccoxLNOJEfu63yBPn8eoXa7PrdiSGTmjs2RY8/tr7eTO8Wu/Yr14k0xVA==", "dev": true, "requires": { "@cspotcode/source-map-support": "0.8.1", @@ -24944,9 +24944,9 @@ "dev": true }, "wrangler": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.22.0.tgz", - "integrity": "sha512-m8qVO3YxhUTII+4U889G/f5UuLSvMkUkCNatupV2f/SJ+iqaWtP1QbuQII8bs2J/O4rqxsz46Wu2S50u7tKB5Q==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.23.0.tgz", + "integrity": "sha512-JSeDt3IwA4TEmg/V3tRblImPjdxynBt9PUVO/acQJ83XGlMMSwswDKL1FuwvbFzgX6+JXc3GMHeu7r8AQIxw9w==", "dev": true, "requires": { "@cloudflare/kv-asset-handler": "0.4.0", @@ -24954,7 +24954,7 @@ "blake3-wasm": "2.1.5", "esbuild": "0.25.4", "fsevents": "~2.3.2", - "miniflare": "4.20250617.4", + "miniflare": "4.20250617.5", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.17", "workerd": "1.20250617.0" diff --git a/package.json b/package.json index 9e140046585..f8ace915d0f 100644 --- a/package.json +++ b/package.json @@ -133,6 +133,6 @@ "sinon-chai": "3.7.0", "typescript": "5.8.3", "url-toolkit": "2.2.5", - "wrangler": "4.22.0" + "wrangler": "4.23.0" } } From 94342c66257ec24011cef17b1b4ed85289b08348 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 10 Jul 2025 11:27:00 +0000 Subject: [PATCH 07/52] chore(deps): update dependency wrangler to v4.24.1 --- package-lock.json | 437 ++++++++++++++++++++++++++-------------------- package.json | 2 +- 2 files changed, 244 insertions(+), 195 deletions(-) diff --git a/package-lock.json b/package-lock.json index 54b5b1db2c3..685d4b93904 100644 --- a/package-lock.json +++ b/package-lock.json @@ -75,7 +75,7 @@ "sinon-chai": "3.7.0", "typescript": "5.8.3", "url-toolkit": "2.2.5", - "wrangler": "4.23.0" + "wrangler": "4.24.1" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -1844,9 +1844,9 @@ } }, "node_modules/@cloudflare/workerd-darwin-64": { - "version": "1.20250617.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20250617.0.tgz", - "integrity": "sha512-toG8JUKVLIks4oOJLe9FeuixE84pDpMZ32ip7mCpE7JaFc5BqGFvevk0YC/db3T71AQlialjRwioH3jS/dzItA==", + "version": "1.20250709.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20250709.0.tgz", + "integrity": "sha512-VqwcvnbI8FNCP87ZWNHA3/sAC5U9wMbNnjBG0sHEYzM7B9RPHKYHdVKdBEWhzZXnkQYMK81IHm4CZsK16XxAuQ==", "cpu": [ "x64" ], @@ -1860,9 +1860,9 @@ } }, "node_modules/@cloudflare/workerd-darwin-arm64": { - "version": "1.20250617.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20250617.0.tgz", - "integrity": "sha512-JTX0exbC9/ZtMmQQA8tDZEZFMXZrxOpTUj2hHnsUkErWYkr5SSZH04RBhPg6dU4VL8bXuB5/eJAh7+P9cZAp7g==", + "version": "1.20250709.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20250709.0.tgz", + "integrity": "sha512-A54ttSgXMM4huChPTThhkieOjpDxR+srVOO9zjTHVIyoQxA8zVsku4CcY/GQ95RczMV+yCKVVu/tAME7vwBFuA==", "cpu": [ "arm64" ], @@ -1876,9 +1876,9 @@ } }, "node_modules/@cloudflare/workerd-linux-64": { - "version": "1.20250617.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20250617.0.tgz", - "integrity": "sha512-8jkSoVRJ+1bOx3tuWlZCGaGCV2ew7/jFMl6V3CPXOoEtERUHsZBQLVkQIGKcmC/LKSj7f/mpyBUeu2EPTo2HEg==", + "version": "1.20250709.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20250709.0.tgz", + "integrity": "sha512-no4O3OK+VXINIxv99OHJDpIgML2ZssrSvImwLtULzqm+cl4t1PIfXNRUqj89ujTkmad+L9y4G6dBQMPCLnmlGg==", "cpu": [ "x64" ], @@ -1892,9 +1892,9 @@ } }, "node_modules/@cloudflare/workerd-linux-arm64": { - "version": "1.20250617.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20250617.0.tgz", - "integrity": "sha512-YAzcOyu897z5dQKFzme1oujGWMGEJCR7/Wrrm1nSP6dqutxFPTubRADM8BHn2CV3ij//vaPnAeLmZE3jVwOwig==", + "version": "1.20250709.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20250709.0.tgz", + "integrity": "sha512-7cNICk2Qd+m4QGrcmWyAuZJXTHt1ud6isA+dic7Yk42WZmwXhlcUATyvFD9FSQNFcldjuRB4n8JlWEFqZBn+lw==", "cpu": [ "arm64" ], @@ -1908,9 +1908,9 @@ } }, "node_modules/@cloudflare/workerd-windows-64": { - "version": "1.20250617.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20250617.0.tgz", - "integrity": "sha512-XWM/6sagDrO0CYDKhXhPjM23qusvIN1ju9ZEml6gOQs8tNOFnq6Cn6X9FAmnyapRFCGUSEC3HZYJAm7zwVKaMA==", + "version": "1.20250709.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20250709.0.tgz", + "integrity": "sha512-j1AyO8V/62Q23EJplWgzBlRCqo/diXgox58AbDqSqgyzCBAlvUzXQRDBab/FPNG/erRqt7I1zQhahrBhrM0uLA==", "cpu": [ "x64" ], @@ -3272,6 +3272,44 @@ "node": ">=14" } }, + "node_modules/@poppinss/colors": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@poppinss/colors/-/colors-4.1.5.tgz", + "integrity": "sha512-FvdDqtcRCtz6hThExcFOgW0cWX+xwSMWcRuQe5ZEb2m7cVQOAVZOIMt+/v9RxGiD9/OY16qJBXK4CVKWAPalBw==", + "dev": true, + "dependencies": { + "kleur": "^4.1.5" + } + }, + "node_modules/@poppinss/dumper": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@poppinss/dumper/-/dumper-0.6.4.tgz", + "integrity": "sha512-iG0TIdqv8xJ3Lt9O8DrPRxw1MRLjNpoqiSGU03P/wNLP/s0ra0udPJ1J2Tx5M0J3H/cVyEgpbn8xUKRY9j59kQ==", + "dev": true, + "dependencies": { + "@poppinss/colors": "^4.1.5", + "@sindresorhus/is": "^7.0.2", + "supports-color": "^10.0.0" + } + }, + "node_modules/@poppinss/dumper/node_modules/supports-color": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.0.0.tgz", + "integrity": "sha512-HRVVSbCCMbj7/kdWF9Q+bbckjBHLtHMEoJWlkmYzzdwhYMkjkOwubLM6t7NbWKjgKamGDrWL1++KrjUO1t9oAQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/@poppinss/exception": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@poppinss/exception/-/exception-1.2.2.tgz", + "integrity": "sha512-m7bpKCD4QMlFCjA/nKTs23fuvoVFoA83brRKmObCUNmi/9tVu8Ve3w4YQAnJu4q3Tjf5fr685HYIC/IA2zHRSg==", + "dev": true + }, "node_modules/@rollup/plugin-alias": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-alias/-/plugin-alias-5.1.1.tgz", @@ -3893,6 +3931,18 @@ "string-argv": "~0.3.1" } }, + "node_modules/@sindresorhus/is": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.0.2.tgz", + "integrity": "sha512-d9xRovfKNz1SKieM0qJdO+PQonjnnIfSNWfHYnBSJ9hkjm0ZPw6HlxscDXYstp3z+7V2GOFHc+J0CYrYTjqCJw==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, "node_modules/@sinonjs/commons": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", @@ -3943,6 +3993,12 @@ "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", "dev": true }, + "node_modules/@speed-highlight/core": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@speed-highlight/core/-/core-1.2.7.tgz", + "integrity": "sha512-0dxmVj4gxg3Jg879kvFS/msl4s9F3T9UXC1InxgOf7t5NvcPD97u/WTA5vL/IxWHMn7qSxBozqrnnE2wvl1m8g==", + "dev": true + }, "node_modules/@svta/common-media-library": { "version": "0.15.1", "resolved": "https://registry.npmjs.org/@svta/common-media-library/-/common-media-library-0.15.1.tgz", @@ -4912,16 +4968,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/as-table": { - "version": "1.0.55", - "resolved": "https://registry.npmjs.org/as-table/-/as-table-1.0.55.tgz", - "integrity": "sha512-xvsWESUJn0JN421Xb9MQw6AsMHRCUknCe0Wjlxvjud80mU4E6hQf1A6NzQKcYNmYw62MfzEtXc+badstZP3JpQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "printable-characters": "^1.0.42" - } - }, "node_modules/assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", @@ -6342,6 +6388,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/error-stack-parser-es": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz", + "integrity": "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/es-abstract": { "version": "1.24.0", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", @@ -7817,24 +7872,6 @@ "node": ">= 0.4" } }, - "node_modules/get-source": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/get-source/-/get-source-2.0.12.tgz", - "integrity": "sha512-X5+4+iD+HoSeEED+uwrQ07BOQr0kEDFMVqqpBuI+RaZBpBpHCuXxo70bjar6f0b0u/DQJsJ7ssurpP0V60Az+w==", - "dev": true, - "license": "Unlicense", - "dependencies": { - "data-uri-to-buffer": "^2.0.0", - "source-map": "^0.6.1" - } - }, - "node_modules/get-source/node_modules/data-uri-to-buffer": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-2.0.2.tgz", - "integrity": "sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA==", - "dev": true, - "license": "MIT" - }, "node_modules/get-stream": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", @@ -9480,6 +9517,15 @@ "node": ">=0.10.0" } }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/kuler": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", @@ -10387,9 +10433,9 @@ } }, "node_modules/miniflare": { - "version": "4.20250617.5", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20250617.5.tgz", - "integrity": "sha512-Qqn30jR6dCjXaKVizT6vH4KOb+GyLccoxLNOJEfu63yBPn8eoXa7PrdiSGTmjs2RY8/tr7eTO8Wu/Yr14k0xVA==", + "version": "4.20250709.0", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20250709.0.tgz", + "integrity": "sha512-dRGXi6Do9ArQZt7205QGWZ1tD6k6xQNY/mAZBAtiaQYvKxFuNyiHYlFnSN8Co4AFCVOozo/U52sVAaHvlcmnew==", "dev": true, "dependencies": { "@cspotcode/source-map-support": "0.8.1", @@ -10400,9 +10446,9 @@ "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "^5.28.5", - "workerd": "1.20250617.0", + "workerd": "1.20250709.0", "ws": "8.18.0", - "youch": "3.3.4", + "youch": "4.1.0-beta.10", "zod": "3.22.3" }, "bin": { @@ -10772,16 +10818,6 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "node_modules/mustache": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", - "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", - "dev": true, - "license": "MIT", - "bin": { - "mustache": "bin/mustache" - } - }, "node_modules/nano-spawn": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nano-spawn/-/nano-spawn-1.0.2.tgz", @@ -11669,13 +11705,6 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/printable-characters": { - "version": "1.0.42", - "resolved": "https://registry.npmjs.org/printable-characters/-/printable-characters-1.0.42.tgz", - "integrity": "sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==", - "dev": true, - "license": "Unlicense" - }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -12951,17 +12980,6 @@ "node": "*" } }, - "node_modules/stacktracey": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/stacktracey/-/stacktracey-2.1.8.tgz", - "integrity": "sha512-Kpij9riA+UNg7TnphqjH7/CzctQ/owJGNbFkfEeve4Z4uxT5+JapVLFXcsurIfN34gnTWZNJ/f7NMG0E8JDzTw==", - "dev": true, - "license": "Unlicense", - "dependencies": { - "as-table": "^1.0.36", - "get-source": "^2.0.12" - } - }, "node_modules/statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", @@ -14384,9 +14402,9 @@ "dev": true }, "node_modules/workerd": { - "version": "1.20250617.0", - "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20250617.0.tgz", - "integrity": "sha512-Uv6p0PYUHp/W/aWfUPLkZVAoAjapisM27JJlwcX9wCPTfCfnuegGOxFMvvlYpmNaX4YCwEdLCwuNn3xkpSkuZw==", + "version": "1.20250709.0", + "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20250709.0.tgz", + "integrity": "sha512-BqLPpmvRN+TYUSG61OkWamsGdEuMwgvabP8m0QOHIfofnrD2YVyWqE1kXJ0GH5EsVEuWamE5sR8XpTfsGBmIpg==", "dev": true, "hasInstallScript": true, "bin": { @@ -14396,11 +14414,11 @@ "node": ">=16" }, "optionalDependencies": { - "@cloudflare/workerd-darwin-64": "1.20250617.0", - "@cloudflare/workerd-darwin-arm64": "1.20250617.0", - "@cloudflare/workerd-linux-64": "1.20250617.0", - "@cloudflare/workerd-linux-arm64": "1.20250617.0", - "@cloudflare/workerd-windows-64": "1.20250617.0" + "@cloudflare/workerd-darwin-64": "1.20250709.0", + "@cloudflare/workerd-darwin-arm64": "1.20250709.0", + "@cloudflare/workerd-linux-64": "1.20250709.0", + "@cloudflare/workerd-linux-arm64": "1.20250709.0", + "@cloudflare/workerd-windows-64": "1.20250709.0" } }, "node_modules/workerpool": { @@ -14410,19 +14428,19 @@ "dev": true }, "node_modules/wrangler": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.23.0.tgz", - "integrity": "sha512-JSeDt3IwA4TEmg/V3tRblImPjdxynBt9PUVO/acQJ83XGlMMSwswDKL1FuwvbFzgX6+JXc3GMHeu7r8AQIxw9w==", + "version": "4.24.1", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.24.1.tgz", + "integrity": "sha512-n7d5BQyOQU7WyEYMC3zNarWzsqWttsusP6qWz4rbbydmnWKSP1wLtsv0yKiyz/LYFumpolz48d5phHp04nR6uw==", "dev": true, "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", "@cloudflare/unenv-preset": "2.3.3", "blake3-wasm": "2.1.5", "esbuild": "0.25.4", - "miniflare": "4.20250617.5", + "miniflare": "4.20250709.0", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.17", - "workerd": "1.20250617.0" + "workerd": "1.20250709.0" }, "bin": { "wrangler": "bin/wrangler.js", @@ -14435,7 +14453,7 @@ "fsevents": "~2.3.2" }, "peerDependencies": { - "@cloudflare/workers-types": "^4.20250617.0" + "@cloudflare/workers-types": "^4.20250709.0" }, "peerDependenciesMeta": { "@cloudflare/workers-types": { @@ -14780,25 +14798,35 @@ } }, "node_modules/youch": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/youch/-/youch-3.3.4.tgz", - "integrity": "sha512-UeVBXie8cA35DS6+nBkls68xaBBXCye0CNznrhszZjTbRVnJKQuNsyLKBTTL4ln1o1rh2PKtv35twV7irj5SEg==", + "version": "4.1.0-beta.10", + "resolved": "https://registry.npmjs.org/youch/-/youch-4.1.0-beta.10.tgz", + "integrity": "sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==", "dev": true, - "license": "MIT", "dependencies": { - "cookie": "^0.7.1", - "mustache": "^4.2.0", - "stacktracey": "^2.1.8" + "@poppinss/colors": "^4.1.5", + "@poppinss/dumper": "^0.6.4", + "@speed-highlight/core": "^1.2.7", + "cookie": "^1.0.2", + "youch-core": "^0.3.3" + } + }, + "node_modules/youch-core": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/youch-core/-/youch-core-0.3.3.tgz", + "integrity": "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==", + "dev": true, + "dependencies": { + "@poppinss/exception": "^1.2.2", + "error-stack-parser-es": "^1.0.5" } }, "node_modules/youch/node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", "dev": true, - "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">=18" } }, "node_modules/zod": { @@ -16026,37 +16054,37 @@ "requires": {} }, "@cloudflare/workerd-darwin-64": { - "version": "1.20250617.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20250617.0.tgz", - "integrity": "sha512-toG8JUKVLIks4oOJLe9FeuixE84pDpMZ32ip7mCpE7JaFc5BqGFvevk0YC/db3T71AQlialjRwioH3jS/dzItA==", + "version": "1.20250709.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20250709.0.tgz", + "integrity": "sha512-VqwcvnbI8FNCP87ZWNHA3/sAC5U9wMbNnjBG0sHEYzM7B9RPHKYHdVKdBEWhzZXnkQYMK81IHm4CZsK16XxAuQ==", "dev": true, "optional": true }, "@cloudflare/workerd-darwin-arm64": { - "version": "1.20250617.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20250617.0.tgz", - "integrity": "sha512-JTX0exbC9/ZtMmQQA8tDZEZFMXZrxOpTUj2hHnsUkErWYkr5SSZH04RBhPg6dU4VL8bXuB5/eJAh7+P9cZAp7g==", + "version": "1.20250709.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20250709.0.tgz", + "integrity": "sha512-A54ttSgXMM4huChPTThhkieOjpDxR+srVOO9zjTHVIyoQxA8zVsku4CcY/GQ95RczMV+yCKVVu/tAME7vwBFuA==", "dev": true, "optional": true }, "@cloudflare/workerd-linux-64": { - "version": "1.20250617.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20250617.0.tgz", - "integrity": "sha512-8jkSoVRJ+1bOx3tuWlZCGaGCV2ew7/jFMl6V3CPXOoEtERUHsZBQLVkQIGKcmC/LKSj7f/mpyBUeu2EPTo2HEg==", + "version": "1.20250709.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20250709.0.tgz", + "integrity": "sha512-no4O3OK+VXINIxv99OHJDpIgML2ZssrSvImwLtULzqm+cl4t1PIfXNRUqj89ujTkmad+L9y4G6dBQMPCLnmlGg==", "dev": true, "optional": true }, "@cloudflare/workerd-linux-arm64": { - "version": "1.20250617.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20250617.0.tgz", - "integrity": "sha512-YAzcOyu897z5dQKFzme1oujGWMGEJCR7/Wrrm1nSP6dqutxFPTubRADM8BHn2CV3ij//vaPnAeLmZE3jVwOwig==", + "version": "1.20250709.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20250709.0.tgz", + "integrity": "sha512-7cNICk2Qd+m4QGrcmWyAuZJXTHt1ud6isA+dic7Yk42WZmwXhlcUATyvFD9FSQNFcldjuRB4n8JlWEFqZBn+lw==", "dev": true, "optional": true }, "@cloudflare/workerd-windows-64": { - "version": "1.20250617.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20250617.0.tgz", - "integrity": "sha512-XWM/6sagDrO0CYDKhXhPjM23qusvIN1ju9ZEml6gOQs8tNOFnq6Cn6X9FAmnyapRFCGUSEC3HZYJAm7zwVKaMA==", + "version": "1.20250709.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20250709.0.tgz", + "integrity": "sha512-j1AyO8V/62Q23EJplWgzBlRCqo/diXgox58AbDqSqgyzCBAlvUzXQRDBab/FPNG/erRqt7I1zQhahrBhrM0uLA==", "dev": true, "optional": true }, @@ -16809,6 +16837,40 @@ "dev": true, "optional": true }, + "@poppinss/colors": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@poppinss/colors/-/colors-4.1.5.tgz", + "integrity": "sha512-FvdDqtcRCtz6hThExcFOgW0cWX+xwSMWcRuQe5ZEb2m7cVQOAVZOIMt+/v9RxGiD9/OY16qJBXK4CVKWAPalBw==", + "dev": true, + "requires": { + "kleur": "^4.1.5" + } + }, + "@poppinss/dumper": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@poppinss/dumper/-/dumper-0.6.4.tgz", + "integrity": "sha512-iG0TIdqv8xJ3Lt9O8DrPRxw1MRLjNpoqiSGU03P/wNLP/s0ra0udPJ1J2Tx5M0J3H/cVyEgpbn8xUKRY9j59kQ==", + "dev": true, + "requires": { + "@poppinss/colors": "^4.1.5", + "@sindresorhus/is": "^7.0.2", + "supports-color": "^10.0.0" + }, + "dependencies": { + "supports-color": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.0.0.tgz", + "integrity": "sha512-HRVVSbCCMbj7/kdWF9Q+bbckjBHLtHMEoJWlkmYzzdwhYMkjkOwubLM6t7NbWKjgKamGDrWL1++KrjUO1t9oAQ==", + "dev": true + } + } + }, + "@poppinss/exception": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@poppinss/exception/-/exception-1.2.2.tgz", + "integrity": "sha512-m7bpKCD4QMlFCjA/nKTs23fuvoVFoA83brRKmObCUNmi/9tVu8Ve3w4YQAnJu4q3Tjf5fr685HYIC/IA2zHRSg==", + "dev": true + }, "@rollup/plugin-alias": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-alias/-/plugin-alias-5.1.1.tgz", @@ -17166,6 +17228,12 @@ "string-argv": "~0.3.1" } }, + "@sindresorhus/is": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.0.2.tgz", + "integrity": "sha512-d9xRovfKNz1SKieM0qJdO+PQonjnnIfSNWfHYnBSJ9hkjm0ZPw6HlxscDXYstp3z+7V2GOFHc+J0CYrYTjqCJw==", + "dev": true + }, "@sinonjs/commons": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", @@ -17215,6 +17283,12 @@ "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", "dev": true }, + "@speed-highlight/core": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@speed-highlight/core/-/core-1.2.7.tgz", + "integrity": "sha512-0dxmVj4gxg3Jg879kvFS/msl4s9F3T9UXC1InxgOf7t5NvcPD97u/WTA5vL/IxWHMn7qSxBozqrnnE2wvl1m8g==", + "dev": true + }, "@svta/common-media-library": { "version": "0.15.1", "resolved": "https://registry.npmjs.org/@svta/common-media-library/-/common-media-library-0.15.1.tgz", @@ -17984,15 +18058,6 @@ "is-array-buffer": "^3.0.4" } }, - "as-table": { - "version": "1.0.55", - "resolved": "https://registry.npmjs.org/as-table/-/as-table-1.0.55.tgz", - "integrity": "sha512-xvsWESUJn0JN421Xb9MQw6AsMHRCUknCe0Wjlxvjud80mU4E6hQf1A6NzQKcYNmYw62MfzEtXc+badstZP3JpQ==", - "dev": true, - "requires": { - "printable-characters": "^1.0.42" - } - }, "assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", @@ -19065,6 +19130,12 @@ "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", "dev": true }, + "error-stack-parser-es": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz", + "integrity": "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==", + "dev": true + }, "es-abstract": { "version": "1.24.0", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", @@ -20135,24 +20206,6 @@ "es-object-atoms": "^1.0.0" } }, - "get-source": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/get-source/-/get-source-2.0.12.tgz", - "integrity": "sha512-X5+4+iD+HoSeEED+uwrQ07BOQr0kEDFMVqqpBuI+RaZBpBpHCuXxo70bjar6f0b0u/DQJsJ7ssurpP0V60Az+w==", - "dev": true, - "requires": { - "data-uri-to-buffer": "^2.0.0", - "source-map": "^0.6.1" - }, - "dependencies": { - "data-uri-to-buffer": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-2.0.2.tgz", - "integrity": "sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA==", - "dev": true - } - } - }, "get-stream": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", @@ -21328,6 +21381,12 @@ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true }, + "kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true + }, "kuler": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", @@ -21974,9 +22033,9 @@ "dev": true }, "miniflare": { - "version": "4.20250617.5", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20250617.5.tgz", - "integrity": "sha512-Qqn30jR6dCjXaKVizT6vH4KOb+GyLccoxLNOJEfu63yBPn8eoXa7PrdiSGTmjs2RY8/tr7eTO8Wu/Yr14k0xVA==", + "version": "4.20250709.0", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20250709.0.tgz", + "integrity": "sha512-dRGXi6Do9ArQZt7205QGWZ1tD6k6xQNY/mAZBAtiaQYvKxFuNyiHYlFnSN8Co4AFCVOozo/U52sVAaHvlcmnew==", "dev": true, "requires": { "@cspotcode/source-map-support": "0.8.1", @@ -21987,9 +22046,9 @@ "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "^5.28.5", - "workerd": "1.20250617.0", + "workerd": "1.20250709.0", "ws": "8.18.0", - "youch": "3.3.4", + "youch": "4.1.0-beta.10", "zod": "3.22.3" } }, @@ -22259,12 +22318,6 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "mustache": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", - "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", - "dev": true - }, "nano-spawn": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nano-spawn/-/nano-spawn-1.0.2.tgz", @@ -22931,12 +22984,6 @@ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true }, - "printable-characters": { - "version": "1.0.42", - "resolved": "https://registry.npmjs.org/printable-characters/-/printable-characters-1.0.42.tgz", - "integrity": "sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==", - "dev": true - }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -23880,16 +23927,6 @@ "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", "dev": true }, - "stacktracey": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/stacktracey/-/stacktracey-2.1.8.tgz", - "integrity": "sha512-Kpij9riA+UNg7TnphqjH7/CzctQ/owJGNbFkfEeve4Z4uxT5+JapVLFXcsurIfN34gnTWZNJ/f7NMG0E8JDzTw==", - "dev": true, - "requires": { - "as-table": "^1.0.36", - "get-source": "^2.0.12" - } - }, "statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", @@ -24925,16 +24962,16 @@ "dev": true }, "workerd": { - "version": "1.20250617.0", - "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20250617.0.tgz", - "integrity": "sha512-Uv6p0PYUHp/W/aWfUPLkZVAoAjapisM27JJlwcX9wCPTfCfnuegGOxFMvvlYpmNaX4YCwEdLCwuNn3xkpSkuZw==", + "version": "1.20250709.0", + "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20250709.0.tgz", + "integrity": "sha512-BqLPpmvRN+TYUSG61OkWamsGdEuMwgvabP8m0QOHIfofnrD2YVyWqE1kXJ0GH5EsVEuWamE5sR8XpTfsGBmIpg==", "dev": true, "requires": { - "@cloudflare/workerd-darwin-64": "1.20250617.0", - "@cloudflare/workerd-darwin-arm64": "1.20250617.0", - "@cloudflare/workerd-linux-64": "1.20250617.0", - "@cloudflare/workerd-linux-arm64": "1.20250617.0", - "@cloudflare/workerd-windows-64": "1.20250617.0" + "@cloudflare/workerd-darwin-64": "1.20250709.0", + "@cloudflare/workerd-darwin-arm64": "1.20250709.0", + "@cloudflare/workerd-linux-64": "1.20250709.0", + "@cloudflare/workerd-linux-arm64": "1.20250709.0", + "@cloudflare/workerd-windows-64": "1.20250709.0" } }, "workerpool": { @@ -24944,9 +24981,9 @@ "dev": true }, "wrangler": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.23.0.tgz", - "integrity": "sha512-JSeDt3IwA4TEmg/V3tRblImPjdxynBt9PUVO/acQJ83XGlMMSwswDKL1FuwvbFzgX6+JXc3GMHeu7r8AQIxw9w==", + "version": "4.24.1", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.24.1.tgz", + "integrity": "sha512-n7d5BQyOQU7WyEYMC3zNarWzsqWttsusP6qWz4rbbydmnWKSP1wLtsv0yKiyz/LYFumpolz48d5phHp04nR6uw==", "dev": true, "requires": { "@cloudflare/kv-asset-handler": "0.4.0", @@ -24954,10 +24991,10 @@ "blake3-wasm": "2.1.5", "esbuild": "0.25.4", "fsevents": "~2.3.2", - "miniflare": "4.20250617.5", + "miniflare": "4.20250709.0", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.17", - "workerd": "1.20250617.0" + "workerd": "1.20250709.0" } }, "wrap-ansi": { @@ -25200,24 +25237,36 @@ "dev": true }, "youch": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/youch/-/youch-3.3.4.tgz", - "integrity": "sha512-UeVBXie8cA35DS6+nBkls68xaBBXCye0CNznrhszZjTbRVnJKQuNsyLKBTTL4ln1o1rh2PKtv35twV7irj5SEg==", + "version": "4.1.0-beta.10", + "resolved": "https://registry.npmjs.org/youch/-/youch-4.1.0-beta.10.tgz", + "integrity": "sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==", "dev": true, "requires": { - "cookie": "^0.7.1", - "mustache": "^4.2.0", - "stacktracey": "^2.1.8" + "@poppinss/colors": "^4.1.5", + "@poppinss/dumper": "^0.6.4", + "@speed-highlight/core": "^1.2.7", + "cookie": "^1.0.2", + "youch-core": "^0.3.3" }, "dependencies": { "cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", "dev": true } } }, + "youch-core": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/youch-core/-/youch-core-0.3.3.tgz", + "integrity": "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==", + "dev": true, + "requires": { + "@poppinss/exception": "^1.2.2", + "error-stack-parser-es": "^1.0.5" + } + }, "zod": { "version": "3.22.3", "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.3.tgz", diff --git a/package.json b/package.json index f8ace915d0f..ffdb228acde 100644 --- a/package.json +++ b/package.json @@ -133,6 +133,6 @@ "sinon-chai": "3.7.0", "typescript": "5.8.3", "url-toolkit": "2.2.5", - "wrangler": "4.23.0" + "wrangler": "4.24.1" } } From 032531cabfb5371aef3188ca214b11aea2f0314d Mon Sep 17 00:00:00 2001 From: Rob Walch Date: Thu, 10 Jul 2025 08:49:23 -0700 Subject: [PATCH 08/52] Replace deprecated babel plugins and update renovate config with ignore rules --- build-config.js | 6 +-- package-lock.json | 134 ++-------------------------------------------- package.json | 6 +-- renovate.json | 5 ++ 4 files changed, 14 insertions(+), 137 deletions(-) diff --git a/build-config.js b/build-config.js index 78dbc2464e5..755e6baea58 100644 --- a/build-config.js +++ b/build-config.js @@ -136,12 +136,12 @@ const babelTsWithPresetEnvTargets = ({ targets, stripConsole }) => ], plugins: [ [ - '@babel/plugin-proposal-class-properties', + '@babel/plugin-transform-class-properties', { loose: true, }, ], - '@babel/plugin-proposal-object-rest-spread', + '@babel/plugin-transform-object-rest-spread', { visitor: { CallExpression: function (espath) { @@ -172,7 +172,7 @@ const babelTsWithPresetEnvTargets = ({ targets, stripConsole }) => }, }, ['@babel/plugin-transform-object-assign'], - ['@babel/plugin-proposal-optional-chaining'], + ['@babel/plugin-transform-optional-chaining'], ...(stripConsole ? [ diff --git a/package-lock.json b/package-lock.json index 685d4b93904..02ab05ea549 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,10 +9,10 @@ "devDependencies": { "@babel/core": "7.28.0", "@babel/helper-module-imports": "7.27.1", - "@babel/plugin-proposal-class-properties": "7.18.6", - "@babel/plugin-proposal-object-rest-spread": "7.20.7", - "@babel/plugin-proposal-optional-chaining": "7.21.0", + "@babel/plugin-transform-class-properties": "7.27.1", "@babel/plugin-transform-object-assign": "7.27.1", + "@babel/plugin-transform-object-rest-spread": "7.28.0", + "@babel/plugin-transform-optional-chaining": "7.27.1", "@babel/preset-env": "7.28.0", "@babel/preset-typescript": "7.27.1", "@babel/register": "7.27.1", @@ -603,58 +603,6 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-proposal-class-properties": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", - "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", - "dev": true, - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-object-rest-spread": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", - "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.20.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-optional-chaining": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", - "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-proposal-private-property-in-object": { "version": "7.21.0-placeholder-for-preset-env.2", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", @@ -712,30 +660,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-typescript": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", @@ -15238,40 +15162,6 @@ "@babel/traverse": "^7.27.1" } }, - "@babel/plugin-proposal-class-properties": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", - "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-proposal-object-rest-spread": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", - "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.20.7" - } - }, - "@babel/plugin-proposal-optional-chaining": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", - "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - } - }, "@babel/plugin-proposal-private-property-in-object": { "version": "7.21.0-placeholder-for-preset-env.2", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", @@ -15306,24 +15196,6 @@ "@babel/helper-plugin-utils": "^7.27.1" } }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, "@babel/plugin-syntax-typescript": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", diff --git a/package.json b/package.json index ffdb228acde..3517ca5eb49 100644 --- a/package.json +++ b/package.json @@ -67,10 +67,10 @@ "devDependencies": { "@babel/core": "7.28.0", "@babel/helper-module-imports": "7.27.1", - "@babel/plugin-proposal-class-properties": "7.18.6", - "@babel/plugin-proposal-object-rest-spread": "7.20.7", - "@babel/plugin-proposal-optional-chaining": "7.21.0", + "@babel/plugin-transform-class-properties": "7.27.1", "@babel/plugin-transform-object-assign": "7.27.1", + "@babel/plugin-transform-object-rest-spread": "7.28.0", + "@babel/plugin-transform-optional-chaining": "7.27.1", "@babel/preset-env": "7.28.0", "@babel/preset-typescript": "7.27.1", "@babel/register": "7.27.1", diff --git a/renovate.json b/renovate.json index 82b98f68735..f8121865a5c 100644 --- a/renovate.json +++ b/renovate.json @@ -12,7 +12,12 @@ "major": { "addLabels": ["semver-major"] }, + "ignoreDeps": ["FileSaver.js", "@types/chart.js"], "packageRules": [ + { + "matchDatasources": ["html"], + "enabled": false + }, { "rangeStrategy": "bump", "matchPackageNames": ["*"] From a705e621e7edf205243bf948b6eb6a603b69950f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 10 Jul 2025 16:32:29 +0000 Subject: [PATCH 09/52] chore(deps): update dependency chromedriver to v138.0.2 --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 02ab05ea549..62edfcf9b7d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,7 +36,7 @@ "babel-plugin-transform-remove-console": "6.9.4", "chai": "4.5.0", "chart.js": "2.9.4", - "chromedriver": "138.0.1", + "chromedriver": "138.0.2", "doctoc": "2.2.1", "es-check": "9.1.4", "eslint": "8.57.1", @@ -5479,9 +5479,9 @@ } }, "node_modules/chromedriver": { - "version": "138.0.1", - "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-138.0.1.tgz", - "integrity": "sha512-QS/Z1qB2OpKsPUlJjkfKmpf9lGw6ObB0dX5+dP3M0gdtbu80TUnS+EjXbtu6YkbMfr2/Qt8IfOONrawNW1GwhA==", + "version": "138.0.2", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-138.0.2.tgz", + "integrity": "sha512-mmAtTCK0GHum+zbgcv3PYDX7tqNdTXDsd2t6WWI/Tm/Ts2xCGlc5XUcL3oxw1Dn/kmPJOurwrYJKO7vV4xkisA==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -18364,9 +18364,9 @@ "peer": true }, "chromedriver": { - "version": "138.0.1", - "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-138.0.1.tgz", - "integrity": "sha512-QS/Z1qB2OpKsPUlJjkfKmpf9lGw6ObB0dX5+dP3M0gdtbu80TUnS+EjXbtu6YkbMfr2/Qt8IfOONrawNW1GwhA==", + "version": "138.0.2", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-138.0.2.tgz", + "integrity": "sha512-mmAtTCK0GHum+zbgcv3PYDX7tqNdTXDsd2t6WWI/Tm/Ts2xCGlc5XUcL3oxw1Dn/kmPJOurwrYJKO7vV4xkisA==", "dev": true, "requires": { "@testim/chrome-version": "^1.1.4", diff --git a/package.json b/package.json index 3517ca5eb49..928458d587b 100644 --- a/package.json +++ b/package.json @@ -94,7 +94,7 @@ "babel-plugin-transform-remove-console": "6.9.4", "chai": "4.5.0", "chart.js": "2.9.4", - "chromedriver": "138.0.1", + "chromedriver": "138.0.2", "doctoc": "2.2.1", "es-check": "9.1.4", "eslint": "8.57.1", From f9a4938e7071075864a6a875d543201f9a10e601 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 10 Jul 2025 22:53:33 +0000 Subject: [PATCH 10/52] chore(deps): update dependency eslint-plugin-n to v17.21.0 --- package-lock.json | 52 +++++++++++++++++++++++------------------------ package.json | 2 +- 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/package-lock.json b/package-lock.json index 62edfcf9b7d..7334635891e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,7 +43,7 @@ "eslint-config-prettier": "10.1.5", "eslint-plugin-import": "2.32.0", "eslint-plugin-mocha": "10.5.0", - "eslint-plugin-n": "17.20.0", + "eslint-plugin-n": "17.21.0", "eslint-plugin-no-for-of-loops": "1.0.1", "eslint-plugin-promise": "7.2.1", "eventemitter3": "5.0.1", @@ -6893,13 +6893,12 @@ } }, "node_modules/eslint-plugin-n": { - "version": "17.20.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-17.20.0.tgz", - "integrity": "sha512-IRSoatgB/NQJZG5EeTbv/iAx1byOGdbbyhQrNvWdCfTnmPxUT0ao9/eGOeG7ljD8wJBsxwE8f6tES5Db0FRKEw==", + "version": "17.21.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-17.21.0.tgz", + "integrity": "sha512-1+iZ8We4ZlwVMtb/DcHG3y5/bZOdazIpa/4TySo22MLKdwrLcfrX0hbadnCvykSQCCmkAnWmIP8jZVb2AAq29A==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.5.0", - "@typescript-eslint/utils": "^8.26.1", "enhanced-resolve": "^5.17.1", "eslint-plugin-es-x": "^7.8.0", "get-tsconfig": "^4.8.1", @@ -6928,18 +6927,6 @@ "balanced-match": "^1.0.0" } }, - "node_modules/eslint-plugin-n/node_modules/globals": { - "version": "15.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", - "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", - "dev": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/eslint-plugin-n/node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -7908,6 +7895,18 @@ "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", "dev": true }, + "node_modules/globals": { + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/globalthis": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", @@ -19523,13 +19522,12 @@ } }, "eslint-plugin-n": { - "version": "17.20.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-17.20.0.tgz", - "integrity": "sha512-IRSoatgB/NQJZG5EeTbv/iAx1byOGdbbyhQrNvWdCfTnmPxUT0ao9/eGOeG7ljD8wJBsxwE8f6tES5Db0FRKEw==", + "version": "17.21.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-17.21.0.tgz", + "integrity": "sha512-1+iZ8We4ZlwVMtb/DcHG3y5/bZOdazIpa/4TySo22MLKdwrLcfrX0hbadnCvykSQCCmkAnWmIP8jZVb2AAq29A==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.5.0", - "@typescript-eslint/utils": "^8.26.1", "enhanced-resolve": "^5.17.1", "eslint-plugin-es-x": "^7.8.0", "get-tsconfig": "^4.8.1", @@ -19549,12 +19547,6 @@ "balanced-match": "^1.0.0" } }, - "globals": { - "version": "15.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", - "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", - "dev": true - }, "minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -20162,6 +20154,12 @@ "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", "dev": true }, + "globals": { + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", + "dev": true + }, "globalthis": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", diff --git a/package.json b/package.json index 928458d587b..4d4e15f5299 100644 --- a/package.json +++ b/package.json @@ -101,7 +101,7 @@ "eslint-config-prettier": "10.1.5", "eslint-plugin-import": "2.32.0", "eslint-plugin-mocha": "10.5.0", - "eslint-plugin-n": "17.20.0", + "eslint-plugin-n": "17.21.0", "eslint-plugin-no-for-of-loops": "1.0.1", "eslint-plugin-promise": "7.2.1", "eventemitter3": "5.0.1", From 853b3f552fa6de33bce83e209db4f2fc2390b997 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 11 Jul 2025 16:10:30 +0000 Subject: [PATCH 11/52] chore(deps): update dependency rollup to v4.44.2 --- package-lock.json | 338 +++++++++++++++++++++++----------------------- package.json | 2 +- 2 files changed, 170 insertions(+), 170 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7334635891e..96f19598948 100644 --- a/package-lock.json +++ b/package-lock.json @@ -66,7 +66,7 @@ "npm-run-all2": "8.0.4", "prettier": "3.6.2", "promise-polyfill": "8.3.0", - "rollup": "4.44.1", + "rollup": "4.44.2", "rollup-plugin-istanbul": "5.0.0", "sauce-connect-launcher": "1.3.2", "selenium-webdriver": "4.34.0", @@ -3451,9 +3451,9 @@ "dev": true }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.1.tgz", - "integrity": "sha512-JAcBr1+fgqx20m7Fwe1DxPUl/hPkee6jA6Pl7n1v2EFiktAHenTaXl5aIFjUIEsfn9w3HE4gK1lEgNGMzBDs1w==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.2.tgz", + "integrity": "sha512-g0dF8P1e2QYPOj1gu7s/3LVP6kze9A7m6x0BZ9iTdXK8N5c2V7cpBKHV3/9A4Zd8xxavdhK0t4PnqjkqVmUc9Q==", "cpu": [ "arm" ], @@ -3464,9 +3464,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.1.tgz", - "integrity": "sha512-RurZetXqTu4p+G0ChbnkwBuAtwAbIwJkycw1n6GvlGlBuS4u5qlr5opix8cBAYFJgaY05TWtM+LaoFggUmbZEQ==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.2.tgz", + "integrity": "sha512-Yt5MKrOosSbSaAK5Y4J+vSiID57sOvpBNBR6K7xAaQvk3MkcNVV0f9fE20T+41WYN8hDn6SGFlFrKudtx4EoxA==", "cpu": [ "arm64" ], @@ -3477,9 +3477,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.1.tgz", - "integrity": "sha512-fM/xPesi7g2M7chk37LOnmnSTHLG/v2ggWqKj3CCA1rMA4mm5KVBT1fNoswbo1JhPuNNZrVwpTvlCVggv8A2zg==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.2.tgz", + "integrity": "sha512-EsnFot9ZieM35YNA26nhbLTJBHD0jTwWpPwmRVDzjylQT6gkar+zenfb8mHxWpRrbn+WytRRjE0WKsfaxBkVUA==", "cpu": [ "arm64" ], @@ -3490,9 +3490,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.1.tgz", - "integrity": "sha512-gDnWk57urJrkrHQ2WVx9TSVTH7lSlU7E3AFqiko+bgjlh78aJ88/3nycMax52VIVjIm3ObXnDL2H00e/xzoipw==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.2.tgz", + "integrity": "sha512-dv/t1t1RkCvJdWWxQ2lWOO+b7cMsVw5YFaS04oHpZRWehI1h0fV1gF4wgGCTyQHHjJDfbNpwOi6PXEafRBBezw==", "cpu": [ "x64" ], @@ -3503,9 +3503,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.1.tgz", - "integrity": "sha512-wnFQmJ/zPThM5zEGcnDcCJeYJgtSLjh1d//WuHzhf6zT3Md1BvvhJnWoy+HECKu2bMxaIcfWiu3bJgx6z4g2XA==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.2.tgz", + "integrity": "sha512-W4tt4BLorKND4qeHElxDoim0+BsprFTwb+vriVQnFFtT/P6v/xO5I99xvYnVzKWrK6j7Hb0yp3x7V5LUbaeOMg==", "cpu": [ "arm64" ], @@ -3516,9 +3516,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.1.tgz", - "integrity": "sha512-uBmIxoJ4493YATvU2c0upGz87f99e3wop7TJgOA/bXMFd2SvKCI7xkxY/5k50bv7J6dw1SXT4MQBQSLn8Bb/Uw==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.2.tgz", + "integrity": "sha512-tdT1PHopokkuBVyHjvYehnIe20fxibxFCEhQP/96MDSOcyjM/shlTkZZLOufV3qO6/FQOSiJTBebhVc12JyPTA==", "cpu": [ "x64" ], @@ -3529,9 +3529,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.1.tgz", - "integrity": "sha512-n0edDmSHlXFhrlmTK7XBuwKlG5MbS7yleS1cQ9nn4kIeW+dJH+ExqNgQ0RrFRew8Y+0V/x6C5IjsHrJmiHtkxQ==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.2.tgz", + "integrity": "sha512-+xmiDGGaSfIIOXMzkhJ++Oa0Gwvl9oXUeIiwarsdRXSe27HUIvjbSIpPxvnNsRebsNdUo7uAiQVgBD1hVriwSQ==", "cpu": [ "arm" ], @@ -3542,9 +3542,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.1.tgz", - "integrity": "sha512-8WVUPy3FtAsKSpyk21kV52HCxB+me6YkbkFHATzC2Yd3yuqHwy2lbFL4alJOLXKljoRw08Zk8/xEj89cLQ/4Nw==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.2.tgz", + "integrity": "sha512-bDHvhzOfORk3wt8yxIra8N4k/N0MnKInCW5OGZaeDYa/hMrdPaJzo7CSkjKZqX4JFUWjUGm88lI6QJLCM7lDrA==", "cpu": [ "arm" ], @@ -3555,9 +3555,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.1.tgz", - "integrity": "sha512-yuktAOaeOgorWDeFJggjuCkMGeITfqvPgkIXhDqsfKX8J3jGyxdDZgBV/2kj/2DyPaLiX6bPdjJDTu9RB8lUPQ==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.2.tgz", + "integrity": "sha512-NMsDEsDiYghTbeZWEGnNi4F0hSbGnsuOG+VnNvxkKg0IGDvFh7UVpM/14mnMwxRxUf9AdAVJgHPvKXf6FpMB7A==", "cpu": [ "arm64" ], @@ -3568,9 +3568,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.1.tgz", - "integrity": "sha512-W+GBM4ifET1Plw8pdVaecwUgxmiH23CfAUj32u8knq0JPFyK4weRy6H7ooxYFD19YxBulL0Ktsflg5XS7+7u9g==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.2.tgz", + "integrity": "sha512-lb5bxXnxXglVq+7imxykIp5xMq+idehfl+wOgiiix0191av84OqbjUED+PRC5OA8eFJYj5xAGcpAZ0pF2MnW+A==", "cpu": [ "arm64" ], @@ -3581,9 +3581,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.1.tgz", - "integrity": "sha512-1zqnUEMWp9WrGVuVak6jWTl4fEtrVKfZY7CvcBmUUpxAJ7WcSowPSAWIKa/0o5mBL/Ij50SIf9tuirGx63Ovew==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.2.tgz", + "integrity": "sha512-Yl5Rdpf9pIc4GW1PmkUGHdMtbx0fBLE1//SxDmuf3X0dUC57+zMepow2LK0V21661cjXdTn8hO2tXDdAWAqE5g==", "cpu": [ "loong64" ], @@ -3594,9 +3594,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.1.tgz", - "integrity": "sha512-Rl3JKaRu0LHIx7ExBAAnf0JcOQetQffaw34T8vLlg9b1IhzcBgaIdnvEbbsZq9uZp3uAH+JkHd20Nwn0h9zPjA==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.2.tgz", + "integrity": "sha512-03vUDH+w55s680YYryyr78jsO1RWU9ocRMaeV2vMniJJW/6HhoTBwyyiiTPVHNWLnhsnwcQ0oH3S9JSBEKuyqw==", "cpu": [ "ppc64" ], @@ -3607,9 +3607,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.1.tgz", - "integrity": "sha512-j5akelU3snyL6K3N/iX7otLBIl347fGwmd95U5gS/7z6T4ftK288jKq3A5lcFKcx7wwzb5rgNvAg3ZbV4BqUSw==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.2.tgz", + "integrity": "sha512-iYtAqBg5eEMG4dEfVlkqo05xMOk6y/JXIToRca2bAWuqjrJYJlx/I7+Z+4hSrsWU8GdJDFPL4ktV3dy4yBSrzg==", "cpu": [ "riscv64" ], @@ -3620,9 +3620,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.1.tgz", - "integrity": "sha512-ppn5llVGgrZw7yxbIm8TTvtj1EoPgYUAbfw0uDjIOzzoqlZlZrLJ/KuiE7uf5EpTpCTrNt1EdtzF0naMm0wGYg==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.2.tgz", + "integrity": "sha512-e6vEbgaaqz2yEHqtkPXa28fFuBGmUJ0N2dOJK8YUfijejInt9gfCSA7YDdJ4nYlv67JfP3+PSWFX4IVw/xRIPg==", "cpu": [ "riscv64" ], @@ -3633,9 +3633,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.1.tgz", - "integrity": "sha512-Hu6hEdix0oxtUma99jSP7xbvjkUM/ycke/AQQ4EC5g7jNRLLIwjcNwaUy95ZKBJJwg1ZowsclNnjYqzN4zwkAw==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.2.tgz", + "integrity": "sha512-evFOtkmVdY3udE+0QKrV5wBx7bKI0iHz5yEVx5WqDJkxp9YQefy4Mpx3RajIVcM6o7jxTvVd/qpC1IXUhGc1Mw==", "cpu": [ "s390x" ], @@ -3646,9 +3646,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.1.tgz", - "integrity": "sha512-EtnsrmZGomz9WxK1bR5079zee3+7a+AdFlghyd6VbAjgRJDbTANJ9dcPIPAi76uG05micpEL+gPGmAKYTschQw==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.2.tgz", + "integrity": "sha512-/bXb0bEsWMyEkIsUL2Yt5nFB5naLAwyOWMEviQfQY1x3l5WsLKgvZf66TM7UTfED6erckUVUJQ/jJ1FSpm3pRQ==", "cpu": [ "x64" ], @@ -3659,9 +3659,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.1.tgz", - "integrity": "sha512-iAS4p+J1az6Usn0f8xhgL4PaU878KEtutP4hqw52I4IO6AGoyOkHCxcc4bqufv1tQLdDWFx8lR9YlwxKuv3/3g==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.2.tgz", + "integrity": "sha512-3D3OB1vSSBXmkGEZR27uiMRNiwN08/RVAcBKwhUYPaiZ8bcvdeEwWPvbnXvvXHY+A/7xluzcN+kaiOFNiOZwWg==", "cpu": [ "x64" ], @@ -3672,9 +3672,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.1.tgz", - "integrity": "sha512-NtSJVKcXwcqozOl+FwI41OH3OApDyLk3kqTJgx8+gp6On9ZEt5mYhIsKNPGuaZr3p9T6NWPKGU/03Vw4CNU9qg==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.2.tgz", + "integrity": "sha512-VfU0fsMK+rwdK8mwODqYeM2hDrF2WiHaSmCBrS7gColkQft95/8tphyzv2EupVxn3iE0FI78wzffoULH1G+dkw==", "cpu": [ "arm64" ], @@ -3685,9 +3685,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.1.tgz", - "integrity": "sha512-JYA3qvCOLXSsnTR3oiyGws1Dm0YTuxAAeaYGVlGpUsHqloPcFjPg+X0Fj2qODGLNwQOAcCiQmHub/V007kiH5A==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.2.tgz", + "integrity": "sha512-+qMUrkbUurpE6DVRjiJCNGZBGo9xM4Y0FXU5cjgudWqIBWbcLkjE3XprJUsOFgC6xjBClwVa9k6O3A7K3vxb5Q==", "cpu": [ "ia32" ], @@ -3698,9 +3698,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.1.tgz", - "integrity": "sha512-J8o22LuF0kTe7m+8PvW9wk3/bRq5+mRo5Dqo6+vXb7otCm3TPhYOJqOaQtGU9YMWQSL3krMnoOxMr0+9E6F3Ug==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.2.tgz", + "integrity": "sha512-3+QZROYfJ25PDcxFF66UEk8jGWigHJeecZILvkPkyQN7oc5BvFo4YEXFkOs154j3FTMp9mn9Ky8RCOwastduEA==", "cpu": [ "x64" ], @@ -12120,9 +12120,9 @@ } }, "node_modules/rollup": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.1.tgz", - "integrity": "sha512-x8H8aPvD+xbl0Do8oez5f5o8eMS3trfCghc4HhLAnCkj7Vl0d1JWGs0UF/D886zLW2rOj2QymV/JcSSsw+XDNg==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.2.tgz", + "integrity": "sha512-PVoapzTwSEcelaWGth3uR66u7ZRo6qhPHc0f2uRO9fX6XDVNrIiGYS0Pj9+R8yIIYSD/mCx2b16Ws9itljKSPg==", "dev": true, "dependencies": { "@types/estree": "1.0.8" @@ -12135,26 +12135,26 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.44.1", - "@rollup/rollup-android-arm64": "4.44.1", - "@rollup/rollup-darwin-arm64": "4.44.1", - "@rollup/rollup-darwin-x64": "4.44.1", - "@rollup/rollup-freebsd-arm64": "4.44.1", - "@rollup/rollup-freebsd-x64": "4.44.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.44.1", - "@rollup/rollup-linux-arm-musleabihf": "4.44.1", - "@rollup/rollup-linux-arm64-gnu": "4.44.1", - "@rollup/rollup-linux-arm64-musl": "4.44.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.44.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.44.1", - "@rollup/rollup-linux-riscv64-gnu": "4.44.1", - "@rollup/rollup-linux-riscv64-musl": "4.44.1", - "@rollup/rollup-linux-s390x-gnu": "4.44.1", - "@rollup/rollup-linux-x64-gnu": "4.44.1", - "@rollup/rollup-linux-x64-musl": "4.44.1", - "@rollup/rollup-win32-arm64-msvc": "4.44.1", - "@rollup/rollup-win32-ia32-msvc": "4.44.1", - "@rollup/rollup-win32-x64-msvc": "4.44.1", + "@rollup/rollup-android-arm-eabi": "4.44.2", + "@rollup/rollup-android-arm64": "4.44.2", + "@rollup/rollup-darwin-arm64": "4.44.2", + "@rollup/rollup-darwin-x64": "4.44.2", + "@rollup/rollup-freebsd-arm64": "4.44.2", + "@rollup/rollup-freebsd-x64": "4.44.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.44.2", + "@rollup/rollup-linux-arm-musleabihf": "4.44.2", + "@rollup/rollup-linux-arm64-gnu": "4.44.2", + "@rollup/rollup-linux-arm64-musl": "4.44.2", + "@rollup/rollup-linux-loongarch64-gnu": "4.44.2", + "@rollup/rollup-linux-powerpc64le-gnu": "4.44.2", + "@rollup/rollup-linux-riscv64-gnu": "4.44.2", + "@rollup/rollup-linux-riscv64-musl": "4.44.2", + "@rollup/rollup-linux-s390x-gnu": "4.44.2", + "@rollup/rollup-linux-x64-gnu": "4.44.2", + "@rollup/rollup-linux-x64-musl": "4.44.2", + "@rollup/rollup-win32-arm64-msvc": "4.44.2", + "@rollup/rollup-win32-ia32-msvc": "4.44.2", + "@rollup/rollup-win32-x64-msvc": "4.44.2", "fsevents": "~2.3.2" } }, @@ -16853,142 +16853,142 @@ } }, "@rollup/rollup-android-arm-eabi": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.1.tgz", - "integrity": "sha512-JAcBr1+fgqx20m7Fwe1DxPUl/hPkee6jA6Pl7n1v2EFiktAHenTaXl5aIFjUIEsfn9w3HE4gK1lEgNGMzBDs1w==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.2.tgz", + "integrity": "sha512-g0dF8P1e2QYPOj1gu7s/3LVP6kze9A7m6x0BZ9iTdXK8N5c2V7cpBKHV3/9A4Zd8xxavdhK0t4PnqjkqVmUc9Q==", "dev": true, "optional": true }, "@rollup/rollup-android-arm64": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.1.tgz", - "integrity": "sha512-RurZetXqTu4p+G0ChbnkwBuAtwAbIwJkycw1n6GvlGlBuS4u5qlr5opix8cBAYFJgaY05TWtM+LaoFggUmbZEQ==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.2.tgz", + "integrity": "sha512-Yt5MKrOosSbSaAK5Y4J+vSiID57sOvpBNBR6K7xAaQvk3MkcNVV0f9fE20T+41WYN8hDn6SGFlFrKudtx4EoxA==", "dev": true, "optional": true }, "@rollup/rollup-darwin-arm64": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.1.tgz", - "integrity": "sha512-fM/xPesi7g2M7chk37LOnmnSTHLG/v2ggWqKj3CCA1rMA4mm5KVBT1fNoswbo1JhPuNNZrVwpTvlCVggv8A2zg==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.2.tgz", + "integrity": "sha512-EsnFot9ZieM35YNA26nhbLTJBHD0jTwWpPwmRVDzjylQT6gkar+zenfb8mHxWpRrbn+WytRRjE0WKsfaxBkVUA==", "dev": true, "optional": true }, "@rollup/rollup-darwin-x64": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.1.tgz", - "integrity": "sha512-gDnWk57urJrkrHQ2WVx9TSVTH7lSlU7E3AFqiko+bgjlh78aJ88/3nycMax52VIVjIm3ObXnDL2H00e/xzoipw==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.2.tgz", + "integrity": "sha512-dv/t1t1RkCvJdWWxQ2lWOO+b7cMsVw5YFaS04oHpZRWehI1h0fV1gF4wgGCTyQHHjJDfbNpwOi6PXEafRBBezw==", "dev": true, "optional": true }, "@rollup/rollup-freebsd-arm64": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.1.tgz", - "integrity": "sha512-wnFQmJ/zPThM5zEGcnDcCJeYJgtSLjh1d//WuHzhf6zT3Md1BvvhJnWoy+HECKu2bMxaIcfWiu3bJgx6z4g2XA==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.2.tgz", + "integrity": "sha512-W4tt4BLorKND4qeHElxDoim0+BsprFTwb+vriVQnFFtT/P6v/xO5I99xvYnVzKWrK6j7Hb0yp3x7V5LUbaeOMg==", "dev": true, "optional": true }, "@rollup/rollup-freebsd-x64": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.1.tgz", - "integrity": "sha512-uBmIxoJ4493YATvU2c0upGz87f99e3wop7TJgOA/bXMFd2SvKCI7xkxY/5k50bv7J6dw1SXT4MQBQSLn8Bb/Uw==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.2.tgz", + "integrity": "sha512-tdT1PHopokkuBVyHjvYehnIe20fxibxFCEhQP/96MDSOcyjM/shlTkZZLOufV3qO6/FQOSiJTBebhVc12JyPTA==", "dev": true, "optional": true }, "@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.1.tgz", - "integrity": "sha512-n0edDmSHlXFhrlmTK7XBuwKlG5MbS7yleS1cQ9nn4kIeW+dJH+ExqNgQ0RrFRew8Y+0V/x6C5IjsHrJmiHtkxQ==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.2.tgz", + "integrity": "sha512-+xmiDGGaSfIIOXMzkhJ++Oa0Gwvl9oXUeIiwarsdRXSe27HUIvjbSIpPxvnNsRebsNdUo7uAiQVgBD1hVriwSQ==", "dev": true, "optional": true }, "@rollup/rollup-linux-arm-musleabihf": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.1.tgz", - "integrity": "sha512-8WVUPy3FtAsKSpyk21kV52HCxB+me6YkbkFHATzC2Yd3yuqHwy2lbFL4alJOLXKljoRw08Zk8/xEj89cLQ/4Nw==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.2.tgz", + "integrity": "sha512-bDHvhzOfORk3wt8yxIra8N4k/N0MnKInCW5OGZaeDYa/hMrdPaJzo7CSkjKZqX4JFUWjUGm88lI6QJLCM7lDrA==", "dev": true, "optional": true }, "@rollup/rollup-linux-arm64-gnu": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.1.tgz", - "integrity": "sha512-yuktAOaeOgorWDeFJggjuCkMGeITfqvPgkIXhDqsfKX8J3jGyxdDZgBV/2kj/2DyPaLiX6bPdjJDTu9RB8lUPQ==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.2.tgz", + "integrity": "sha512-NMsDEsDiYghTbeZWEGnNi4F0hSbGnsuOG+VnNvxkKg0IGDvFh7UVpM/14mnMwxRxUf9AdAVJgHPvKXf6FpMB7A==", "dev": true, "optional": true }, "@rollup/rollup-linux-arm64-musl": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.1.tgz", - "integrity": "sha512-W+GBM4ifET1Plw8pdVaecwUgxmiH23CfAUj32u8knq0JPFyK4weRy6H7ooxYFD19YxBulL0Ktsflg5XS7+7u9g==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.2.tgz", + "integrity": "sha512-lb5bxXnxXglVq+7imxykIp5xMq+idehfl+wOgiiix0191av84OqbjUED+PRC5OA8eFJYj5xAGcpAZ0pF2MnW+A==", "dev": true, "optional": true }, "@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.1.tgz", - "integrity": "sha512-1zqnUEMWp9WrGVuVak6jWTl4fEtrVKfZY7CvcBmUUpxAJ7WcSowPSAWIKa/0o5mBL/Ij50SIf9tuirGx63Ovew==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.2.tgz", + "integrity": "sha512-Yl5Rdpf9pIc4GW1PmkUGHdMtbx0fBLE1//SxDmuf3X0dUC57+zMepow2LK0V21661cjXdTn8hO2tXDdAWAqE5g==", "dev": true, "optional": true }, "@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.1.tgz", - "integrity": "sha512-Rl3JKaRu0LHIx7ExBAAnf0JcOQetQffaw34T8vLlg9b1IhzcBgaIdnvEbbsZq9uZp3uAH+JkHd20Nwn0h9zPjA==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.2.tgz", + "integrity": "sha512-03vUDH+w55s680YYryyr78jsO1RWU9ocRMaeV2vMniJJW/6HhoTBwyyiiTPVHNWLnhsnwcQ0oH3S9JSBEKuyqw==", "dev": true, "optional": true }, "@rollup/rollup-linux-riscv64-gnu": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.1.tgz", - "integrity": "sha512-j5akelU3snyL6K3N/iX7otLBIl347fGwmd95U5gS/7z6T4ftK288jKq3A5lcFKcx7wwzb5rgNvAg3ZbV4BqUSw==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.2.tgz", + "integrity": "sha512-iYtAqBg5eEMG4dEfVlkqo05xMOk6y/JXIToRca2bAWuqjrJYJlx/I7+Z+4hSrsWU8GdJDFPL4ktV3dy4yBSrzg==", "dev": true, "optional": true }, "@rollup/rollup-linux-riscv64-musl": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.1.tgz", - "integrity": "sha512-ppn5llVGgrZw7yxbIm8TTvtj1EoPgYUAbfw0uDjIOzzoqlZlZrLJ/KuiE7uf5EpTpCTrNt1EdtzF0naMm0wGYg==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.2.tgz", + "integrity": "sha512-e6vEbgaaqz2yEHqtkPXa28fFuBGmUJ0N2dOJK8YUfijejInt9gfCSA7YDdJ4nYlv67JfP3+PSWFX4IVw/xRIPg==", "dev": true, "optional": true }, "@rollup/rollup-linux-s390x-gnu": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.1.tgz", - "integrity": "sha512-Hu6hEdix0oxtUma99jSP7xbvjkUM/ycke/AQQ4EC5g7jNRLLIwjcNwaUy95ZKBJJwg1ZowsclNnjYqzN4zwkAw==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.2.tgz", + "integrity": "sha512-evFOtkmVdY3udE+0QKrV5wBx7bKI0iHz5yEVx5WqDJkxp9YQefy4Mpx3RajIVcM6o7jxTvVd/qpC1IXUhGc1Mw==", "dev": true, "optional": true }, "@rollup/rollup-linux-x64-gnu": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.1.tgz", - "integrity": "sha512-EtnsrmZGomz9WxK1bR5079zee3+7a+AdFlghyd6VbAjgRJDbTANJ9dcPIPAi76uG05micpEL+gPGmAKYTschQw==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.2.tgz", + "integrity": "sha512-/bXb0bEsWMyEkIsUL2Yt5nFB5naLAwyOWMEviQfQY1x3l5WsLKgvZf66TM7UTfED6erckUVUJQ/jJ1FSpm3pRQ==", "dev": true, "optional": true }, "@rollup/rollup-linux-x64-musl": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.1.tgz", - "integrity": "sha512-iAS4p+J1az6Usn0f8xhgL4PaU878KEtutP4hqw52I4IO6AGoyOkHCxcc4bqufv1tQLdDWFx8lR9YlwxKuv3/3g==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.2.tgz", + "integrity": "sha512-3D3OB1vSSBXmkGEZR27uiMRNiwN08/RVAcBKwhUYPaiZ8bcvdeEwWPvbnXvvXHY+A/7xluzcN+kaiOFNiOZwWg==", "dev": true, "optional": true }, "@rollup/rollup-win32-arm64-msvc": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.1.tgz", - "integrity": "sha512-NtSJVKcXwcqozOl+FwI41OH3OApDyLk3kqTJgx8+gp6On9ZEt5mYhIsKNPGuaZr3p9T6NWPKGU/03Vw4CNU9qg==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.2.tgz", + "integrity": "sha512-VfU0fsMK+rwdK8mwODqYeM2hDrF2WiHaSmCBrS7gColkQft95/8tphyzv2EupVxn3iE0FI78wzffoULH1G+dkw==", "dev": true, "optional": true }, "@rollup/rollup-win32-ia32-msvc": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.1.tgz", - "integrity": "sha512-JYA3qvCOLXSsnTR3oiyGws1Dm0YTuxAAeaYGVlGpUsHqloPcFjPg+X0Fj2qODGLNwQOAcCiQmHub/V007kiH5A==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.2.tgz", + "integrity": "sha512-+qMUrkbUurpE6DVRjiJCNGZBGo9xM4Y0FXU5cjgudWqIBWbcLkjE3XprJUsOFgC6xjBClwVa9k6O3A7K3vxb5Q==", "dev": true, "optional": true }, "@rollup/rollup-win32-x64-msvc": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.1.tgz", - "integrity": "sha512-J8o22LuF0kTe7m+8PvW9wk3/bRq5+mRo5Dqo6+vXb7otCm3TPhYOJqOaQtGU9YMWQSL3krMnoOxMr0+9E6F3Ug==", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.2.tgz", + "integrity": "sha512-3+QZROYfJ25PDcxFF66UEk8jGWigHJeecZILvkPkyQN7oc5BvFo4YEXFkOs154j3FTMp9mn9Ky8RCOwastduEA==", "dev": true, "optional": true }, @@ -23219,31 +23219,31 @@ } }, "rollup": { - "version": "4.44.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.1.tgz", - "integrity": "sha512-x8H8aPvD+xbl0Do8oez5f5o8eMS3trfCghc4HhLAnCkj7Vl0d1JWGs0UF/D886zLW2rOj2QymV/JcSSsw+XDNg==", - "dev": true, - "requires": { - "@rollup/rollup-android-arm-eabi": "4.44.1", - "@rollup/rollup-android-arm64": "4.44.1", - "@rollup/rollup-darwin-arm64": "4.44.1", - "@rollup/rollup-darwin-x64": "4.44.1", - "@rollup/rollup-freebsd-arm64": "4.44.1", - "@rollup/rollup-freebsd-x64": "4.44.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.44.1", - "@rollup/rollup-linux-arm-musleabihf": "4.44.1", - "@rollup/rollup-linux-arm64-gnu": "4.44.1", - "@rollup/rollup-linux-arm64-musl": "4.44.1", - "@rollup/rollup-linux-loongarch64-gnu": "4.44.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.44.1", - "@rollup/rollup-linux-riscv64-gnu": "4.44.1", - "@rollup/rollup-linux-riscv64-musl": "4.44.1", - "@rollup/rollup-linux-s390x-gnu": "4.44.1", - "@rollup/rollup-linux-x64-gnu": "4.44.1", - "@rollup/rollup-linux-x64-musl": "4.44.1", - "@rollup/rollup-win32-arm64-msvc": "4.44.1", - "@rollup/rollup-win32-ia32-msvc": "4.44.1", - "@rollup/rollup-win32-x64-msvc": "4.44.1", + "version": "4.44.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.2.tgz", + "integrity": "sha512-PVoapzTwSEcelaWGth3uR66u7ZRo6qhPHc0f2uRO9fX6XDVNrIiGYS0Pj9+R8yIIYSD/mCx2b16Ws9itljKSPg==", + "dev": true, + "requires": { + "@rollup/rollup-android-arm-eabi": "4.44.2", + "@rollup/rollup-android-arm64": "4.44.2", + "@rollup/rollup-darwin-arm64": "4.44.2", + "@rollup/rollup-darwin-x64": "4.44.2", + "@rollup/rollup-freebsd-arm64": "4.44.2", + "@rollup/rollup-freebsd-x64": "4.44.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.44.2", + "@rollup/rollup-linux-arm-musleabihf": "4.44.2", + "@rollup/rollup-linux-arm64-gnu": "4.44.2", + "@rollup/rollup-linux-arm64-musl": "4.44.2", + "@rollup/rollup-linux-loongarch64-gnu": "4.44.2", + "@rollup/rollup-linux-powerpc64le-gnu": "4.44.2", + "@rollup/rollup-linux-riscv64-gnu": "4.44.2", + "@rollup/rollup-linux-riscv64-musl": "4.44.2", + "@rollup/rollup-linux-s390x-gnu": "4.44.2", + "@rollup/rollup-linux-x64-gnu": "4.44.2", + "@rollup/rollup-linux-x64-musl": "4.44.2", + "@rollup/rollup-win32-arm64-msvc": "4.44.2", + "@rollup/rollup-win32-ia32-msvc": "4.44.2", + "@rollup/rollup-win32-x64-msvc": "4.44.2", "@types/estree": "1.0.8", "fsevents": "~2.3.2" }, diff --git a/package.json b/package.json index 4d4e15f5299..8919836a332 100644 --- a/package.json +++ b/package.json @@ -124,7 +124,7 @@ "npm-run-all2": "8.0.4", "prettier": "3.6.2", "promise-polyfill": "8.3.0", - "rollup": "4.44.1", + "rollup": "4.44.2", "rollup-plugin-istanbul": "5.0.0", "sauce-connect-launcher": "1.3.2", "selenium-webdriver": "4.34.0", From 81d30d55f0e77c5685af7c732296cdb63da48c5c Mon Sep 17 00:00:00 2001 From: Rob Walch Date: Mon, 14 Jul 2025 09:37:28 -0700 Subject: [PATCH 12/52] Reset TS video parser with init segment Fixes #7402 --- src/demux/tsdemuxer.ts | 37 +++++++++++++------------------------ 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/src/demux/tsdemuxer.ts b/src/demux/tsdemuxer.ts index aa52b4881a9..863e0baadc1 100644 --- a/src/demux/tsdemuxer.ts +++ b/src/demux/tsdemuxer.ts @@ -191,6 +191,7 @@ class TSDemuxer implements Demuxer { this._audioTrack.segmentCodec = 'aac'; // flush any partial content + this.videoParser = null; this.aacOverFlow = null; this.remainderData = null; this.audioCodec = audioCodec; @@ -291,18 +292,7 @@ class TSDemuxer implements Demuxer { case videoPid: if (stt) { if (videoData && (pes = parsePES(videoData, this.logger))) { - if (this.videoParser === null) { - switch (videoTrack.segmentCodec) { - case 'avc': - this.videoParser = new AvcVideoParser(); - break; - case 'hevc': - if (__USE_M2TS_ADVANCED_CODECS__) { - this.videoParser = new HevcVideoParser(); - } - break; - } - } + this.readyVideoParser(videoTrack.segmentCodec); if (this.videoParser !== null) { this.videoParser.parsePES(videoTrack, textTrack, pes, false); } @@ -477,18 +467,7 @@ class TSDemuxer implements Demuxer { // try to parse last PES packets let pes: PES | null; if (videoData && (pes = parsePES(videoData, this.logger))) { - if (this.videoParser === null) { - switch (videoTrack.segmentCodec) { - case 'avc': - this.videoParser = new AvcVideoParser(); - break; - case 'hevc': - if (__USE_M2TS_ADVANCED_CODECS__) { - this.videoParser = new HevcVideoParser(); - } - break; - } - } + this.readyVideoParser(videoTrack.segmentCodec); if (this.videoParser !== null) { this.videoParser.parsePES( videoTrack as DemuxedVideoTrack, @@ -557,6 +536,16 @@ class TSDemuxer implements Demuxer { return this.decrypt(demuxResult, sampleAes); } + private readyVideoParser(codec: string | undefined) { + if (this.videoParser === null) { + if (codec === 'avc') { + this.videoParser = new AvcVideoParser(); + } else if (__USE_M2TS_ADVANCED_CODECS__ && codec === 'hevc') { + this.videoParser = new HevcVideoParser(); + } + } + } + private decrypt( demuxResult: DemuxerResult, sampleAes: SampleAesDecrypter, From e18f74824c5137a83ccfb6dfc6bbd5b5f1020acd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 14 Jul 2025 21:22:13 +0000 Subject: [PATCH 13/52] chore(deps): update typescript-eslint monorepo to v8.36.0 --- package-lock.json | 214 +++++++++++++++++++++++----------------------- package.json | 4 +- 2 files changed, 109 insertions(+), 109 deletions(-) diff --git a/package-lock.json b/package-lock.json index 96f19598948..b61b2cf0730 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,8 +30,8 @@ "@types/chart.js": "2.9.41", "@types/mocha": "10.0.10", "@types/sinon-chai": "3.2.12", - "@typescript-eslint/eslint-plugin": "8.35.1", - "@typescript-eslint/parser": "8.35.1", + "@typescript-eslint/eslint-plugin": "8.36.0", + "@typescript-eslint/parser": "8.36.0", "babel-loader": "10.0.0", "babel-plugin-transform-remove-console": "6.9.4", "chai": "4.5.0", @@ -4124,16 +4124,16 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.35.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.35.1.tgz", - "integrity": "sha512-9XNTlo7P7RJxbVeICaIIIEipqxLKguyh+3UbXuT2XQuFp6d8VOeDEGuz5IiX0dgZo8CiI6aOFLg4e8cF71SFVg==", + "version": "8.36.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.36.0.tgz", + "integrity": "sha512-lZNihHUVB6ZZiPBNgOQGSxUASI7UJWhT8nHyUGCnaQ28XFCw98IfrMCG3rUl1uwUWoAvodJQby2KTs79UTcrAg==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.35.1", - "@typescript-eslint/type-utils": "8.35.1", - "@typescript-eslint/utils": "8.35.1", - "@typescript-eslint/visitor-keys": "8.35.1", + "@typescript-eslint/scope-manager": "8.36.0", + "@typescript-eslint/type-utils": "8.36.0", + "@typescript-eslint/utils": "8.36.0", + "@typescript-eslint/visitor-keys": "8.36.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -4147,7 +4147,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.35.1", + "@typescript-eslint/parser": "^8.36.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } @@ -4162,15 +4162,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.35.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.35.1.tgz", - "integrity": "sha512-3MyiDfrfLeK06bi/g9DqJxP5pV74LNv4rFTyvGDmT3x2p1yp1lOd+qYZfiRPIOf/oON+WRZR5wxxuF85qOar+w==", + "version": "8.36.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.36.0.tgz", + "integrity": "sha512-FuYgkHwZLuPbZjQHzJXrtXreJdFMKl16BFYyRrLxDhWr6Qr7Kbcu2s1Yhu8tsiMXw1S0W1pjfFfYEt+R604s+Q==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.35.1", - "@typescript-eslint/types": "8.35.1", - "@typescript-eslint/typescript-estree": "8.35.1", - "@typescript-eslint/visitor-keys": "8.35.1", + "@typescript-eslint/scope-manager": "8.36.0", + "@typescript-eslint/types": "8.36.0", + "@typescript-eslint/typescript-estree": "8.36.0", + "@typescript-eslint/visitor-keys": "8.36.0", "debug": "^4.3.4" }, "engines": { @@ -4186,13 +4186,13 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.35.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.35.1.tgz", - "integrity": "sha512-VYxn/5LOpVxADAuP3NrnxxHYfzVtQzLKeldIhDhzC8UHaiQvYlXvKuVho1qLduFbJjjy5U5bkGwa3rUGUb1Q6Q==", + "version": "8.36.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.36.0.tgz", + "integrity": "sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g==", "dev": true, "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.35.1", - "@typescript-eslint/types": "^8.35.1", + "@typescript-eslint/tsconfig-utils": "^8.36.0", + "@typescript-eslint/types": "^8.36.0", "debug": "^4.3.4" }, "engines": { @@ -4207,13 +4207,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.35.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.35.1.tgz", - "integrity": "sha512-s/Bpd4i7ht2934nG+UoSPlYXd08KYz3bmjLEb7Ye1UVob0d1ENiT3lY8bsCmik4RqfSbPw9xJJHbugpPpP5JUg==", + "version": "8.36.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.36.0.tgz", + "integrity": "sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.35.1", - "@typescript-eslint/visitor-keys": "8.35.1" + "@typescript-eslint/types": "8.36.0", + "@typescript-eslint/visitor-keys": "8.36.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4224,9 +4224,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.35.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.35.1.tgz", - "integrity": "sha512-K5/U9VmT9dTHoNowWZpz+/TObS3xqC5h0xAIjXPw+MNcKV9qg6eSatEnmeAwkjHijhACH0/N7bkhKvbt1+DXWQ==", + "version": "8.36.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.36.0.tgz", + "integrity": "sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4240,13 +4240,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.35.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.35.1.tgz", - "integrity": "sha512-HOrUBlfVRz5W2LIKpXzZoy6VTZzMu2n8q9C2V/cFngIC5U1nStJgv0tMV4sZPzdf4wQm9/ToWUFPMN9Vq9VJQQ==", + "version": "8.36.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.36.0.tgz", + "integrity": "sha512-5aaGYG8cVDd6cxfk/ynpYzxBRZJk7w/ymto6uiyUFtdCozQIsQWh7M28/6r57Fwkbweng8qAzoMCPwSJfWlmsg==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "8.35.1", - "@typescript-eslint/utils": "8.35.1", + "@typescript-eslint/typescript-estree": "8.36.0", + "@typescript-eslint/utils": "8.36.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -4263,9 +4263,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.35.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.35.1.tgz", - "integrity": "sha512-q/O04vVnKHfrrhNAscndAn1tuQhIkwqnaW+eu5waD5IPts2eX1dgJxgqcPx5BX109/qAz7IG6VrEPTOYKCNfRQ==", + "version": "8.36.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.36.0.tgz", + "integrity": "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4276,15 +4276,15 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.35.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.35.1.tgz", - "integrity": "sha512-Vvpuvj4tBxIka7cPs6Y1uvM7gJgdF5Uu9F+mBJBPY4MhvjrjWGK4H0lVgLJd/8PWZ23FTqsaJaLEkBCFUk8Y9g==", + "version": "8.36.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.36.0.tgz", + "integrity": "sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg==", "dev": true, "dependencies": { - "@typescript-eslint/project-service": "8.35.1", - "@typescript-eslint/tsconfig-utils": "8.35.1", - "@typescript-eslint/types": "8.35.1", - "@typescript-eslint/visitor-keys": "8.35.1", + "@typescript-eslint/project-service": "8.36.0", + "@typescript-eslint/tsconfig-utils": "8.36.0", + "@typescript-eslint/types": "8.36.0", + "@typescript-eslint/visitor-keys": "8.36.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -4328,15 +4328,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.35.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.35.1.tgz", - "integrity": "sha512-lhnwatFmOFcazAsUm3ZnZFpXSxiwoa1Lj50HphnDe1Et01NF4+hrdXONSUHIcbVu2eFb1bAf+5yjXkGVkXBKAQ==", + "version": "8.36.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.36.0.tgz", + "integrity": "sha512-VOqmHu42aEMT+P2qYjylw6zP/3E/HvptRwdn/PZxyV27KhZg2IOszXod4NcXisWzPAGSS4trE/g4moNj6XmH2g==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.35.1", - "@typescript-eslint/types": "8.35.1", - "@typescript-eslint/typescript-estree": "8.35.1" + "@typescript-eslint/scope-manager": "8.36.0", + "@typescript-eslint/types": "8.36.0", + "@typescript-eslint/typescript-estree": "8.36.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4351,12 +4351,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.35.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.35.1.tgz", - "integrity": "sha512-VRwixir4zBWCSTP/ljEo091lbpypz57PoeAQ9imjG+vbeof9LplljsL1mos4ccG6H9IjfrVGM359RozUnuFhpw==", + "version": "8.36.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.36.0.tgz", + "integrity": "sha512-vZrhV2lRPWDuGoxcmrzRZyxAggPL+qp3WzUrlZD+slFueDiYHxeBa34dUXPuC0RmGKzl4lS5kFJYvKCq9cnNDA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.35.1", + "@typescript-eslint/types": "8.36.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -17356,16 +17356,16 @@ } }, "@typescript-eslint/eslint-plugin": { - "version": "8.35.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.35.1.tgz", - "integrity": "sha512-9XNTlo7P7RJxbVeICaIIIEipqxLKguyh+3UbXuT2XQuFp6d8VOeDEGuz5IiX0dgZo8CiI6aOFLg4e8cF71SFVg==", + "version": "8.36.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.36.0.tgz", + "integrity": "sha512-lZNihHUVB6ZZiPBNgOQGSxUASI7UJWhT8nHyUGCnaQ28XFCw98IfrMCG3rUl1uwUWoAvodJQby2KTs79UTcrAg==", "dev": true, "requires": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.35.1", - "@typescript-eslint/type-utils": "8.35.1", - "@typescript-eslint/utils": "8.35.1", - "@typescript-eslint/visitor-keys": "8.35.1", + "@typescript-eslint/scope-manager": "8.36.0", + "@typescript-eslint/type-utils": "8.36.0", + "@typescript-eslint/utils": "8.36.0", + "@typescript-eslint/visitor-keys": "8.36.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -17381,74 +17381,74 @@ } }, "@typescript-eslint/parser": { - "version": "8.35.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.35.1.tgz", - "integrity": "sha512-3MyiDfrfLeK06bi/g9DqJxP5pV74LNv4rFTyvGDmT3x2p1yp1lOd+qYZfiRPIOf/oON+WRZR5wxxuF85qOar+w==", + "version": "8.36.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.36.0.tgz", + "integrity": "sha512-FuYgkHwZLuPbZjQHzJXrtXreJdFMKl16BFYyRrLxDhWr6Qr7Kbcu2s1Yhu8tsiMXw1S0W1pjfFfYEt+R604s+Q==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "8.35.1", - "@typescript-eslint/types": "8.35.1", - "@typescript-eslint/typescript-estree": "8.35.1", - "@typescript-eslint/visitor-keys": "8.35.1", + "@typescript-eslint/scope-manager": "8.36.0", + "@typescript-eslint/types": "8.36.0", + "@typescript-eslint/typescript-estree": "8.36.0", + "@typescript-eslint/visitor-keys": "8.36.0", "debug": "^4.3.4" } }, "@typescript-eslint/project-service": { - "version": "8.35.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.35.1.tgz", - "integrity": "sha512-VYxn/5LOpVxADAuP3NrnxxHYfzVtQzLKeldIhDhzC8UHaiQvYlXvKuVho1qLduFbJjjy5U5bkGwa3rUGUb1Q6Q==", + "version": "8.36.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.36.0.tgz", + "integrity": "sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g==", "dev": true, "requires": { - "@typescript-eslint/tsconfig-utils": "^8.35.1", - "@typescript-eslint/types": "^8.35.1", + "@typescript-eslint/tsconfig-utils": "^8.36.0", + "@typescript-eslint/types": "^8.36.0", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "8.35.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.35.1.tgz", - "integrity": "sha512-s/Bpd4i7ht2934nG+UoSPlYXd08KYz3bmjLEb7Ye1UVob0d1ENiT3lY8bsCmik4RqfSbPw9xJJHbugpPpP5JUg==", + "version": "8.36.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.36.0.tgz", + "integrity": "sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA==", "dev": true, "requires": { - "@typescript-eslint/types": "8.35.1", - "@typescript-eslint/visitor-keys": "8.35.1" + "@typescript-eslint/types": "8.36.0", + "@typescript-eslint/visitor-keys": "8.36.0" } }, "@typescript-eslint/tsconfig-utils": { - "version": "8.35.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.35.1.tgz", - "integrity": "sha512-K5/U9VmT9dTHoNowWZpz+/TObS3xqC5h0xAIjXPw+MNcKV9qg6eSatEnmeAwkjHijhACH0/N7bkhKvbt1+DXWQ==", + "version": "8.36.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.36.0.tgz", + "integrity": "sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA==", "dev": true, "requires": {} }, "@typescript-eslint/type-utils": { - "version": "8.35.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.35.1.tgz", - "integrity": "sha512-HOrUBlfVRz5W2LIKpXzZoy6VTZzMu2n8q9C2V/cFngIC5U1nStJgv0tMV4sZPzdf4wQm9/ToWUFPMN9Vq9VJQQ==", + "version": "8.36.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.36.0.tgz", + "integrity": "sha512-5aaGYG8cVDd6cxfk/ynpYzxBRZJk7w/ymto6uiyUFtdCozQIsQWh7M28/6r57Fwkbweng8qAzoMCPwSJfWlmsg==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "8.35.1", - "@typescript-eslint/utils": "8.35.1", + "@typescript-eslint/typescript-estree": "8.36.0", + "@typescript-eslint/utils": "8.36.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" } }, "@typescript-eslint/types": { - "version": "8.35.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.35.1.tgz", - "integrity": "sha512-q/O04vVnKHfrrhNAscndAn1tuQhIkwqnaW+eu5waD5IPts2eX1dgJxgqcPx5BX109/qAz7IG6VrEPTOYKCNfRQ==", + "version": "8.36.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.36.0.tgz", + "integrity": "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "8.35.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.35.1.tgz", - "integrity": "sha512-Vvpuvj4tBxIka7cPs6Y1uvM7gJgdF5Uu9F+mBJBPY4MhvjrjWGK4H0lVgLJd/8PWZ23FTqsaJaLEkBCFUk8Y9g==", + "version": "8.36.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.36.0.tgz", + "integrity": "sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg==", "dev": true, "requires": { - "@typescript-eslint/project-service": "8.35.1", - "@typescript-eslint/tsconfig-utils": "8.35.1", - "@typescript-eslint/types": "8.35.1", - "@typescript-eslint/visitor-keys": "8.35.1", + "@typescript-eslint/project-service": "8.36.0", + "@typescript-eslint/tsconfig-utils": "8.36.0", + "@typescript-eslint/types": "8.36.0", + "@typescript-eslint/visitor-keys": "8.36.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -17478,24 +17478,24 @@ } }, "@typescript-eslint/utils": { - "version": "8.35.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.35.1.tgz", - "integrity": "sha512-lhnwatFmOFcazAsUm3ZnZFpXSxiwoa1Lj50HphnDe1Et01NF4+hrdXONSUHIcbVu2eFb1bAf+5yjXkGVkXBKAQ==", + "version": "8.36.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.36.0.tgz", + "integrity": "sha512-VOqmHu42aEMT+P2qYjylw6zP/3E/HvptRwdn/PZxyV27KhZg2IOszXod4NcXisWzPAGSS4trE/g4moNj6XmH2g==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.35.1", - "@typescript-eslint/types": "8.35.1", - "@typescript-eslint/typescript-estree": "8.35.1" + "@typescript-eslint/scope-manager": "8.36.0", + "@typescript-eslint/types": "8.36.0", + "@typescript-eslint/typescript-estree": "8.36.0" } }, "@typescript-eslint/visitor-keys": { - "version": "8.35.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.35.1.tgz", - "integrity": "sha512-VRwixir4zBWCSTP/ljEo091lbpypz57PoeAQ9imjG+vbeof9LplljsL1mos4ccG6H9IjfrVGM359RozUnuFhpw==", + "version": "8.36.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.36.0.tgz", + "integrity": "sha512-vZrhV2lRPWDuGoxcmrzRZyxAggPL+qp3WzUrlZD+slFueDiYHxeBa34dUXPuC0RmGKzl4lS5kFJYvKCq9cnNDA==", "dev": true, "requires": { - "@typescript-eslint/types": "8.35.1", + "@typescript-eslint/types": "8.36.0", "eslint-visitor-keys": "^4.2.1" }, "dependencies": { diff --git a/package.json b/package.json index 8919836a332..6ba4f169dad 100644 --- a/package.json +++ b/package.json @@ -88,8 +88,8 @@ "@types/chart.js": "2.9.41", "@types/mocha": "10.0.10", "@types/sinon-chai": "3.2.12", - "@typescript-eslint/eslint-plugin": "8.35.1", - "@typescript-eslint/parser": "8.35.1", + "@typescript-eslint/eslint-plugin": "8.36.0", + "@typescript-eslint/parser": "8.36.0", "babel-loader": "10.0.0", "babel-plugin-transform-remove-console": "6.9.4", "chai": "4.5.0", From 85468d2c189710176ced0687d502604f69025ce1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 14 Jul 2025 21:38:05 +0000 Subject: [PATCH 14/52] chore(deps): update typescript-eslint monorepo to v8.37.0 --- package-lock.json | 216 +++++++++++++++++++++++----------------------- package.json | 4 +- 2 files changed, 111 insertions(+), 109 deletions(-) diff --git a/package-lock.json b/package-lock.json index b61b2cf0730..8fad4523664 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,8 +30,8 @@ "@types/chart.js": "2.9.41", "@types/mocha": "10.0.10", "@types/sinon-chai": "3.2.12", - "@typescript-eslint/eslint-plugin": "8.36.0", - "@typescript-eslint/parser": "8.36.0", + "@typescript-eslint/eslint-plugin": "8.37.0", + "@typescript-eslint/parser": "8.37.0", "babel-loader": "10.0.0", "babel-plugin-transform-remove-console": "6.9.4", "chai": "4.5.0", @@ -4124,16 +4124,16 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.36.0.tgz", - "integrity": "sha512-lZNihHUVB6ZZiPBNgOQGSxUASI7UJWhT8nHyUGCnaQ28XFCw98IfrMCG3rUl1uwUWoAvodJQby2KTs79UTcrAg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.37.0.tgz", + "integrity": "sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/type-utils": "8.36.0", - "@typescript-eslint/utils": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/type-utils": "8.37.0", + "@typescript-eslint/utils": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -4147,7 +4147,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.36.0", + "@typescript-eslint/parser": "^8.37.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } @@ -4162,15 +4162,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.36.0.tgz", - "integrity": "sha512-FuYgkHwZLuPbZjQHzJXrtXreJdFMKl16BFYyRrLxDhWr6Qr7Kbcu2s1Yhu8tsiMXw1S0W1pjfFfYEt+R604s+Q==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.37.0.tgz", + "integrity": "sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "debug": "^4.3.4" }, "engines": { @@ -4186,13 +4186,13 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.36.0.tgz", - "integrity": "sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", + "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", "dev": true, "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.36.0", - "@typescript-eslint/types": "^8.36.0", + "@typescript-eslint/tsconfig-utils": "^8.37.0", + "@typescript-eslint/types": "^8.37.0", "debug": "^4.3.4" }, "engines": { @@ -4207,13 +4207,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.36.0.tgz", - "integrity": "sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", + "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0" + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4224,9 +4224,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.36.0.tgz", - "integrity": "sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", + "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4240,13 +4240,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.36.0.tgz", - "integrity": "sha512-5aaGYG8cVDd6cxfk/ynpYzxBRZJk7w/ymto6uiyUFtdCozQIsQWh7M28/6r57Fwkbweng8qAzoMCPwSJfWlmsg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz", + "integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/utils": "8.36.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/utils": "8.37.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -4263,9 +4264,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.36.0.tgz", - "integrity": "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", + "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4276,15 +4277,15 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.36.0.tgz", - "integrity": "sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", + "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", "dev": true, "dependencies": { - "@typescript-eslint/project-service": "8.36.0", - "@typescript-eslint/tsconfig-utils": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/project-service": "8.37.0", + "@typescript-eslint/tsconfig-utils": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -4328,15 +4329,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.36.0.tgz", - "integrity": "sha512-VOqmHu42aEMT+P2qYjylw6zP/3E/HvptRwdn/PZxyV27KhZg2IOszXod4NcXisWzPAGSS4trE/g4moNj6XmH2g==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", + "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0" + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4351,12 +4352,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.36.0.tgz", - "integrity": "sha512-vZrhV2lRPWDuGoxcmrzRZyxAggPL+qp3WzUrlZD+slFueDiYHxeBa34dUXPuC0RmGKzl4lS5kFJYvKCq9cnNDA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", + "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.36.0", + "@typescript-eslint/types": "8.37.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -17356,16 +17357,16 @@ } }, "@typescript-eslint/eslint-plugin": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.36.0.tgz", - "integrity": "sha512-lZNihHUVB6ZZiPBNgOQGSxUASI7UJWhT8nHyUGCnaQ28XFCw98IfrMCG3rUl1uwUWoAvodJQby2KTs79UTcrAg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.37.0.tgz", + "integrity": "sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==", "dev": true, "requires": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/type-utils": "8.36.0", - "@typescript-eslint/utils": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/type-utils": "8.37.0", + "@typescript-eslint/utils": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -17381,74 +17382,75 @@ } }, "@typescript-eslint/parser": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.36.0.tgz", - "integrity": "sha512-FuYgkHwZLuPbZjQHzJXrtXreJdFMKl16BFYyRrLxDhWr6Qr7Kbcu2s1Yhu8tsiMXw1S0W1pjfFfYEt+R604s+Q==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.37.0.tgz", + "integrity": "sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "debug": "^4.3.4" } }, "@typescript-eslint/project-service": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.36.0.tgz", - "integrity": "sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", + "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", "dev": true, "requires": { - "@typescript-eslint/tsconfig-utils": "^8.36.0", - "@typescript-eslint/types": "^8.36.0", + "@typescript-eslint/tsconfig-utils": "^8.37.0", + "@typescript-eslint/types": "^8.37.0", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.36.0.tgz", - "integrity": "sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", + "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", "dev": true, "requires": { - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0" + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0" } }, "@typescript-eslint/tsconfig-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.36.0.tgz", - "integrity": "sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", + "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", "dev": true, "requires": {} }, "@typescript-eslint/type-utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.36.0.tgz", - "integrity": "sha512-5aaGYG8cVDd6cxfk/ynpYzxBRZJk7w/ymto6uiyUFtdCozQIsQWh7M28/6r57Fwkbweng8qAzoMCPwSJfWlmsg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz", + "integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "8.36.0", - "@typescript-eslint/utils": "8.36.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0", + "@typescript-eslint/utils": "8.37.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" } }, "@typescript-eslint/types": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.36.0.tgz", - "integrity": "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", + "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.36.0.tgz", - "integrity": "sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", + "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", "dev": true, "requires": { - "@typescript-eslint/project-service": "8.36.0", - "@typescript-eslint/tsconfig-utils": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/visitor-keys": "8.36.0", + "@typescript-eslint/project-service": "8.37.0", + "@typescript-eslint/tsconfig-utils": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/visitor-keys": "8.37.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -17478,24 +17480,24 @@ } }, "@typescript-eslint/utils": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.36.0.tgz", - "integrity": "sha512-VOqmHu42aEMT+P2qYjylw6zP/3E/HvptRwdn/PZxyV27KhZg2IOszXod4NcXisWzPAGSS4trE/g4moNj6XmH2g==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", + "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.36.0", - "@typescript-eslint/types": "8.36.0", - "@typescript-eslint/typescript-estree": "8.36.0" + "@typescript-eslint/scope-manager": "8.37.0", + "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/typescript-estree": "8.37.0" } }, "@typescript-eslint/visitor-keys": { - "version": "8.36.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.36.0.tgz", - "integrity": "sha512-vZrhV2lRPWDuGoxcmrzRZyxAggPL+qp3WzUrlZD+slFueDiYHxeBa34dUXPuC0RmGKzl4lS5kFJYvKCq9cnNDA==", + "version": "8.37.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", + "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", "dev": true, "requires": { - "@typescript-eslint/types": "8.36.0", + "@typescript-eslint/types": "8.37.0", "eslint-visitor-keys": "^4.2.1" }, "dependencies": { diff --git a/package.json b/package.json index 6ba4f169dad..c974f667c69 100644 --- a/package.json +++ b/package.json @@ -88,8 +88,8 @@ "@types/chart.js": "2.9.41", "@types/mocha": "10.0.10", "@types/sinon-chai": "3.2.12", - "@typescript-eslint/eslint-plugin": "8.36.0", - "@typescript-eslint/parser": "8.36.0", + "@typescript-eslint/eslint-plugin": "8.37.0", + "@typescript-eslint/parser": "8.37.0", "babel-loader": "10.0.0", "babel-plugin-transform-remove-console": "6.9.4", "chai": "4.5.0", From d313067793d4a50026361452792176e5d1ddc465 Mon Sep 17 00:00:00 2001 From: Rob Walch Date: Wed, 16 Jul 2025 14:42:29 -0700 Subject: [PATCH 15/52] Fix unnecessary condition in fragment-tracker --- src/controller/fragment-tracker.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/controller/fragment-tracker.ts b/src/controller/fragment-tracker.ts index 6ffb3b7f87b..fffdf49293b 100644 --- a/src/controller/fragment-tracker.ts +++ b/src/controller/fragment-tracker.ts @@ -88,11 +88,10 @@ export class FragmentTracker implements ComponentAPI { if (!activePart) { break; } - const appendedPTS = activePart.end; if ( activePart.start <= position && - appendedPTS !== null && - position <= appendedPTS + position <= activePart.end && + activePart.loaded ) { return activePart; } From 7f40ca56584fc1c2cc0453958b8af715308da6a4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 17 Jul 2025 04:36:24 +0000 Subject: [PATCH 16/52] chore(deps): update dependency @svta/common-media-library to v0.16.0 --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8fad4523664..d85e04f60d8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,7 @@ "@rollup/plugin-replace": "6.0.2", "@rollup/plugin-terser": "0.4.4", "@rollup/plugin-typescript": "12.1.4", - "@svta/common-media-library": "0.15.1", + "@svta/common-media-library": "0.16.0", "@types/chai": "4.3.20", "@types/chart.js": "2.9.41", "@types/mocha": "10.0.10", @@ -3924,9 +3924,9 @@ "dev": true }, "node_modules/@svta/common-media-library": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@svta/common-media-library/-/common-media-library-0.15.1.tgz", - "integrity": "sha512-Ig26anQU/MVRxrikBv1cMmeJqhXG683jxA7q9iscoMygcYd4+XmLjM0kcshZ574izow4X+j/NcU5vTrvVXF9sQ==", + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@svta/common-media-library/-/common-media-library-0.16.0.tgz", + "integrity": "sha512-qR8z8b5cQbVzaxT7iso1xSDve/se90Aq+F5caOatk+Np9u+2VRXUkXFCKx7vKQ1ILACGjLv6Yhpx+2dGWI/Bqg==", "dev": true, "engines": { "node": ">=20" @@ -17162,9 +17162,9 @@ "dev": true }, "@svta/common-media-library": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@svta/common-media-library/-/common-media-library-0.15.1.tgz", - "integrity": "sha512-Ig26anQU/MVRxrikBv1cMmeJqhXG683jxA7q9iscoMygcYd4+XmLjM0kcshZ574izow4X+j/NcU5vTrvVXF9sQ==", + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@svta/common-media-library/-/common-media-library-0.16.0.tgz", + "integrity": "sha512-qR8z8b5cQbVzaxT7iso1xSDve/se90Aq+F5caOatk+Np9u+2VRXUkXFCKx7vKQ1ILACGjLv6Yhpx+2dGWI/Bqg==", "dev": true }, "@testim/chrome-version": { diff --git a/package.json b/package.json index c974f667c69..938fc232375 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "@rollup/plugin-replace": "6.0.2", "@rollup/plugin-terser": "0.4.4", "@rollup/plugin-typescript": "12.1.4", - "@svta/common-media-library": "0.15.1", + "@svta/common-media-library": "0.16.0", "@types/chai": "4.3.20", "@types/chart.js": "2.9.41", "@types/mocha": "10.0.10", From 47df678828220eb7780027e0d675c454f0fae757 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 17 Jul 2025 19:14:30 +0000 Subject: [PATCH 17/52] chore(deps): update dependency wrangler to v4.24.3 --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index d85e04f60d8..7b7784510b0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -75,7 +75,7 @@ "sinon-chai": "3.7.0", "typescript": "5.8.3", "url-toolkit": "2.2.5", - "wrangler": "4.24.1" + "wrangler": "4.24.3" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -14352,9 +14352,9 @@ "dev": true }, "node_modules/wrangler": { - "version": "4.24.1", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.24.1.tgz", - "integrity": "sha512-n7d5BQyOQU7WyEYMC3zNarWzsqWttsusP6qWz4rbbydmnWKSP1wLtsv0yKiyz/LYFumpolz48d5phHp04nR6uw==", + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.24.3.tgz", + "integrity": "sha512-stB1Wfs5NKlspsAzz8SBujBKsDqT5lpCyrL+vSUMy3uueEtI1A5qyORbKoJhIguEbwHfWS39mBsxzm6Vm1J2cg==", "dev": true, "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", @@ -24853,9 +24853,9 @@ "dev": true }, "wrangler": { - "version": "4.24.1", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.24.1.tgz", - "integrity": "sha512-n7d5BQyOQU7WyEYMC3zNarWzsqWttsusP6qWz4rbbydmnWKSP1wLtsv0yKiyz/LYFumpolz48d5phHp04nR6uw==", + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.24.3.tgz", + "integrity": "sha512-stB1Wfs5NKlspsAzz8SBujBKsDqT5lpCyrL+vSUMy3uueEtI1A5qyORbKoJhIguEbwHfWS39mBsxzm6Vm1J2cg==", "dev": true, "requires": { "@cloudflare/kv-asset-handler": "0.4.0", diff --git a/package.json b/package.json index 938fc232375..cb6fb5b7a91 100644 --- a/package.json +++ b/package.json @@ -133,6 +133,6 @@ "sinon-chai": "3.7.0", "typescript": "5.8.3", "url-toolkit": "2.2.5", - "wrangler": "4.24.1" + "wrangler": "4.24.3" } } From 638c0b9641939be71e2b2442c5876552d04c38fc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 17 Jul 2025 19:30:35 +0000 Subject: [PATCH 18/52] chore(deps): update dependency wrangler to v4.25.0 --- package-lock.json | 175 ++++++++++++++++++++-------------------------- package.json | 2 +- 2 files changed, 77 insertions(+), 100 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7b7784510b0..0820345124b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -75,7 +75,7 @@ "sinon-chai": "3.7.0", "typescript": "5.8.3", "url-toolkit": "2.2.5", - "wrangler": "4.24.3" + "wrangler": "4.25.0" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -1768,9 +1768,9 @@ } }, "node_modules/@cloudflare/workerd-darwin-64": { - "version": "1.20250709.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20250709.0.tgz", - "integrity": "sha512-VqwcvnbI8FNCP87ZWNHA3/sAC5U9wMbNnjBG0sHEYzM7B9RPHKYHdVKdBEWhzZXnkQYMK81IHm4CZsK16XxAuQ==", + "version": "1.20250712.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20250712.0.tgz", + "integrity": "sha512-M6S6a/LQ0Jb0R+g0XhlYi1adGifvYmxA5mD/i9TuZZgjs2bIm5ELuka/n3SCnI98ltvlx3HahRaHagAtOilsFg==", "cpu": [ "x64" ], @@ -1784,9 +1784,9 @@ } }, "node_modules/@cloudflare/workerd-darwin-arm64": { - "version": "1.20250709.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20250709.0.tgz", - "integrity": "sha512-A54ttSgXMM4huChPTThhkieOjpDxR+srVOO9zjTHVIyoQxA8zVsku4CcY/GQ95RczMV+yCKVVu/tAME7vwBFuA==", + "version": "1.20250712.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20250712.0.tgz", + "integrity": "sha512-7sFzn6rvAcnLy7MktFL42dYtzL0Idw/kiUmNf2P3TvsBRoShhLK5ZKhbw+NAhvU8e4pXWm5lkE0XmpieA0zNjw==", "cpu": [ "arm64" ], @@ -1800,9 +1800,9 @@ } }, "node_modules/@cloudflare/workerd-linux-64": { - "version": "1.20250709.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20250709.0.tgz", - "integrity": "sha512-no4O3OK+VXINIxv99OHJDpIgML2ZssrSvImwLtULzqm+cl4t1PIfXNRUqj89ujTkmad+L9y4G6dBQMPCLnmlGg==", + "version": "1.20250712.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20250712.0.tgz", + "integrity": "sha512-EFRrGe/bqK7NHtht7vNlbrDpfvH3eRvtJOgsTpEQEysDjVmlK6pVJxSnLy9Hg1zlLY15IfhfGC+K2qisseHGJQ==", "cpu": [ "x64" ], @@ -1816,9 +1816,9 @@ } }, "node_modules/@cloudflare/workerd-linux-arm64": { - "version": "1.20250709.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20250709.0.tgz", - "integrity": "sha512-7cNICk2Qd+m4QGrcmWyAuZJXTHt1ud6isA+dic7Yk42WZmwXhlcUATyvFD9FSQNFcldjuRB4n8JlWEFqZBn+lw==", + "version": "1.20250712.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20250712.0.tgz", + "integrity": "sha512-rG8JUleddhUHQVwpXOYv0VbL0S9kOtR9PNKecgVhFpxEhC8aTeg2HNBBjo8st7IfcUvY8WaW3pD3qdAMZ05UwQ==", "cpu": [ "arm64" ], @@ -1832,9 +1832,9 @@ } }, "node_modules/@cloudflare/workerd-windows-64": { - "version": "1.20250709.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20250709.0.tgz", - "integrity": "sha512-j1AyO8V/62Q23EJplWgzBlRCqo/diXgox58AbDqSqgyzCBAlvUzXQRDBab/FPNG/erRqt7I1zQhahrBhrM0uLA==", + "version": "1.20250712.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20250712.0.tgz", + "integrity": "sha512-qS8H5RCYwE21Om9wo5/F807ClBJIfknhuLBj16eYxvJcj9JqgAKWi12BGgjyGxHuJJjeoQ63lr4wHAdbFntDDg==", "cpu": [ "x64" ], @@ -2401,16 +2401,6 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/@fastify/busboy": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", - "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14" - } - }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", @@ -10357,9 +10347,9 @@ } }, "node_modules/miniflare": { - "version": "4.20250709.0", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20250709.0.tgz", - "integrity": "sha512-dRGXi6Do9ArQZt7205QGWZ1tD6k6xQNY/mAZBAtiaQYvKxFuNyiHYlFnSN8Co4AFCVOozo/U52sVAaHvlcmnew==", + "version": "4.20250712.0", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20250712.0.tgz", + "integrity": "sha512-o7zYqG4pMi3gQTjiGhgZ82bQfexNwK+bzAaNlu8f7m3Kra4DcU5LC9nznfq2rfIBnUnMgwtU2VUfMlN1TuI8Og==", "dev": true, "dependencies": { "@cspotcode/source-map-support": "0.8.1", @@ -10369,8 +10359,8 @@ "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", - "undici": "^5.28.5", - "workerd": "1.20250709.0", + "undici": "^7.10.0", + "workerd": "1.20250712.0", "ws": "8.18.0", "youch": "4.1.0-beta.10", "zod": "3.22.3" @@ -13663,16 +13653,12 @@ "dev": true }, "node_modules/undici": { - "version": "5.29.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", - "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.11.0.tgz", + "integrity": "sha512-heTSIac3iLhsmZhUCjyS3JQEkZELateufzZuBaVM5RHXdSBMb1LPMQf5x+FH7qjsZYDP0ttAc3nnVpUB+wYbOg==", "dev": true, - "license": "MIT", - "dependencies": { - "@fastify/busboy": "^2.0.0" - }, "engines": { - "node": ">=14.0" + "node": ">=20.18.1" } }, "node_modules/undici-types": { @@ -14326,9 +14312,9 @@ "dev": true }, "node_modules/workerd": { - "version": "1.20250709.0", - "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20250709.0.tgz", - "integrity": "sha512-BqLPpmvRN+TYUSG61OkWamsGdEuMwgvabP8m0QOHIfofnrD2YVyWqE1kXJ0GH5EsVEuWamE5sR8XpTfsGBmIpg==", + "version": "1.20250712.0", + "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20250712.0.tgz", + "integrity": "sha512-7h+k1OxREpiZW0849g0uQNexRWMcs5i5gUGhJzCY8nIx6Tv4D/ndlXJ47lEFj7/LQdp165IL9dM2D5uDiedZrg==", "dev": true, "hasInstallScript": true, "bin": { @@ -14338,11 +14324,11 @@ "node": ">=16" }, "optionalDependencies": { - "@cloudflare/workerd-darwin-64": "1.20250709.0", - "@cloudflare/workerd-darwin-arm64": "1.20250709.0", - "@cloudflare/workerd-linux-64": "1.20250709.0", - "@cloudflare/workerd-linux-arm64": "1.20250709.0", - "@cloudflare/workerd-windows-64": "1.20250709.0" + "@cloudflare/workerd-darwin-64": "1.20250712.0", + "@cloudflare/workerd-darwin-arm64": "1.20250712.0", + "@cloudflare/workerd-linux-64": "1.20250712.0", + "@cloudflare/workerd-linux-arm64": "1.20250712.0", + "@cloudflare/workerd-windows-64": "1.20250712.0" } }, "node_modules/workerpool": { @@ -14352,19 +14338,19 @@ "dev": true }, "node_modules/wrangler": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.24.3.tgz", - "integrity": "sha512-stB1Wfs5NKlspsAzz8SBujBKsDqT5lpCyrL+vSUMy3uueEtI1A5qyORbKoJhIguEbwHfWS39mBsxzm6Vm1J2cg==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.25.0.tgz", + "integrity": "sha512-SepXQbzWkdp0O7qAx3i0go+fw5I0VkDqLV2G3ewbffO5k8kpvuCkhalS23KO+7+o8+Oa3vfC7x+16IL3rj2n4w==", "dev": true, "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", "@cloudflare/unenv-preset": "2.3.3", "blake3-wasm": "2.1.5", "esbuild": "0.25.4", - "miniflare": "4.20250709.0", + "miniflare": "4.20250712.0", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.17", - "workerd": "1.20250709.0" + "workerd": "1.20250712.0" }, "bin": { "wrangler": "bin/wrangler.js", @@ -14377,7 +14363,7 @@ "fsevents": "~2.3.2" }, "peerDependencies": { - "@cloudflare/workers-types": "^4.20250709.0" + "@cloudflare/workers-types": "^4.20250712.0" }, "peerDependenciesMeta": { "@cloudflare/workers-types": { @@ -15926,37 +15912,37 @@ "requires": {} }, "@cloudflare/workerd-darwin-64": { - "version": "1.20250709.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20250709.0.tgz", - "integrity": "sha512-VqwcvnbI8FNCP87ZWNHA3/sAC5U9wMbNnjBG0sHEYzM7B9RPHKYHdVKdBEWhzZXnkQYMK81IHm4CZsK16XxAuQ==", + "version": "1.20250712.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20250712.0.tgz", + "integrity": "sha512-M6S6a/LQ0Jb0R+g0XhlYi1adGifvYmxA5mD/i9TuZZgjs2bIm5ELuka/n3SCnI98ltvlx3HahRaHagAtOilsFg==", "dev": true, "optional": true }, "@cloudflare/workerd-darwin-arm64": { - "version": "1.20250709.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20250709.0.tgz", - "integrity": "sha512-A54ttSgXMM4huChPTThhkieOjpDxR+srVOO9zjTHVIyoQxA8zVsku4CcY/GQ95RczMV+yCKVVu/tAME7vwBFuA==", + "version": "1.20250712.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20250712.0.tgz", + "integrity": "sha512-7sFzn6rvAcnLy7MktFL42dYtzL0Idw/kiUmNf2P3TvsBRoShhLK5ZKhbw+NAhvU8e4pXWm5lkE0XmpieA0zNjw==", "dev": true, "optional": true }, "@cloudflare/workerd-linux-64": { - "version": "1.20250709.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20250709.0.tgz", - "integrity": "sha512-no4O3OK+VXINIxv99OHJDpIgML2ZssrSvImwLtULzqm+cl4t1PIfXNRUqj89ujTkmad+L9y4G6dBQMPCLnmlGg==", + "version": "1.20250712.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20250712.0.tgz", + "integrity": "sha512-EFRrGe/bqK7NHtht7vNlbrDpfvH3eRvtJOgsTpEQEysDjVmlK6pVJxSnLy9Hg1zlLY15IfhfGC+K2qisseHGJQ==", "dev": true, "optional": true }, "@cloudflare/workerd-linux-arm64": { - "version": "1.20250709.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20250709.0.tgz", - "integrity": "sha512-7cNICk2Qd+m4QGrcmWyAuZJXTHt1ud6isA+dic7Yk42WZmwXhlcUATyvFD9FSQNFcldjuRB4n8JlWEFqZBn+lw==", + "version": "1.20250712.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20250712.0.tgz", + "integrity": "sha512-rG8JUleddhUHQVwpXOYv0VbL0S9kOtR9PNKecgVhFpxEhC8aTeg2HNBBjo8st7IfcUvY8WaW3pD3qdAMZ05UwQ==", "dev": true, "optional": true }, "@cloudflare/workerd-windows-64": { - "version": "1.20250709.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20250709.0.tgz", - "integrity": "sha512-j1AyO8V/62Q23EJplWgzBlRCqo/diXgox58AbDqSqgyzCBAlvUzXQRDBab/FPNG/erRqt7I1zQhahrBhrM0uLA==", + "version": "1.20250712.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20250712.0.tgz", + "integrity": "sha512-qS8H5RCYwE21Om9wo5/F807ClBJIfknhuLBj16eYxvJcj9JqgAKWi12BGgjyGxHuJJjeoQ63lr4wHAdbFntDDg==", "dev": true, "optional": true }, @@ -16232,12 +16218,6 @@ "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true }, - "@fastify/busboy": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", - "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", - "dev": true - }, "@humanwhocodes/config-array": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", @@ -21905,9 +21885,9 @@ "dev": true }, "miniflare": { - "version": "4.20250709.0", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20250709.0.tgz", - "integrity": "sha512-dRGXi6Do9ArQZt7205QGWZ1tD6k6xQNY/mAZBAtiaQYvKxFuNyiHYlFnSN8Co4AFCVOozo/U52sVAaHvlcmnew==", + "version": "4.20250712.0", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20250712.0.tgz", + "integrity": "sha512-o7zYqG4pMi3gQTjiGhgZ82bQfexNwK+bzAaNlu8f7m3Kra4DcU5LC9nznfq2rfIBnUnMgwtU2VUfMlN1TuI8Og==", "dev": true, "requires": { "@cspotcode/source-map-support": "0.8.1", @@ -21917,8 +21897,8 @@ "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", - "undici": "^5.28.5", - "workerd": "1.20250709.0", + "undici": "^7.10.0", + "workerd": "1.20250712.0", "ws": "8.18.0", "youch": "4.1.0-beta.10", "zod": "3.22.3" @@ -24325,13 +24305,10 @@ "dev": true }, "undici": { - "version": "5.29.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", - "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", - "dev": true, - "requires": { - "@fastify/busboy": "^2.0.0" - } + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.11.0.tgz", + "integrity": "sha512-heTSIac3iLhsmZhUCjyS3JQEkZELateufzZuBaVM5RHXdSBMb1LPMQf5x+FH7qjsZYDP0ttAc3nnVpUB+wYbOg==", + "dev": true }, "undici-types": { "version": "6.20.0", @@ -24834,16 +24811,16 @@ "dev": true }, "workerd": { - "version": "1.20250709.0", - "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20250709.0.tgz", - "integrity": "sha512-BqLPpmvRN+TYUSG61OkWamsGdEuMwgvabP8m0QOHIfofnrD2YVyWqE1kXJ0GH5EsVEuWamE5sR8XpTfsGBmIpg==", + "version": "1.20250712.0", + "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20250712.0.tgz", + "integrity": "sha512-7h+k1OxREpiZW0849g0uQNexRWMcs5i5gUGhJzCY8nIx6Tv4D/ndlXJ47lEFj7/LQdp165IL9dM2D5uDiedZrg==", "dev": true, "requires": { - "@cloudflare/workerd-darwin-64": "1.20250709.0", - "@cloudflare/workerd-darwin-arm64": "1.20250709.0", - "@cloudflare/workerd-linux-64": "1.20250709.0", - "@cloudflare/workerd-linux-arm64": "1.20250709.0", - "@cloudflare/workerd-windows-64": "1.20250709.0" + "@cloudflare/workerd-darwin-64": "1.20250712.0", + "@cloudflare/workerd-darwin-arm64": "1.20250712.0", + "@cloudflare/workerd-linux-64": "1.20250712.0", + "@cloudflare/workerd-linux-arm64": "1.20250712.0", + "@cloudflare/workerd-windows-64": "1.20250712.0" } }, "workerpool": { @@ -24853,9 +24830,9 @@ "dev": true }, "wrangler": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.24.3.tgz", - "integrity": "sha512-stB1Wfs5NKlspsAzz8SBujBKsDqT5lpCyrL+vSUMy3uueEtI1A5qyORbKoJhIguEbwHfWS39mBsxzm6Vm1J2cg==", + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.25.0.tgz", + "integrity": "sha512-SepXQbzWkdp0O7qAx3i0go+fw5I0VkDqLV2G3ewbffO5k8kpvuCkhalS23KO+7+o8+Oa3vfC7x+16IL3rj2n4w==", "dev": true, "requires": { "@cloudflare/kv-asset-handler": "0.4.0", @@ -24863,10 +24840,10 @@ "blake3-wasm": "2.1.5", "esbuild": "0.25.4", "fsevents": "~2.3.2", - "miniflare": "4.20250709.0", + "miniflare": "4.20250712.0", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.17", - "workerd": "1.20250709.0" + "workerd": "1.20250712.0" } }, "wrap-ansi": { diff --git a/package.json b/package.json index cb6fb5b7a91..82751cf8c99 100644 --- a/package.json +++ b/package.json @@ -133,6 +133,6 @@ "sinon-chai": "3.7.0", "typescript": "5.8.3", "url-toolkit": "2.2.5", - "wrangler": "4.24.3" + "wrangler": "4.25.0" } } From 08fc81cc53d3d17d5a016eca2df2a7732d62ec67 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 19 Jul 2025 09:25:46 +0000 Subject: [PATCH 19/52] chore(deps): update dependency rollup to v4.45.0 --- package-lock.json | 338 +++++++++++++++++++++++----------------------- package.json | 2 +- 2 files changed, 170 insertions(+), 170 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0820345124b..842f4e78d22 100644 --- a/package-lock.json +++ b/package-lock.json @@ -66,7 +66,7 @@ "npm-run-all2": "8.0.4", "prettier": "3.6.2", "promise-polyfill": "8.3.0", - "rollup": "4.44.2", + "rollup": "4.45.0", "rollup-plugin-istanbul": "5.0.0", "sauce-connect-launcher": "1.3.2", "selenium-webdriver": "4.34.0", @@ -3441,9 +3441,9 @@ "dev": true }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.2.tgz", - "integrity": "sha512-g0dF8P1e2QYPOj1gu7s/3LVP6kze9A7m6x0BZ9iTdXK8N5c2V7cpBKHV3/9A4Zd8xxavdhK0t4PnqjkqVmUc9Q==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.45.0.tgz", + "integrity": "sha512-2o/FgACbji4tW1dzXOqAV15Eu7DdgbKsF2QKcxfG4xbh5iwU7yr5RRP5/U+0asQliSYv5M4o7BevlGIoSL0LXg==", "cpu": [ "arm" ], @@ -3454,9 +3454,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.2.tgz", - "integrity": "sha512-Yt5MKrOosSbSaAK5Y4J+vSiID57sOvpBNBR6K7xAaQvk3MkcNVV0f9fE20T+41WYN8hDn6SGFlFrKudtx4EoxA==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.45.0.tgz", + "integrity": "sha512-PSZ0SvMOjEAxwZeTx32eI/j5xSYtDCRxGu5k9zvzoY77xUNssZM+WV6HYBLROpY5CkXsbQjvz40fBb7WPwDqtQ==", "cpu": [ "arm64" ], @@ -3467,9 +3467,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.2.tgz", - "integrity": "sha512-EsnFot9ZieM35YNA26nhbLTJBHD0jTwWpPwmRVDzjylQT6gkar+zenfb8mHxWpRrbn+WytRRjE0WKsfaxBkVUA==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.45.0.tgz", + "integrity": "sha512-BA4yPIPssPB2aRAWzmqzQ3y2/KotkLyZukVB7j3psK/U3nVJdceo6qr9pLM2xN6iRP/wKfxEbOb1yrlZH6sYZg==", "cpu": [ "arm64" ], @@ -3480,9 +3480,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.2.tgz", - "integrity": "sha512-dv/t1t1RkCvJdWWxQ2lWOO+b7cMsVw5YFaS04oHpZRWehI1h0fV1gF4wgGCTyQHHjJDfbNpwOi6PXEafRBBezw==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.45.0.tgz", + "integrity": "sha512-Pr2o0lvTwsiG4HCr43Zy9xXrHspyMvsvEw4FwKYqhli4FuLE5FjcZzuQ4cfPe0iUFCvSQG6lACI0xj74FDZKRA==", "cpu": [ "x64" ], @@ -3493,9 +3493,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.2.tgz", - "integrity": "sha512-W4tt4BLorKND4qeHElxDoim0+BsprFTwb+vriVQnFFtT/P6v/xO5I99xvYnVzKWrK6j7Hb0yp3x7V5LUbaeOMg==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.45.0.tgz", + "integrity": "sha512-lYE8LkE5h4a/+6VnnLiL14zWMPnx6wNbDG23GcYFpRW1V9hYWHAw9lBZ6ZUIrOaoK7NliF1sdwYGiVmziUF4vA==", "cpu": [ "arm64" ], @@ -3506,9 +3506,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.2.tgz", - "integrity": "sha512-tdT1PHopokkuBVyHjvYehnIe20fxibxFCEhQP/96MDSOcyjM/shlTkZZLOufV3qO6/FQOSiJTBebhVc12JyPTA==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.45.0.tgz", + "integrity": "sha512-PVQWZK9sbzpvqC9Q0GlehNNSVHR+4m7+wET+7FgSnKG3ci5nAMgGmr9mGBXzAuE5SvguCKJ6mHL6vq1JaJ/gvw==", "cpu": [ "x64" ], @@ -3519,9 +3519,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.2.tgz", - "integrity": "sha512-+xmiDGGaSfIIOXMzkhJ++Oa0Gwvl9oXUeIiwarsdRXSe27HUIvjbSIpPxvnNsRebsNdUo7uAiQVgBD1hVriwSQ==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.45.0.tgz", + "integrity": "sha512-hLrmRl53prCcD+YXTfNvXd776HTxNh8wPAMllusQ+amcQmtgo3V5i/nkhPN6FakW+QVLoUUr2AsbtIRPFU3xIA==", "cpu": [ "arm" ], @@ -3532,9 +3532,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.2.tgz", - "integrity": "sha512-bDHvhzOfORk3wt8yxIra8N4k/N0MnKInCW5OGZaeDYa/hMrdPaJzo7CSkjKZqX4JFUWjUGm88lI6QJLCM7lDrA==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.45.0.tgz", + "integrity": "sha512-XBKGSYcrkdiRRjl+8XvrUR3AosXU0NvF7VuqMsm7s5nRy+nt58ZMB19Jdp1RdqewLcaYnpk8zeVs/4MlLZEJxw==", "cpu": [ "arm" ], @@ -3545,9 +3545,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.2.tgz", - "integrity": "sha512-NMsDEsDiYghTbeZWEGnNi4F0hSbGnsuOG+VnNvxkKg0IGDvFh7UVpM/14mnMwxRxUf9AdAVJgHPvKXf6FpMB7A==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.45.0.tgz", + "integrity": "sha512-fRvZZPUiBz7NztBE/2QnCS5AtqLVhXmUOPj9IHlfGEXkapgImf4W9+FSkL8cWqoAjozyUzqFmSc4zh2ooaeF6g==", "cpu": [ "arm64" ], @@ -3558,9 +3558,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.2.tgz", - "integrity": "sha512-lb5bxXnxXglVq+7imxykIp5xMq+idehfl+wOgiiix0191av84OqbjUED+PRC5OA8eFJYj5xAGcpAZ0pF2MnW+A==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.45.0.tgz", + "integrity": "sha512-Btv2WRZOcUGi8XU80XwIvzTg4U6+l6D0V6sZTrZx214nrwxw5nAi8hysaXj/mctyClWgesyuxbeLylCBNauimg==", "cpu": [ "arm64" ], @@ -3571,9 +3571,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.2.tgz", - "integrity": "sha512-Yl5Rdpf9pIc4GW1PmkUGHdMtbx0fBLE1//SxDmuf3X0dUC57+zMepow2LK0V21661cjXdTn8hO2tXDdAWAqE5g==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.45.0.tgz", + "integrity": "sha512-Li0emNnwtUZdLwHjQPBxn4VWztcrw/h7mgLyHiEI5Z0MhpeFGlzaiBHpSNVOMB/xucjXTTcO+dhv469Djr16KA==", "cpu": [ "loong64" ], @@ -3584,9 +3584,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.2.tgz", - "integrity": "sha512-03vUDH+w55s680YYryyr78jsO1RWU9ocRMaeV2vMniJJW/6HhoTBwyyiiTPVHNWLnhsnwcQ0oH3S9JSBEKuyqw==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.45.0.tgz", + "integrity": "sha512-sB8+pfkYx2kvpDCfd63d5ScYT0Fz1LO6jIb2zLZvmK9ob2D8DeVqrmBDE0iDK8KlBVmsTNzrjr3G1xV4eUZhSw==", "cpu": [ "ppc64" ], @@ -3597,9 +3597,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.2.tgz", - "integrity": "sha512-iYtAqBg5eEMG4dEfVlkqo05xMOk6y/JXIToRca2bAWuqjrJYJlx/I7+Z+4hSrsWU8GdJDFPL4ktV3dy4yBSrzg==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.45.0.tgz", + "integrity": "sha512-5GQ6PFhh7E6jQm70p1aW05G2cap5zMOvO0se5JMecHeAdj5ZhWEHbJ4hiKpfi1nnnEdTauDXxPgXae/mqjow9w==", "cpu": [ "riscv64" ], @@ -3610,9 +3610,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.2.tgz", - "integrity": "sha512-e6vEbgaaqz2yEHqtkPXa28fFuBGmUJ0N2dOJK8YUfijejInt9gfCSA7YDdJ4nYlv67JfP3+PSWFX4IVw/xRIPg==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.45.0.tgz", + "integrity": "sha512-N/euLsBd1rekWcuduakTo/dJw6U6sBP3eUq+RXM9RNfPuWTvG2w/WObDkIvJ2KChy6oxZmOSC08Ak2OJA0UiAA==", "cpu": [ "riscv64" ], @@ -3623,9 +3623,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.2.tgz", - "integrity": "sha512-evFOtkmVdY3udE+0QKrV5wBx7bKI0iHz5yEVx5WqDJkxp9YQefy4Mpx3RajIVcM6o7jxTvVd/qpC1IXUhGc1Mw==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.45.0.tgz", + "integrity": "sha512-2l9sA7d7QdikL0xQwNMO3xURBUNEWyHVHfAsHsUdq+E/pgLTUcCE+gih5PCdmyHmfTDeXUWVhqL0WZzg0nua3g==", "cpu": [ "s390x" ], @@ -3636,9 +3636,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.2.tgz", - "integrity": "sha512-/bXb0bEsWMyEkIsUL2Yt5nFB5naLAwyOWMEviQfQY1x3l5WsLKgvZf66TM7UTfED6erckUVUJQ/jJ1FSpm3pRQ==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.45.0.tgz", + "integrity": "sha512-XZdD3fEEQcwG2KrJDdEQu7NrHonPxxaV0/w2HpvINBdcqebz1aL+0vM2WFJq4DeiAVT6F5SUQas65HY5JDqoPw==", "cpu": [ "x64" ], @@ -3649,9 +3649,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.2.tgz", - "integrity": "sha512-3D3OB1vSSBXmkGEZR27uiMRNiwN08/RVAcBKwhUYPaiZ8bcvdeEwWPvbnXvvXHY+A/7xluzcN+kaiOFNiOZwWg==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.45.0.tgz", + "integrity": "sha512-7ayfgvtmmWgKWBkCGg5+xTQ0r5V1owVm67zTrsEY1008L5ro7mCyGYORomARt/OquB9KY7LpxVBZes+oSniAAQ==", "cpu": [ "x64" ], @@ -3662,9 +3662,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.2.tgz", - "integrity": "sha512-VfU0fsMK+rwdK8mwODqYeM2hDrF2WiHaSmCBrS7gColkQft95/8tphyzv2EupVxn3iE0FI78wzffoULH1G+dkw==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.45.0.tgz", + "integrity": "sha512-B+IJgcBnE2bm93jEW5kHisqvPITs4ddLOROAcOc/diBgrEiQJJ6Qcjby75rFSmH5eMGrqJryUgJDhrfj942apQ==", "cpu": [ "arm64" ], @@ -3675,9 +3675,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.2.tgz", - "integrity": "sha512-+qMUrkbUurpE6DVRjiJCNGZBGo9xM4Y0FXU5cjgudWqIBWbcLkjE3XprJUsOFgC6xjBClwVa9k6O3A7K3vxb5Q==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.45.0.tgz", + "integrity": "sha512-+CXwwG66g0/FpWOnP/v1HnrGVSOygK/osUbu3wPRy8ECXjoYKjRAyfxYpDQOfghC5qPJYLPH0oN4MCOjwgdMug==", "cpu": [ "ia32" ], @@ -3688,9 +3688,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.2.tgz", - "integrity": "sha512-3+QZROYfJ25PDcxFF66UEk8jGWigHJeecZILvkPkyQN7oc5BvFo4YEXFkOs154j3FTMp9mn9Ky8RCOwastduEA==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.45.0.tgz", + "integrity": "sha512-SRf1cytG7wqcHVLrBc9VtPK4pU5wxiB/lNIkNmW2ApKXIg+RpqwHfsaEK+e7eH4A1BpI6BX/aBWXxZCIrJg3uA==", "cpu": [ "x64" ], @@ -12111,9 +12111,9 @@ } }, "node_modules/rollup": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.2.tgz", - "integrity": "sha512-PVoapzTwSEcelaWGth3uR66u7ZRo6qhPHc0f2uRO9fX6XDVNrIiGYS0Pj9+R8yIIYSD/mCx2b16Ws9itljKSPg==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.45.0.tgz", + "integrity": "sha512-WLjEcJRIo7i3WDDgOIJqVI2d+lAC3EwvOGy+Xfq6hs+GQuAA4Di/H72xmXkOhrIWFg2PFYSKZYfH0f4vfKXN4A==", "dev": true, "dependencies": { "@types/estree": "1.0.8" @@ -12126,26 +12126,26 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.44.2", - "@rollup/rollup-android-arm64": "4.44.2", - "@rollup/rollup-darwin-arm64": "4.44.2", - "@rollup/rollup-darwin-x64": "4.44.2", - "@rollup/rollup-freebsd-arm64": "4.44.2", - "@rollup/rollup-freebsd-x64": "4.44.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.44.2", - "@rollup/rollup-linux-arm-musleabihf": "4.44.2", - "@rollup/rollup-linux-arm64-gnu": "4.44.2", - "@rollup/rollup-linux-arm64-musl": "4.44.2", - "@rollup/rollup-linux-loongarch64-gnu": "4.44.2", - "@rollup/rollup-linux-powerpc64le-gnu": "4.44.2", - "@rollup/rollup-linux-riscv64-gnu": "4.44.2", - "@rollup/rollup-linux-riscv64-musl": "4.44.2", - "@rollup/rollup-linux-s390x-gnu": "4.44.2", - "@rollup/rollup-linux-x64-gnu": "4.44.2", - "@rollup/rollup-linux-x64-musl": "4.44.2", - "@rollup/rollup-win32-arm64-msvc": "4.44.2", - "@rollup/rollup-win32-ia32-msvc": "4.44.2", - "@rollup/rollup-win32-x64-msvc": "4.44.2", + "@rollup/rollup-android-arm-eabi": "4.45.0", + "@rollup/rollup-android-arm64": "4.45.0", + "@rollup/rollup-darwin-arm64": "4.45.0", + "@rollup/rollup-darwin-x64": "4.45.0", + "@rollup/rollup-freebsd-arm64": "4.45.0", + "@rollup/rollup-freebsd-x64": "4.45.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.45.0", + "@rollup/rollup-linux-arm-musleabihf": "4.45.0", + "@rollup/rollup-linux-arm64-gnu": "4.45.0", + "@rollup/rollup-linux-arm64-musl": "4.45.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.45.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.45.0", + "@rollup/rollup-linux-riscv64-gnu": "4.45.0", + "@rollup/rollup-linux-riscv64-musl": "4.45.0", + "@rollup/rollup-linux-s390x-gnu": "4.45.0", + "@rollup/rollup-linux-x64-gnu": "4.45.0", + "@rollup/rollup-linux-x64-musl": "4.45.0", + "@rollup/rollup-win32-arm64-msvc": "4.45.0", + "@rollup/rollup-win32-ia32-msvc": "4.45.0", + "@rollup/rollup-win32-x64-msvc": "4.45.0", "fsevents": "~2.3.2" } }, @@ -16834,142 +16834,142 @@ } }, "@rollup/rollup-android-arm-eabi": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.2.tgz", - "integrity": "sha512-g0dF8P1e2QYPOj1gu7s/3LVP6kze9A7m6x0BZ9iTdXK8N5c2V7cpBKHV3/9A4Zd8xxavdhK0t4PnqjkqVmUc9Q==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.45.0.tgz", + "integrity": "sha512-2o/FgACbji4tW1dzXOqAV15Eu7DdgbKsF2QKcxfG4xbh5iwU7yr5RRP5/U+0asQliSYv5M4o7BevlGIoSL0LXg==", "dev": true, "optional": true }, "@rollup/rollup-android-arm64": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.2.tgz", - "integrity": "sha512-Yt5MKrOosSbSaAK5Y4J+vSiID57sOvpBNBR6K7xAaQvk3MkcNVV0f9fE20T+41WYN8hDn6SGFlFrKudtx4EoxA==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.45.0.tgz", + "integrity": "sha512-PSZ0SvMOjEAxwZeTx32eI/j5xSYtDCRxGu5k9zvzoY77xUNssZM+WV6HYBLROpY5CkXsbQjvz40fBb7WPwDqtQ==", "dev": true, "optional": true }, "@rollup/rollup-darwin-arm64": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.2.tgz", - "integrity": "sha512-EsnFot9ZieM35YNA26nhbLTJBHD0jTwWpPwmRVDzjylQT6gkar+zenfb8mHxWpRrbn+WytRRjE0WKsfaxBkVUA==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.45.0.tgz", + "integrity": "sha512-BA4yPIPssPB2aRAWzmqzQ3y2/KotkLyZukVB7j3psK/U3nVJdceo6qr9pLM2xN6iRP/wKfxEbOb1yrlZH6sYZg==", "dev": true, "optional": true }, "@rollup/rollup-darwin-x64": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.2.tgz", - "integrity": "sha512-dv/t1t1RkCvJdWWxQ2lWOO+b7cMsVw5YFaS04oHpZRWehI1h0fV1gF4wgGCTyQHHjJDfbNpwOi6PXEafRBBezw==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.45.0.tgz", + "integrity": "sha512-Pr2o0lvTwsiG4HCr43Zy9xXrHspyMvsvEw4FwKYqhli4FuLE5FjcZzuQ4cfPe0iUFCvSQG6lACI0xj74FDZKRA==", "dev": true, "optional": true }, "@rollup/rollup-freebsd-arm64": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.2.tgz", - "integrity": "sha512-W4tt4BLorKND4qeHElxDoim0+BsprFTwb+vriVQnFFtT/P6v/xO5I99xvYnVzKWrK6j7Hb0yp3x7V5LUbaeOMg==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.45.0.tgz", + "integrity": "sha512-lYE8LkE5h4a/+6VnnLiL14zWMPnx6wNbDG23GcYFpRW1V9hYWHAw9lBZ6ZUIrOaoK7NliF1sdwYGiVmziUF4vA==", "dev": true, "optional": true }, "@rollup/rollup-freebsd-x64": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.2.tgz", - "integrity": "sha512-tdT1PHopokkuBVyHjvYehnIe20fxibxFCEhQP/96MDSOcyjM/shlTkZZLOufV3qO6/FQOSiJTBebhVc12JyPTA==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.45.0.tgz", + "integrity": "sha512-PVQWZK9sbzpvqC9Q0GlehNNSVHR+4m7+wET+7FgSnKG3ci5nAMgGmr9mGBXzAuE5SvguCKJ6mHL6vq1JaJ/gvw==", "dev": true, "optional": true }, "@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.2.tgz", - "integrity": "sha512-+xmiDGGaSfIIOXMzkhJ++Oa0Gwvl9oXUeIiwarsdRXSe27HUIvjbSIpPxvnNsRebsNdUo7uAiQVgBD1hVriwSQ==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.45.0.tgz", + "integrity": "sha512-hLrmRl53prCcD+YXTfNvXd776HTxNh8wPAMllusQ+amcQmtgo3V5i/nkhPN6FakW+QVLoUUr2AsbtIRPFU3xIA==", "dev": true, "optional": true }, "@rollup/rollup-linux-arm-musleabihf": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.2.tgz", - "integrity": "sha512-bDHvhzOfORk3wt8yxIra8N4k/N0MnKInCW5OGZaeDYa/hMrdPaJzo7CSkjKZqX4JFUWjUGm88lI6QJLCM7lDrA==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.45.0.tgz", + "integrity": "sha512-XBKGSYcrkdiRRjl+8XvrUR3AosXU0NvF7VuqMsm7s5nRy+nt58ZMB19Jdp1RdqewLcaYnpk8zeVs/4MlLZEJxw==", "dev": true, "optional": true }, "@rollup/rollup-linux-arm64-gnu": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.2.tgz", - "integrity": "sha512-NMsDEsDiYghTbeZWEGnNi4F0hSbGnsuOG+VnNvxkKg0IGDvFh7UVpM/14mnMwxRxUf9AdAVJgHPvKXf6FpMB7A==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.45.0.tgz", + "integrity": "sha512-fRvZZPUiBz7NztBE/2QnCS5AtqLVhXmUOPj9IHlfGEXkapgImf4W9+FSkL8cWqoAjozyUzqFmSc4zh2ooaeF6g==", "dev": true, "optional": true }, "@rollup/rollup-linux-arm64-musl": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.2.tgz", - "integrity": "sha512-lb5bxXnxXglVq+7imxykIp5xMq+idehfl+wOgiiix0191av84OqbjUED+PRC5OA8eFJYj5xAGcpAZ0pF2MnW+A==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.45.0.tgz", + "integrity": "sha512-Btv2WRZOcUGi8XU80XwIvzTg4U6+l6D0V6sZTrZx214nrwxw5nAi8hysaXj/mctyClWgesyuxbeLylCBNauimg==", "dev": true, "optional": true }, "@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.2.tgz", - "integrity": "sha512-Yl5Rdpf9pIc4GW1PmkUGHdMtbx0fBLE1//SxDmuf3X0dUC57+zMepow2LK0V21661cjXdTn8hO2tXDdAWAqE5g==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.45.0.tgz", + "integrity": "sha512-Li0emNnwtUZdLwHjQPBxn4VWztcrw/h7mgLyHiEI5Z0MhpeFGlzaiBHpSNVOMB/xucjXTTcO+dhv469Djr16KA==", "dev": true, "optional": true }, "@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.2.tgz", - "integrity": "sha512-03vUDH+w55s680YYryyr78jsO1RWU9ocRMaeV2vMniJJW/6HhoTBwyyiiTPVHNWLnhsnwcQ0oH3S9JSBEKuyqw==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.45.0.tgz", + "integrity": "sha512-sB8+pfkYx2kvpDCfd63d5ScYT0Fz1LO6jIb2zLZvmK9ob2D8DeVqrmBDE0iDK8KlBVmsTNzrjr3G1xV4eUZhSw==", "dev": true, "optional": true }, "@rollup/rollup-linux-riscv64-gnu": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.2.tgz", - "integrity": "sha512-iYtAqBg5eEMG4dEfVlkqo05xMOk6y/JXIToRca2bAWuqjrJYJlx/I7+Z+4hSrsWU8GdJDFPL4ktV3dy4yBSrzg==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.45.0.tgz", + "integrity": "sha512-5GQ6PFhh7E6jQm70p1aW05G2cap5zMOvO0se5JMecHeAdj5ZhWEHbJ4hiKpfi1nnnEdTauDXxPgXae/mqjow9w==", "dev": true, "optional": true }, "@rollup/rollup-linux-riscv64-musl": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.2.tgz", - "integrity": "sha512-e6vEbgaaqz2yEHqtkPXa28fFuBGmUJ0N2dOJK8YUfijejInt9gfCSA7YDdJ4nYlv67JfP3+PSWFX4IVw/xRIPg==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.45.0.tgz", + "integrity": "sha512-N/euLsBd1rekWcuduakTo/dJw6U6sBP3eUq+RXM9RNfPuWTvG2w/WObDkIvJ2KChy6oxZmOSC08Ak2OJA0UiAA==", "dev": true, "optional": true }, "@rollup/rollup-linux-s390x-gnu": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.2.tgz", - "integrity": "sha512-evFOtkmVdY3udE+0QKrV5wBx7bKI0iHz5yEVx5WqDJkxp9YQefy4Mpx3RajIVcM6o7jxTvVd/qpC1IXUhGc1Mw==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.45.0.tgz", + "integrity": "sha512-2l9sA7d7QdikL0xQwNMO3xURBUNEWyHVHfAsHsUdq+E/pgLTUcCE+gih5PCdmyHmfTDeXUWVhqL0WZzg0nua3g==", "dev": true, "optional": true }, "@rollup/rollup-linux-x64-gnu": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.2.tgz", - "integrity": "sha512-/bXb0bEsWMyEkIsUL2Yt5nFB5naLAwyOWMEviQfQY1x3l5WsLKgvZf66TM7UTfED6erckUVUJQ/jJ1FSpm3pRQ==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.45.0.tgz", + "integrity": "sha512-XZdD3fEEQcwG2KrJDdEQu7NrHonPxxaV0/w2HpvINBdcqebz1aL+0vM2WFJq4DeiAVT6F5SUQas65HY5JDqoPw==", "dev": true, "optional": true }, "@rollup/rollup-linux-x64-musl": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.2.tgz", - "integrity": "sha512-3D3OB1vSSBXmkGEZR27uiMRNiwN08/RVAcBKwhUYPaiZ8bcvdeEwWPvbnXvvXHY+A/7xluzcN+kaiOFNiOZwWg==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.45.0.tgz", + "integrity": "sha512-7ayfgvtmmWgKWBkCGg5+xTQ0r5V1owVm67zTrsEY1008L5ro7mCyGYORomARt/OquB9KY7LpxVBZes+oSniAAQ==", "dev": true, "optional": true }, "@rollup/rollup-win32-arm64-msvc": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.2.tgz", - "integrity": "sha512-VfU0fsMK+rwdK8mwODqYeM2hDrF2WiHaSmCBrS7gColkQft95/8tphyzv2EupVxn3iE0FI78wzffoULH1G+dkw==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.45.0.tgz", + "integrity": "sha512-B+IJgcBnE2bm93jEW5kHisqvPITs4ddLOROAcOc/diBgrEiQJJ6Qcjby75rFSmH5eMGrqJryUgJDhrfj942apQ==", "dev": true, "optional": true }, "@rollup/rollup-win32-ia32-msvc": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.2.tgz", - "integrity": "sha512-+qMUrkbUurpE6DVRjiJCNGZBGo9xM4Y0FXU5cjgudWqIBWbcLkjE3XprJUsOFgC6xjBClwVa9k6O3A7K3vxb5Q==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.45.0.tgz", + "integrity": "sha512-+CXwwG66g0/FpWOnP/v1HnrGVSOygK/osUbu3wPRy8ECXjoYKjRAyfxYpDQOfghC5qPJYLPH0oN4MCOjwgdMug==", "dev": true, "optional": true }, "@rollup/rollup-win32-x64-msvc": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.2.tgz", - "integrity": "sha512-3+QZROYfJ25PDcxFF66UEk8jGWigHJeecZILvkPkyQN7oc5BvFo4YEXFkOs154j3FTMp9mn9Ky8RCOwastduEA==", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.45.0.tgz", + "integrity": "sha512-SRf1cytG7wqcHVLrBc9VtPK4pU5wxiB/lNIkNmW2ApKXIg+RpqwHfsaEK+e7eH4A1BpI6BX/aBWXxZCIrJg3uA==", "dev": true, "optional": true }, @@ -23201,31 +23201,31 @@ } }, "rollup": { - "version": "4.44.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.2.tgz", - "integrity": "sha512-PVoapzTwSEcelaWGth3uR66u7ZRo6qhPHc0f2uRO9fX6XDVNrIiGYS0Pj9+R8yIIYSD/mCx2b16Ws9itljKSPg==", - "dev": true, - "requires": { - "@rollup/rollup-android-arm-eabi": "4.44.2", - "@rollup/rollup-android-arm64": "4.44.2", - "@rollup/rollup-darwin-arm64": "4.44.2", - "@rollup/rollup-darwin-x64": "4.44.2", - "@rollup/rollup-freebsd-arm64": "4.44.2", - "@rollup/rollup-freebsd-x64": "4.44.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.44.2", - "@rollup/rollup-linux-arm-musleabihf": "4.44.2", - "@rollup/rollup-linux-arm64-gnu": "4.44.2", - "@rollup/rollup-linux-arm64-musl": "4.44.2", - "@rollup/rollup-linux-loongarch64-gnu": "4.44.2", - "@rollup/rollup-linux-powerpc64le-gnu": "4.44.2", - "@rollup/rollup-linux-riscv64-gnu": "4.44.2", - "@rollup/rollup-linux-riscv64-musl": "4.44.2", - "@rollup/rollup-linux-s390x-gnu": "4.44.2", - "@rollup/rollup-linux-x64-gnu": "4.44.2", - "@rollup/rollup-linux-x64-musl": "4.44.2", - "@rollup/rollup-win32-arm64-msvc": "4.44.2", - "@rollup/rollup-win32-ia32-msvc": "4.44.2", - "@rollup/rollup-win32-x64-msvc": "4.44.2", + "version": "4.45.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.45.0.tgz", + "integrity": "sha512-WLjEcJRIo7i3WDDgOIJqVI2d+lAC3EwvOGy+Xfq6hs+GQuAA4Di/H72xmXkOhrIWFg2PFYSKZYfH0f4vfKXN4A==", + "dev": true, + "requires": { + "@rollup/rollup-android-arm-eabi": "4.45.0", + "@rollup/rollup-android-arm64": "4.45.0", + "@rollup/rollup-darwin-arm64": "4.45.0", + "@rollup/rollup-darwin-x64": "4.45.0", + "@rollup/rollup-freebsd-arm64": "4.45.0", + "@rollup/rollup-freebsd-x64": "4.45.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.45.0", + "@rollup/rollup-linux-arm-musleabihf": "4.45.0", + "@rollup/rollup-linux-arm64-gnu": "4.45.0", + "@rollup/rollup-linux-arm64-musl": "4.45.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.45.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.45.0", + "@rollup/rollup-linux-riscv64-gnu": "4.45.0", + "@rollup/rollup-linux-riscv64-musl": "4.45.0", + "@rollup/rollup-linux-s390x-gnu": "4.45.0", + "@rollup/rollup-linux-x64-gnu": "4.45.0", + "@rollup/rollup-linux-x64-musl": "4.45.0", + "@rollup/rollup-win32-arm64-msvc": "4.45.0", + "@rollup/rollup-win32-ia32-msvc": "4.45.0", + "@rollup/rollup-win32-x64-msvc": "4.45.0", "@types/estree": "1.0.8", "fsevents": "~2.3.2" }, diff --git a/package.json b/package.json index 82751cf8c99..0847ce67e92 100644 --- a/package.json +++ b/package.json @@ -124,7 +124,7 @@ "npm-run-all2": "8.0.4", "prettier": "3.6.2", "promise-polyfill": "8.3.0", - "rollup": "4.44.2", + "rollup": "4.45.0", "rollup-plugin-istanbul": "5.0.0", "sauce-connect-launcher": "1.3.2", "selenium-webdriver": "4.34.0", From e061287a3b8f143739f7aba867fec4065902970b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 19 Jul 2025 09:41:39 +0000 Subject: [PATCH 20/52] chore(deps): update dependency rollup to v4.45.1 --- package-lock.json | 338 +++++++++++++++++++++++----------------------- package.json | 2 +- 2 files changed, 170 insertions(+), 170 deletions(-) diff --git a/package-lock.json b/package-lock.json index 842f4e78d22..233f9fe5404 100644 --- a/package-lock.json +++ b/package-lock.json @@ -66,7 +66,7 @@ "npm-run-all2": "8.0.4", "prettier": "3.6.2", "promise-polyfill": "8.3.0", - "rollup": "4.45.0", + "rollup": "4.45.1", "rollup-plugin-istanbul": "5.0.0", "sauce-connect-launcher": "1.3.2", "selenium-webdriver": "4.34.0", @@ -3441,9 +3441,9 @@ "dev": true }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.45.0.tgz", - "integrity": "sha512-2o/FgACbji4tW1dzXOqAV15Eu7DdgbKsF2QKcxfG4xbh5iwU7yr5RRP5/U+0asQliSYv5M4o7BevlGIoSL0LXg==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.45.1.tgz", + "integrity": "sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==", "cpu": [ "arm" ], @@ -3454,9 +3454,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.45.0.tgz", - "integrity": "sha512-PSZ0SvMOjEAxwZeTx32eI/j5xSYtDCRxGu5k9zvzoY77xUNssZM+WV6HYBLROpY5CkXsbQjvz40fBb7WPwDqtQ==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.45.1.tgz", + "integrity": "sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==", "cpu": [ "arm64" ], @@ -3467,9 +3467,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.45.0.tgz", - "integrity": "sha512-BA4yPIPssPB2aRAWzmqzQ3y2/KotkLyZukVB7j3psK/U3nVJdceo6qr9pLM2xN6iRP/wKfxEbOb1yrlZH6sYZg==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.45.1.tgz", + "integrity": "sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==", "cpu": [ "arm64" ], @@ -3480,9 +3480,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.45.0.tgz", - "integrity": "sha512-Pr2o0lvTwsiG4HCr43Zy9xXrHspyMvsvEw4FwKYqhli4FuLE5FjcZzuQ4cfPe0iUFCvSQG6lACI0xj74FDZKRA==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.45.1.tgz", + "integrity": "sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==", "cpu": [ "x64" ], @@ -3493,9 +3493,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.45.0.tgz", - "integrity": "sha512-lYE8LkE5h4a/+6VnnLiL14zWMPnx6wNbDG23GcYFpRW1V9hYWHAw9lBZ6ZUIrOaoK7NliF1sdwYGiVmziUF4vA==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.45.1.tgz", + "integrity": "sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==", "cpu": [ "arm64" ], @@ -3506,9 +3506,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.45.0.tgz", - "integrity": "sha512-PVQWZK9sbzpvqC9Q0GlehNNSVHR+4m7+wET+7FgSnKG3ci5nAMgGmr9mGBXzAuE5SvguCKJ6mHL6vq1JaJ/gvw==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.45.1.tgz", + "integrity": "sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==", "cpu": [ "x64" ], @@ -3519,9 +3519,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.45.0.tgz", - "integrity": "sha512-hLrmRl53prCcD+YXTfNvXd776HTxNh8wPAMllusQ+amcQmtgo3V5i/nkhPN6FakW+QVLoUUr2AsbtIRPFU3xIA==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.45.1.tgz", + "integrity": "sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==", "cpu": [ "arm" ], @@ -3532,9 +3532,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.45.0.tgz", - "integrity": "sha512-XBKGSYcrkdiRRjl+8XvrUR3AosXU0NvF7VuqMsm7s5nRy+nt58ZMB19Jdp1RdqewLcaYnpk8zeVs/4MlLZEJxw==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.45.1.tgz", + "integrity": "sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==", "cpu": [ "arm" ], @@ -3545,9 +3545,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.45.0.tgz", - "integrity": "sha512-fRvZZPUiBz7NztBE/2QnCS5AtqLVhXmUOPj9IHlfGEXkapgImf4W9+FSkL8cWqoAjozyUzqFmSc4zh2ooaeF6g==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.45.1.tgz", + "integrity": "sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==", "cpu": [ "arm64" ], @@ -3558,9 +3558,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.45.0.tgz", - "integrity": "sha512-Btv2WRZOcUGi8XU80XwIvzTg4U6+l6D0V6sZTrZx214nrwxw5nAi8hysaXj/mctyClWgesyuxbeLylCBNauimg==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.45.1.tgz", + "integrity": "sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==", "cpu": [ "arm64" ], @@ -3571,9 +3571,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.45.0.tgz", - "integrity": "sha512-Li0emNnwtUZdLwHjQPBxn4VWztcrw/h7mgLyHiEI5Z0MhpeFGlzaiBHpSNVOMB/xucjXTTcO+dhv469Djr16KA==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.45.1.tgz", + "integrity": "sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==", "cpu": [ "loong64" ], @@ -3584,9 +3584,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.45.0.tgz", - "integrity": "sha512-sB8+pfkYx2kvpDCfd63d5ScYT0Fz1LO6jIb2zLZvmK9ob2D8DeVqrmBDE0iDK8KlBVmsTNzrjr3G1xV4eUZhSw==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.45.1.tgz", + "integrity": "sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==", "cpu": [ "ppc64" ], @@ -3597,9 +3597,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.45.0.tgz", - "integrity": "sha512-5GQ6PFhh7E6jQm70p1aW05G2cap5zMOvO0se5JMecHeAdj5ZhWEHbJ4hiKpfi1nnnEdTauDXxPgXae/mqjow9w==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.45.1.tgz", + "integrity": "sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==", "cpu": [ "riscv64" ], @@ -3610,9 +3610,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.45.0.tgz", - "integrity": "sha512-N/euLsBd1rekWcuduakTo/dJw6U6sBP3eUq+RXM9RNfPuWTvG2w/WObDkIvJ2KChy6oxZmOSC08Ak2OJA0UiAA==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.45.1.tgz", + "integrity": "sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==", "cpu": [ "riscv64" ], @@ -3623,9 +3623,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.45.0.tgz", - "integrity": "sha512-2l9sA7d7QdikL0xQwNMO3xURBUNEWyHVHfAsHsUdq+E/pgLTUcCE+gih5PCdmyHmfTDeXUWVhqL0WZzg0nua3g==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.45.1.tgz", + "integrity": "sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==", "cpu": [ "s390x" ], @@ -3636,9 +3636,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.45.0.tgz", - "integrity": "sha512-XZdD3fEEQcwG2KrJDdEQu7NrHonPxxaV0/w2HpvINBdcqebz1aL+0vM2WFJq4DeiAVT6F5SUQas65HY5JDqoPw==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.45.1.tgz", + "integrity": "sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==", "cpu": [ "x64" ], @@ -3649,9 +3649,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.45.0.tgz", - "integrity": "sha512-7ayfgvtmmWgKWBkCGg5+xTQ0r5V1owVm67zTrsEY1008L5ro7mCyGYORomARt/OquB9KY7LpxVBZes+oSniAAQ==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.45.1.tgz", + "integrity": "sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==", "cpu": [ "x64" ], @@ -3662,9 +3662,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.45.0.tgz", - "integrity": "sha512-B+IJgcBnE2bm93jEW5kHisqvPITs4ddLOROAcOc/diBgrEiQJJ6Qcjby75rFSmH5eMGrqJryUgJDhrfj942apQ==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.45.1.tgz", + "integrity": "sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==", "cpu": [ "arm64" ], @@ -3675,9 +3675,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.45.0.tgz", - "integrity": "sha512-+CXwwG66g0/FpWOnP/v1HnrGVSOygK/osUbu3wPRy8ECXjoYKjRAyfxYpDQOfghC5qPJYLPH0oN4MCOjwgdMug==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.45.1.tgz", + "integrity": "sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==", "cpu": [ "ia32" ], @@ -3688,9 +3688,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.45.0.tgz", - "integrity": "sha512-SRf1cytG7wqcHVLrBc9VtPK4pU5wxiB/lNIkNmW2ApKXIg+RpqwHfsaEK+e7eH4A1BpI6BX/aBWXxZCIrJg3uA==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.45.1.tgz", + "integrity": "sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==", "cpu": [ "x64" ], @@ -12111,9 +12111,9 @@ } }, "node_modules/rollup": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.45.0.tgz", - "integrity": "sha512-WLjEcJRIo7i3WDDgOIJqVI2d+lAC3EwvOGy+Xfq6hs+GQuAA4Di/H72xmXkOhrIWFg2PFYSKZYfH0f4vfKXN4A==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.45.1.tgz", + "integrity": "sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==", "dev": true, "dependencies": { "@types/estree": "1.0.8" @@ -12126,26 +12126,26 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.45.0", - "@rollup/rollup-android-arm64": "4.45.0", - "@rollup/rollup-darwin-arm64": "4.45.0", - "@rollup/rollup-darwin-x64": "4.45.0", - "@rollup/rollup-freebsd-arm64": "4.45.0", - "@rollup/rollup-freebsd-x64": "4.45.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.45.0", - "@rollup/rollup-linux-arm-musleabihf": "4.45.0", - "@rollup/rollup-linux-arm64-gnu": "4.45.0", - "@rollup/rollup-linux-arm64-musl": "4.45.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.45.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.45.0", - "@rollup/rollup-linux-riscv64-gnu": "4.45.0", - "@rollup/rollup-linux-riscv64-musl": "4.45.0", - "@rollup/rollup-linux-s390x-gnu": "4.45.0", - "@rollup/rollup-linux-x64-gnu": "4.45.0", - "@rollup/rollup-linux-x64-musl": "4.45.0", - "@rollup/rollup-win32-arm64-msvc": "4.45.0", - "@rollup/rollup-win32-ia32-msvc": "4.45.0", - "@rollup/rollup-win32-x64-msvc": "4.45.0", + "@rollup/rollup-android-arm-eabi": "4.45.1", + "@rollup/rollup-android-arm64": "4.45.1", + "@rollup/rollup-darwin-arm64": "4.45.1", + "@rollup/rollup-darwin-x64": "4.45.1", + "@rollup/rollup-freebsd-arm64": "4.45.1", + "@rollup/rollup-freebsd-x64": "4.45.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.45.1", + "@rollup/rollup-linux-arm-musleabihf": "4.45.1", + "@rollup/rollup-linux-arm64-gnu": "4.45.1", + "@rollup/rollup-linux-arm64-musl": "4.45.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.45.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.45.1", + "@rollup/rollup-linux-riscv64-gnu": "4.45.1", + "@rollup/rollup-linux-riscv64-musl": "4.45.1", + "@rollup/rollup-linux-s390x-gnu": "4.45.1", + "@rollup/rollup-linux-x64-gnu": "4.45.1", + "@rollup/rollup-linux-x64-musl": "4.45.1", + "@rollup/rollup-win32-arm64-msvc": "4.45.1", + "@rollup/rollup-win32-ia32-msvc": "4.45.1", + "@rollup/rollup-win32-x64-msvc": "4.45.1", "fsevents": "~2.3.2" } }, @@ -16834,142 +16834,142 @@ } }, "@rollup/rollup-android-arm-eabi": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.45.0.tgz", - "integrity": "sha512-2o/FgACbji4tW1dzXOqAV15Eu7DdgbKsF2QKcxfG4xbh5iwU7yr5RRP5/U+0asQliSYv5M4o7BevlGIoSL0LXg==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.45.1.tgz", + "integrity": "sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==", "dev": true, "optional": true }, "@rollup/rollup-android-arm64": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.45.0.tgz", - "integrity": "sha512-PSZ0SvMOjEAxwZeTx32eI/j5xSYtDCRxGu5k9zvzoY77xUNssZM+WV6HYBLROpY5CkXsbQjvz40fBb7WPwDqtQ==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.45.1.tgz", + "integrity": "sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==", "dev": true, "optional": true }, "@rollup/rollup-darwin-arm64": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.45.0.tgz", - "integrity": "sha512-BA4yPIPssPB2aRAWzmqzQ3y2/KotkLyZukVB7j3psK/U3nVJdceo6qr9pLM2xN6iRP/wKfxEbOb1yrlZH6sYZg==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.45.1.tgz", + "integrity": "sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==", "dev": true, "optional": true }, "@rollup/rollup-darwin-x64": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.45.0.tgz", - "integrity": "sha512-Pr2o0lvTwsiG4HCr43Zy9xXrHspyMvsvEw4FwKYqhli4FuLE5FjcZzuQ4cfPe0iUFCvSQG6lACI0xj74FDZKRA==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.45.1.tgz", + "integrity": "sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==", "dev": true, "optional": true }, "@rollup/rollup-freebsd-arm64": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.45.0.tgz", - "integrity": "sha512-lYE8LkE5h4a/+6VnnLiL14zWMPnx6wNbDG23GcYFpRW1V9hYWHAw9lBZ6ZUIrOaoK7NliF1sdwYGiVmziUF4vA==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.45.1.tgz", + "integrity": "sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==", "dev": true, "optional": true }, "@rollup/rollup-freebsd-x64": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.45.0.tgz", - "integrity": "sha512-PVQWZK9sbzpvqC9Q0GlehNNSVHR+4m7+wET+7FgSnKG3ci5nAMgGmr9mGBXzAuE5SvguCKJ6mHL6vq1JaJ/gvw==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.45.1.tgz", + "integrity": "sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==", "dev": true, "optional": true }, "@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.45.0.tgz", - "integrity": "sha512-hLrmRl53prCcD+YXTfNvXd776HTxNh8wPAMllusQ+amcQmtgo3V5i/nkhPN6FakW+QVLoUUr2AsbtIRPFU3xIA==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.45.1.tgz", + "integrity": "sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==", "dev": true, "optional": true }, "@rollup/rollup-linux-arm-musleabihf": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.45.0.tgz", - "integrity": "sha512-XBKGSYcrkdiRRjl+8XvrUR3AosXU0NvF7VuqMsm7s5nRy+nt58ZMB19Jdp1RdqewLcaYnpk8zeVs/4MlLZEJxw==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.45.1.tgz", + "integrity": "sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==", "dev": true, "optional": true }, "@rollup/rollup-linux-arm64-gnu": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.45.0.tgz", - "integrity": "sha512-fRvZZPUiBz7NztBE/2QnCS5AtqLVhXmUOPj9IHlfGEXkapgImf4W9+FSkL8cWqoAjozyUzqFmSc4zh2ooaeF6g==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.45.1.tgz", + "integrity": "sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==", "dev": true, "optional": true }, "@rollup/rollup-linux-arm64-musl": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.45.0.tgz", - "integrity": "sha512-Btv2WRZOcUGi8XU80XwIvzTg4U6+l6D0V6sZTrZx214nrwxw5nAi8hysaXj/mctyClWgesyuxbeLylCBNauimg==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.45.1.tgz", + "integrity": "sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==", "dev": true, "optional": true }, "@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.45.0.tgz", - "integrity": "sha512-Li0emNnwtUZdLwHjQPBxn4VWztcrw/h7mgLyHiEI5Z0MhpeFGlzaiBHpSNVOMB/xucjXTTcO+dhv469Djr16KA==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.45.1.tgz", + "integrity": "sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==", "dev": true, "optional": true }, "@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.45.0.tgz", - "integrity": "sha512-sB8+pfkYx2kvpDCfd63d5ScYT0Fz1LO6jIb2zLZvmK9ob2D8DeVqrmBDE0iDK8KlBVmsTNzrjr3G1xV4eUZhSw==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.45.1.tgz", + "integrity": "sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==", "dev": true, "optional": true }, "@rollup/rollup-linux-riscv64-gnu": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.45.0.tgz", - "integrity": "sha512-5GQ6PFhh7E6jQm70p1aW05G2cap5zMOvO0se5JMecHeAdj5ZhWEHbJ4hiKpfi1nnnEdTauDXxPgXae/mqjow9w==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.45.1.tgz", + "integrity": "sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==", "dev": true, "optional": true }, "@rollup/rollup-linux-riscv64-musl": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.45.0.tgz", - "integrity": "sha512-N/euLsBd1rekWcuduakTo/dJw6U6sBP3eUq+RXM9RNfPuWTvG2w/WObDkIvJ2KChy6oxZmOSC08Ak2OJA0UiAA==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.45.1.tgz", + "integrity": "sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==", "dev": true, "optional": true }, "@rollup/rollup-linux-s390x-gnu": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.45.0.tgz", - "integrity": "sha512-2l9sA7d7QdikL0xQwNMO3xURBUNEWyHVHfAsHsUdq+E/pgLTUcCE+gih5PCdmyHmfTDeXUWVhqL0WZzg0nua3g==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.45.1.tgz", + "integrity": "sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==", "dev": true, "optional": true }, "@rollup/rollup-linux-x64-gnu": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.45.0.tgz", - "integrity": "sha512-XZdD3fEEQcwG2KrJDdEQu7NrHonPxxaV0/w2HpvINBdcqebz1aL+0vM2WFJq4DeiAVT6F5SUQas65HY5JDqoPw==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.45.1.tgz", + "integrity": "sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==", "dev": true, "optional": true }, "@rollup/rollup-linux-x64-musl": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.45.0.tgz", - "integrity": "sha512-7ayfgvtmmWgKWBkCGg5+xTQ0r5V1owVm67zTrsEY1008L5ro7mCyGYORomARt/OquB9KY7LpxVBZes+oSniAAQ==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.45.1.tgz", + "integrity": "sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==", "dev": true, "optional": true }, "@rollup/rollup-win32-arm64-msvc": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.45.0.tgz", - "integrity": "sha512-B+IJgcBnE2bm93jEW5kHisqvPITs4ddLOROAcOc/diBgrEiQJJ6Qcjby75rFSmH5eMGrqJryUgJDhrfj942apQ==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.45.1.tgz", + "integrity": "sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==", "dev": true, "optional": true }, "@rollup/rollup-win32-ia32-msvc": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.45.0.tgz", - "integrity": "sha512-+CXwwG66g0/FpWOnP/v1HnrGVSOygK/osUbu3wPRy8ECXjoYKjRAyfxYpDQOfghC5qPJYLPH0oN4MCOjwgdMug==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.45.1.tgz", + "integrity": "sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==", "dev": true, "optional": true }, "@rollup/rollup-win32-x64-msvc": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.45.0.tgz", - "integrity": "sha512-SRf1cytG7wqcHVLrBc9VtPK4pU5wxiB/lNIkNmW2ApKXIg+RpqwHfsaEK+e7eH4A1BpI6BX/aBWXxZCIrJg3uA==", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.45.1.tgz", + "integrity": "sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==", "dev": true, "optional": true }, @@ -23201,31 +23201,31 @@ } }, "rollup": { - "version": "4.45.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.45.0.tgz", - "integrity": "sha512-WLjEcJRIo7i3WDDgOIJqVI2d+lAC3EwvOGy+Xfq6hs+GQuAA4Di/H72xmXkOhrIWFg2PFYSKZYfH0f4vfKXN4A==", - "dev": true, - "requires": { - "@rollup/rollup-android-arm-eabi": "4.45.0", - "@rollup/rollup-android-arm64": "4.45.0", - "@rollup/rollup-darwin-arm64": "4.45.0", - "@rollup/rollup-darwin-x64": "4.45.0", - "@rollup/rollup-freebsd-arm64": "4.45.0", - "@rollup/rollup-freebsd-x64": "4.45.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.45.0", - "@rollup/rollup-linux-arm-musleabihf": "4.45.0", - "@rollup/rollup-linux-arm64-gnu": "4.45.0", - "@rollup/rollup-linux-arm64-musl": "4.45.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.45.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.45.0", - "@rollup/rollup-linux-riscv64-gnu": "4.45.0", - "@rollup/rollup-linux-riscv64-musl": "4.45.0", - "@rollup/rollup-linux-s390x-gnu": "4.45.0", - "@rollup/rollup-linux-x64-gnu": "4.45.0", - "@rollup/rollup-linux-x64-musl": "4.45.0", - "@rollup/rollup-win32-arm64-msvc": "4.45.0", - "@rollup/rollup-win32-ia32-msvc": "4.45.0", - "@rollup/rollup-win32-x64-msvc": "4.45.0", + "version": "4.45.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.45.1.tgz", + "integrity": "sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==", + "dev": true, + "requires": { + "@rollup/rollup-android-arm-eabi": "4.45.1", + "@rollup/rollup-android-arm64": "4.45.1", + "@rollup/rollup-darwin-arm64": "4.45.1", + "@rollup/rollup-darwin-x64": "4.45.1", + "@rollup/rollup-freebsd-arm64": "4.45.1", + "@rollup/rollup-freebsd-x64": "4.45.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.45.1", + "@rollup/rollup-linux-arm-musleabihf": "4.45.1", + "@rollup/rollup-linux-arm64-gnu": "4.45.1", + "@rollup/rollup-linux-arm64-musl": "4.45.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.45.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.45.1", + "@rollup/rollup-linux-riscv64-gnu": "4.45.1", + "@rollup/rollup-linux-riscv64-musl": "4.45.1", + "@rollup/rollup-linux-s390x-gnu": "4.45.1", + "@rollup/rollup-linux-x64-gnu": "4.45.1", + "@rollup/rollup-linux-x64-musl": "4.45.1", + "@rollup/rollup-win32-arm64-msvc": "4.45.1", + "@rollup/rollup-win32-ia32-msvc": "4.45.1", + "@rollup/rollup-win32-x64-msvc": "4.45.1", "@types/estree": "1.0.8", "fsevents": "~2.3.2" }, diff --git a/package.json b/package.json index 0847ce67e92..fcf869fdd4e 100644 --- a/package.json +++ b/package.json @@ -124,7 +124,7 @@ "npm-run-all2": "8.0.4", "prettier": "3.6.2", "promise-polyfill": "8.3.0", - "rollup": "4.45.0", + "rollup": "4.45.1", "rollup-plugin-istanbul": "5.0.0", "sauce-connect-launcher": "1.3.2", "selenium-webdriver": "4.34.0", From cbe5f890378215e6f445079d9190cc3328211bbb Mon Sep 17 00:00:00 2001 From: devoldemar Date: Sat, 19 Jul 2025 14:57:32 +0500 Subject: [PATCH 21/52] Ignore offsets related to default_display_window_flag in HEVC-TS demuxer --- src/demux/video/hevc-video-parser.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/demux/video/hevc-video-parser.ts b/src/demux/video/hevc-video-parser.ts index 485ee85c6bd..61d00640a4c 100644 --- a/src/demux/video/hevc-video-parser.ts +++ b/src/demux/video/hevc-video-parser.ts @@ -516,10 +516,10 @@ class HevcVideoParser extends BaseVideoParser { eg.readBoolean(); // frame_field_info_present_flag default_display_window_flag = eg.readBoolean(); if (default_display_window_flag) { - pic_left_offset += eg.readUEG(); - pic_right_offset += eg.readUEG(); - pic_top_offset += eg.readUEG(); - pic_bottom_offset += eg.readUEG(); + eg.skipUEG(); + eg.skipUEG(); + eg.skipUEG(); + eg.skipUEG(); } const vui_timing_info_present_flag = eg.readBoolean(); if (vui_timing_info_present_flag) { @@ -604,7 +604,7 @@ class HevcVideoParser extends BaseVideoParser { let width = pic_width_in_luma_samples, height = pic_height_in_luma_samples; - if (conformance_window_flag || default_display_window_flag) { + if (conformance_window_flag) { let chroma_scale_w = 1, chroma_scale_h = 1; if (chroma_format_idc === 1) { From 67b62e9b20487b642ca87ba04e624a0847ca4301 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 23 Jul 2025 02:53:47 +0000 Subject: [PATCH 22/52] chore(deps): update dependency chromedriver to v138.0.3 --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 233f9fe5404..9929e70c2ab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,7 +36,7 @@ "babel-plugin-transform-remove-console": "6.9.4", "chai": "4.5.0", "chart.js": "2.9.4", - "chromedriver": "138.0.2", + "chromedriver": "138.0.3", "doctoc": "2.2.1", "es-check": "9.1.4", "eslint": "8.57.1", @@ -5470,9 +5470,9 @@ } }, "node_modules/chromedriver": { - "version": "138.0.2", - "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-138.0.2.tgz", - "integrity": "sha512-mmAtTCK0GHum+zbgcv3PYDX7tqNdTXDsd2t6WWI/Tm/Ts2xCGlc5XUcL3oxw1Dn/kmPJOurwrYJKO7vV4xkisA==", + "version": "138.0.3", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-138.0.3.tgz", + "integrity": "sha512-RKcfzbUthmQzFmy91F9StQQwNZ72khp3febF/RntpkDKhhCkwor0cgop00diwzAVSUq1s2e8B54Iema9FQnynw==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -18345,9 +18345,9 @@ "peer": true }, "chromedriver": { - "version": "138.0.2", - "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-138.0.2.tgz", - "integrity": "sha512-mmAtTCK0GHum+zbgcv3PYDX7tqNdTXDsd2t6WWI/Tm/Ts2xCGlc5XUcL3oxw1Dn/kmPJOurwrYJKO7vV4xkisA==", + "version": "138.0.3", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-138.0.3.tgz", + "integrity": "sha512-RKcfzbUthmQzFmy91F9StQQwNZ72khp3febF/RntpkDKhhCkwor0cgop00diwzAVSUq1s2e8B54Iema9FQnynw==", "dev": true, "requires": { "@testim/chrome-version": "^1.1.4", diff --git a/package.json b/package.json index fcf869fdd4e..c1d6adac6df 100644 --- a/package.json +++ b/package.json @@ -94,7 +94,7 @@ "babel-plugin-transform-remove-console": "6.9.4", "chai": "4.5.0", "chart.js": "2.9.4", - "chromedriver": "138.0.2", + "chromedriver": "138.0.3", "doctoc": "2.2.1", "es-check": "9.1.4", "eslint": "8.57.1", From 51c4784c7db85e4f22d43d8a111649c6597fb0a2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 23 Jul 2025 03:10:13 +0000 Subject: [PATCH 23/52] chore(deps): update node.js to v22.17.1 --- .node-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.node-version b/.node-version index fc37597bccd..7377d130eda 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -22.17.0 +22.17.1 From e51bc871cea2abd7bc586455780fd89a21c2df99 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 25 Jul 2025 18:41:25 +0000 Subject: [PATCH 24/52] chore(deps): update dependency eslint-config-prettier to v10.1.8 --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9929e70c2ab..427f9d71ee9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,7 +40,7 @@ "doctoc": "2.2.1", "es-check": "9.1.4", "eslint": "8.57.1", - "eslint-config-prettier": "10.1.5", + "eslint-config-prettier": "10.1.8", "eslint-plugin-import": "2.32.0", "eslint-plugin-mocha": "10.5.0", "eslint-plugin-n": "17.21.0", @@ -6707,9 +6707,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz", - "integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==", + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "bin": { "eslint-config-prettier": "bin/cli.js" @@ -19369,9 +19369,9 @@ } }, "eslint-config-prettier": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz", - "integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==", + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "requires": {} }, diff --git a/package.json b/package.json index c1d6adac6df..6b7baa8f22a 100644 --- a/package.json +++ b/package.json @@ -98,7 +98,7 @@ "doctoc": "2.2.1", "es-check": "9.1.4", "eslint": "8.57.1", - "eslint-config-prettier": "10.1.5", + "eslint-config-prettier": "10.1.8", "eslint-plugin-import": "2.32.0", "eslint-plugin-mocha": "10.5.0", "eslint-plugin-n": "17.21.0", From 07ac7456e5cbbefc85ab92b051f114d398be8263 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 19:36:38 +0000 Subject: [PATCH 25/52] chore(deps): update typescript-eslint monorepo to v8.38.0 --- package-lock.json | 218 +++++++++++++++++++++++----------------------- package.json | 4 +- 2 files changed, 111 insertions(+), 111 deletions(-) diff --git a/package-lock.json b/package-lock.json index 427f9d71ee9..894de2ac800 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,8 +30,8 @@ "@types/chart.js": "2.9.41", "@types/mocha": "10.0.10", "@types/sinon-chai": "3.2.12", - "@typescript-eslint/eslint-plugin": "8.37.0", - "@typescript-eslint/parser": "8.37.0", + "@typescript-eslint/eslint-plugin": "8.38.0", + "@typescript-eslint/parser": "8.38.0", "babel-loader": "10.0.0", "babel-plugin-transform-remove-console": "6.9.4", "chai": "4.5.0", @@ -4114,16 +4114,16 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.37.0.tgz", - "integrity": "sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz", + "integrity": "sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/type-utils": "8.37.0", - "@typescript-eslint/utils": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/type-utils": "8.38.0", + "@typescript-eslint/utils": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -4137,7 +4137,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.37.0", + "@typescript-eslint/parser": "^8.38.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } @@ -4152,15 +4152,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.37.0.tgz", - "integrity": "sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.38.0.tgz", + "integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "debug": "^4.3.4" }, "engines": { @@ -4176,13 +4176,13 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", - "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz", + "integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==", "dev": true, "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.37.0", - "@typescript-eslint/types": "^8.37.0", + "@typescript-eslint/tsconfig-utils": "^8.38.0", + "@typescript-eslint/types": "^8.38.0", "debug": "^4.3.4" }, "engines": { @@ -4197,13 +4197,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", - "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz", + "integrity": "sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0" + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4214,9 +4214,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", - "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz", + "integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4230,14 +4230,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz", - "integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.38.0.tgz", + "integrity": "sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/utils": "8.37.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -4254,9 +4254,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", - "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz", + "integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4267,15 +4267,15 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", - "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz", + "integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==", "dev": true, "dependencies": { - "@typescript-eslint/project-service": "8.37.0", - "@typescript-eslint/tsconfig-utils": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/project-service": "8.38.0", + "@typescript-eslint/tsconfig-utils": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -4319,15 +4319,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", - "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz", + "integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0" + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4342,12 +4342,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", - "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz", + "integrity": "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/types": "8.38.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -17337,16 +17337,16 @@ } }, "@typescript-eslint/eslint-plugin": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.37.0.tgz", - "integrity": "sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.38.0.tgz", + "integrity": "sha512-CPoznzpuAnIOl4nhj4tRr4gIPj5AfKgkiJmGQDaq+fQnRJTYlcBjbX3wbciGmpoPf8DREufuPRe1tNMZnGdanA==", "dev": true, "requires": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/type-utils": "8.37.0", - "@typescript-eslint/utils": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/type-utils": "8.38.0", + "@typescript-eslint/utils": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -17362,75 +17362,75 @@ } }, "@typescript-eslint/parser": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.37.0.tgz", - "integrity": "sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.38.0.tgz", + "integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "debug": "^4.3.4" } }, "@typescript-eslint/project-service": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz", - "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.38.0.tgz", + "integrity": "sha512-dbK7Jvqcb8c9QfH01YB6pORpqX1mn5gDZc9n63Ak/+jD67oWXn3Gs0M6vddAN+eDXBCS5EmNWzbSxsn9SzFWWg==", "dev": true, "requires": { - "@typescript-eslint/tsconfig-utils": "^8.37.0", - "@typescript-eslint/types": "^8.37.0", + "@typescript-eslint/tsconfig-utils": "^8.38.0", + "@typescript-eslint/types": "^8.38.0", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz", - "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.38.0.tgz", + "integrity": "sha512-WJw3AVlFFcdT9Ri1xs/lg8LwDqgekWXWhH3iAF+1ZM+QPd7oxQ6jvtW/JPwzAScxitILUIFs0/AnQ/UWHzbATQ==", "dev": true, "requires": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0" + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0" } }, "@typescript-eslint/tsconfig-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz", - "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.38.0.tgz", + "integrity": "sha512-Lum9RtSE3EroKk/bYns+sPOodqb2Fv50XOl/gMviMKNvanETUuUcC9ObRbzrJ4VSd2JalPqgSAavwrPiPvnAiQ==", "dev": true, "requires": {} }, "@typescript-eslint/type-utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz", - "integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.38.0.tgz", + "integrity": "sha512-c7jAvGEZVf0ao2z+nnz8BUaHZD09Agbh+DY7qvBQqLiz8uJzRgVPj5YvOh8I8uEiH8oIUGIfHzMwUcGVco/SJg==", "dev": true, "requires": { - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0", - "@typescript-eslint/utils": "8.37.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0", + "@typescript-eslint/utils": "8.38.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" } }, "@typescript-eslint/types": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz", - "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.38.0.tgz", + "integrity": "sha512-wzkUfX3plUqij4YwWaJyqhiPE5UCRVlFpKn1oCRn2O1bJ592XxWJj8ROQ3JD5MYXLORW84063z3tZTb/cs4Tyw==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz", - "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.38.0.tgz", + "integrity": "sha512-fooELKcAKzxux6fA6pxOflpNS0jc+nOQEEOipXFNjSlBS6fqrJOVY/whSn70SScHrcJ2LDsxWrneFoWYSVfqhQ==", "dev": true, "requires": { - "@typescript-eslint/project-service": "8.37.0", - "@typescript-eslint/tsconfig-utils": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/visitor-keys": "8.37.0", + "@typescript-eslint/project-service": "8.38.0", + "@typescript-eslint/tsconfig-utils": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/visitor-keys": "8.38.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -17460,24 +17460,24 @@ } }, "@typescript-eslint/utils": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz", - "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.38.0.tgz", + "integrity": "sha512-hHcMA86Hgt+ijJlrD8fX0j1j8w4C92zue/8LOPAFioIno+W0+L7KqE8QZKCcPGc/92Vs9x36w/4MPTJhqXdyvg==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.37.0", - "@typescript-eslint/types": "8.37.0", - "@typescript-eslint/typescript-estree": "8.37.0" + "@typescript-eslint/scope-manager": "8.38.0", + "@typescript-eslint/types": "8.38.0", + "@typescript-eslint/typescript-estree": "8.38.0" } }, "@typescript-eslint/visitor-keys": { - "version": "8.37.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz", - "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==", + "version": "8.38.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.38.0.tgz", + "integrity": "sha512-pWrTcoFNWuwHlA9CvlfSsGWs14JxfN1TH25zM5L7o0pRLhsoZkDnTsXfQRJBEWJoV5DL0jf+Z+sxiud+K0mq1g==", "dev": true, "requires": { - "@typescript-eslint/types": "8.37.0", + "@typescript-eslint/types": "8.38.0", "eslint-visitor-keys": "^4.2.1" }, "dependencies": { diff --git a/package.json b/package.json index 6b7baa8f22a..b7946e42daa 100644 --- a/package.json +++ b/package.json @@ -88,8 +88,8 @@ "@types/chart.js": "2.9.41", "@types/mocha": "10.0.10", "@types/sinon-chai": "3.2.12", - "@typescript-eslint/eslint-plugin": "8.37.0", - "@typescript-eslint/parser": "8.37.0", + "@typescript-eslint/eslint-plugin": "8.38.0", + "@typescript-eslint/parser": "8.38.0", "babel-loader": "10.0.0", "babel-plugin-transform-remove-console": "6.9.4", "chai": "4.5.0", From a7b7e08123908f85a2d7ce8528f09ac5adb4fda8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 02:40:10 +0000 Subject: [PATCH 26/52] chore(deps): update dependency @svta/common-media-library to v0.17.1 --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 894de2ac800..2a2f8a07fc4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,7 @@ "@rollup/plugin-replace": "6.0.2", "@rollup/plugin-terser": "0.4.4", "@rollup/plugin-typescript": "12.1.4", - "@svta/common-media-library": "0.16.0", + "@svta/common-media-library": "0.17.1", "@types/chai": "4.3.20", "@types/chart.js": "2.9.41", "@types/mocha": "10.0.10", @@ -3914,9 +3914,9 @@ "dev": true }, "node_modules/@svta/common-media-library": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/@svta/common-media-library/-/common-media-library-0.16.0.tgz", - "integrity": "sha512-qR8z8b5cQbVzaxT7iso1xSDve/se90Aq+F5caOatk+Np9u+2VRXUkXFCKx7vKQ1ILACGjLv6Yhpx+2dGWI/Bqg==", + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/@svta/common-media-library/-/common-media-library-0.17.1.tgz", + "integrity": "sha512-UcmqRe1cJ/OloNEeXqKJjbw6MAKPaGZUk4yLsy1QOwtzUmo7hDVvZeIoqXlmoXZNQRQ/P1MsNuboKirsTw9e7w==", "dev": true, "engines": { "node": ">=20" @@ -17142,9 +17142,9 @@ "dev": true }, "@svta/common-media-library": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/@svta/common-media-library/-/common-media-library-0.16.0.tgz", - "integrity": "sha512-qR8z8b5cQbVzaxT7iso1xSDve/se90Aq+F5caOatk+Np9u+2VRXUkXFCKx7vKQ1ILACGjLv6Yhpx+2dGWI/Bqg==", + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/@svta/common-media-library/-/common-media-library-0.17.1.tgz", + "integrity": "sha512-UcmqRe1cJ/OloNEeXqKJjbw6MAKPaGZUk4yLsy1QOwtzUmo7hDVvZeIoqXlmoXZNQRQ/P1MsNuboKirsTw9e7w==", "dev": true }, "@testim/chrome-version": { diff --git a/package.json b/package.json index b7946e42daa..9d3a8141e08 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "@rollup/plugin-replace": "6.0.2", "@rollup/plugin-terser": "0.4.4", "@rollup/plugin-typescript": "12.1.4", - "@svta/common-media-library": "0.16.0", + "@svta/common-media-library": "0.17.1", "@types/chai": "4.3.20", "@types/chart.js": "2.9.41", "@types/mocha": "10.0.10", From 986b3eafbb790d69fbcdf29c2cefdc9517e11106 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 18:13:45 +0000 Subject: [PATCH 27/52] chore(deps): update dependency wrangler to v4.25.1 --- package-lock.json | 30 +++++++++++++++--------------- package.json | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2a2f8a07fc4..421d24a4965 100644 --- a/package-lock.json +++ b/package-lock.json @@ -75,7 +75,7 @@ "sinon-chai": "3.7.0", "typescript": "5.8.3", "url-toolkit": "2.2.5", - "wrangler": "4.25.0" + "wrangler": "4.25.1" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -10347,9 +10347,9 @@ } }, "node_modules/miniflare": { - "version": "4.20250712.0", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20250712.0.tgz", - "integrity": "sha512-o7zYqG4pMi3gQTjiGhgZ82bQfexNwK+bzAaNlu8f7m3Kra4DcU5LC9nznfq2rfIBnUnMgwtU2VUfMlN1TuI8Og==", + "version": "4.20250712.1", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20250712.1.tgz", + "integrity": "sha512-46gB3FGPOsy+EpFGufjhr8agYycO/55d6l0y7hNJ13NcTVwrObMg/0HmI3pC5yQj0974IVXzBgUfDBMAX6thow==", "dev": true, "dependencies": { "@cspotcode/source-map-support": "0.8.1", @@ -14338,16 +14338,16 @@ "dev": true }, "node_modules/wrangler": { - "version": "4.25.0", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.25.0.tgz", - "integrity": "sha512-SepXQbzWkdp0O7qAx3i0go+fw5I0VkDqLV2G3ewbffO5k8kpvuCkhalS23KO+7+o8+Oa3vfC7x+16IL3rj2n4w==", + "version": "4.25.1", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.25.1.tgz", + "integrity": "sha512-4Tlg+jmqxCX3xFm+Nz1b4jHHY9iOu1EyJ17SSCCJ6MGp+FCGtXgr+CynT94+MP0v/qKQUkMKjoeJ5FNDunZ9cA==", "dev": true, "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", "@cloudflare/unenv-preset": "2.3.3", "blake3-wasm": "2.1.5", "esbuild": "0.25.4", - "miniflare": "4.20250712.0", + "miniflare": "4.20250712.1", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.17", "workerd": "1.20250712.0" @@ -21885,9 +21885,9 @@ "dev": true }, "miniflare": { - "version": "4.20250712.0", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20250712.0.tgz", - "integrity": "sha512-o7zYqG4pMi3gQTjiGhgZ82bQfexNwK+bzAaNlu8f7m3Kra4DcU5LC9nznfq2rfIBnUnMgwtU2VUfMlN1TuI8Og==", + "version": "4.20250712.1", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20250712.1.tgz", + "integrity": "sha512-46gB3FGPOsy+EpFGufjhr8agYycO/55d6l0y7hNJ13NcTVwrObMg/0HmI3pC5yQj0974IVXzBgUfDBMAX6thow==", "dev": true, "requires": { "@cspotcode/source-map-support": "0.8.1", @@ -24830,9 +24830,9 @@ "dev": true }, "wrangler": { - "version": "4.25.0", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.25.0.tgz", - "integrity": "sha512-SepXQbzWkdp0O7qAx3i0go+fw5I0VkDqLV2G3ewbffO5k8kpvuCkhalS23KO+7+o8+Oa3vfC7x+16IL3rj2n4w==", + "version": "4.25.1", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.25.1.tgz", + "integrity": "sha512-4Tlg+jmqxCX3xFm+Nz1b4jHHY9iOu1EyJ17SSCCJ6MGp+FCGtXgr+CynT94+MP0v/qKQUkMKjoeJ5FNDunZ9cA==", "dev": true, "requires": { "@cloudflare/kv-asset-handler": "0.4.0", @@ -24840,7 +24840,7 @@ "blake3-wasm": "2.1.5", "esbuild": "0.25.4", "fsevents": "~2.3.2", - "miniflare": "4.20250712.0", + "miniflare": "4.20250712.1", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.17", "workerd": "1.20250712.0" diff --git a/package.json b/package.json index 9d3a8141e08..2869f00faa3 100644 --- a/package.json +++ b/package.json @@ -133,6 +133,6 @@ "sinon-chai": "3.7.0", "typescript": "5.8.3", "url-toolkit": "2.2.5", - "wrangler": "4.25.0" + "wrangler": "4.25.1" } } From e0e07ab034571ae6669d497289024b1755f8e07c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 29 Jul 2025 18:31:03 +0000 Subject: [PATCH 28/52] chore(deps): update dependency wrangler to v4.26.1 --- package-lock.json | 254 +++++++++++++++++++++++----------------------- package.json | 2 +- 2 files changed, 128 insertions(+), 128 deletions(-) diff --git a/package-lock.json b/package-lock.json index 421d24a4965..caa67a406e0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -75,7 +75,7 @@ "sinon-chai": "3.7.0", "typescript": "5.8.3", "url-toolkit": "2.2.5", - "wrangler": "4.25.1" + "wrangler": "4.26.1" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -1752,25 +1752,10 @@ "node": ">=10.0.0" } }, - "node_modules/@cloudflare/unenv-preset": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/@cloudflare/unenv-preset/-/unenv-preset-2.3.3.tgz", - "integrity": "sha512-/M3MEcj3V2WHIRSW1eAQBPRJ6JnGQHc6JKMAPLkDb7pLs3m6X9ES/+K3ceGqxI6TKeF32AWAi7ls0AYzVxCP0A==", - "dev": true, - "peerDependencies": { - "unenv": "2.0.0-rc.17", - "workerd": "^1.20250508.0" - }, - "peerDependenciesMeta": { - "workerd": { - "optional": true - } - } - }, "node_modules/@cloudflare/workerd-darwin-64": { - "version": "1.20250712.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20250712.0.tgz", - "integrity": "sha512-M6S6a/LQ0Jb0R+g0XhlYi1adGifvYmxA5mD/i9TuZZgjs2bIm5ELuka/n3SCnI98ltvlx3HahRaHagAtOilsFg==", + "version": "1.20250726.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20250726.0.tgz", + "integrity": "sha512-SOpQqQ2blLY0io/vErve44vJC1M5i7RHuMBdrdEPIEtxiLBTdOOVp4nqZ3KchocxZjskgTc2N4N3b5hNYuKDGw==", "cpu": [ "x64" ], @@ -1784,9 +1769,9 @@ } }, "node_modules/@cloudflare/workerd-darwin-arm64": { - "version": "1.20250712.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20250712.0.tgz", - "integrity": "sha512-7sFzn6rvAcnLy7MktFL42dYtzL0Idw/kiUmNf2P3TvsBRoShhLK5ZKhbw+NAhvU8e4pXWm5lkE0XmpieA0zNjw==", + "version": "1.20250726.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20250726.0.tgz", + "integrity": "sha512-I+TOQ+YQahxL/K7eS2GJzv5CZzSVaZoyqfB15Q71MT/+wyzPCaFDTt+fg3uXdwpaIQEMUfqFNpTQSqbKHAYNgA==", "cpu": [ "arm64" ], @@ -1800,9 +1785,9 @@ } }, "node_modules/@cloudflare/workerd-linux-64": { - "version": "1.20250712.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20250712.0.tgz", - "integrity": "sha512-EFRrGe/bqK7NHtht7vNlbrDpfvH3eRvtJOgsTpEQEysDjVmlK6pVJxSnLy9Hg1zlLY15IfhfGC+K2qisseHGJQ==", + "version": "1.20250726.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20250726.0.tgz", + "integrity": "sha512-WSCv4o2uOW6b++ROVazrEW+jjZdBqCmXmmt7uVVfvjVxlzoYVwK9IvV2IXe4gsJ99HG9I0YCa7AT743cZ7TNNg==", "cpu": [ "x64" ], @@ -1816,9 +1801,9 @@ } }, "node_modules/@cloudflare/workerd-linux-arm64": { - "version": "1.20250712.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20250712.0.tgz", - "integrity": "sha512-rG8JUleddhUHQVwpXOYv0VbL0S9kOtR9PNKecgVhFpxEhC8aTeg2HNBBjo8st7IfcUvY8WaW3pD3qdAMZ05UwQ==", + "version": "1.20250726.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20250726.0.tgz", + "integrity": "sha512-jNokAGL3EQqH+31b0dX8+tlbKdjt/0UtTLvgD1e+7bOD92lzjYMa/CixHyMIY/FVvhsN4TNqfiz4cqroABTlhg==", "cpu": [ "arm64" ], @@ -1832,9 +1817,9 @@ } }, "node_modules/@cloudflare/workerd-windows-64": { - "version": "1.20250712.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20250712.0.tgz", - "integrity": "sha512-qS8H5RCYwE21Om9wo5/F807ClBJIfknhuLBj16eYxvJcj9JqgAKWi12BGgjyGxHuJJjeoQ63lr4wHAdbFntDDg==", + "version": "1.20250726.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20250726.0.tgz", + "integrity": "sha512-DiPTY63TNh6/ylvfutNQzYZi688x6NJDjQoqf5uiCp7xHweWx+GpVs42sZPeeXqCNvhm4dYjHjuigXJNh7t8Uw==", "cpu": [ "x64" ], @@ -7256,11 +7241,10 @@ } }, "node_modules/exsolve": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.5.tgz", - "integrity": "sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg==", - "dev": true, - "license": "MIT" + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz", + "integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==", + "dev": true }, "node_modules/extend": { "version": "3.0.2", @@ -10347,9 +10331,9 @@ } }, "node_modules/miniflare": { - "version": "4.20250712.1", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20250712.1.tgz", - "integrity": "sha512-46gB3FGPOsy+EpFGufjhr8agYycO/55d6l0y7hNJ13NcTVwrObMg/0HmI3pC5yQj0974IVXzBgUfDBMAX6thow==", + "version": "4.20250726.0", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20250726.0.tgz", + "integrity": "sha512-7+/RQQ9dNsyGfR2XN2RDLultf7HHrJ5YltSXSeyQGUpzGU3iYlFhh9Smg+ygkkOJ3+trf0bgwixOnqnnWpc9ZQ==", "dev": true, "dependencies": { "@cspotcode/source-map-support": "0.8.1", @@ -10360,7 +10344,7 @@ "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "^7.10.0", - "workerd": "1.20250712.0", + "workerd": "1.20250726.0", "ws": "8.18.0", "youch": "4.1.0-beta.10", "zod": "3.22.3" @@ -13668,20 +13652,6 @@ "dev": true, "license": "MIT" }, - "node_modules/unenv": { - "version": "2.0.0-rc.17", - "resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.17.tgz", - "integrity": "sha512-B06u0wXkEd+o5gOCMl/ZHl5cfpYbDZKAT+HWTL+Hws6jWu7dCiqBBXXXzMFcFVJb8D4ytAnYmxJA83uwOQRSsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "defu": "^6.1.4", - "exsolve": "^1.0.4", - "ohash": "^2.0.11", - "pathe": "^2.0.3", - "ufo": "^1.6.1" - } - }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", @@ -14312,9 +14282,9 @@ "dev": true }, "node_modules/workerd": { - "version": "1.20250712.0", - "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20250712.0.tgz", - "integrity": "sha512-7h+k1OxREpiZW0849g0uQNexRWMcs5i5gUGhJzCY8nIx6Tv4D/ndlXJ47lEFj7/LQdp165IL9dM2D5uDiedZrg==", + "version": "1.20250726.0", + "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20250726.0.tgz", + "integrity": "sha512-wDZqSKfIfQ2eVTUL6UawXdXEKPPyzRTnVdbhoKGq3NFrMxd+7v1cNH92u8775Qo1zO5S+GyWonQmZPFakXLvGw==", "dev": true, "hasInstallScript": true, "bin": { @@ -14324,11 +14294,11 @@ "node": ">=16" }, "optionalDependencies": { - "@cloudflare/workerd-darwin-64": "1.20250712.0", - "@cloudflare/workerd-darwin-arm64": "1.20250712.0", - "@cloudflare/workerd-linux-64": "1.20250712.0", - "@cloudflare/workerd-linux-arm64": "1.20250712.0", - "@cloudflare/workerd-windows-64": "1.20250712.0" + "@cloudflare/workerd-darwin-64": "1.20250726.0", + "@cloudflare/workerd-darwin-arm64": "1.20250726.0", + "@cloudflare/workerd-linux-64": "1.20250726.0", + "@cloudflare/workerd-linux-arm64": "1.20250726.0", + "@cloudflare/workerd-windows-64": "1.20250726.0" } }, "node_modules/workerpool": { @@ -14338,19 +14308,19 @@ "dev": true }, "node_modules/wrangler": { - "version": "4.25.1", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.25.1.tgz", - "integrity": "sha512-4Tlg+jmqxCX3xFm+Nz1b4jHHY9iOu1EyJ17SSCCJ6MGp+FCGtXgr+CynT94+MP0v/qKQUkMKjoeJ5FNDunZ9cA==", + "version": "4.26.1", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.26.1.tgz", + "integrity": "sha512-zGFEtHrjTAWOngm+zwEvYCxFwMSIBrzHa3Yu6rAxYMEzsT8PPvo2rdswyUJiUkpE9s2Depr37opceaY7JxEYFw==", "dev": true, "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", - "@cloudflare/unenv-preset": "2.3.3", + "@cloudflare/unenv-preset": "2.5.0", "blake3-wasm": "2.1.5", "esbuild": "0.25.4", - "miniflare": "4.20250712.1", + "miniflare": "4.20250726.0", "path-to-regexp": "6.3.0", - "unenv": "2.0.0-rc.17", - "workerd": "1.20250712.0" + "unenv": "2.0.0-rc.19", + "workerd": "1.20250726.0" }, "bin": { "wrangler": "bin/wrangler.js", @@ -14363,7 +14333,7 @@ "fsevents": "~2.3.2" }, "peerDependencies": { - "@cloudflare/workers-types": "^4.20250712.0" + "@cloudflare/workers-types": "^4.20250726.0" }, "peerDependenciesMeta": { "@cloudflare/workers-types": { @@ -14371,6 +14341,34 @@ } } }, + "node_modules/wrangler/node_modules/@cloudflare/unenv-preset": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@cloudflare/unenv-preset/-/unenv-preset-2.5.0.tgz", + "integrity": "sha512-CZe9B2VbjIQjBTyc+KoZcN1oUcm4T6GgCXoel9O7647djHuSRAa6sM6G+NdxWArATZgeMMbsvn9C50GCcnIatA==", + "dev": true, + "peerDependencies": { + "unenv": "2.0.0-rc.19", + "workerd": "^1.20250722.0" + }, + "peerDependenciesMeta": { + "workerd": { + "optional": true + } + } + }, + "node_modules/wrangler/node_modules/unenv": { + "version": "2.0.0-rc.19", + "resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.19.tgz", + "integrity": "sha512-t/OMHBNAkknVCI7bVB9OWjUUAwhVv9vsPIAGnNUxnu3FxPQN11rjh0sksLMzc3g7IlTgvHmOTl4JM7JHpcv5wA==", + "dev": true, + "dependencies": { + "defu": "^6.1.4", + "exsolve": "^1.0.7", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "ufo": "^1.6.1" + } + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -15904,45 +15902,38 @@ } } }, - "@cloudflare/unenv-preset": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/@cloudflare/unenv-preset/-/unenv-preset-2.3.3.tgz", - "integrity": "sha512-/M3MEcj3V2WHIRSW1eAQBPRJ6JnGQHc6JKMAPLkDb7pLs3m6X9ES/+K3ceGqxI6TKeF32AWAi7ls0AYzVxCP0A==", - "dev": true, - "requires": {} - }, "@cloudflare/workerd-darwin-64": { - "version": "1.20250712.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20250712.0.tgz", - "integrity": "sha512-M6S6a/LQ0Jb0R+g0XhlYi1adGifvYmxA5mD/i9TuZZgjs2bIm5ELuka/n3SCnI98ltvlx3HahRaHagAtOilsFg==", + "version": "1.20250726.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20250726.0.tgz", + "integrity": "sha512-SOpQqQ2blLY0io/vErve44vJC1M5i7RHuMBdrdEPIEtxiLBTdOOVp4nqZ3KchocxZjskgTc2N4N3b5hNYuKDGw==", "dev": true, "optional": true }, "@cloudflare/workerd-darwin-arm64": { - "version": "1.20250712.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20250712.0.tgz", - "integrity": "sha512-7sFzn6rvAcnLy7MktFL42dYtzL0Idw/kiUmNf2P3TvsBRoShhLK5ZKhbw+NAhvU8e4pXWm5lkE0XmpieA0zNjw==", + "version": "1.20250726.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20250726.0.tgz", + "integrity": "sha512-I+TOQ+YQahxL/K7eS2GJzv5CZzSVaZoyqfB15Q71MT/+wyzPCaFDTt+fg3uXdwpaIQEMUfqFNpTQSqbKHAYNgA==", "dev": true, "optional": true }, "@cloudflare/workerd-linux-64": { - "version": "1.20250712.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20250712.0.tgz", - "integrity": "sha512-EFRrGe/bqK7NHtht7vNlbrDpfvH3eRvtJOgsTpEQEysDjVmlK6pVJxSnLy9Hg1zlLY15IfhfGC+K2qisseHGJQ==", + "version": "1.20250726.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20250726.0.tgz", + "integrity": "sha512-WSCv4o2uOW6b++ROVazrEW+jjZdBqCmXmmt7uVVfvjVxlzoYVwK9IvV2IXe4gsJ99HG9I0YCa7AT743cZ7TNNg==", "dev": true, "optional": true }, "@cloudflare/workerd-linux-arm64": { - "version": "1.20250712.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20250712.0.tgz", - "integrity": "sha512-rG8JUleddhUHQVwpXOYv0VbL0S9kOtR9PNKecgVhFpxEhC8aTeg2HNBBjo8st7IfcUvY8WaW3pD3qdAMZ05UwQ==", + "version": "1.20250726.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20250726.0.tgz", + "integrity": "sha512-jNokAGL3EQqH+31b0dX8+tlbKdjt/0UtTLvgD1e+7bOD92lzjYMa/CixHyMIY/FVvhsN4TNqfiz4cqroABTlhg==", "dev": true, "optional": true }, "@cloudflare/workerd-windows-64": { - "version": "1.20250712.0", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20250712.0.tgz", - "integrity": "sha512-qS8H5RCYwE21Om9wo5/F807ClBJIfknhuLBj16eYxvJcj9JqgAKWi12BGgjyGxHuJJjeoQ63lr4wHAdbFntDDg==", + "version": "1.20250726.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20250726.0.tgz", + "integrity": "sha512-DiPTY63TNh6/ylvfutNQzYZi688x6NJDjQoqf5uiCp7xHweWx+GpVs42sZPeeXqCNvhm4dYjHjuigXJNh7t8Uw==", "dev": true, "optional": true }, @@ -19680,9 +19671,9 @@ "dev": true }, "exsolve": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.5.tgz", - "integrity": "sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz", + "integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==", "dev": true }, "extend": { @@ -21885,9 +21876,9 @@ "dev": true }, "miniflare": { - "version": "4.20250712.1", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20250712.1.tgz", - "integrity": "sha512-46gB3FGPOsy+EpFGufjhr8agYycO/55d6l0y7hNJ13NcTVwrObMg/0HmI3pC5yQj0974IVXzBgUfDBMAX6thow==", + "version": "4.20250726.0", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20250726.0.tgz", + "integrity": "sha512-7+/RQQ9dNsyGfR2XN2RDLultf7HHrJ5YltSXSeyQGUpzGU3iYlFhh9Smg+ygkkOJ3+trf0bgwixOnqnnWpc9ZQ==", "dev": true, "requires": { "@cspotcode/source-map-support": "0.8.1", @@ -21898,7 +21889,7 @@ "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "^7.10.0", - "workerd": "1.20250712.0", + "workerd": "1.20250726.0", "ws": "8.18.0", "youch": "4.1.0-beta.10", "zod": "3.22.3" @@ -24316,19 +24307,6 @@ "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", "dev": true }, - "unenv": { - "version": "2.0.0-rc.17", - "resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.17.tgz", - "integrity": "sha512-B06u0wXkEd+o5gOCMl/ZHl5cfpYbDZKAT+HWTL+Hws6jWu7dCiqBBXXXzMFcFVJb8D4ytAnYmxJA83uwOQRSsg==", - "dev": true, - "requires": { - "defu": "^6.1.4", - "exsolve": "^1.0.4", - "ohash": "^2.0.11", - "pathe": "^2.0.3", - "ufo": "^1.6.1" - } - }, "unicode-canonical-property-names-ecmascript": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", @@ -24811,16 +24789,16 @@ "dev": true }, "workerd": { - "version": "1.20250712.0", - "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20250712.0.tgz", - "integrity": "sha512-7h+k1OxREpiZW0849g0uQNexRWMcs5i5gUGhJzCY8nIx6Tv4D/ndlXJ47lEFj7/LQdp165IL9dM2D5uDiedZrg==", + "version": "1.20250726.0", + "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20250726.0.tgz", + "integrity": "sha512-wDZqSKfIfQ2eVTUL6UawXdXEKPPyzRTnVdbhoKGq3NFrMxd+7v1cNH92u8775Qo1zO5S+GyWonQmZPFakXLvGw==", "dev": true, "requires": { - "@cloudflare/workerd-darwin-64": "1.20250712.0", - "@cloudflare/workerd-darwin-arm64": "1.20250712.0", - "@cloudflare/workerd-linux-64": "1.20250712.0", - "@cloudflare/workerd-linux-arm64": "1.20250712.0", - "@cloudflare/workerd-windows-64": "1.20250712.0" + "@cloudflare/workerd-darwin-64": "1.20250726.0", + "@cloudflare/workerd-darwin-arm64": "1.20250726.0", + "@cloudflare/workerd-linux-64": "1.20250726.0", + "@cloudflare/workerd-linux-arm64": "1.20250726.0", + "@cloudflare/workerd-windows-64": "1.20250726.0" } }, "workerpool": { @@ -24830,20 +24808,42 @@ "dev": true }, "wrangler": { - "version": "4.25.1", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.25.1.tgz", - "integrity": "sha512-4Tlg+jmqxCX3xFm+Nz1b4jHHY9iOu1EyJ17SSCCJ6MGp+FCGtXgr+CynT94+MP0v/qKQUkMKjoeJ5FNDunZ9cA==", + "version": "4.26.1", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.26.1.tgz", + "integrity": "sha512-zGFEtHrjTAWOngm+zwEvYCxFwMSIBrzHa3Yu6rAxYMEzsT8PPvo2rdswyUJiUkpE9s2Depr37opceaY7JxEYFw==", "dev": true, "requires": { "@cloudflare/kv-asset-handler": "0.4.0", - "@cloudflare/unenv-preset": "2.3.3", + "@cloudflare/unenv-preset": "2.5.0", "blake3-wasm": "2.1.5", "esbuild": "0.25.4", "fsevents": "~2.3.2", - "miniflare": "4.20250712.1", + "miniflare": "4.20250726.0", "path-to-regexp": "6.3.0", - "unenv": "2.0.0-rc.17", - "workerd": "1.20250712.0" + "unenv": "2.0.0-rc.19", + "workerd": "1.20250726.0" + }, + "dependencies": { + "@cloudflare/unenv-preset": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@cloudflare/unenv-preset/-/unenv-preset-2.5.0.tgz", + "integrity": "sha512-CZe9B2VbjIQjBTyc+KoZcN1oUcm4T6GgCXoel9O7647djHuSRAa6sM6G+NdxWArATZgeMMbsvn9C50GCcnIatA==", + "dev": true, + "requires": {} + }, + "unenv": { + "version": "2.0.0-rc.19", + "resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.19.tgz", + "integrity": "sha512-t/OMHBNAkknVCI7bVB9OWjUUAwhVv9vsPIAGnNUxnu3FxPQN11rjh0sksLMzc3g7IlTgvHmOTl4JM7JHpcv5wA==", + "dev": true, + "requires": { + "defu": "^6.1.4", + "exsolve": "^1.0.7", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "ufo": "^1.6.1" + } + } } }, "wrap-ansi": { diff --git a/package.json b/package.json index 2869f00faa3..1643f6014da 100644 --- a/package.json +++ b/package.json @@ -133,6 +133,6 @@ "sinon-chai": "3.7.0", "typescript": "5.8.3", "url-toolkit": "2.2.5", - "wrangler": "4.25.1" + "wrangler": "4.26.1" } } From 2fb519b6456299b59c9c33f760accece03d2e2f6 Mon Sep 17 00:00:00 2001 From: Rob Walch Date: Tue, 29 Jul 2025 12:18:44 -0700 Subject: [PATCH 29/52] Schedule renovate to run on the 28th day of the month (#7439) --- renovate.json | 1 + 1 file changed, 1 insertion(+) diff --git a/renovate.json b/renovate.json index f8121865a5c..69f0454f5f3 100644 --- a/renovate.json +++ b/renovate.json @@ -1,6 +1,7 @@ { "extends": ["config:recommended"], "labels": ["dependencies", "skip-change-log"], + "schedule": ["* 0 28 * *"], "prHourlyLimit": 0, "prConcurrentLimit": 0, "prCreation": "immediate", From 8d1053cd94edbf899e705380af91940f9e6989af Mon Sep 17 00:00:00 2001 From: Rob Walch Date: Thu, 31 Jul 2025 00:08:43 -0700 Subject: [PATCH 30/52] Timestamp offset stability fix for muxed audiovideo mp4 (#7436) * Fix resetting of initPTS when muxed "audiovideo" sample start times are inconsistent Fixes #7431 * Continue loading parts even with gaps and warn when frag selects fallback likely due to a gap --- api-extractor/report/hls.js.api.md | 15 +++- src/controller/audio-stream-controller.ts | 6 +- src/controller/base-stream-controller.ts | 28 +++++-- src/controller/stream-controller.ts | 9 ++- src/controller/timeline-controller.ts | 8 +- src/demux/audio/base-audio-demuxer.ts | 9 ++- src/demux/transmuxer-interface.ts | 4 +- src/demux/transmuxer.ts | 8 +- src/hls.ts | 5 +- src/remux/mp4-remuxer.ts | 27 +++++-- src/remux/passthrough-remuxer.ts | 96 ++++++++++++++--------- src/types/events.ts | 1 + src/types/remuxer.ts | 4 +- src/utils/imsc1-ttml-parser.ts | 4 +- src/utils/timescale-conversion.ts | 2 + src/utils/webvtt-parser.ts | 4 +- 16 files changed, 149 insertions(+), 81 deletions(-) diff --git a/api-extractor/report/hls.js.api.md b/api-extractor/report/hls.js.api.md index 2d780afb20b..11627be99e7 100644 --- a/api-extractor/report/hls.js.api.md +++ b/api-extractor/report/hls.js.api.md @@ -197,7 +197,7 @@ export class AudioStreamController extends BaseStreamController implements Netwo // (undocumented) protected onHandlerDestroying(): void; // (undocumented) - onInitPtsFound(event: Events.INIT_PTS_FOUND, { frag, id, initPTS, timescale }: InitPTSFoundData): void; + onInitPtsFound(event: Events.INIT_PTS_FOUND, { frag, id, initPTS, timescale, trackId }: InitPTSFoundData): void; // (undocumented) protected onManifestLoading(): void; // (undocumented) @@ -458,7 +458,7 @@ export class BaseStreamController extends TaskLoop implements NetworkComponentAP // (undocumented) get inFlightFrag(): InFlightData; // (undocumented) - protected initPTS: RationalTimestamp[]; + protected initPTS: TimestampOffset[]; // (undocumented) protected isLoopLoading(frag: Fragment, targetBufferTime: number): boolean; // (undocumented) @@ -2584,6 +2584,8 @@ export interface InitPTSFoundData { initPTS: number; // (undocumented) timescale: number; + // (undocumented) + trackId: number; } // Warning: (ae-missing-release-tag) "InitSegmentData" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -4796,6 +4798,13 @@ export enum TimelineOccupancy { Range = 1 } +// Warning: (ae-missing-release-tag) "TimestampOffset" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type TimestampOffset = RationalTimestamp & { + trackId: number; +}; + // Warning: (ae-missing-release-tag) "Track" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -4866,7 +4875,7 @@ export class TransmuxerInterface { // (undocumented) flush(chunkMeta: ChunkMetadata): void; // (undocumented) - push(data: ArrayBuffer, initSegmentData: Uint8Array | undefined, audioCodec: string | undefined, videoCodec: string | undefined, frag: MediaFragment, part: Part | null, duration: number, accurateTimeOffset: boolean, chunkMeta: ChunkMetadata, defaultInitPTS?: RationalTimestamp): void; + push(data: ArrayBuffer, initSegmentData: Uint8Array | undefined, audioCodec: string | undefined, videoCodec: string | undefined, frag: MediaFragment, part: Part | null, duration: number, accurateTimeOffset: boolean, chunkMeta: ChunkMetadata, defaultInitPTS?: TimestampOffset): void; // (undocumented) reset(): void; } diff --git a/src/controller/audio-stream-controller.ts b/src/controller/audio-stream-controller.ts index f3517c041c9..1e4f60d64d5 100644 --- a/src/controller/audio-stream-controller.ts +++ b/src/controller/audio-stream-controller.ts @@ -142,16 +142,16 @@ class AudioStreamController // INIT_PTS_FOUND is triggered when the video track parsed in the stream-controller has a new PTS value onInitPtsFound( event: Events.INIT_PTS_FOUND, - { frag, id, initPTS, timescale }: InitPTSFoundData, + { frag, id, initPTS, timescale, trackId }: InitPTSFoundData, ) { // Always update the new INIT PTS // Can change due level switch if (id === PlaylistLevelType.MAIN) { const cc = frag.cc; const inFlightFrag = this.fragCurrent; - this.initPTS[cc] = { baseTime: initPTS, timescale }; + this.initPTS[cc] = { baseTime: initPTS, timescale, trackId }; this.log( - `InitPTS for cc: ${cc} found from main: ${initPTS}/${timescale}`, + `InitPTS for cc: ${cc} found from main: ${initPTS / timescale} (${initPTS}/${timescale}) trackId: ${trackId}`, ); this.mainAnchor = frag; // If we are waiting, tick immediately to unblock audio fragment transmuxing diff --git a/src/controller/base-stream-controller.ts b/src/controller/base-stream-controller.ts index 67a43d88ece..d70e03a66a1 100644 --- a/src/controller/base-stream-controller.ts +++ b/src/controller/base-stream-controller.ts @@ -59,7 +59,7 @@ import type { import type { Level } from '../types/level'; import type { RemuxedTrack } from '../types/remuxer'; import type { Bufferable, BufferInfo } from '../utils/buffer-helper'; -import type { RationalTimestamp } from '../utils/timescale-conversion'; +import type { TimestampOffset } from '../utils/timescale-conversion'; type ResolveFragLoaded = (FragLoadedEndData) => void; type RejectFragLoaded = (LoadError) => void; @@ -111,7 +111,7 @@ export default class BaseStreamController protected levelLastLoaded: Level | null = null; protected startFragRequested: boolean = false; protected decrypter: Decrypter; - protected initPTS: RationalTimestamp[] = []; + protected initPTS: TimestampOffset[] = []; protected buffering: boolean = true; protected loadingParts: boolean = false; private loopSn?: string | number; @@ -912,7 +912,7 @@ export default class BaseStreamController this.log( `LL-Part loading OFF after next part miss @${targetBufferTime.toFixed( 2, - )}`, + )} Check buffer at sn: ${frag.sn} loaded parts: ${details.partList?.filter((p) => p.loaded).map((p) => `[${p.start}-${p.end}]`)}`, ); this.loadingParts = false; } else if (!frag.url) { @@ -1496,9 +1496,14 @@ export default class BaseStreamController if (loaded) { nextPart = -1; } else if ( - (contiguous || part.independent || independentAttrOmitted) && - part.fragment === frag + contiguous || + ((part.independent || independentAttrOmitted) && part.fragment === frag) ) { + if (part.fragment !== frag) { + this.warn( + `Need buffer at ${targetBufferTime} but next unloaded part starts at ${part.start}`, + ); + } nextPart = i; } contiguous = loaded; @@ -1510,8 +1515,17 @@ export default class BaseStreamController partList: Part[], targetBufferTime: number, ): boolean { - const lastPart = partList[partList.length - 1]; - return lastPart && targetBufferTime > lastPart.start && lastPart.loaded; + let part: Part; + for (let i = partList.length; i--; ) { + part = partList[i]; + if (!part.loaded) { + return false; + } + if (targetBufferTime > part.start) { + return true; + } + } + return false; } /* diff --git a/src/controller/stream-controller.ts b/src/controller/stream-controller.ts index 93e692be61b..e62b92aa7dd 100644 --- a/src/controller/stream-controller.ts +++ b/src/controller/stream-controller.ts @@ -1248,7 +1248,6 @@ export default class StreamController }); } - // This would be nice if Number.isFinite acted as a typeguard, but it doesn't. See: https://github.com/Microsoft/TypeScript/issues/10038 const baseTime = initSegment.initPTS as number; const timescale = initSegment.timescale as number; const initPTS = this.initPTS[frag.cc]; @@ -1258,12 +1257,18 @@ export default class StreamController initPTS.baseTime !== baseTime || initPTS.timescale !== timescale) ) { - this.initPTS[frag.cc] = { baseTime, timescale }; + const trackId = initSegment.trackId as number; + this.initPTS[frag.cc] = { + baseTime, + timescale, + trackId, + }; hls.trigger(Events.INIT_PTS_FOUND, { frag, id, initPTS: baseTime, timescale, + trackId, }); } } diff --git a/src/controller/timeline-controller.ts b/src/controller/timeline-controller.ts index 00942d277e4..7f8e38240fd 100644 --- a/src/controller/timeline-controller.ts +++ b/src/controller/timeline-controller.ts @@ -36,7 +36,7 @@ import type { MediaPlaylist } from '../types/media-playlist'; import type { VTTCCs } from '../types/vtt'; import type { CaptionScreen } from '../utils/cea-608-parser'; import type { CuesInterface } from '../utils/cues'; -import type { RationalTimestamp } from '../utils/timescale-conversion'; +import type { TimestampOffset } from '../utils/timescale-conversion'; type TrackProperties = { label: string; @@ -61,7 +61,7 @@ export class TimelineController implements ComponentAPI { private Cues: CuesInterface; private textTracks: Array = []; private tracks: Array = []; - private initPTS: RationalTimestamp[] = []; + private initPTS: TimestampOffset[] = []; private unparsedVttFrags: Array = []; private captionsTracks: Record = {}; private nonNativeCaptionsTracks: Record = {}; @@ -191,11 +191,11 @@ export class TimelineController implements ComponentAPI { // Triggered when an initial PTS is found; used for synchronisation of WebVTT. private onInitPtsFound( event: Events.INIT_PTS_FOUND, - { frag, id, initPTS, timescale }: InitPTSFoundData, + { frag, id, initPTS, timescale, trackId }: InitPTSFoundData, ) { const { unparsedVttFrags } = this; if (id === PlaylistLevelType.MAIN) { - this.initPTS[frag.cc] = { baseTime: initPTS, timescale }; + this.initPTS[frag.cc] = { baseTime: initPTS, timescale, trackId }; } // Due to asynchronous processing, initial PTS may arrive later than the first VTT fragments are loaded. diff --git a/src/demux/audio/base-audio-demuxer.ts b/src/demux/audio/base-audio-demuxer.ts index 5d9a382d281..4a980e11023 100644 --- a/src/demux/audio/base-audio-demuxer.ts +++ b/src/demux/audio/base-audio-demuxer.ts @@ -14,7 +14,10 @@ import { } from '../../types/demuxer'; import { appendUint8Array } from '../../utils/mp4-tools'; import { dummyTrack } from '../dummy-demuxed-track'; -import type { RationalTimestamp } from '../../utils/timescale-conversion'; +import type { + RationalTimestamp, + TimestampOffset, +} from '../../utils/timescale-conversion'; class BaseAudioDemuxer implements Demuxer { protected _audioTrack?: DemuxedAudioTrack; @@ -22,7 +25,7 @@ class BaseAudioDemuxer implements Demuxer { protected frameIndex: number = 0; protected cachedData: Uint8Array | null = null; protected basePTS: number | null = null; - protected initPTS: RationalTimestamp | null = null; + protected initPTS: TimestampOffset | null = null; protected lastPTS: number | null = null; resetInitSegment( @@ -42,7 +45,7 @@ class BaseAudioDemuxer implements Demuxer { }; } - resetTimeStamp(deaultTimestamp: RationalTimestamp | null) { + resetTimeStamp(deaultTimestamp: TimestampOffset | null) { this.initPTS = deaultTimestamp; this.resetContiguity(); } diff --git a/src/demux/transmuxer-interface.ts b/src/demux/transmuxer-interface.ts index 3ae429dacce..50f79aa527b 100644 --- a/src/demux/transmuxer-interface.ts +++ b/src/demux/transmuxer-interface.ts @@ -21,7 +21,7 @@ import type Hls from '../hls'; import type { MediaFragment, Part } from '../loader/fragment'; import type { ErrorData, FragDecryptedData } from '../types/events'; import type { ChunkMetadata, TransmuxerResult } from '../types/transmuxer'; -import type { RationalTimestamp } from '../utils/timescale-conversion'; +import type { TimestampOffset } from '../utils/timescale-conversion'; let transmuxerInstanceCount: number = 0; @@ -193,7 +193,7 @@ export default class TransmuxerInterface { duration: number, accurateTimeOffset: boolean, chunkMeta: ChunkMetadata, - defaultInitPTS?: RationalTimestamp, + defaultInitPTS?: TimestampOffset, ) { chunkMeta.transmuxing.start = self.performance.now(); const { instanceNo, transmuxer } = this; diff --git a/src/demux/transmuxer.ts b/src/demux/transmuxer.ts index 45d8d8db863..136a5443482 100644 --- a/src/demux/transmuxer.ts +++ b/src/demux/transmuxer.ts @@ -21,7 +21,7 @@ import type { Remuxer } from '../types/remuxer'; import type { ChunkMetadata, TransmuxerResult } from '../types/transmuxer'; import type { TypeSupported } from '../utils/codecs'; import type { ILogger } from '../utils/logger'; -import type { RationalTimestamp } from '../utils/timescale-conversion'; +import type { TimestampOffset } from '../utils/timescale-conversion'; let now: () => number; // performance.now() not available on WebWorker, at least on Safari Desktop @@ -312,7 +312,7 @@ export default class Transmuxer { chunkMeta.transmuxing.executeEnd = now(); } - resetInitialTimestamp(defaultInitPts: RationalTimestamp | null) { + resetInitialTimestamp(defaultInitPts: TimestampOffset | null) { const { demuxer, remuxer } = this; if (!demuxer || !remuxer) { return; @@ -517,14 +517,14 @@ export class TransmuxConfig { public videoCodec?: string; public initSegmentData?: Uint8Array; public duration: number; - public defaultInitPts: RationalTimestamp | null; + public defaultInitPts: TimestampOffset | null; constructor( audioCodec: string | undefined, videoCodec: string | undefined, initSegmentData: Uint8Array | undefined, duration: number, - defaultInitPts?: RationalTimestamp, + defaultInitPts?: TimestampOffset, ) { this.audioCodec = audioCodec; this.videoCodec = videoCodec; diff --git a/src/hls.ts b/src/hls.ts index 663cb277885..718005282f7 100644 --- a/src/hls.ts +++ b/src/hls.ts @@ -1533,4 +1533,7 @@ export type { KeySystems, KeySystemFormats, } from './utils/mediakeys-helper'; -export type { RationalTimestamp } from './utils/timescale-conversion'; +export type { + RationalTimestamp, + TimestampOffset, +} from './utils/timescale-conversion'; diff --git a/src/remux/mp4-remuxer.ts b/src/remux/mp4-remuxer.ts index f97bfabd6af..9bd59545f98 100644 --- a/src/remux/mp4-remuxer.ts +++ b/src/remux/mp4-remuxer.ts @@ -27,7 +27,10 @@ import type { } from '../types/remuxer'; import type { TrackSet } from '../types/track'; import type { TypeSupported } from '../utils/codecs'; -import type { RationalTimestamp } from '../utils/timescale-conversion'; +import type { + RationalTimestamp, + TimestampOffset, +} from '../utils/timescale-conversion'; const MAX_SILENT_FRAME_DURATION = 10 * 1000; // 10 seconds const AAC_SAMPLES_PER_FRAME = 1024; @@ -62,8 +65,8 @@ export default class MP4Remuxer extends Logger implements Remuxer { private readonly config: HlsConfig; private readonly typeSupported: TypeSupported; private ISGenerated: boolean = false; - private _initPTS: RationalTimestamp | null = null; - private _initDTS: RationalTimestamp | null = null; + private _initPTS: TimestampOffset | null = null; + private _initDTS: TimestampOffset | null = null; private nextVideoTs: number | null = null; private nextAudioTs: number | null = null; private videoSampleDuration: number | null = null; @@ -103,7 +106,7 @@ export default class MP4Remuxer extends Logger implements Remuxer { this.config = this.videoTrackConfig = this._initPTS = this._initDTS = null; } - resetTimeStamp(defaultTimeStamp: RationalTimestamp | null) { + resetTimeStamp(defaultTimeStamp: TimestampOffset | null) { this.log('initPTS & initDTS reset'); this._initPTS = this._initDTS = defaultTimeStamp; } @@ -347,7 +350,7 @@ export default class MP4Remuxer extends Logger implements Remuxer { let initPTS: number | undefined; let initDTS: number | undefined; let timescale: number | undefined; - let trackId: number | undefined; + let trackId: number = -1; if (computePTSDTS) { initPTS = initDTS = Infinity; @@ -439,13 +442,23 @@ export default class MP4Remuxer extends Logger implements Remuxer { if (Object.keys(tracks).length) { this.ISGenerated = true; if (computePTSDTS) { + if (_initPTS) { + this.warn( + `Timestamps at playlist time: ${accurateTimeOffset ? '' : '~'}${timeOffset} ${initPTS! / timescale!} != initPTS: ${_initPTS.baseTime / _initPTS.timescale} (${_initPTS.baseTime}/${_initPTS.timescale}) trackId: ${_initPTS.trackId}`, + ); + } + this.log( + `Found initPTS at playlist time: ${timeOffset} offset: ${initPTS! / timescale!} (${initPTS}/${timescale}) trackId: ${trackId}`, + ); this._initPTS = { baseTime: initPTS as number, timescale: timescale as number, + trackId: trackId as number, }; this._initDTS = { baseTime: initDTS as number, timescale: timescale as number, + trackId: trackId as number, }; } else { initPTS = timescale = undefined; @@ -1134,8 +1147,8 @@ function findKeyframeIndex(samples: Array): number { export function flushTextTrackMetadataCueSamples( track: DemuxedMetadataTrack, timeOffset: number, - initPTS: RationalTimestamp, - initDTS: RationalTimestamp, + initPTS: TimestampOffset, + initDTS: TimestampOffset, ): RemuxedMetadata | undefined { const length = track.samples.length; if (!length) { diff --git a/src/remux/passthrough-remuxer.ts b/src/remux/passthrough-remuxer.ts index 22faa8a4503..de243ed10fe 100644 --- a/src/remux/passthrough-remuxer.ts +++ b/src/remux/passthrough-remuxer.ts @@ -25,14 +25,14 @@ import type { import type { TrackSet } from '../types/track'; import type { TypeSupported } from '../utils/codecs'; import type { InitData, InitDataTrack, TrackTimes } from '../utils/mp4-tools'; -import type { RationalTimestamp } from '../utils/timescale-conversion'; +import type { TimestampOffset } from '../utils/timescale-conversion'; class PassThroughRemuxer extends Logger implements Remuxer { private emitInitSegment: boolean = false; private audioCodec?: string; private videoCodec?: string; private initData?: InitData; - private initPTS: (RationalTimestamp & { trackId?: number }) | null = null; + private initPTS: TimestampOffset | null = null; private initTracks?: TrackSet; private lastEndTime: number | null = null; private isVideoContiguous: boolean = false; @@ -48,7 +48,7 @@ class PassThroughRemuxer extends Logger implements Remuxer { public destroy() {} - public resetTimeStamp(defaultInitPTS: RationalTimestamp | null) { + public resetTimeStamp(defaultInitPTS: TimestampOffset | null) { this.lastEndTime = null; const initPTS = this.initPTS; if (initPTS && defaultInitPTS) { @@ -182,7 +182,7 @@ class PassThroughRemuxer extends Logger implements Remuxer { return result; } if (this.emitInitSegment) { - initSegment.tracks = this.initTracks as TrackSet; + initSegment.tracks = this.initTracks; this.emitInitSegment = false; } @@ -199,53 +199,71 @@ class PassThroughRemuxer extends Logger implements Remuxer { const videoEndTime = toStartEndOrDefault(videoSampleTimestamps, 0, true); const audioEndTime = toStartEndOrDefault(audioSampleTimestamps, 0, true); - let baseOffsetSamples: TrackTimes | undefined; let decodeTime = timeOffset; - let duration: number = 0; - if ( + let duration = 0; + + const syncOnAudio = audioSampleTimestamps && (!videoSampleTimestamps || (!initPTS && audioStartTime < videoStartTime) || - (initPTS && initPTS.trackId === initData.audio!.id)) - ) { - initSegment.trackId = initData.audio!.id; - baseOffsetSamples = audioSampleTimestamps; - duration = audioEndTime - audioStartTime; - } else if (videoSampleTimestamps) { - initSegment.trackId = initData.video!.id; - baseOffsetSamples = videoSampleTimestamps; - duration = videoEndTime - videoStartTime; - } + (initPTS && initPTS.trackId === initData.audio!.id)); + const baseOffsetSamples = syncOnAudio + ? audioSampleTimestamps + : videoSampleTimestamps; + if (baseOffsetSamples) { const timescale = baseOffsetSamples.timescale; + const baseTime = baseOffsetSamples.start - timeOffset * timescale; + const trackId = syncOnAudio ? initData.audio!.id : initData.video!.id; + decodeTime = baseOffsetSamples.start / timescale; - initSegment.initPTS = baseOffsetSamples.start - timeOffset * timescale; - initSegment.timescale = timescale; - if (!initPTS) { - this.initPTS = initPTS = { - baseTime: initSegment.initPTS, - timescale, - trackId: initSegment.trackId, - }; - } - } + duration = syncOnAudio + ? audioEndTime - audioStartTime + : videoEndTime - videoStartTime; - if ( - (accurateTimeOffset || !initPTS) && - (isInvalidInitPts(initPTS, decodeTime, timeOffset, duration) || - initSegment.timescale !== initPTS.timescale) - ) { - initSegment.initPTS = decodeTime - timeOffset; - initSegment.timescale = 1; - if (initPTS && initPTS.timescale === 1) { - this.warn( - `Adjusting initPTS @${timeOffset} from ${initPTS.baseTime / initPTS.timescale} to ${initSegment.initPTS}`, + if ( + (accurateTimeOffset || !initPTS) && + (isInvalidInitPts(initPTS, decodeTime, timeOffset, duration) || + timescale !== initPTS.timescale) + ) { + if (initPTS) { + this.warn( + `Timestamps at playlist time: ${accurateTimeOffset ? '' : '~'}${timeOffset} ${baseTime / timescale} != initPTS: ${initPTS.baseTime / initPTS.timescale} (${initPTS.baseTime}/${initPTS.timescale}) trackId: ${initPTS.trackId}`, + ); + } + this.log( + `Found initPTS at playlist time: ${timeOffset} offset: ${decodeTime - timeOffset} (${baseTime}/${timescale}) trackId: ${trackId}`, ); + initPTS = null; + initSegment.initPTS = baseTime; + initSegment.timescale = timescale; + initSegment.trackId = trackId; + } + } else { + this.warn( + `No audio or video samples found for initPTS at playlist time: ${timeOffset}`, + ); + } + if (!initPTS) { + if ( + !initSegment.timescale || + initSegment.trackId === undefined || + initSegment.initPTS === undefined + ) { + this.warn('Could not set initPTS'); + initSegment.initPTS = decodeTime; + initSegment.timescale = 1; + initSegment.trackId = -1; } this.initPTS = initPTS = { baseTime: initSegment.initPTS, - timescale: 1, + timescale: initSegment.timescale, + trackId: initSegment.trackId, }; + } else { + initSegment.initPTS = initPTS.baseTime; + initSegment.timescale = initPTS.timescale; + initSegment.trackId = initPTS.trackId; } const startTime = audioTrack @@ -348,7 +366,7 @@ function toStartEndOrDefault( } function isInvalidInitPts( - initPTS: RationalTimestamp | null, + initPTS: TimestampOffset | null, startDTS: number, timeOffset: number, duration: number, diff --git a/src/types/events.ts b/src/types/events.ts index 2d88349ec5c..f0138eac60f 100644 --- a/src/types/events.ts +++ b/src/types/events.ts @@ -374,6 +374,7 @@ export interface InitPTSFoundData { frag: MediaFragment; initPTS: number; timescale: number; + trackId: number; } export interface FragLoadingData { diff --git a/src/types/remuxer.ts b/src/types/remuxer.ts index b6f2b6019b6..9981456ba53 100644 --- a/src/types/remuxer.ts +++ b/src/types/remuxer.ts @@ -11,7 +11,7 @@ import type { import type { PlaylistLevelType } from './loader'; import type { TrackSet } from './track'; import type { DecryptData } from '../loader/level-key'; -import type { RationalTimestamp } from '../utils/timescale-conversion'; +import type { TimestampOffset } from '../utils/timescale-conversion'; export interface Remuxer { remux( @@ -30,7 +30,7 @@ export interface Remuxer { videoCodec: string | undefined, decryptdata: DecryptData | null, ): void; - resetTimeStamp(defaultInitPTS: RationalTimestamp | null): void; + resetTimeStamp(defaultInitPTS: TimestampOffset | null): void; resetNextTimestamp(): void; destroy(): void; } diff --git a/src/utils/imsc1-ttml-parser.ts b/src/utils/imsc1-ttml-parser.ts index 764176fc10e..2a6dc5b6d9c 100644 --- a/src/utils/imsc1-ttml-parser.ts +++ b/src/utils/imsc1-ttml-parser.ts @@ -4,7 +4,7 @@ import { toTimescaleFromScale } from './timescale-conversion'; import VTTCue from './vttcue'; import { parseTimeStamp } from './vttparser'; import { generateCueId } from './webvtt-parser'; -import type { RationalTimestamp } from './timescale-conversion'; +import type { TimestampOffset } from './timescale-conversion'; export const IMSC1_CODEC = 'stpp.ttml.im1t'; @@ -24,7 +24,7 @@ const textAlignToLineAlign: Partial> = { export function parseIMSC1( payload: ArrayBuffer, - initPTS: RationalTimestamp, + initPTS: TimestampOffset, callBack: (cues: Array) => any, errorCallBack: (error: Error) => any, ) { diff --git a/src/utils/timescale-conversion.ts b/src/utils/timescale-conversion.ts index f46aa531871..c2762a6ba32 100644 --- a/src/utils/timescale-conversion.ts +++ b/src/utils/timescale-conversion.ts @@ -5,6 +5,8 @@ export type RationalTimestamp = { timescale: number; // ticks per second }; +export type TimestampOffset = RationalTimestamp & { trackId: number }; + export function toTimescaleFromBase( baseTime: number, destScale: number, diff --git a/src/utils/webvtt-parser.ts b/src/utils/webvtt-parser.ts index 7e44e3da932..57a683ee518 100644 --- a/src/utils/webvtt-parser.ts +++ b/src/utils/webvtt-parser.ts @@ -3,7 +3,7 @@ import { hash } from './hash'; import { toMpegTsClockFromTimescale } from './timescale-conversion'; import { VTTParser } from './vttparser'; import { normalizePts } from '../remux/mp4-remuxer'; -import type { RationalTimestamp } from './timescale-conversion'; +import type { TimestampOffset } from './timescale-conversion'; import type { VTTCCs } from '../types/vtt'; const LINEBREAKS = /\r\n|\n\r|\n|\r/g; @@ -80,7 +80,7 @@ const calculateOffset = function (vttCCs: VTTCCs, cc, presentationTime) { export function parseWebVTT( vttByteArray: ArrayBuffer, - initPTS: RationalTimestamp | undefined, + initPTS: TimestampOffset | undefined, vttCCs: VTTCCs, cc: number, timeOffset: number, From d46bf0e3115a1ce16fc3744587a626c7f73f5e1c Mon Sep 17 00:00:00 2001 From: Rob Walch Date: Tue, 29 Jul 2025 18:47:51 -0700 Subject: [PATCH 31/52] Fix FairPlay Streaming "encrypted" event handling regression Fixes regression introduced in v1.6.7 with #7380 --- src/controller/eme-controller.ts | 103 +++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/src/controller/eme-controller.ts b/src/controller/eme-controller.ts index 5ce41a32e2e..c641f3e367b 100644 --- a/src/controller/eme-controller.ts +++ b/src/controller/eme-controller.ts @@ -22,6 +22,8 @@ import { KeySystems, requestMediaKeySystemAccess, } from '../utils/mediakeys-helper'; +import { bin2str, parseSinf } from '../utils/mp4-tools'; +import { base64Decode } from '../utils/numeric-encoding-utils'; import { stringify } from '../utils/safe-json-stringify'; import { strToUtf8array } from '../utils/utf8-utils'; import type { EMEControllerConfig, HlsConfig, LoadPolicy } from '../config'; @@ -552,6 +554,105 @@ class EMEController extends Logger implements ComponentAPI { return this.attemptKeySystemAccess(keySystemsToAttempt); } + private onMediaEncrypted = (event: MediaEncryptedEvent) => { + const { initDataType, initData } = event; + const logMessage = `"${event.type}" event: init data type: "${initDataType}"`; + this.debug(logMessage); + + // Ignore event when initData is null + if (initData === null) { + return; + } + + if (!this.keyFormatPromise) { + let keySystems = Object.keys( + this.keySystemAccessPromises, + ) as KeySystems[]; + if (!keySystems.length) { + keySystems = getKeySystemsForConfig(this.config); + } + const keyFormats = keySystems + .map(keySystemDomainToKeySystemFormat) + .filter((k) => !!k) as KeySystemFormats[]; + this.keyFormatPromise = this.getKeyFormatPromise(keyFormats); + } + + this.keyFormatPromise.then((keySystemFormat) => { + const keySystem = keySystemFormatToKeySystemDomain(keySystemFormat); + if (initDataType !== 'sinf' || keySystem !== KeySystems.FAIRPLAY) { + this.log( + `Ignoring "${event.type}" event with init data type: "${initDataType}" for selected key-system ${keySystem}`, + ); + return; + } + + // Match sinf keyId to playlist skd://keyId= + let keyId: Uint8Array | undefined; + try { + const json = bin2str(new Uint8Array(initData)); + const sinf = base64Decode(JSON.parse(json).sinf); + const tenc = parseSinf(sinf); + if (!tenc) { + throw new Error( + `'schm' box missing or not cbcs/cenc with schi > tenc`, + ); + } + keyId = new Uint8Array(tenc.subarray(8, 24)); + } catch (error) { + this.warn(`${logMessage} Failed to parse sinf: ${error}`); + return; + } + + const keyIdHex = Hex.hexDump(keyId); + const { keyIdToKeySessionPromise, mediaKeySessions } = this; + let keySessionContextPromise = keyIdToKeySessionPromise[keyIdHex]; + + for (let i = 0; i < mediaKeySessions.length; i++) { + // Match playlist key + const keyContext = mediaKeySessions[i]; + const decryptdata = keyContext.decryptdata; + if (!decryptdata.keyId) { + continue; + } + const oldKeyIdHex = Hex.hexDump(decryptdata.keyId); + if ( + keyIdHex === oldKeyIdHex || + decryptdata.uri.replace(/-/g, '').indexOf(keyIdHex) !== -1 + ) { + keySessionContextPromise = keyIdToKeySessionPromise[oldKeyIdHex]; + if (!keySessionContextPromise) { + continue; + } + if (decryptdata.pssh) { + break; + } + delete keyIdToKeySessionPromise[oldKeyIdHex]; + decryptdata.pssh = new Uint8Array(initData); + decryptdata.keyId = keyId; + keySessionContextPromise = keyIdToKeySessionPromise[keyIdHex] = + keySessionContextPromise.then(() => { + return this.generateRequestWithPreferredKeySession( + keyContext, + initDataType, + initData, + 'encrypted-event-key-match', + ); + }); + keySessionContextPromise.catch((error) => this.handleError(error)); + break; + } + } + + if (!keySessionContextPromise) { + this.handleError( + new Error( + `Key ID ${keyIdHex} not encountered in playlist. Key-system sessions ${mediaKeySessions.length}.`, + ), + ); + } + }); + }; + private onWaitingForKey = (event: Event) => { this.log(`"${event.type}" event`); }; @@ -1130,6 +1231,7 @@ class EMEController extends Logger implements ComponentAPI { // keep reference of media this.media = media; + addEventListener(media, 'encrypted', this.onMediaEncrypted); addEventListener(media, 'waitingforkey', this.onWaitingForKey); } @@ -1137,6 +1239,7 @@ class EMEController extends Logger implements ComponentAPI { const media = this.media; if (media) { + removeEventListener(media, 'encrypted', this.onMediaEncrypted); removeEventListener(media, 'waitingforkey', this.onWaitingForKey); this.media = null; this.mediaKeys = null; From d5ddabae2a37e0b370c8759dd057169ab23fd87b Mon Sep 17 00:00:00 2001 From: Rob Walch Date: Wed, 30 Jul 2025 15:12:27 -0700 Subject: [PATCH 32/52] Unblock audio appends when video source buffer is at EoS --- src/controller/buffer-controller.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/controller/buffer-controller.ts b/src/controller/buffer-controller.ts index 9011a9032f4..56bf7d19deb 100755 --- a/src/controller/buffer-controller.ts +++ b/src/controller/buffer-controller.ts @@ -790,7 +790,12 @@ transfer tracks: ${stringify(transferredTracks, (key, value) => (key === 'initSe if (videoSb && sn !== 'initSegment') { const partOrFrag = part || (frag as MediaFragment); const blockedAudioAppend = this.blockedAudioAppend; - if (type === 'audio' && parent !== 'main' && !this.blockedAudioAppend) { + if ( + type === 'audio' && + parent !== 'main' && + !this.blockedAudioAppend && + !(videoTrack.ending || videoTrack.ended) + ) { const pStart = partOrFrag.start; const pTime = pStart + partOrFrag.duration * 0.05; const vbuffered = videoSb.buffered; @@ -1077,6 +1082,9 @@ transfer tracks: ${stringify(transferredTracks, (key, value) => (key === 'initSe this.tracksEnded(); this.hls.trigger(Events.BUFFERED_TO_END, undefined); } + } else if (data.type === 'video') { + // Make sure pending audio appends are unblocked when video reaches end + this.unblockAudio(); } } From a19f42425d9088760d680f54edc141e9fd50e72f Mon Sep 17 00:00:00 2001 From: Rob Walch Date: Mon, 4 Aug 2025 15:41:12 -0700 Subject: [PATCH 33/52] Interstitials improvements and fixes (#7425) * Interstitials improvements - Fix DateRange mapping in Live with fragment reference sliding (use `frag.setStart` over `frag.start =` to update tagAnchor ref) - Skip gaps between interstitial assets - Make MEDIA_ATTACHED async on transferMedia (this.media undefined in id3-track-controller onMediaAttached otherwise) - Add metadata cue timing to assets for better event timing - Removed unnecessary conditionals in changed files (added looser typing in cases where object[index] result may be undefined) Fixes #7426 * Fix `fragment.byteLength` with no byte range * Load next asset m3u8 on asset-list loaded (w/o append-in-place) * Prevent schedule from advancing past interstitials loading asset-lists (with no duration) * Handle append-in-line interstitials with shorter that playout-limit fill * Advance asset player buffering on asset error prior to starting interstitial * Update asset duration on first playlist update * Workaround asset buffered to end edge cases * Do not flush when QuotaExceededError is received by asset players while playing primary * Handle seeking to unplayable range caused by truncated interstitial --- api-extractor/report/hls.js.api.md | 14 +- src/controller/base-playlist-controller.ts | 6 +- src/controller/base-stream-controller.ts | 6 +- src/controller/buffer-controller.ts | 47 +- src/controller/fragment-tracker.ts | 36 +- src/controller/gap-controller.ts | 120 +- src/controller/id3-track-controller.ts | 199 +-- src/controller/interstitial-player.ts | 67 +- src/controller/interstitials-controller.ts | 1164 ++++++++++------- src/controller/interstitials-schedule.ts | 3 +- src/controller/stream-controller.ts | 9 +- src/loader/fragment.ts | 14 +- src/loader/m3u8-parser.ts | 31 +- src/loader/playlist-loader.ts | 2 +- src/types/events.ts | 2 +- src/utils/discontinuities.ts | 13 +- src/utils/texttrack-utils.ts | 5 +- .../controller/audio-stream-controller.ts | 35 +- .../unit/controller/base-stream-controller.ts | 2 +- tests/unit/controller/buffer-controller.ts | 1 - ...-controller.js => cap-level-controller.ts} | 70 +- tests/unit/controller/eme-controller.ts | 27 - tests/unit/controller/fragment-tracker.ts | 4 +- .../controller/interstitials-controller.ts | 69 +- tests/unit/controller/level-controller.ts | 14 +- tests/unit/controller/level-helper.ts | 49 +- tests/unit/controller/stream-controller.ts | 6 +- tests/unit/hls.ts | 4 +- tests/unit/loader/m3u8-parser.ts | 42 +- tests/unit/utils/mediacapabilities-helper.ts | 5 + tests/unit/utils/mock-level.ts | 15 + 31 files changed, 1149 insertions(+), 932 deletions(-) rename tests/unit/controller/{cap-level-controller.js => cap-level-controller.ts} (90%) create mode 100644 tests/unit/utils/mock-level.ts diff --git a/api-extractor/report/hls.js.api.md b/api-extractor/report/hls.js.api.md index 11627be99e7..d1eca8cb450 100644 --- a/api-extractor/report/hls.js.api.md +++ b/api-extractor/report/hls.js.api.md @@ -631,7 +631,7 @@ export interface BufferAppendingData { // (undocumented) chunkMeta: ChunkMetadata; // (undocumented) - data: Uint8Array; + data: Uint8Array; // (undocumented) frag: Fragment; // (undocumented) @@ -1807,7 +1807,7 @@ export class Fragment extends BaseSegment { level: number; // (undocumented) levelkeys?: { - [key: string]: LevelKey; + [key: string]: LevelKey | undefined; }; // (undocumented) loader: Loader | null; @@ -1925,7 +1925,7 @@ export class FragmentTracker implements ComponentAPI { detectPartialFragments(data: FragBufferedData): void; // (undocumented) fragBuffered(frag: MediaFragment, force?: true): void; - getAppendedFrag(position: number, levelType: PlaylistLevelType): Fragment | Part | null; + getAppendedFrag(position: number, levelType: PlaylistLevelType): MediaFragment | Part | null; getBufferedFrag(position: number, levelType: PlaylistLevelType): MediaFragment | null; // (undocumented) getFragAtPos(position: number, levelType: PlaylistLevelType, buffered?: boolean): MediaFragment | null; @@ -2188,12 +2188,14 @@ export class HlsAssetPlayer { // (undocumented) get duration(): number; // (undocumented) - readonly hls: Hls; + hls: Hls | null; // (undocumented) - readonly interstitial: InterstitialEvent; + interstitial: InterstitialEvent; // (undocumented) get interstitialId(): InterstitialId; // (undocumented) + loadSource(): void; + // (undocumented) get media(): HTMLMediaElement | null; // (undocumented) off(event: E, listener: HlsListeners[E], context?: Context): void; @@ -2800,6 +2802,8 @@ export interface InterstitialPlayer { // (undocumented) assetPlayers: (HlsAssetPlayer | null)[]; // (undocumented) + bufferedEnd: number; + // (undocumented) currentTime: number; // (undocumented) duration: number; diff --git a/src/controller/base-playlist-controller.ts b/src/controller/base-playlist-controller.ts index 6ff6a58cd63..20d41b30ce6 100644 --- a/src/controller/base-playlist-controller.ts +++ b/src/controller/base-playlist-controller.ts @@ -87,8 +87,8 @@ export default class BasePlaylistController } if (foundIndex !== -1) { const attr = renditionReports[foundIndex]; - const msn = parseInt(attr['LAST-MSN']) || previous?.lastPartSn; - let part = parseInt(attr['LAST-PART']) || previous?.lastPartIndex; + const msn = parseInt(attr['LAST-MSN']) || previous.lastPartSn; + let part = parseInt(attr['LAST-PART']) || previous.lastPartIndex; if (this.hls.config.lowLatencyMode) { const currentGoal = Math.min( previous.age - previous.partTarget, @@ -164,7 +164,7 @@ export default class BasePlaylistController const offset = Math.max(timelineOffset || 0, 0); details.appliedTimelineOffset = offset; details.fragments.forEach((frag) => { - frag.start = frag.playlistOffset + offset; + frag.setStart(frag.playlistOffset + offset); }); } diff --git a/src/controller/base-stream-controller.ts b/src/controller/base-stream-controller.ts index d70e03a66a1..1a761b030ea 100644 --- a/src/controller/base-stream-controller.ts +++ b/src/controller/base-stream-controller.ts @@ -1397,7 +1397,7 @@ export default class BaseStreamController } protected get primaryPrefetch(): boolean { - if (interstitialsEnabled(this.hls.config)) { + if (interstitialsEnabled(this.config)) { const playingInterstitial = this.hls.interstitialsManager?.playingItem?.event; if (playingInterstitial) { @@ -1415,7 +1415,7 @@ export default class BaseStreamController return frag; } if ( - interstitialsEnabled(this.hls.config) && + interstitialsEnabled(this.config) && frag.type !== PlaylistLevelType.SUBTITLE ) { // Do not load fragments outside the buffering schedule segment @@ -1912,7 +1912,7 @@ export default class BaseStreamController // this happens on IE/Edge, refer to https://github.com/video-dev/hls.js/pull/708 // in that case flush the whole audio buffer to recover this.warn( - `Buffer full error while media.currentTime is not buffered, flush ${playlistType} buffer`, + `Buffer full error while media.currentTime (${this.getLoadPosition()}) is not buffered, flush ${playlistType} buffer`, ); } if (frag) { diff --git a/src/controller/buffer-controller.ts b/src/controller/buffer-controller.ts index 56bf7d19deb..920e9220534 100755 --- a/src/controller/buffer-controller.ts +++ b/src/controller/buffer-controller.ts @@ -278,9 +278,9 @@ export default class BufferController extends Logger implements ComponentAPI { data: MediaAttachingData, ) { const media = (this.media = data.media); - const MediaSource = getMediaSource(this.appendSource); this.transferData = this.overrides = undefined; - if (media && MediaSource) { + const MediaSource = getMediaSource(this.appendSource); + if (MediaSource) { const transferringMedia = !!data.mediaSource; if (transferringMedia || data.overrides) { this.transferData = data; @@ -318,7 +318,7 @@ export default class BufferController extends Logger implements ComponentAPI { private assignMediaSource(ms: MediaSource) { this.log( - `${this.transferData?.mediaSource === ms ? 'transferred' : 'created'} media source: ${ms.constructor?.name}`, + `${this.transferData?.mediaSource === ms ? 'transferred' : 'created'} media source: ${(ms.constructor as any)?.name}`, ); // MediaSource listeners are arrow functions with a lexical scope, and do not need to be bound ms.addEventListener('sourceopen', this._onMediaSourceOpen); @@ -343,9 +343,11 @@ export default class BufferController extends Logger implements ComponentAPI { : null; const trackCount = trackNames ? trackNames.length : 0; const mediaSourceOpenCallback = () => { - if (this.media && this.mediaSourceOpenOrEnded) { - this._onMediaSourceOpen(); - } + Promise.resolve().then(() => { + if (this.media && this.mediaSourceOpenOrEnded) { + this._onMediaSourceOpen(); + } + }); }; if (transferredTracks && trackNames && trackCount) { if (!this.tracksReady) { @@ -435,7 +437,7 @@ transfer tracks: ${stringify(transferredTracks, (key, value) => (key === 'initSe } private _onEndStreaming = (event) => { - if (!this.hls) { + if (!this.hls as any) { return; } if (this.mediaSource?.readyState !== 'open') { @@ -445,7 +447,7 @@ transfer tracks: ${stringify(transferredTracks, (key, value) => (key === 'initSe }; private _onStartStreaming = (event) => { - if (!this.hls) { + if (!this.hls as any) { return; } this.hls.resumeBuffering(); @@ -1030,10 +1032,15 @@ transfer tracks: ${stringify(transferredTracks, (key, value) => (key === 'initSe public get bufferedToEnd(): boolean { return ( this.sourceBufferCount > 0 && - !this.sourceBuffers.some( - ([type]) => - type && (!this.tracks[type]?.ended || this.tracks[type]?.ending), - ) + !this.sourceBuffers.some(([type]) => { + if (type) { + const track = this.tracks[type]; + if (track) { + return !track.ended || track.ending; + } + } + return false; + }) ); } @@ -1170,13 +1177,14 @@ transfer tracks: ${stringify(transferredTracks, (key, value) => (key === 'initSe ); } + const frontBufferFlushThreshold = config.frontBufferFlushThreshold; if ( - Number.isFinite(config.frontBufferFlushThreshold) && - config.frontBufferFlushThreshold > 0 + Number.isFinite(frontBufferFlushThreshold) && + frontBufferFlushThreshold > 0 ) { const frontBufferLength = Math.max( config.maxBufferLength, - config.frontBufferFlushThreshold, + frontBufferFlushThreshold, ); const maxFrontBufferLength = Math.max(frontBufferLength, targetDuration); @@ -1281,7 +1289,7 @@ transfer tracks: ${stringify(transferredTracks, (key, value) => (key === 'initSe const playlistEnd = details.edge; if (details.live && this.hls.config.liveDurationInfinity) { const len = details.fragments.length; - if (len && details.live && !!mediaSource.setLiveSeekableRange) { + if (len && !!(mediaSource as any).setLiveSeekableRange) { const start = Math.max(0, details.fragmentStart); const end = Math.max(start, playlistEnd); @@ -1555,7 +1563,7 @@ transfer tracks: ${stringify(transferredTracks, (key, value) => (key === 'initSe }; private get mediaSrc(): string | undefined { - const media = this.media?.querySelector?.('source') || this.media; + const media = (this.media?.querySelector as any)?.('source') || this.media; return media?.src; } @@ -1655,7 +1663,10 @@ transfer tracks: ${stringify(transferredTracks, (key, value) => (key === 'initSe } // This method must result in an updateend event; if append is not called, onSBUpdateEnd must be called manually - private appendExecutor(data: Uint8Array, type: SourceBufferName) { + private appendExecutor( + data: Uint8Array, + type: SourceBufferName, + ) { const track = this.tracks[type]; const sb = track?.buffer; if (!sb) { diff --git a/src/controller/fragment-tracker.ts b/src/controller/fragment-tracker.ts index fffdf49293b..af3b0583751 100644 --- a/src/controller/fragment-tracker.ts +++ b/src/controller/fragment-tracker.ts @@ -36,7 +36,7 @@ export class FragmentTracker implements ComponentAPI { | null = Object.create(null); private bufferPadding: number = 0.2; - private hls: Hls; + private hls: Hls | null; private hasGaps: boolean = false; constructor(hls: Hls) { @@ -47,24 +47,30 @@ export class FragmentTracker implements ComponentAPI { private _registerListeners() { const { hls } = this; - hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); - hls.on(Events.BUFFER_APPENDED, this.onBufferAppended, this); - hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this); - hls.on(Events.FRAG_LOADED, this.onFragLoaded, this); + if (hls) { + hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); + hls.on(Events.BUFFER_APPENDED, this.onBufferAppended, this); + hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this); + hls.on(Events.FRAG_LOADED, this.onFragLoaded, this); + } } private _unregisterListeners() { const { hls } = this; - hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); - hls.off(Events.BUFFER_APPENDED, this.onBufferAppended, this); - hls.off(Events.FRAG_BUFFERED, this.onFragBuffered, this); - hls.off(Events.FRAG_LOADED, this.onFragLoaded, this); + if (hls) { + hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); + hls.off(Events.BUFFER_APPENDED, this.onBufferAppended, this); + hls.off(Events.FRAG_BUFFERED, this.onFragBuffered, this); + hls.off(Events.FRAG_LOADED, this.onFragLoaded, this); + } } public destroy() { this._unregisterListeners(); // @ts-ignore - this.fragments = + this.hls = + // @ts-ignore + this.fragments = // @ts-ignore this.activePartLists = // @ts-ignore @@ -80,12 +86,12 @@ export class FragmentTracker implements ComponentAPI { public getAppendedFrag( position: number, levelType: PlaylistLevelType, - ): Fragment | Part | null { + ): MediaFragment | Part | null { const activeParts = this.activePartLists[levelType]; if (activeParts) { for (let i = activeParts.length; i--; ) { const activePart = activeParts[i]; - if (!activePart) { + if (!activePart as any) { break; } if ( @@ -533,10 +539,12 @@ export class FragmentTracker implements ComponentAPI { function isPartial(fragmentEntity: FragmentEntity): boolean { return ( fragmentEntity.buffered && - (fragmentEntity.body.gap || + !!( + fragmentEntity.body.gap || fragmentEntity.range.video?.partial || fragmentEntity.range.audio?.partial || - fragmentEntity.range.audiovideo?.partial) + fragmentEntity.range.audiovideo?.partial + ) ); } diff --git a/src/controller/gap-controller.ts b/src/controller/gap-controller.ts index 76b4d8dd527..5ef6869ce37 100644 --- a/src/controller/gap-controller.ts +++ b/src/controller/gap-controller.ts @@ -13,13 +13,14 @@ import type { InFlightData } from './base-stream-controller'; import type { InFlightFragments } from '../hls'; import type Hls from '../hls'; import type { FragmentTracker } from './fragment-tracker'; -import type { Fragment, MediaFragment } from '../loader/fragment'; +import type { Fragment, MediaFragment, Part } from '../loader/fragment'; import type { SourceBufferName } from '../types/buffer'; import type { BufferAppendedData, MediaAttachedData, MediaDetachingData, } from '../types/events'; +import type { ErrorData } from '../types/events'; import type { BufferInfo } from '../utils/buffer-helper'; export const MAX_START_GAP_JUMP = 2.0; @@ -28,8 +29,8 @@ export const SKIP_BUFFER_RANGE_START = 0.05; const TICK_INTERVAL = 100; export default class GapController extends TaskLoop { - private hls: Hls | null = null; - private fragmentTracker: FragmentTracker | null = null; + private hls: Hls | null; + private fragmentTracker: FragmentTracker | null; private media: HTMLMediaElement | null = null; private mediaSource?: MediaSource; @@ -267,10 +268,10 @@ export default class GapController extends TaskLoop { const maxStartGapJump = isLive ? levelDetails!.targetduration * 2 : MAX_START_GAP_JUMP; - const partialOrGap = fragmentTracker.getPartialFragment(currentTime); - if (startJump > 0 && (startJump <= maxStartGapJump || partialOrGap)) { + const appended = appendedFragAtPosition(currentTime, fragmentTracker); + if (startJump > 0 && (startJump <= maxStartGapJump || appended)) { if (!media.paused) { - this._trySkipBufferHole(partialOrGap); + this._trySkipBufferHole(appended); } return; } @@ -314,7 +315,7 @@ export default class GapController extends TaskLoop { } // Report stalling after trying to fix this._reportStall(bufferInfo); - if (!this.media || !this.hls) { + if (!this.media || (!this.hls as any)) { return; } } @@ -398,8 +399,13 @@ export default class GapController extends TaskLoop { this.warn(error.message); // Magic number to flush the pipeline without interuption to audio playback: this.media.currentTime += 0.000001; - const frag = - this.fragmentTracker.getPartialFragment(currentTime) || undefined; + let frag: MediaFragment | Part | null | undefined = + appendedFragAtPosition(currentTime, this.fragmentTracker); + if (frag && 'fragment' in frag) { + frag = frag.fragment; + } else if (!frag) { + frag = undefined; + } const bufferInfo = BufferHelper.bufferInfo( this.media, currentTime, @@ -439,14 +445,14 @@ export default class GapController extends TaskLoop { } const levelDetails = this.hls?.latestLevelDetails; - const partial = fragmentTracker.getPartialFragment(currentTime); + const appended = appendedFragAtPosition(currentTime, fragmentTracker); if ( - partial || + appended || (levelDetails?.live && currentTime < levelDetails.fragmentStart) ) { // Try to skip over the buffer hole caused by a partial fragment // This method isn't limited by the size of the gap between buffered ranges - const targetTime = this._trySkipBufferHole(partial); + const targetTime = this._trySkipBufferHole(appended); // we return here in this case, meaning // the branch below only executes when we haven't seeked to a new position if (targetTime || !this.media) { @@ -526,10 +532,10 @@ export default class GapController extends TaskLoop { /** * Attempts to fix buffer stalls by jumping over known gaps caused by partial fragments - * @param partial - The partial fragment found at the current time (where playback is stalling). + * @param appended - The fragment or part found at the current time (where playback is stalling). * @private */ - private _trySkipBufferHole(partial: MediaFragment | null): number { + private _trySkipBufferHole(appended: MediaFragment | Part | null): number { const { fragmentTracker, media } = this; const config = this.hls?.config; if (!media || !fragmentTracker || !config) { @@ -559,46 +565,34 @@ export default class GapController extends TaskLoop { startGap = true; } } - if (!startGap) { - const startProvisioned = - partial || - fragmentTracker.getAppendedFrag( - currentTime, - PlaylistLevelType.MAIN, - ); - if (startProvisioned) { - // Do not seek when selected variant playlist is unloaded - if (!this.hls.loadLevelObj?.details) { - return 0; - } - // Do not seek when required fragments are inflight or appending - const inFlightDependency = getInFlightDependency( - this.hls.inFlightFragments, - startTime, - ); - if (inFlightDependency) { - return 0; - } - // Do not seek if we can't walk tracked fragments to end of gap - let moreToLoad = false; - let pos = startProvisioned.end; - while (pos < startTime) { - const provisioned = - fragmentTracker.getAppendedFrag( - pos, - PlaylistLevelType.MAIN, - ) || fragmentTracker.getPartialFragment(pos); - if (provisioned) { - pos += provisioned.duration; - } else { - moreToLoad = true; - break; - } - } - if (moreToLoad) { - return 0; + if (!startGap && appended) { + // Do not seek when selected variant playlist is unloaded + if (!this.hls.loadLevelObj?.details) { + return 0; + } + // Do not seek when required fragments are inflight or appending + const inFlightDependency = getInFlightDependency( + this.hls.inFlightFragments, + startTime, + ); + if (inFlightDependency) { + return 0; + } + // Do not seek if we can't walk tracked fragments to end of gap + let moreToLoad = false; + let pos = appended.end; + while (pos < startTime) { + const provisioned = appendedFragAtPosition(pos, fragmentTracker); + if (provisioned) { + pos += provisioned.duration; + } else { + moreToLoad = true; + break; } } + if (moreToLoad) { + return 0; + } } } const targetTime = Math.max( @@ -610,20 +604,27 @@ export default class GapController extends TaskLoop { ); this.moved = true; media.currentTime = targetTime; - if (!partial?.gap) { + if (!appended?.gap) { const error = new Error( `fragment loaded with buffer holes, seeking from ${currentTime} to ${targetTime}`, ); - this.hls.trigger(Events.ERROR, { + const errorData: ErrorData = { type: ErrorTypes.MEDIA_ERROR, details: ErrorDetails.BUFFER_SEEK_OVER_HOLE, fatal: false, error, reason: error.message, - frag: partial || undefined, buffer: bufferInfo.len, bufferInfo, - }); + }; + if (appended) { + if ('fragment' in appended) { + errorData.part = appended; + } else { + errorData.frag = appended; + } + } + this.hls.trigger(Events.ERROR, errorData); } return targetTime; } @@ -705,3 +706,10 @@ function inFlight(inFlightData: InFlightData | undefined): Fragment | null { } return inFlightData.frag; } + +function appendedFragAtPosition(pos: number, fragmentTracker: FragmentTracker) { + return ( + fragmentTracker.getAppendedFrag(pos, PlaylistLevelType.MAIN) || + fragmentTracker.getPartialFragment(pos) + ); +} diff --git a/src/controller/id3-track-controller.ts b/src/controller/id3-track-controller.ts index e3c022b239f..24a8d0d1e43 100644 --- a/src/controller/id3-track-controller.ts +++ b/src/controller/id3-track-controller.ts @@ -13,6 +13,7 @@ import { removeCuesInRange, sendAddTrackEvent, } from '../utils/texttrack-utils'; +import type { MediaFragment } from '../hls'; import type Hls from '../hls'; import type { DateRange } from '../loader/date-range'; import type { LevelDetails } from '../loader/level-details'; @@ -36,7 +37,7 @@ const MIN_CUE_DURATION = 0.25; function getCueClass(): typeof VTTCue | typeof TextTrackCue | undefined { if (typeof self === 'undefined') return undefined; - return self.VTTCue || self.TextTrackCue; + return (self.VTTCue as typeof VTTCue | undefined) || self.TextTrackCue; } function createCueWithDataFields( @@ -75,18 +76,20 @@ const MAX_CUE_ENDTIME = (() => { })(); class ID3TrackController implements ComponentAPI { - private hls: Hls; + private hls: Hls | null; private id3Track: TextTrack | null = null; private media: HTMLMediaElement | null = null; private dateRangeCuesAppended: Record< string, - { - cues: Record; - dateRange: DateRange; - durationKnown: boolean; - } + | { + cues: Record; + dateRange: DateRange; + durationKnown: boolean; + } + | undefined > = {}; private removeCues: boolean = true; + private assetCue?: VTTCue | TextTrackCue; constructor(hls) { this.hls = hls; @@ -104,26 +107,30 @@ class ID3TrackController implements ComponentAPI { private _registerListeners() { const { hls } = this; - hls.on(Events.MEDIA_ATTACHING, this.onMediaAttaching, this); - hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this); - hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this); - hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); - hls.on(Events.FRAG_PARSING_METADATA, this.onFragParsingMetadata, this); - hls.on(Events.BUFFER_FLUSHING, this.onBufferFlushing, this); - hls.on(Events.LEVEL_UPDATED, this.onLevelUpdated, this); - hls.on(Events.LEVEL_PTS_UPDATED, this.onLevelPtsUpdated, this); + if (hls) { + hls.on(Events.MEDIA_ATTACHING, this.onMediaAttaching, this); + hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this); + hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this); + hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); + hls.on(Events.FRAG_PARSING_METADATA, this.onFragParsingMetadata, this); + hls.on(Events.BUFFER_FLUSHING, this.onBufferFlushing, this); + hls.on(Events.LEVEL_UPDATED, this.onLevelUpdated, this); + hls.on(Events.LEVEL_PTS_UPDATED, this.onLevelPtsUpdated, this); + } } private _unregisterListeners() { const { hls } = this; - hls.off(Events.MEDIA_ATTACHING, this.onMediaAttaching, this); - hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this); - hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this); - hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); - hls.off(Events.FRAG_PARSING_METADATA, this.onFragParsingMetadata, this); - hls.off(Events.BUFFER_FLUSHING, this.onBufferFlushing, this); - hls.off(Events.LEVEL_UPDATED, this.onLevelUpdated, this); - hls.off(Events.LEVEL_PTS_UPDATED, this.onLevelPtsUpdated, this); + if (hls) { + hls.off(Events.MEDIA_ATTACHING, this.onMediaAttaching, this); + hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this); + hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this); + hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); + hls.off(Events.FRAG_PARSING_METADATA, this.onFragParsingMetadata, this); + hls.off(Events.BUFFER_FLUSHING, this.onBufferFlushing, this); + hls.off(Events.LEVEL_UPDATED, this.onLevelUpdated, this); + hls.off(Events.LEVEL_PTS_UPDATED, this.onLevelPtsUpdated, this); + } } private onEventCueEnter = () => { @@ -145,7 +152,7 @@ class ID3TrackController implements ComponentAPI { } private onMediaAttached() { - const details = this.hls.latestLevelDetails; + const details = this.hls?.latestLevelDetails; if (details) { this.updateDateRangeCues(details); } @@ -200,15 +207,11 @@ class ID3TrackController implements ComponentAPI { event: Events.FRAG_PARSING_METADATA, data: FragParsingMetadataData, ) { - if (!this.media) { + if (!this.media || !this.hls) { return; } - const { - hls: { - config: { enableEmsgMetadataCues, enableID3MetadataCues }, - }, - } = this; + const { enableEmsgMetadataCues, enableID3MetadataCues } = this.hls.config; if (!enableEmsgMetadataCues && !enableID3MetadataCues) { return; } @@ -235,35 +238,33 @@ class ID3TrackController implements ComponentAPI { } const frames = getId3Frames(samples[i].data); - if (frames) { - const startTime = samples[i].pts; - let endTime: number = startTime + samples[i].duration; + const startTime = samples[i].pts; + let endTime: number = startTime + samples[i].duration; - if (endTime > MAX_CUE_ENDTIME) { - endTime = MAX_CUE_ENDTIME; - } + if (endTime > MAX_CUE_ENDTIME) { + endTime = MAX_CUE_ENDTIME; + } - const timeDiff = endTime - startTime; - if (timeDiff <= 0) { - endTime = startTime + MIN_CUE_DURATION; - } + const timeDiff = endTime - startTime; + if (timeDiff <= 0) { + endTime = startTime + MIN_CUE_DURATION; + } - for (let j = 0; j < frames.length; j++) { - const frame = frames[j]; - // Safari doesn't put the timestamp frame in the TextTrack - if (!isId3TimestampFrame(frame)) { - // add a bounds to any unbounded cues - this.updateId3CueEnds(startTime, type); - const cue = createCueWithDataFields( - Cue, - startTime, - endTime, - frame, - type, - ); - if (cue) { - this.id3Track.addCue(cue); - } + for (let j = 0; j < frames.length; j++) { + const frame = frames[j]; + // Safari doesn't put the timestamp frame in the TextTrack + if (!isId3TimestampFrame(frame)) { + // add a bounds to any unbounded cues + this.updateId3CueEnds(startTime, type); + const cue = createCueWithDataFields( + Cue, + startTime, + endTime, + frame, + type, + ); + if (cue) { + this.id3Track.addCue(cue); } } } @@ -335,11 +336,49 @@ class ID3TrackController implements ComponentAPI { } private updateDateRangeCues(details: LevelDetails, removeOldCues?: true) { + if (!this.hls || !this.media) { + return; + } + const { + assetPlayerId, + timelineOffset, + enableDateRangeMetadataCues, + interstitialsController, + } = this.hls.config; + if (!enableDateRangeMetadataCues) { + return; + } + + const Cue = getCueClass(); if ( - !this.media || - !details.hasProgramDateTime || - !this.hls.config.enableDateRangeMetadataCues + __USE_INTERSTITIALS__ && + assetPlayerId && + timelineOffset && + !interstitialsController ) { + const { fragmentStart, fragmentEnd } = details; + let cue = this.assetCue; + if (cue) { + cue.startTime = fragmentStart; + cue.endTime = fragmentEnd; + } else if (Cue) { + cue = this.assetCue = createCueWithDataFields( + Cue, + fragmentStart, + fragmentEnd, + { assetPlayerId: this.hls.config.assetPlayerId }, + 'hlsjs.interstitial.asset', + ); + if (cue) { + cue.id = assetPlayerId; + this.id3Track ||= this.createTrack(this.media); + this.id3Track.addCue(cue); + cue.addEventListener('enter', this.onEventCueEnter); + } + } + } + + if (!details.hasProgramDateTime) { return; } const { id3Track } = this; @@ -354,36 +393,39 @@ class ID3TrackController implements ComponentAPI { ); for (let i = idsToRemove.length; i--; ) { const id = idsToRemove[i]; - const cues = dateRangeCuesAppended[id].cues; + const cues = dateRangeCuesAppended[id]?.cues; delete dateRangeCuesAppended[id]; - Object.keys(cues).forEach((key) => { - try { + if (cues) { + Object.keys(cues).forEach((key) => { const cue = cues[key]; - cue.removeEventListener('enter', this.onEventCueEnter); - id3Track.removeCue(cue); - } catch (e) { - /* no-op */ - } - }); + if (cue) { + cue.removeEventListener('enter', this.onEventCueEnter); + try { + id3Track.removeCue(cue); + } catch (e) { + /* no-op */ + } + } + }); + } } } else { dateRangeCuesAppended = this.dateRangeCuesAppended = {}; } } // Exit if the playlist does not have Date Ranges or does not have Program Date Time - const lastFragment = details.fragments[details.fragments.length - 1]; + const lastFragment = details.fragments[details.fragments.length - 1] as + | MediaFragment + | undefined; if (ids.length === 0 || !Number.isFinite(lastFragment?.programDateTime)) { return; } - if (!this.id3Track) { - this.id3Track = this.createTrack(this.media); - } + this.id3Track ||= this.createTrack(this.media); - const Cue = getCueClass(); for (let i = 0; i < ids.length; i++) { const id = ids[i]; - const dateRange = dateRanges[id]; + const dateRange = dateRanges[id]!; const startTime = dateRange.startTime; // Process DateRanges to determine end-time (known DURATION, END-DATE, or END-ON-NEXT) @@ -399,7 +441,7 @@ class ID3TrackController implements ComponentAPI { const nextDateRangeWithSameClass = ids.reduce( (candidateDateRange: DateRange | null, id) => { if (id !== dateRange.id) { - const otherDateRange = dateRanges[id]; + const otherDateRange = dateRanges[id]!; if ( otherDateRange.class === dateRange.class && otherDateRange.startDate > dateRange.startDate && @@ -429,7 +471,7 @@ class ID3TrackController implements ComponentAPI { } const cue = cues[key]; if (cue) { - if (durationKnown && !appendedDateRangeCues.durationKnown) { + if (durationKnown && !appendedDateRangeCues?.durationKnown) { cue.endTime = endTime; } else if (Math.abs(cue.startTime - startTime) > 0.01) { cue.startTime = startTime; @@ -452,10 +494,7 @@ class ID3TrackController implements ComponentAPI { cue.id = id; this.id3Track.addCue(cue); cues[key] = cue; - if ( - __USE_INTERSTITIALS__ && - this.hls.config.interstitialsController - ) { + if (__USE_INTERSTITIALS__ && interstitialsController) { if (key === 'X-ASSET-LIST' || key === 'X-ASSET-URL') { cue.addEventListener('enter', this.onEventCueEnter); } diff --git a/src/controller/interstitial-player.ts b/src/controller/interstitial-player.ts index 4f5af35bcc3..82fb92d1b27 100644 --- a/src/controller/interstitial-player.ts +++ b/src/controller/interstitial-player.ts @@ -14,6 +14,7 @@ import type Hls from '../hls'; import type { BufferCodecsData, MediaAttachingData } from '../types/events'; export interface InterstitialPlayer { + bufferedEnd: number; currentTime: number; duration: number; assetPlayers: (HlsAssetPlayer | null)[]; @@ -25,8 +26,8 @@ export type HlsAssetPlayerConfig = Partial & Required>; export class HlsAssetPlayer { - public readonly hls: Hls; - public readonly interstitial: InterstitialEvent; + public hls: Hls | null; + public interstitial: InterstitialEvent; public readonly assetItem: InterstitialAssetItem; public tracks: Partial | null = null; private hasDetails: boolean = false; @@ -43,14 +44,6 @@ export class HlsAssetPlayer { const hls = (this.hls = new HlsPlayerClass(userConfig)); this.interstitial = interstitial; this.assetItem = assetItem; - let uri: string = assetItem.uri; - try { - uri = getInterstitialUrl(uri, userConfig.primarySessionId).href; - } catch (error) { - // Ignore error parsing ASSET_URI or adding _HLS_primary_id to it. The - // issue should surface as an INTERSTITIAL_ASSET_ERROR loading the asset. - } - hls.loadSource(uri); const detailsLoaded = () => { this.hasDetails = true; }; @@ -77,7 +70,24 @@ export class HlsAssetPlayer { } get appendInPlace(): boolean { - return this.interstitial?.appendInPlace || false; + return this.interstitial.appendInPlace; + } + + loadSource() { + const hls = this.hls; + if (!hls) { + return; + } + if (!hls.url) { + let uri: string = this.assetItem.uri; + try { + uri = getInterstitialUrl(uri, hls.config.primarySessionId || '').href; + } catch (error) { + // Ignore error parsing ASSET_URI or adding _HLS_primary_id to it. The + // issue should surface as an INTERSTITIAL_ASSET_ERROR loading the asset. + } + hls.loadSource(uri); + } } bufferedInPlaceToEnd(media?: HTMLMediaElement | null) { @@ -87,17 +97,18 @@ export class HlsAssetPlayer { if (this.hls?.bufferedToEnd) { return true; } - if (!media || !this._bufferedEosTime) { + if (!media) { return false; } + const duration = this._bufferedEosTime || this.duration; const start = this.timelineOffset; const bufferInfo = BufferHelper.bufferInfo(media, start, 0); const bufferedEnd = this.getAssetTime(bufferInfo.end); - return bufferedEnd >= this._bufferedEosTime - 0.02; + return bufferedEnd >= duration - 0.02; } private checkPlayout = () => { - if (this.reachedPlayout(this.currentTime)) { + if (this.reachedPlayout(this.currentTime) && this.hls) { this.hls.trigger(Events.PLAYOUT_LIMIT_REACHED, {}); } }; @@ -172,7 +183,7 @@ export class HlsAssetPlayer { const timelineOffset = this.timelineOffset; if (value !== timelineOffset) { const diff = value - timelineOffset; - if (Math.abs(diff) > 1 / 90000) { + if (Math.abs(diff) > 1 / 90000 && this.hls) { if (this.hasDetails) { throw new Error( `Cannot set timelineOffset after playlists are loaded`, @@ -208,39 +219,41 @@ export class HlsAssetPlayer { destroy() { this.removeMediaListeners(); - this.hls.destroy(); - // @ts-ignore - this.hls = this.interstitial = null; + if (this.hls) { + this.hls.destroy(); + } + this.hls = null; // @ts-ignore this.tracks = this.mediaAttached = this.checkPlayout = null; } attachMedia(data: HTMLMediaElement | MediaAttachingData) { - this.hls.attachMedia(data); + this.loadSource(); + this.hls?.attachMedia(data); } detachMedia() { this.removeMediaListeners(); this.mediaAttached = null; - this.hls.detachMedia(); + this.hls?.detachMedia(); } resumeBuffering() { - this.hls.resumeBuffering(); + this.hls?.resumeBuffering(); } pauseBuffering() { - this.hls.pauseBuffering(); + this.hls?.pauseBuffering(); } transferMedia() { this.bufferSnapShot(); - return this.hls.transferMedia(); + return this.hls?.transferMedia() || null; } resetDetails() { const hls = this.hls; - if (this.hasDetails) { + if (hls && this.hasDetails) { hls.stopLoad(); const deleteDetails = (obj) => delete obj.details; hls.levels.forEach(deleteDetails); @@ -255,7 +268,7 @@ export class HlsAssetPlayer { listener: HlsListeners[E], context?: Context, ) { - this.hls.on(event, listener); + this.hls?.on(event, listener); } once( @@ -263,7 +276,7 @@ export class HlsAssetPlayer { listener: HlsListeners[E], context?: Context, ) { - this.hls.once(event, listener); + this.hls?.once(event, listener); } off( @@ -271,7 +284,7 @@ export class HlsAssetPlayer { listener: HlsListeners[E], context?: Context, ) { - this.hls.off(event, listener); + this.hls?.off(event, listener); } toString(): string { diff --git a/src/controller/interstitials-controller.ts b/src/controller/interstitials-controller.ts index 23df59f7858..78a5dbc09ca 100644 --- a/src/controller/interstitials-controller.ts +++ b/src/controller/interstitials-controller.ts @@ -117,7 +117,7 @@ export default class InterstitialsController private timelinePos: number = -1; // Schedule - private schedule: InterstitialsSchedule; + private schedule: InterstitialsSchedule | null; // Schedule playback and buffering state private playingItem: InterstitialScheduleItem | null = null; @@ -143,48 +143,51 @@ export default class InterstitialsController private registerListeners() { const hls = this.hls; - hls.on(Events.MEDIA_ATTACHING, this.onMediaAttaching, this); - hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this); - hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this); - hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); - hls.on(Events.LEVEL_UPDATED, this.onLevelUpdated, this); - hls.on(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this); - hls.on(Events.AUDIO_TRACK_UPDATED, this.onAudioTrackUpdated, this); - hls.on(Events.SUBTITLE_TRACK_SWITCH, this.onSubtitleTrackSwitch, this); - hls.on(Events.SUBTITLE_TRACK_UPDATED, this.onSubtitleTrackUpdated, this); - hls.on(Events.EVENT_CUE_ENTER, this.onInterstitialCueEnter, this); - hls.on(Events.ASSET_LIST_LOADED, this.onAssetListLoaded, this); - hls.on(Events.BUFFER_APPENDED, this.onBufferAppended, this); - hls.on(Events.BUFFER_FLUSHED, this.onBufferFlushed, this); - hls.on(Events.BUFFERED_TO_END, this.onBufferedToEnd, this); - hls.on(Events.MEDIA_ENDED, this.onMediaEnded, this); - hls.on(Events.ERROR, this.onError, this); - hls.on(Events.DESTROYING, this.onDestroying, this); + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (hls) { + hls.on(Events.MEDIA_ATTACHING, this.onMediaAttaching, this); + hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this); + hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this); + hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); + hls.on(Events.LEVEL_UPDATED, this.onLevelUpdated, this); + hls.on(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this); + hls.on(Events.AUDIO_TRACK_UPDATED, this.onAudioTrackUpdated, this); + hls.on(Events.SUBTITLE_TRACK_SWITCH, this.onSubtitleTrackSwitch, this); + hls.on(Events.SUBTITLE_TRACK_UPDATED, this.onSubtitleTrackUpdated, this); + hls.on(Events.EVENT_CUE_ENTER, this.onInterstitialCueEnter, this); + hls.on(Events.ASSET_LIST_LOADED, this.onAssetListLoaded, this); + hls.on(Events.BUFFER_APPENDED, this.onBufferAppended, this); + hls.on(Events.BUFFER_FLUSHED, this.onBufferFlushed, this); + hls.on(Events.BUFFERED_TO_END, this.onBufferedToEnd, this); + hls.on(Events.MEDIA_ENDED, this.onMediaEnded, this); + hls.on(Events.ERROR, this.onError, this); + hls.on(Events.DESTROYING, this.onDestroying, this); + } } private unregisterListeners() { const hls = this.hls; - if (!hls) { - return; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (hls) { + hls.off(Events.MEDIA_ATTACHING, this.onMediaAttaching, this); + hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this); + hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this); + hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); + hls.off(Events.LEVEL_UPDATED, this.onLevelUpdated, this); + hls.off(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this); + hls.off(Events.AUDIO_TRACK_UPDATED, this.onAudioTrackUpdated, this); + hls.off(Events.SUBTITLE_TRACK_SWITCH, this.onSubtitleTrackSwitch, this); + hls.off(Events.SUBTITLE_TRACK_UPDATED, this.onSubtitleTrackUpdated, this); + hls.off(Events.EVENT_CUE_ENTER, this.onInterstitialCueEnter, this); + hls.off(Events.ASSET_LIST_LOADED, this.onAssetListLoaded, this); + hls.off(Events.BUFFER_CODECS, this.onBufferCodecs, this); + hls.off(Events.BUFFER_APPENDED, this.onBufferAppended, this); + hls.off(Events.BUFFER_FLUSHED, this.onBufferFlushed, this); + hls.off(Events.BUFFERED_TO_END, this.onBufferedToEnd, this); + hls.off(Events.MEDIA_ENDED, this.onMediaEnded, this); + hls.off(Events.ERROR, this.onError, this); + hls.off(Events.DESTROYING, this.onDestroying, this); } - hls.off(Events.MEDIA_ATTACHING, this.onMediaAttaching, this); - hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this); - hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this); - hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); - hls.off(Events.LEVEL_UPDATED, this.onLevelUpdated, this); - hls.off(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this); - hls.off(Events.AUDIO_TRACK_UPDATED, this.onAudioTrackUpdated, this); - hls.off(Events.SUBTITLE_TRACK_SWITCH, this.onSubtitleTrackSwitch, this); - hls.off(Events.SUBTITLE_TRACK_UPDATED, this.onSubtitleTrackUpdated, this); - hls.off(Events.EVENT_CUE_ENTER, this.onInterstitialCueEnter, this); - hls.off(Events.ASSET_LIST_LOADED, this.onAssetListLoaded, this); - hls.off(Events.BUFFER_CODECS, this.onBufferCodecs, this); - hls.off(Events.BUFFER_APPENDED, this.onBufferAppended, this); - hls.off(Events.BUFFER_FLUSHED, this.onBufferFlushed, this); - hls.off(Events.BUFFERED_TO_END, this.onBufferedToEnd, this); - hls.off(Events.MEDIA_ENDED, this.onMediaEnded, this); - hls.off(Events.ERROR, this.onError, this); - hls.off(Events.DESTROYING, this.onDestroying, this); } startLoad() { @@ -208,6 +211,7 @@ export default class InterstitialsController destroy() { this.unregisterListeners(); this.stopLoad(); + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (this.assetListLoader) { this.assetListLoader.destroy(); } @@ -221,10 +225,11 @@ export default class InterstitialsController this.mediaSelection = this.requiredTracks = this.altSelection = + this.schedule = this.manager = null; // @ts-ignore - this.hls = this.HlsPlayerClass = this.schedule = this.log = null; + this.hls = this.HlsPlayerClass = this.log = null; // @ts-ignore this.assetListLoader = null; // @ts-ignore @@ -317,345 +322,366 @@ export default class InterstitialsController } public get interstitialsManager(): InterstitialsManager | null { - if (!this.manager) { - if (!this.hls) { - return null; - } - const c = this; - const effectiveBufferingItem = () => c.bufferingItem || c.waitingItem; - const getAssetPlayer = (asset: InterstitialAssetItem | null) => - asset ? c.getAssetPlayer(asset.identifier) : asset; - const getMappedTime = ( - item: InterstitialScheduleItem | null, - timelineType: TimelineType, - asset: InterstitialAssetItem | null, - controllerField: 'bufferedPos' | 'timelinePos', - assetPlayerField: 'bufferedEnd' | 'currentTime', - ) => { - if (item) { - let time = item[timelineType].start; - const interstitial = item.event; - if (interstitial) { - if ( - timelineType === 'playout' || - interstitial.timelineOccupancy !== TimelineOccupancy.Point - ) { - const assetPlayer = getAssetPlayer(asset); - if (assetPlayer?.interstitial === interstitial) { - time += - assetPlayer.assetItem.startOffset + - assetPlayer[assetPlayerField]; - } - } - } else { - const value = - controllerField === 'bufferedPos' - ? getBufferedEnd() - : c[controllerField]; - time += value - item.start; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (!this.hls) { + return null; + } + if (this.manager) { + return this.manager; + } + const c = this; + const effectiveBufferingItem = () => c.bufferingItem || c.waitingItem; + const getAssetPlayer = (asset: InterstitialAssetItem | null) => + asset ? c.getAssetPlayer(asset.identifier) : asset; + const getMappedTime = ( + item: InterstitialScheduleItem | null, + timelineType: TimelineType, + asset: InterstitialAssetItem | null, + controllerField: 'bufferedPos' | 'timelinePos', + assetPlayerField: 'bufferedEnd' | 'currentTime', + ): number => { + if (item) { + let time = ( + item[timelineType] as { + start: number; + end: number; } - return time; - } - return 0; - }; - const findMappedTime = ( - primaryTime: number, - timelineType: TimelineType, - ): number => { - if ( - primaryTime !== 0 && - timelineType !== 'primary' && - c.schedule.length - ) { - const index = c.schedule.findItemIndexAtTime(primaryTime); - const item = c.schedule.items?.[index]; - if (item) { - const diff = item[timelineType].start - item.start; - return primaryTime + diff; + ).start; + const interstitial = item.event; + if (interstitial) { + if ( + timelineType === 'playout' || + interstitial.timelineOccupancy !== TimelineOccupancy.Point + ) { + const assetPlayer = getAssetPlayer(asset); + if (assetPlayer?.interstitial === interstitial) { + time += + assetPlayer.assetItem.startOffset + + assetPlayer[assetPlayerField]; + } } + } else { + const value = + controllerField === 'bufferedPos' + ? getBufferedEnd() + : c[controllerField]; + time += value - item.start; } - return primaryTime; - }; - const getBufferedEnd = (): number => { - const value = c.bufferedPos; - if (value === Number.MAX_VALUE) { - return getMappedDuration('primary'); + return time; + } + return 0; + }; + const findMappedTime = ( + primaryTime: number, + timelineType: TimelineType, + ): number => { + if ( + primaryTime !== 0 && + timelineType !== 'primary' && + c.schedule?.length + ) { + const index = c.schedule.findItemIndexAtTime(primaryTime); + const item = c.schedule.items?.[index]; + if (item) { + const diff = item[timelineType].start - item.start; + return primaryTime + diff; } - return Math.max(value, 0); - }; - const getMappedDuration = (timelineType: TimelineType): number => { - if (c.primaryDetails?.live) { - // return end of last event item or playlist - return c.primaryDetails.edge; + } + return primaryTime; + }; + const getBufferedEnd = (): number => { + const value = c.bufferedPos; + if (value === Number.MAX_VALUE) { + return getMappedDuration('primary'); + } + return Math.max(value, 0); + }; + const getMappedDuration = (timelineType: TimelineType): number => { + if (c.primaryDetails?.live) { + // return end of last event item or playlist + return c.primaryDetails.edge; + } + return c.schedule?.durations[timelineType] || 0; + }; + const seekTo = (time: number, timelineType: TimelineType) => { + const item = c.effectivePlayingItem; + if (item?.event?.restrictions.skip || !c.schedule) { + return; + } + c.log(`seek to ${time} "${timelineType}"`); + const playingItem = c.effectivePlayingItem; + const targetIndex = c.schedule.findItemIndexAtTime(time, timelineType); + const targetItem = c.schedule.items?.[targetIndex]; + const bufferingPlayer = c.getBufferingPlayer(); + const bufferingInterstitial = bufferingPlayer?.interstitial; + const appendInPlace = bufferingInterstitial?.appendInPlace; + const seekInItem = playingItem && c.itemsMatch(playingItem, targetItem); + if (playingItem && (appendInPlace || seekInItem)) { + // seek in asset player or primary media (appendInPlace) + const assetPlayer = getAssetPlayer(c.playingAsset); + const media = assetPlayer?.media || c.primaryMedia; + if (media) { + const currentTime = + timelineType === 'primary' + ? media.currentTime + : getMappedTime( + playingItem, + timelineType, + c.playingAsset, + 'timelinePos', + 'currentTime', + ); + + const diff = time - currentTime; + const seekToTime = + (appendInPlace ? currentTime : media.currentTime) + diff; + if ( + seekToTime >= 0 && + (!assetPlayer || + appendInPlace || + seekToTime <= assetPlayer.duration) + ) { + media.currentTime = seekToTime; + return; + } } - return c.schedule.durations[timelineType]; - }; - const seekTo = (time: number, timelineType: TimelineType) => { - const item = c.effectivePlayingItem; - if (item?.event?.restrictions.skip) { - return; + } + // seek out of item or asset + if (targetItem) { + let seekToTime = time; + if (timelineType !== 'primary') { + const primarySegmentStart = targetItem[timelineType].start; + const diff = time - primarySegmentStart; + seekToTime = targetItem.start + diff; } - c.log(`seek to ${time} "${timelineType}"`); - const playingItem = c.effectivePlayingItem; - const targetIndex = c.schedule.findItemIndexAtTime(time, timelineType); - const targetItem = c.schedule.items?.[targetIndex]; - const bufferingPlayer = c.getBufferingPlayer(); - const bufferingInterstitial = bufferingPlayer?.interstitial; - const appendInPlace = bufferingInterstitial?.appendInPlace; - const seekInItem = playingItem && c.itemsMatch(playingItem, targetItem); - if (playingItem && (appendInPlace || seekInItem)) { - // seek in asset player or primary media (appendInPlace) - const assetPlayer = getAssetPlayer(c.playingAsset); - const media = assetPlayer?.media || c.primaryMedia; + const targetIsPrimary = !c.isInterstitial(targetItem); + if ( + (!c.isInterstitial(playingItem) || playingItem.event.appendInPlace) && + (targetIsPrimary || targetItem.event.appendInPlace) + ) { + const media = + c.media || (appendInPlace ? bufferingPlayer?.media : null); if (media) { - const currentTime = - timelineType === 'primary' - ? media.currentTime - : getMappedTime( - playingItem, - timelineType, - c.playingAsset, - 'timelinePos', - 'currentTime', - ); - - const diff = time - currentTime; - const seekToTime = - (appendInPlace ? currentTime : media.currentTime) + diff; - if ( - seekToTime >= 0 && - (!assetPlayer || - appendInPlace || - seekToTime <= assetPlayer.duration) - ) { - media.currentTime = seekToTime; + media.currentTime = seekToTime; + } + } else if (playingItem) { + // check if an Interstitial between the current item and target item has an X-RESTRICT JUMP restriction + const playingIndex = c.findItemIndex(playingItem); + if (targetIndex > playingIndex) { + const jumpIndex = c.schedule.findJumpRestrictedIndex( + playingIndex + 1, + targetIndex, + ); + if (jumpIndex > playingIndex) { + c.setSchedulePosition(jumpIndex); return; } } - } - // seek out of item or asset - if (targetItem) { - let seekToTime = time; - if (timelineType !== 'primary') { - const primarySegmentStart = targetItem[timelineType].start; - const diff = time - primarySegmentStart; - seekToTime = targetItem.start + diff; - } - const targetIsPrimary = !c.isInterstitial(targetItem); - if ( - (!c.isInterstitial(playingItem) || - playingItem.event.appendInPlace) && - (targetIsPrimary || targetItem.event.appendInPlace) - ) { - const media = - c.media || (appendInPlace ? bufferingPlayer?.media : null); - if (media) { - media.currentTime = seekToTime; - } - } else if (playingItem) { - // check if an Interstitial between the current item and target item has an X-RESTRICT JUMP restriction - const playingIndex = c.findItemIndex(playingItem); - if (targetIndex > playingIndex) { - const jumpIndex = c.schedule.findJumpRestrictedIndex( - playingIndex + 1, - targetIndex, - ); - if (jumpIndex > playingIndex) { - c.setSchedulePosition(jumpIndex); - return; - } - } - let assetIndex = 0; - if (targetIsPrimary) { - c.timelinePos = seekToTime; - c.checkBuffer(); - } else { - const assetList = targetItem?.event?.assetList; - if (assetList) { - const eventTime = - time - (targetItem[timelineType] || targetItem).start; - for (let i = assetList.length; i--; ) { - const asset = assetList[i]; - if ( - asset.duration && - eventTime >= asset.startOffset && - eventTime < asset.startOffset + asset.duration - ) { - assetIndex = i; - break; - } - } + let assetIndex = 0; + if (targetIsPrimary) { + c.timelinePos = seekToTime; + c.checkBuffer(); + } else { + const assetList = targetItem.event.assetList; + const eventTime = + time - (targetItem[timelineType] || targetItem).start; + for (let i = assetList.length; i--; ) { + const asset = assetList[i]; + if ( + asset.duration && + eventTime >= asset.startOffset && + eventTime < asset.startOffset + asset.duration + ) { + assetIndex = i; + break; } } - c.setSchedulePosition(targetIndex, assetIndex); } + c.setSchedulePosition(targetIndex, assetIndex); } - }; - const getActiveInterstitial = () => { + } + }; + const getActiveInterstitial = () => { + const playingItem = c.effectivePlayingItem; + if (c.isInterstitial(playingItem)) { + return playingItem; + } + const bufferingItem = effectiveBufferingItem(); + if (c.isInterstitial(bufferingItem)) { + return bufferingItem; + } + return null; + }; + const interstitialPlayer: InterstitialPlayer = { + get bufferedEnd() { + const interstitialItem = effectiveBufferingItem(); + const bufferingItem = c.bufferingItem; + if (bufferingItem && bufferingItem === interstitialItem) { + return ( + getMappedTime( + bufferingItem, + 'playout', + c.bufferingAsset, + 'bufferedPos', + 'bufferedEnd', + ) - bufferingItem.playout.start || + c.bufferingAsset?.startOffset || + 0 + ); + } + return 0; + }, + get currentTime() { + const interstitialItem = getActiveInterstitial(); + const playingItem = c.effectivePlayingItem; + if (playingItem && playingItem === interstitialItem) { + return ( + getMappedTime( + playingItem, + 'playout', + c.effectivePlayingAsset, + 'timelinePos', + 'currentTime', + ) - playingItem.playout.start + ); + } + return 0; + }, + set currentTime(time: number) { + const interstitialItem = getActiveInterstitial(); const playingItem = c.effectivePlayingItem; - if (c.isInterstitial(playingItem)) { - return playingItem; + if (playingItem && playingItem === interstitialItem) { + seekTo(time + playingItem.playout.start, 'playout'); } - const bufferingItem = effectiveBufferingItem(); - if (c.isInterstitial(bufferingItem)) { - return bufferingItem; + }, + get duration() { + const interstitialItem = getActiveInterstitial(); + if (interstitialItem) { + return interstitialItem.playout.end - interstitialItem.playout.start; + } + return 0; + }, + get assetPlayers() { + const assetList = getActiveInterstitial()?.event.assetList; + if (assetList) { + return assetList.map((asset) => c.getAssetPlayer(asset.identifier)); + } + return []; + }, + get playingIndex() { + const interstitial = getActiveInterstitial()?.event; + if (interstitial && c.effectivePlayingAsset) { + return interstitial.findAssetIndex(c.effectivePlayingAsset); + } + return -1; + }, + get scheduleItem() { + return getActiveInterstitial(); + }, + }; + return (this.manager = { + get events() { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + return c.schedule?.events?.slice(0) || []; + }, + get schedule() { + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + return c.schedule?.items?.slice(0) || []; + }, + get interstitialPlayer() { + if (getActiveInterstitial()) { + return interstitialPlayer; } return null; - }; - const interstitialPlayer: InterstitialPlayer = { + }, + get playerQueue() { + return c.playerQueue.slice(0); + }, + get bufferingAsset() { + return c.bufferingAsset; + }, + get bufferingItem() { + return effectiveBufferingItem(); + }, + get bufferingIndex() { + const item = effectiveBufferingItem(); + return c.findItemIndex(item); + }, + get playingAsset() { + return c.effectivePlayingAsset; + }, + get playingItem() { + return c.effectivePlayingItem; + }, + get playingIndex() { + const item = c.effectivePlayingItem; + return c.findItemIndex(item); + }, + primary: { + get bufferedEnd() { + return getBufferedEnd(); + }, get currentTime() { - const interstitialItem = getActiveInterstitial(); - const playingItem = c.effectivePlayingItem; - if (playingItem && playingItem === interstitialItem) { - return ( - getMappedTime( - playingItem, - 'playout', - c.effectivePlayingAsset, - 'timelinePos', - 'currentTime', - ) - playingItem.playout.start - ); - } - return 0; + const timelinePos = c.timelinePos; + return timelinePos > 0 ? timelinePos : 0; }, set currentTime(time: number) { - const interstitialItem = getActiveInterstitial(); - const playingItem = c.effectivePlayingItem; - if (playingItem && playingItem === interstitialItem) { - seekTo(time + playingItem.playout.start, 'playout'); - } + seekTo(time, 'primary'); }, get duration() { - const interstitialItem = getActiveInterstitial(); - if (interstitialItem) { - return ( - interstitialItem.playout.end - interstitialItem.playout.start - ); - } - return 0; - }, - get assetPlayers() { - const assetList = getActiveInterstitial()?.event.assetList; - if (assetList) { - return assetList.map((asset) => c.getAssetPlayer(asset.identifier)); - } - return []; - }, - get playingIndex() { - const interstitial = getActiveInterstitial()?.event; - if (interstitial && c.effectivePlayingAsset) { - return interstitial.findAssetIndex(c.effectivePlayingAsset); - } - return -1; - }, - get scheduleItem() { - return getActiveInterstitial(); - }, - }; - this.manager = { - get events() { - return c.schedule?.events?.slice(0) || []; - }, - get schedule() { - return c.schedule?.items?.slice(0) || []; - }, - get interstitialPlayer() { - if (getActiveInterstitial()) { - return interstitialPlayer; - } - return null; - }, - get playerQueue() { - return c.playerQueue.slice(0); - }, - get bufferingAsset() { - return c.bufferingAsset; - }, - get bufferingItem() { - return effectiveBufferingItem(); + return getMappedDuration('primary'); }, - get bufferingIndex() { - const item = effectiveBufferingItem(); - return c.findItemIndex(item); + get seekableStart() { + return c.primaryDetails?.fragmentStart || 0; }, - get playingAsset() { - return c.effectivePlayingAsset; + }, + integrated: { + get bufferedEnd() { + return getMappedTime( + effectiveBufferingItem(), + 'integrated', + c.bufferingAsset, + 'bufferedPos', + 'bufferedEnd', + ); }, - get playingItem() { - return c.effectivePlayingItem; + get currentTime() { + return getMappedTime( + c.effectivePlayingItem, + 'integrated', + c.effectivePlayingAsset, + 'timelinePos', + 'currentTime', + ); }, - get playingIndex() { - const item = c.effectivePlayingItem; - return c.findItemIndex(item); + set currentTime(time: number) { + seekTo(time, 'integrated'); }, - primary: { - get bufferedEnd() { - return getBufferedEnd(); - }, - get currentTime() { - const timelinePos = c.timelinePos; - return timelinePos > 0 ? timelinePos : 0; - }, - set currentTime(time: number) { - seekTo(time, 'primary'); - }, - get duration() { - return getMappedDuration('primary'); - }, - get seekableStart() { - return c.primaryDetails?.fragmentStart || 0; - }, + get duration() { + return getMappedDuration('integrated'); }, - integrated: { - get bufferedEnd() { - return getMappedTime( - effectiveBufferingItem(), - 'integrated', - c.bufferingAsset, - 'bufferedPos', - 'bufferedEnd', - ); - }, - get currentTime() { - return getMappedTime( - c.effectivePlayingItem, - 'integrated', - c.effectivePlayingAsset, - 'timelinePos', - 'currentTime', - ); - }, - set currentTime(time: number) { - seekTo(time, 'integrated'); - }, - get duration() { - return getMappedDuration('integrated'); - }, - get seekableStart() { - return findMappedTime( - c.primaryDetails?.fragmentStart || 0, - 'integrated', - ); - }, + get seekableStart() { + return findMappedTime( + c.primaryDetails?.fragmentStart || 0, + 'integrated', + ); }, - skip: () => { - const item = c.effectivePlayingItem; - const event = item?.event; - if (event && !event.restrictions.skip) { - const index = c.findItemIndex(item); - if (event.appendInPlace) { - const time = item.playout.start + item.event.duration; - seekTo(time + 0.001, 'playout'); - } else { - c.advanceAfterAssetEnded(event, index, Infinity); - } + }, + skip: () => { + const item = c.effectivePlayingItem; + const event = item?.event; + if (event && !event.restrictions.skip) { + const index = c.findItemIndex(item); + if (event.appendInPlace) { + const time = item.playout.start + item.event.duration; + seekTo(time + 0.001, 'playout'); + } else { + c.advanceAfterAssetEnded(event, index, Infinity); } - }, - }; - } - return this.manager; + } + }, + }); } // Schedule getters @@ -805,8 +831,9 @@ export default class InterstitialsController ) { const interstitial = queuedPlayer.interstitial; this.clearInterstitial(queuedPlayer.interstitial, null); - interstitial.appendInPlace = false; - if (interstitial.appendInPlace) { + interstitial.appendInPlace = false; // setter may be a no-op; + // `appendInPlace` getter may still return `true` after insterstitial streaming has begun in that mode. + if (interstitial.appendInPlace as boolean) { this.warn( `Could not change append strategy for queued assets ${interstitial}`, ); @@ -827,15 +854,16 @@ export default class InterstitialsController this.log( `${transferring ? 'transfering MediaSource' : 'attaching media'} to ${ isAssetPlayer ? player : 'Primary' - } from ${logFromSource}`, + } from ${logFromSource} (media.currentTime: ${media.currentTime})`, ); - if (dataToAttach === attachMediaSourceData) { + const schedule = this.schedule; + if (dataToAttach === attachMediaSourceData && schedule) { const isAssetAtEndOfSchedule = isAssetPlayer && - (player as HlsAssetPlayer).assetId === this.schedule.assetIdAtEnd; + (player as HlsAssetPlayer).assetId === schedule.assetIdAtEnd; // Prevent asset players from marking EoS on transferred MediaSource dataToAttach.overrides = { - duration: this.schedule.duration, + duration: schedule.duration, endOfStream: !isAssetPlayer || isAssetAtEndOfSchedule, cueRemoval: !isAssetPlayer, }; @@ -853,7 +881,7 @@ export default class InterstitialsController private onSeeking = () => { const currentTime = this.currentTime; - if (currentTime === undefined || this.playbackDisabled) { + if (currentTime === undefined || this.playbackDisabled || !this.schedule) { return; } const diff = currentTime - this.timelinePos; @@ -885,13 +913,19 @@ export default class InterstitialsController (backwardSeek && currentTime < playingItem.start) || currentTime >= playingItem.end ) { - const scheduleIndex = this.schedule.findItemIndexAtTime(this.timelinePos); + const playingIndex = this.findItemIndex(playingItem); + let scheduleIndex = this.schedule.findItemIndexAtTime(currentTime); + if (scheduleIndex === -1) { + scheduleIndex = playingIndex + (backwardSeek ? -1 : 1); + this.log( + `seeked ${backwardSeek ? 'back ' : ''}to position not covered by schedule ${currentTime} (resolving from ${playingIndex} to ${scheduleIndex})`, + ); + } if (!this.isInterstitial(playingItem) && this.media?.paused) { this.shouldPlay = false; } if (!backwardSeek) { // check if an Interstitial between the current item and target item has an X-RESTRICT JUMP restriction - const playingIndex = this.findItemIndex(playingItem); if (scheduleIndex > playingIndex) { const jumpIndex = this.schedule.findJumpRestrictedIndex( playingIndex + 1, @@ -912,6 +946,7 @@ export default class InterstitialsController // restart Interstitial at end if (this.playingLastItem && this.isInterstitial(playingItem)) { const restartAsset = playingItem.event.assetList[0]; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (restartAsset) { this.endedItem = this.playingItem; this.playingItem = null; @@ -975,7 +1010,7 @@ export default class InterstitialsController // Scheduling methods private checkStart() { const schedule = this.schedule; - const interstitialEvents = schedule.events; + const interstitialEvents = schedule?.events; if (!interstitialEvents || this.playbackDisabled || !this.media) { return; } @@ -1004,6 +1039,23 @@ export default class InterstitialsController } } + private advanceAssetBuffering( + item: InterstitialScheduleEventItem, + assetItem: InterstitialAssetItem, + ) { + const interstitial = item.event; + const assetListIndex = interstitial.findAssetIndex(assetItem); + const nextAssetIndex = getNextAssetIndex(interstitial, assetListIndex); + if (!interstitial.isAssetPastPlayoutLimit(nextAssetIndex)) { + this.bufferedToEvent(item, nextAssetIndex); + } else if (this.schedule) { + const nextItem = this.schedule.items?.[this.findItemIndex(item) + 1]; + if (nextItem) { + this.bufferedToItem(nextItem); + } + } + } + private advanceAfterAssetEnded( interstitial: InterstitialEvent, index: number, @@ -1012,8 +1064,16 @@ export default class InterstitialsController const nextAssetIndex = getNextAssetIndex(interstitial, assetListIndex); if (!interstitial.isAssetPastPlayoutLimit(nextAssetIndex)) { // Advance to next asset list item + if (interstitial.appendInPlace) { + const assetItem = interstitial.assetList[nextAssetIndex] as + | InterstitialAssetItem + | undefined; + if (assetItem) { + this.advanceInPlace(assetItem.timelineStart); + } + } this.setSchedulePosition(index, nextAssetIndex); - } else { + } else if (this.schedule) { // Advance to next schedule segment // check if we've reached the end of the program const scheduleItems = this.schedule.items; @@ -1027,7 +1087,10 @@ export default class InterstitialsController const resumptionTime = interstitial.resumeTime; if (this.timelinePos < resumptionTime) { this.timelinePos = resumptionTime; - this.checkBuffer(); + if (interstitial.appendInPlace) { + this.advanceInPlace(resumptionTime); + } + this.checkBuffer(this.bufferedPos < resumptionTime); } this.setSchedulePosition(nextIndex); } @@ -1039,6 +1102,9 @@ export default class InterstitialsController playingAsset: InterstitialAssetItem, ) { const schedule = this.schedule; + if (!schedule) { + return; + } const parentIdentifier = playingAsset.parentIdentifier; const interstitial = schedule.getEvent(parentIdentifier); if (interstitial) { @@ -1049,14 +1115,14 @@ export default class InterstitialsController } private setSchedulePosition(index: number, assetListIndex?: number) { - const scheduleItems = this.schedule.items; + const scheduleItems = this.schedule?.items; if (!scheduleItems || this.playbackDisabled) { return; } this.log(`setSchedulePosition ${index}, ${assetListIndex}`); const scheduledItem = index >= 0 ? scheduleItems[index] : null; // Cleanup current item / asset - const currentItem = this.playingItem; + const currentItem = this.waitingItem || this.playingItem; const playingLastItem = this.playingLastItem; if (this.isInterstitial(currentItem)) { const interstitial = currentItem.event; @@ -1068,7 +1134,7 @@ export default class InterstitialsController assetId && (!this.eventItemsMatch(currentItem, scheduledItem) || (assetListIndex !== undefined && - assetId !== interstitial.assetList?.[assetListIndex].identifier)) + assetId !== interstitial.assetList[assetListIndex].identifier)) ) { const playingAssetListIndex = interstitial.findAssetIndex(playingAsset); this.log( @@ -1088,7 +1154,8 @@ export default class InterstitialsController // Schedule change occured on INTERSTITIAL_ASSET_ENDED if ( this.itemsMatch(currentItem, this.playingItem) && - !this.playingAsset + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + !this.playingAsset // INTERSTITIAL_ASSET_ENDED side-effect ) { this.advanceAfterAssetEnded( interstitial, @@ -1120,12 +1187,12 @@ export default class InterstitialsController if (interstitial.cue.once) { // Remove interstitial with CUE attribute value of ONCE after it has played this.updateSchedule(); - const items = this.schedule.items; - if (scheduledItem && items) { + const updatedScheduleItems = this.schedule?.items; + if (scheduledItem && updatedScheduleItems) { const updatedIndex = this.findItemIndex(scheduledItem); this.advanceSchedule( updatedIndex, - items, + updatedScheduleItems, assetListIndex, currentItem, playingLastItem, @@ -1150,6 +1217,10 @@ export default class InterstitialsController currentItem: InterstitialScheduleItem | null, playedLastItem: boolean, ) { + const schedule = this.schedule; + if (!schedule) { + return; + } const scheduledItem = index >= 0 ? scheduleItems[index] : null; const media = this.primaryMedia; // Cleanup out of range Interstitials @@ -1157,9 +1228,7 @@ export default class InterstitialsController if (playerQueue.length) { playerQueue.forEach((player) => { const interstitial = player.interstitial; - const queuedIndex = this.schedule.findEventIndex( - interstitial.identifier, - ); + const queuedIndex = schedule.findEventIndex(interstitial.identifier); if (queuedIndex < index || queuedIndex > index + 1) { this.clearInterstitial(interstitial, scheduledItem); } @@ -1175,7 +1244,7 @@ export default class InterstitialsController const interstitial = scheduledItem.event; // find asset index if (assetListIndex === undefined) { - assetListIndex = this.schedule.findAssetIndex( + assetListIndex = schedule.findAssetIndex( interstitial, this.timelinePos, ); @@ -1183,7 +1252,10 @@ export default class InterstitialsController interstitial, assetListIndex - 1, ); - if (interstitial.isAssetPastPlayoutLimit(assetIndexCandidate)) { + if ( + interstitial.isAssetPastPlayoutLimit(assetIndexCandidate) || + (interstitial.appendInPlace && this.timelinePos === scheduledItem.end) + ) { this.advanceAfterAssetEnded(interstitial, index, assetListIndex); return; } @@ -1227,18 +1299,10 @@ export default class InterstitialsController this.playingItem = scheduledItem; // If asset-list is empty or missing asset index, advance to next item - const assetItem = interstitial.assetList[assetListIndex]; + const assetItem = interstitial.assetList[assetListIndex] as + | InterstitialAssetItem + | undefined; if (!assetItem) { - const nextItem = scheduleItems[index + 1]; - const media = this.media; - if ( - nextItem && - media && - !this.isInterstitial(nextItem) && - media.currentTime < nextItem.start - ) { - media.currentTime = this.timelinePos = nextItem.start; - } this.advanceAfterAssetEnded(interstitial, index, assetListIndex || 0); return; } @@ -1259,6 +1323,7 @@ export default class InterstitialsController assetItem, assetListIndex, ); + player.loadSource(); } if (!this.eventItemsMatch(scheduledItem, this.bufferingItem)) { if (interstitial.appendInPlace && this.isAssetBuffered(assetItem)) { @@ -1287,7 +1352,7 @@ export default class InterstitialsController this.playingItem = currentItem; if (!currentItem.event.appendInPlace) { // Media must be re-attached to resume primary schedule if not sharing source - this.attachPrimary(this.schedule.durations.primary, null); + this.attachPrimary(schedule.durations.primary, null); } } } @@ -1297,7 +1362,7 @@ export default class InterstitialsController } private get primaryDetails(): LevelDetails | undefined { - return this.mediaSelection?.main?.details; + return this.mediaSelection?.main.details; } private get primaryLive(): boolean { @@ -1333,7 +1398,7 @@ export default class InterstitialsController return; } - const scheduleItems = this.schedule.items; + const scheduleItems = this.schedule?.items; if (!scheduleItems) { return; } @@ -1431,7 +1496,7 @@ export default class InterstitialsController // HLS.js event callbacks private onManifestLoading() { this.stopLoad(); - this.schedule.reset(); + this.schedule?.reset(); this.emptyPlayerQueue(); this.clearScheduleState(); this.shouldPlay = false; @@ -1447,7 +1512,7 @@ export default class InterstitialsController } private onLevelUpdated(event: Events.LEVEL_UPDATED, data: LevelUpdatedData) { - if (data.level === -1) { + if (data.level === -1 || !this.schedule) { // level was removed return; } @@ -1501,9 +1566,8 @@ export default class InterstitialsController ) { const audioOption = getBasicSelectionOption(data); this.playerQueue.forEach( - (player) => - player.hls.setAudioOption(data) || - player.hls.setAudioOption(audioOption), + ({ hls }) => + hls && (hls.setAudioOption(data) || hls.setAudioOption(audioOption)), ); } @@ -1513,9 +1577,10 @@ export default class InterstitialsController ) { const subtitleOption = getBasicSelectionOption(data); this.playerQueue.forEach( - (player) => - player.hls.setSubtitleOption(data) || - (data.id !== -1 && player.hls.setSubtitleOption(subtitleOption)), + ({ hls }) => + hls && + (hls.setSubtitleOption(data) || + (data.id !== -1 && hls.setSubtitleOption(subtitleOption))), ); } @@ -1550,6 +1615,9 @@ export default class InterstitialsController } private onBufferedToEnd(event: Events.BUFFERED_TO_END) { + if (!this.schedule) { + return; + } // Buffered to post-roll const interstitialEvents = this.schedule.events; if (this.bufferedPos < Number.MAX_VALUE && interstitialEvents) { @@ -1589,6 +1657,9 @@ export default class InterstitialsController previousItems: InterstitialScheduleItem[] | null, ) => { const schedule = this.schedule; + if (!schedule) { + return; + } const playingItem = this.playingItem; const interstitialEvents = schedule.events || []; const scheduleItems = schedule.items || []; @@ -1611,51 +1682,27 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli this.log(`Removed events ${removedIds}`); } - this.playerQueue.forEach((player) => { - if (player.interstitial.appendInPlace) { - const timelineStart = player.assetItem.timelineStart; - const diff = player.timelineOffset - timelineStart; - if (diff) { - try { - player.timelineOffset = timelineStart; - } catch (e) { - if (Math.abs(diff) > ALIGNED_END_THRESHOLD_SECONDS) { - this.warn( - `${e} ("${player.assetId}" ${player.timelineOffset}->${timelineStart})`, - ); - } - } - } - } - }); - // Update schedule item references // Do not replace Interstitial playingItem without a match - used for INTERSTITIAL_ASSET_ENDED and INTERSTITIAL_ENDED - let trimInPlaceForPlayout: null | (() => void) = null; + let updatedPlayingItem: InterstitialScheduleItem | null = null; + let updatedBufferingItem: InterstitialScheduleItem | null = null; if (playingItem) { - const updatedPlayingItem = this.updateItem(playingItem, this.timelinePos); + updatedPlayingItem = this.updateItem(playingItem, this.timelinePos); if (this.itemsMatch(playingItem, updatedPlayingItem)) { this.playingItem = updatedPlayingItem; + } else { this.waitingItem = this.endedItem = null; - trimInPlaceForPlayout = () => - this.trimInPlace(updatedPlayingItem, playingItem); } - } else { - // Clear waitingItem if it has been removed from the schedule - this.waitingItem = this.updateItem(this.waitingItem); - this.endedItem = this.updateItem(this.endedItem); } + // Clear waitingItem if it has been removed from the schedule + this.waitingItem = this.updateItem(this.waitingItem); + this.endedItem = this.updateItem(this.endedItem); // Do not replace Interstitial bufferingItem without a match - used for transfering media element or source const bufferingItem = this.bufferingItem; if (bufferingItem) { - const updatedBufferingItem = this.updateItem( - bufferingItem, - this.bufferedPos, - ); + updatedBufferingItem = this.updateItem(bufferingItem, this.bufferedPos); if (this.itemsMatch(bufferingItem, updatedBufferingItem)) { this.bufferingItem = updatedBufferingItem; - trimInPlaceForPlayout ||= () => - this.trimInPlace(updatedBufferingItem, bufferingItem); } else if (bufferingItem.event) { // Interstitial removed from schedule (Live -> VOD or other scenario where Start Date is outside the range of VOD Playlist) this.bufferingItem = this.playingItem; @@ -1669,6 +1716,24 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli }); }); + this.playerQueue.forEach((player) => { + if (player.interstitial.appendInPlace) { + const timelineStart = player.assetItem.timelineStart; + const diff = player.timelineOffset - timelineStart; + if (diff) { + try { + player.timelineOffset = timelineStart; + } catch (e) { + if (Math.abs(diff) > ALIGNED_END_THRESHOLD_SECONDS) { + this.warn( + `${e} ("${player.assetId}" ${player.timelineOffset}->${timelineStart})`, + ); + } + } + } + } + }); + if (interstitialsUpdated || previousItems) { this.hls.trigger(Events.INTERSTITIALS_UPDATED, { events: interstitialEvents.slice(0), @@ -1688,8 +1753,11 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli return; } - if (trimInPlaceForPlayout) { - trimInPlaceForPlayout(); + if (playingItem) { + this.trimInPlace(updatedPlayingItem, playingItem); + } + if (bufferingItem) { + this.trimInPlace(updatedBufferingItem, bufferingItem); } // Check is buffered to new Interstitial event boundary @@ -1703,10 +1771,10 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli time?: number, ): T | null { // find item in this.schedule.items; - const items = this.schedule.items; + const items = this.schedule?.items; if (previousItem && items) { const index = this.findItemIndex(previousItem, time); - return (items[index] as T) || null; + return (items[index] as T | undefined) || null; } return null; } @@ -1766,7 +1834,7 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli item: InterstitialScheduleItem | null, time?: number, ): number { - return item ? this.schedule.findItemIndex(item, time) : -1; + return item && this.schedule ? this.schedule.findItemIndex(item, time) : -1; } private updateSchedule() { @@ -1774,12 +1842,12 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli if (!mediaSelection) { return; } - this.schedule.updateSchedule(mediaSelection, []); + this.schedule?.updateSchedule(mediaSelection, []); } // Schedule buffer control private checkBuffer(starved?: boolean) { - const items = this.schedule.items; + const items = this.schedule?.items; if (!items) { return; } @@ -1803,7 +1871,7 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli ) { const schedule = this.schedule; const bufferingItem = this.bufferingItem; - if (this.bufferedPos > bufferEnd) { + if (this.bufferedPos > bufferEnd || !schedule) { return; } if (items.length === 1 && this.itemsMatch(items[0], bufferingItem)) { @@ -1827,12 +1895,22 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli ) { bufferEndIndex = nextToBufferIndex; } - if ( - nextToBufferIndex - playingIndex > 1 && - bufferingItem?.event?.appendInPlace === false - ) { - // do not advance buffering item past Interstitial that requires source reset - return; + if (this.isInterstitial(bufferingItem)) { + const interstitial = bufferingItem.event; + if ( + nextToBufferIndex - playingIndex > 1 && + interstitial.appendInPlace === false + ) { + // do not advance buffering item past Interstitial that requires source reset + return; + } + if ( + interstitial.assetList.length === 0 && + interstitial.assetListLoader + ) { + // do not advance buffering item past Interstitial loading asset-list + return; + } } this.bufferedPos = bufferEnd; if (bufferEndIndex > bufferingIndex && bufferEndIndex > playingIndex) { @@ -1884,7 +1962,7 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli const bufferingLast = this.bufferingItem; const schedule = this.schedule; - if (!this.itemsMatch(item, bufferingLast)) { + if (!this.itemsMatch(item, bufferingLast) && schedule) { const { items, events } = schedule; if (!items || !events) { return bufferingLast; @@ -1907,10 +1985,17 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli ); if (!this.playbackDisabled) { if (isInterstitial) { + const bufferIndex = schedule.findAssetIndex( + item.event, + this.bufferedPos, + ); // primary fragment loading will exit early in base-stream-controller while `bufferingItem` is set to an Interstitial block - item.event.assetList.forEach((asset) => { + item.event.assetList.forEach((asset, i) => { const player = this.getAssetPlayer(asset.identifier); if (player) { + if (i === bufferIndex) { + player.loadSource(); + } player.resumeBuffering(); } }); @@ -1978,10 +2063,8 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli // Buffered to Interstitial boundary const player = this.preloadAssets(interstitial, assetListIndex); if (player?.interstitial.appendInPlace) { - // If we have a player and asset list info, start buffering - const assetItem = interstitial.assetList[assetListIndex]; const media = this.primaryMedia; - if (assetItem && media) { + if (media) { this.bufferAssetPlayer(player, media); } } @@ -2060,9 +2143,15 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli this.createAssetPlayer(interstitial, asset, i); } } - return this.getAssetPlayer( - interstitial.assetList[assetListIndex].identifier, - ); + const asset = interstitial.assetList[assetListIndex]; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (asset) { + const player = this.getAssetPlayer(asset.identifier); + if (player) { + player.loadSource(); + } + return player; + } } return null; } @@ -2141,11 +2230,13 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli let videoPreference = userConfig.videoPreference; const currentLevel = primary.loadLevelObj || primary.levels[primary.currentLevel]; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (videoPreference || currentLevel) { videoPreference = Object.assign({}, videoPreference); if (currentLevel.videoCodec) { videoPreference.videoCodec = currentLevel.videoCodec; } + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (currentLevel.videoRange) { videoPreference.allowedVideoRanges = [currentLevel.videoRange]; } @@ -2165,6 +2256,7 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli const assetId = assetItem.identifier; const playerConfig: HlsAssetPlayerConfig = { ...userConfig, + maxMaxBufferLength: Math.min(180, primary.config.maxMaxBufferLength), autoStartLoad: true, startFragPrefetch: true, primarySessionId: primary.sessionId, @@ -2175,9 +2267,14 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli liveDurationInfinity: false, testBandwidth: false, videoPreference, - audioPreference: selectedAudio || userConfig.audioPreference, - subtitlePreference: selectedSubtitle || userConfig.subtitlePreference, + audioPreference: + (selectedAudio as MediaPlaylist | undefined) || + userConfig.audioPreference, + subtitlePreference: + (selectedSubtitle as MediaPlaylist | undefined) || + userConfig.subtitlePreference, }; + // TODO: limit maxMaxBufferLength in asset players to prevent QEE if (interstitial.appendInPlace) { interstitial.appendInPlaceStarted = true; if (assetItem.timelineStart) { @@ -2204,6 +2301,7 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli this.playerQueue.push(player); interstitial.assetList[assetListIndex] = assetItem; // Listen for LevelDetails and PTS change to update duration + let initialDuration = true; const updateAssetPlayerDetails = (details: LevelDetails) => { if (details.live) { const error = new Error( @@ -2215,10 +2313,12 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli details: ErrorDetails.INTERSTITIAL_ASSET_ITEM_ERROR, error, }; + const scheduleIndex = + this.schedule?.findEventIndex(interstitial.identifier) || -1; this.handleAssetItemError( errorData, interstitial, - this.schedule.findEventIndex(interstitial.identifier), + scheduleIndex, assetListIndex, error.message, ); @@ -2227,7 +2327,12 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli // Get time at end of last fragment const duration = details.edge - details.fragmentStart; const currentAssetDuration = assetItem.duration; - if (currentAssetDuration === null || duration > currentAssetDuration) { + if ( + initialDuration || + currentAssetDuration === null || + duration > currentAssetDuration + ) { + initialDuration = false; this.log( `Interstitial asset "${assetId}" duration change ${currentAssetDuration} > ${duration}`, ); @@ -2242,6 +2347,7 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli player.on(Events.LEVEL_PTS_UPDATED, (event, { details }) => updateAssetPlayerDetails(details), ); + player.on(Events.EVENT_CUE_ENTER, () => this.onInterstitialCueEnter()); const onBufferCodecs = ( event: Events.BUFFER_CODECS, data: BufferCodecsData, @@ -2264,7 +2370,7 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli const bufferedToEnd = () => { const inQueuPlayer = this.getAssetPlayer(assetId); this.log(`buffered to end of asset ${inQueuPlayer}`); - if (!inQueuPlayer) { + if (!inQueuPlayer || !this.schedule) { return; } // Preload at end of asset @@ -2273,23 +2379,14 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli ); const item = this.schedule.items?.[scheduleIndex]; if (this.isInterstitial(item)) { - const assetListIndex = interstitial.findAssetIndex(assetItem); - const nextAssetIndex = getNextAssetIndex(interstitial, assetListIndex); - if (!interstitial.isAssetPastPlayoutLimit(nextAssetIndex)) { - this.bufferedToItem(item, nextAssetIndex); - } else { - const nextItem = this.schedule.items?.[scheduleIndex + 1]; - if (nextItem) { - this.bufferedToItem(nextItem); - } - } + this.advanceAssetBuffering(item, assetItem); } }; player.on(Events.BUFFERED_TO_END, bufferedToEnd); const endedWithAssetIndex = (assetIndex) => { return () => { const inQueuPlayer = this.getAssetPlayer(assetId); - if (!inQueuPlayer) { + if (!inQueuPlayer || !this.schedule) { return; } this.shouldPlay = true; @@ -2302,28 +2399,17 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli player.once(Events.MEDIA_ENDED, endedWithAssetIndex(assetListIndex)); player.once(Events.PLAYOUT_LIMIT_REACHED, endedWithAssetIndex(Infinity)); player.on(Events.ERROR, (event: Events.ERROR, data: ErrorData) => { + if (!this.schedule) { + return; + } const inQueuPlayer = this.getAssetPlayer(assetId); if (data.details === ErrorDetails.BUFFER_STALLED_ERROR) { - if (inQueuPlayer?.media) { - const assetCurrentTime = inQueuPlayer.currentTime; - const distanceFromEnd = inQueuPlayer.duration - assetCurrentTime; - if ( - assetCurrentTime && - interstitial.appendInPlace && - distanceFromEnd / inQueuPlayer.media.playbackRate < 0.5 - ) { - this.log( - `Advancing buffer past end of asset ${assetId} ${interstitial} at ${inQueuPlayer.media.currentTime}`, - ); - bufferedToEnd(); - } else { - this.warn( - `Stalled at ${assetCurrentTime} of ${assetCurrentTime + distanceFromEnd} in asset ${assetId} ${interstitial}`, - ); - this.onTimeupdate(); - this.checkBuffer(true); - } + if (inQueuPlayer?.appendInPlace) { + this.handleInPlaceStall(interstitial); + return; } + this.onTimeupdate(); + this.checkBuffer(true); return; } this.handleAssetItemError( @@ -2336,7 +2422,7 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli }); player.on(Events.DESTROYING, () => { const inQueuPlayer = this.getAssetPlayer(assetId); - if (!inQueuPlayer) { + if (!inQueuPlayer || !this.schedule) { return; } const error = new Error(`Asset player destroyed unexpectedly ${assetId}`); @@ -2452,12 +2538,16 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli } private bufferAssetPlayer(player: HlsAssetPlayer, media: HTMLMediaElement) { + if (!this.schedule) { + return; + } const { interstitial, assetItem } = player; const scheduleIndex = this.schedule.findEventIndex(interstitial.identifier); const item = this.schedule.items?.[scheduleIndex]; if (!item) { return; } + player.loadSource(); this.setBufferingItem(item); this.bufferingAsset = assetItem; const bufferingPlayer = this.getBufferingPlayer(); @@ -2479,6 +2569,7 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli if (appendInPlaceNext && assetItem !== this.playingAsset) { // Do not buffer another item if tracks are unknown or incompatible if (!player.tracks) { + this.log(`Waiting for track info before buffering ${player}`); return; } if ( @@ -2509,6 +2600,52 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli this.transferMediaTo(player, media); } + private handleInPlaceStall(interstitial: InterstitialEvent) { + const schedule = this.schedule; + const media = this.primaryMedia; + if (!schedule || !media) { + return; + } + const currentTime = media.currentTime; + const foundAssetIndex = schedule.findAssetIndex(interstitial, currentTime); + const stallingAsset = interstitial.assetList[foundAssetIndex] as + | InterstitialAssetItem + | undefined; + if (stallingAsset) { + const player = this.getAssetPlayer(stallingAsset.identifier); + if (player) { + const assetCurrentTime = + player.currentTime || currentTime - stallingAsset.timelineStart; + const distanceFromEnd = player.duration - assetCurrentTime; + this.warn( + `Stalled at ${assetCurrentTime} of ${assetCurrentTime + distanceFromEnd} in ${player} ${interstitial} (media.currentTime: ${currentTime})`, + ); + if ( + assetCurrentTime && + (distanceFromEnd / media.playbackRate < 0.5 || + player.bufferedInPlaceToEnd(media)) && + player.hls + ) { + const scheduleIndex = schedule.findEventIndex( + interstitial.identifier, + ); + this.advanceAfterAssetEnded( + interstitial, + scheduleIndex, + foundAssetIndex, + ); + } + } + } + } + + private advanceInPlace(time: number) { + const media = this.primaryMedia; + if (media && media.currentTime < time) { + media.currentTime = time; + } + } + private handleAssetItemError( data: ErrorData, interstitial: InterstitialEvent, @@ -2519,11 +2656,15 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli if (data.details === ErrorDetails.BUFFER_STALLED_ERROR) { return; } - const assetItem = interstitial.assetList[assetListIndex]; + const assetItem = (interstitial.assetList[assetListIndex] || + null) as InterstitialAssetItem | null; this.warn( `INTERSTITIAL_ASSET_ERROR ${assetItem ? eventAssetToString(assetItem) : assetItem} ${data.error}`, ); - const assetId = assetItem?.identifier; + if (!this.schedule) { + return; + } + const assetId = assetItem?.identifier || ''; const playerIndex = this.getAssetPlayerQueueIndex(assetId); const player = this.playerQueue[playerIndex] || null; const items = this.schedule.items; @@ -2543,6 +2684,7 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli } const playingAsset = this.playingAsset; + const bufferingAsset = this.bufferingAsset; const error = new Error(errorMessage); if (assetItem) { this.clearAssetPlayer(assetId, null); @@ -2563,6 +2705,12 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli this.primaryFallback(interstitial); } else if (playingAsset && playingAsset.identifier === assetId) { this.advanceAfterAssetEnded(interstitial, scheduleIndex, assetListIndex); + } else if ( + bufferingAsset && + bufferingAsset.identifier === assetId && + this.isInterstitial(this.bufferingItem) + ) { + this.advanceAssetBuffering(this.bufferingItem, bufferingAsset); } } @@ -2576,9 +2724,9 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli this.log( `Fallback to primary from event "${interstitial.identifier}" start: ${ flushStart - } pos: ${this.timelinePos} playing: ${ - playingItem ? segmentToString(playingItem) : '' - } error: ${interstitial.error}`, + } pos: ${this.timelinePos} playing: ${segmentToString( + playingItem, + )} error: ${interstitial.error}`, ); let timelinePos = this.timelinePos; if (timelinePos === -1) { @@ -2592,6 +2740,9 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli this.attachPrimary(flushStart, null); this.flushFrontBuffer(flushStart); } + if (!this.schedule) { + return; + } const scheduleIndex = this.schedule.findItemIndexAtTime(timelinePos); this.setSchedulePosition(scheduleIndex); } else { @@ -2607,7 +2758,7 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli const interstitial = data.event; const interstitialId = interstitial.identifier; const assets = data.assetListResponse.ASSETS; - if (!this.schedule.hasEvent(interstitialId)) { + if (!this.schedule?.hasEvent(interstitialId)) { // Interstitial with id was removed return; } @@ -2658,21 +2809,28 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli this.setBufferingItem(item); } this.setSchedulePosition(scheduleIndex); - } else if ( - bufferingEvent?.identifier === interstitialId && - bufferingEvent.appendInPlace - ) { - // If buffering (but not playback) has reached this item transfer media-source + } else if (bufferingEvent?.identifier === interstitialId) { const assetItem = interstitial.assetList[0]; - const player = this.getAssetPlayer(assetItem.identifier); - const media = this.primaryMedia; - if (assetItem && player && media) { - this.bufferAssetPlayer(player, media); + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (assetItem) { + const player = this.getAssetPlayer(assetItem.identifier); + if (bufferingEvent.appendInPlace) { + // If buffering (but not playback) has reached this item transfer media-source + const media = this.primaryMedia; + if (player && media) { + this.bufferAssetPlayer(player, media); + } + } else if (player) { + player.loadSource(); + } } } } private onError(event: Events.ERROR, data: ErrorData) { + if (!this.schedule) { + return; + } switch (data.details) { case ErrorDetails.ASSET_LIST_PARSING_ERROR: case ErrorDetails.ASSET_LIST_LOAD_ERROR: @@ -2684,6 +2842,18 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli break; } case ErrorDetails.BUFFER_STALLED_ERROR: { + const stallingItem = + this.endedItem || this.waitingItem || this.playingItem; + if ( + this.isInterstitial(stallingItem) && + stallingItem.event.appendInPlace + ) { + this.handleInPlaceStall(stallingItem.event); + return; + } + this.log( + `Primary player stall @${this.timelinePos} bufferedPos: ${this.bufferedPos}`, + ); this.onTimeupdate(); this.checkBuffer(true); break; diff --git a/src/controller/interstitials-schedule.ts b/src/controller/interstitials-schedule.ts index 4193355327d..942bdfb2266 100644 --- a/src/controller/interstitials-schedule.ts +++ b/src/controller/interstitials-schedule.ts @@ -217,7 +217,8 @@ export class InterstitialsSchedule extends Logger { if ( timelinePos === timelineStart || (timelinePos > timelineStart && - timelinePos < timelineStart + (asset.duration || 0)) + (timelinePos < timelineStart + (asset.duration || 0) || + i === length - 1)) ) { return i; } diff --git a/src/controller/stream-controller.ts b/src/controller/stream-controller.ts index e62b92aa7dd..8865b301df0 100644 --- a/src/controller/stream-controller.ts +++ b/src/controller/stream-controller.ts @@ -1062,7 +1062,14 @@ export default class StreamController return; } if (this.reduceLengthAndFlushBuffer(data)) { - this.flushMainBuffer(0, Number.POSITIVE_INFINITY); + const isAssetPlayer = + !this.config.interstitialsController && this.config.assetPlayerId; + if (isAssetPlayer) { + // Use currentTime in buffer estimate to prevent loading more until playback advances + this._hasEnoughToStart = true; + } else { + this.flushMainBuffer(0, Number.POSITIVE_INFINITY); + } } break; case ErrorDetails.INTERNAL_EXCEPTION: diff --git a/src/loader/fragment.ts b/src/loader/fragment.ts index 9431b039a7a..97e5e08cd95 100644 --- a/src/loader/fragment.ts +++ b/src/loader/fragment.ts @@ -177,7 +177,7 @@ export class Fragment extends BaseSegment { // levelkeys are the EXT-X-KEY tags that apply to this segment for decryption // core difference from the private field _decryptdata is the lack of the initialized IV // _decryptdata will set the IV for this segment based on the segment number in the fragment - public levelkeys?: { [key: string]: LevelKey }; + public levelkeys?: { [key: string]: LevelKey | undefined }; // A string representing the fragment type public readonly type: PlaylistLevelType; // A reference to the loader. Set while the fragment is loading, and removed afterwards. Used to abort fragment loading @@ -233,7 +233,7 @@ export class Fragment extends BaseSegment { return total; } } - if (this.byteRange) { + if (this.byteRange.length) { const start = this.byteRange[0]; const end = this.byteRange[1]; if (Number.isFinite(start) && Number.isFinite(end)) { @@ -270,9 +270,11 @@ export class Fragment extends BaseSegment { } else { const keyFormats = Object.keys(this.levelkeys); if (keyFormats.length === 1) { - return (this._decryptdata = this.levelkeys[ - keyFormats[0] - ].getDecryptData(this.sn)); + const levelKey = (this._decryptdata = + this.levelkeys[keyFormats[0]] || null); + if (levelKey) { + return levelKey.getDecryptData(this.sn); + } } else { // Multiple keys. key-loader to call Fragment.setKeyFormat based on selected key-system. } @@ -305,7 +307,7 @@ export class Fragment extends BaseSegment { } else if (this.levelkeys) { const keyFormats = Object.keys(this.levelkeys); const len = keyFormats.length; - if (len > 1 || (len === 1 && this.levelkeys[keyFormats[0]].encrypted)) { + if (len > 1 || (len === 1 && this.levelkeys[keyFormats[0]]?.encrypted)) { return true; } } diff --git a/src/loader/m3u8-parser.ts b/src/loader/m3u8-parser.ts index 36d42a5246c..12eb6e92c99 100644 --- a/src/loader/m3u8-parser.ts +++ b/src/loader/m3u8-parser.ts @@ -43,6 +43,8 @@ type ParsedMultivariantMediaOptions = { 'CLOSED-CAPTIONS'?: MediaPlaylist[]; }; +type LevelKeys = { [key: string]: LevelKey | undefined }; + const MASTER_PLAYLIST_REGEX = /#EXT-X-STREAM-INF:([^\r\n]*)(?:[\r\n](?:#[^\r\n]*)?)*([^\r\n]+)|#EXT-X-(SESSION-DATA|SESSION-KEY|DEFINE|CONTENT-STEERING|START):([^\r\n]*)[\r\n]+/g; const MASTER_PLAYLIST_MEDIA_REGEX = /#EXT-X-MEDIA:(.*)/g; @@ -254,8 +256,9 @@ export default class M3U8Parser { const attrs = new AttrList(result[1], parsed) as MediaAttributes; const type = attrs.TYPE; if (type) { - const groups: (typeof groupsByType)[keyof typeof groupsByType] = - groupsByType[type]; + const groups: + | (typeof groupsByType)[keyof typeof groupsByType] + | undefined = groupsByType[type]; const medias: MediaPlaylist[] = results[type] || []; results[type] = medias; const lang = attrs.LANGUAGE; @@ -328,7 +331,7 @@ export default class M3U8Parser { let frag: Fragment = new Fragment(type, base); let result: RegExpExecArray | RegExpMatchArray | null; let i: number; - let levelkeys: { [key: string]: LevelKey } | undefined; + let levelkeys: LevelKeys | undefined; let firstPdtIndex = -1; let createNextFrag = false; let nextByteRange: string | null = null; @@ -351,7 +354,7 @@ export default class M3U8Parser { frag = new Fragment(type, base); // setup the next fragment for part loading frag.playlistOffset = totalduration; - frag.start = totalduration; + frag.setStart(totalduration); frag.sn = currentSN; frag.cc = discontinuityCounter; if (currentBitrate) { @@ -383,7 +386,7 @@ export default class M3U8Parser { // url if (Number.isFinite(frag.duration)) { frag.playlistOffset = totalduration; - frag.start = totalduration; + frag.setStart(totalduration); if (levelkeys) { setFragLevelKeys(frag, levelkeys, level); } @@ -414,7 +417,7 @@ export default class M3U8Parser { continue; } for (i = 1; i < result.length; i++) { - if (result[i] !== undefined) { + if ((result[i] as any) !== undefined) { break; } } @@ -720,9 +723,6 @@ export default class M3U8Parser { if (!level.live) { lastFragment.endList = true; } - if (firstFragment && level.startCC === undefined) { - level.startCC = firstFragment.cc; - } /** * Backfill any missing PDT values * "If the first EXT-X-PROGRAM-DATE-TIME tag in a Playlist appears after @@ -738,9 +738,6 @@ export default class M3U8Parser { programDateTimes.unshift(firstFragment as MediaFragment); } } - } else { - level.endSN = 0; - level.startCC = 0; } if (level.fragmentHint) { totalduration += level.fragmentHint.duration; @@ -769,7 +766,7 @@ export function mapDateRanges( const playlistEnd = details.live ? Infinity : details.totalduration; const dateRangeIds = Object.keys(details.dateRanges); for (let i = dateRangeIds.length; i--; ) { - const dateRange = details.dateRanges[dateRangeIds[i]]; + const dateRange = details.dateRanges[dateRangeIds[i]]!; const startDateTime = dateRange.startDate.getTime(); dateRange.tagAnchor = lastProgramDateTime.ref; for (let j = programDateTimeCount; j--; ) { @@ -795,7 +792,7 @@ function findFragmentWithStartDate( index: number, endTime: number, ): number { - const pdtFragment = programDateTimes[index]; + const pdtFragment = programDateTimes[index] as MediaFragment | undefined; if (pdtFragment) { // find matching range between PDT tags const pdtStart = pdtFragment.programDateTime as number; @@ -934,7 +931,7 @@ function setInitSegment( frag: Fragment, mapAttrs: AttrList, id: number, - levelkeys: { [key: string]: LevelKey } | undefined, + levelkeys: LevelKeys | undefined, ) { frag.relurl = mapAttrs.URI; if (mapAttrs.BYTERANGE) { @@ -950,7 +947,7 @@ function setInitSegment( function setFragLevelKeys( frag: Fragment, - levelkeys: { [key: string]: LevelKey }, + levelkeys: LevelKeys, level: LevelDetails, ) { frag.levelkeys = levelkeys; @@ -960,7 +957,7 @@ function setFragLevelKeys( encryptedFragments[encryptedFragments.length - 1].levelkeys !== levelkeys) && Object.keys(levelkeys).some( - (format) => levelkeys![format].isCommonEncryption, + (format) => levelkeys[format]!.isCommonEncryption, ) ) { encryptedFragments.push(frag); diff --git a/src/loader/playlist-loader.ts b/src/loader/playlist-loader.ts index 168db972e9e..b589ad1f8fc 100644 --- a/src/loader/playlist-loader.ts +++ b/src/loader/playlist-loader.ts @@ -711,7 +711,7 @@ class PlaylistLoader implements NetworkComponentAPI { } const error = levelDetails.playlistParsingError; if (error) { - this.hls.logger.warn(error); + this.hls.logger.warn(`${error} ${levelDetails.url}`); if (!hls.config.ignorePlaylistParsingErrors) { hls.trigger(Events.ERROR, { type: ErrorTypes.NETWORK_ERROR, diff --git a/src/types/events.ts b/src/types/events.ts index f0138eac60f..975cf49656f 100644 --- a/src/types/events.ts +++ b/src/types/events.ts @@ -91,7 +91,7 @@ export interface BufferAppendingData { chunkMeta: ChunkMetadata; offset?: number | undefined; parent: PlaylistLevelType; - data: Uint8Array; + data: Uint8Array; } export interface BufferAppendedData { diff --git a/src/utils/discontinuities.ts b/src/utils/discontinuities.ts index 795365e05b9..a6abea9ef71 100644 --- a/src/utils/discontinuities.ts +++ b/src/utils/discontinuities.ts @@ -31,11 +31,10 @@ export function shouldAlignOnDiscontinuities( } function adjustFragmentStart(frag: Fragment, sliding: number) { - if (frag) { - const start = frag.start + sliding; - frag.start = frag.startPTS = start; - frag.endPTS = start + frag.duration; - } + const start = frag.start + sliding; + frag.startPTS = start; + frag.setStart(start); + frag.endPTS = start + frag.duration; } export function adjustSlidingStart(sliding: number, details: LevelDetails) { @@ -68,13 +67,13 @@ export function alignStream( return; } alignDiscontinuities(details, switchDetails); - if (!details.alignedSliding && switchDetails) { + if (!details.alignedSliding) { // If the PTS wasn't figured out via discontinuity sequence that means there was no CC increase within the level. // Aligning via Program Date Time should therefore be reliable, since PDT should be the same within the same // discontinuity sequence. alignMediaPlaylistByPDT(details, switchDetails); } - if (!details.alignedSliding && switchDetails && !details.skippedSegments) { + if (!details.alignedSliding && !details.skippedSegments) { // Try to align on sn so that we pick a better start fragment. // Do not perform this on playlists with delta updates as this is only to align levels on switch // and adjustSliding only adjusts fragments after skippedSegments. diff --git a/src/utils/texttrack-utils.ts b/src/utils/texttrack-utils.ts index a39ef3071dc..6ae14eeece6 100644 --- a/src/utils/texttrack-utils.ts +++ b/src/utils/texttrack-utils.ts @@ -49,7 +49,10 @@ export function addCueToTrack(track: TextTrack, cue: VTTCue) { } } -export function clearCurrentCues(track: TextTrack, enterHandler?: () => void) { +export function clearCurrentCues( + track: TextTrack, + enterHandler?: (e?: Event) => void, +) { // When track.mode is disabled, track.cues will be null. // To guarantee the removal of cues, we need to temporarily // change the mode to hidden diff --git a/tests/unit/controller/audio-stream-controller.ts b/tests/unit/controller/audio-stream-controller.ts index 7dd91954a18..2076463f9a3 100644 --- a/tests/unit/controller/audio-stream-controller.ts +++ b/tests/unit/controller/audio-stream-controller.ts @@ -7,12 +7,13 @@ import { State } from '../../../src/controller/base-stream-controller'; import { FragmentTracker } from '../../../src/controller/fragment-tracker'; import { Events } from '../../../src/events'; import Hls from '../../../src/hls'; +import { Fragment } from '../../../src/loader/fragment'; import KeyLoader from '../../../src/loader/key-loader'; import { LoadStats } from '../../../src/loader/load-stats'; import { Level } from '../../../src/types/level'; +import { PlaylistLevelType } from '../../../src/types/loader'; import { AttrList } from '../../../src/utils/attr-list'; import { adjustSlidingStart } from '../../../src/utils/discontinuities'; -import type { Fragment } from '../../../src/loader/fragment'; import type { LevelDetails } from '../../../src/loader/level-details'; import type { AudioTrackLoadedData, @@ -180,19 +181,19 @@ describe('AudioStreamController', function () { const getPlaylistData = function ( startSN: number, endSN: number, - type: 'audio' | 'main', + type: PlaylistLevelType, live: boolean, ) { const targetduration = 10; const fragments: Fragment[] = Array.from(new Array(endSN - startSN)).map( - (u, i) => - ({ - sn: i + startSN, - cc: Math.floor((i + startSN) / 10), - start: i * targetduration, - duration: targetduration, - type, - }) as unknown as Fragment, + (u, i) => { + const frag = new Fragment(type, ''); + frag.sn = i + startSN; + frag.cc = Math.floor((i + startSN) / 10); + frag.setStart(i * targetduration); + frag.duration = targetduration; + return frag; + }, ); return { details: { @@ -221,7 +222,12 @@ describe('AudioStreamController', function () { endSN: number, live: boolean = false, ): LevelLoadedData { - const data = getPlaylistData(startSN, endSN, 'main', live); + const data = getPlaylistData( + startSN, + endSN, + PlaylistLevelType.MAIN, + live, + ); const levelData: LevelLoadedData = { ...data, level: 0, @@ -234,7 +240,12 @@ describe('AudioStreamController', function () { endSN: number, live: boolean = false, ): AudioTrackLoadedData { - const data = getPlaylistData(startSN, endSN, 'audio', live); + const data = getPlaylistData( + startSN, + endSN, + PlaylistLevelType.AUDIO, + live, + ); const audioTrackData: AudioTrackLoadedData = { ...data, groupId: 'audio', diff --git a/tests/unit/controller/base-stream-controller.ts b/tests/unit/controller/base-stream-controller.ts index a7ca4754086..40a3651795c 100644 --- a/tests/unit/controller/base-stream-controller.ts +++ b/tests/unit/controller/base-stream-controller.ts @@ -68,7 +68,7 @@ describe('BaseStreamController', function () { const frag = new Fragment(PlaylistLevelType.MAIN, '') as MediaFragment; frag.duration = 5; frag.sn = i; - frag.start = i * 5; + frag.setStart(i * 5); details.fragments.push(frag); } details.live = live; diff --git a/tests/unit/controller/buffer-controller.ts b/tests/unit/controller/buffer-controller.ts index 3d89bd8524e..86994802693 100644 --- a/tests/unit/controller/buffer-controller.ts +++ b/tests/unit/controller/buffer-controller.ts @@ -137,7 +137,6 @@ describe('BufferController', function () { .stub(bufferController, 'createSourceBuffers') .callsFake(() => { Object.keys(bufferController.tracks).forEach((type) => { - bufferController.tracks ||= {}; bufferController.tracks[type] = { appendBuffer: () => {}, remove: () => {}, diff --git a/tests/unit/controller/cap-level-controller.js b/tests/unit/controller/cap-level-controller.ts similarity index 90% rename from tests/unit/controller/cap-level-controller.js rename to tests/unit/controller/cap-level-controller.ts index b478d41ee05..401a51eee42 100644 --- a/tests/unit/controller/cap-level-controller.js +++ b/tests/unit/controller/cap-level-controller.ts @@ -1,30 +1,38 @@ +import chai from 'chai'; import sinon from 'sinon'; -import Hls from '../../../src/hls'; +import sinonChai from 'sinon-chai'; import CapLevelController from '../../../src/controller/cap-level-controller'; import { Events } from '../../../src/events'; +import Hls from '../../../src/hls'; +import { Level } from '../../../src/types/level'; +import { parsedLevel } from '../utils/mock-level'; + +chai.use(sinonChai); +const expect = chai.expect; -const levels = [ - { +const parsedLevels = [ + parsedLevel({ width: 360, height: 360, - bandwidth: 1000, - }, - { + bitrate: 1000, + }), + parsedLevel({ width: 540, height: 540, - bandwidth: 2000, - }, - { + bitrate: 2000, + }), + parsedLevel({ width: 540, height: 540, - bandwidth: 3000, - }, - { + bitrate: 3000, + }), + parsedLevel({ width: 720, height: 720, - bandwidth: 4000, - }, + bitrate: 4000, + }), ]; +const levels = parsedLevels.map((parsedLevel) => new Level(parsedLevel)); describe('CapLevelController', function () { describe('getMaxLevelByMediaSize', function () { @@ -63,16 +71,6 @@ describe('CapLevelController', function () { const actual = CapLevelController.getMaxLevelByMediaSize([], 5000, 5000); expect(expected).to.equal(actual); }); - - it('Should return -1 if there levels is undefined', function () { - const expected = -1; - const actual = CapLevelController.getMaxLevelByMediaSize( - undefined, - 5000, - 5000, - ); - expect(expected).to.equal(actual); - }); }); describe('getDimensions', function () { @@ -100,7 +98,10 @@ describe('CapLevelController', function () { if (media.parentNode) { media.parentNode.removeChild(media); } - document.body.removeChild(document.querySelector('#test-fixture')); + const fixture = document.querySelector('#test-fixture'); + if (fixture) { + document.body.removeChild(fixture); + } hls.destroy(); }); @@ -126,7 +127,14 @@ describe('CapLevelController', function () { it('gets client bounds width and height when media element is in the DOM', function () { media.style.width = '1280px'; media.style.height = '720px'; - document.querySelector('#test-fixture').appendChild(media); + + const fixture = document.querySelector('#test-fixture'); + if (!fixture) { + expect(fixture).is.not.null; + return; + } + fixture.appendChild(media); + const pixelRatio = capLevelController.contentScaleFactor; const bounds = capLevelController.getDimensions(); expect(bounds.width).to.equal(1280); @@ -150,11 +158,17 @@ describe('CapLevelController', function () { media.style.width = '1280px'; media.style.height = '720px'; - document.querySelector('#test-fixture').appendChild(media); + + const fixture = document.querySelector('#test-fixture'); + if (!fixture) { + expect(fixture).is.not.null; + return; + } + fixture.appendChild(media); + capLevelController.onMediaAttaching(Events.MEDIA_ATTACHING, { media, }); - const pixelRatio = capLevelController.contentScaleFactor; bounds = capLevelController.getDimensions(); expect(bounds.width).to.equal(1280); diff --git a/tests/unit/controller/eme-controller.ts b/tests/unit/controller/eme-controller.ts index 2564181b2db..b94509a3005 100644 --- a/tests/unit/controller/eme-controller.ts +++ b/tests/unit/controller/eme-controller.ts @@ -158,9 +158,6 @@ describe('EMEController', function () { } as any); expect(emePromise).to.be.a('Promise'); - if (!emePromise) { - return; - } return emePromise.finally(() => { expect(media.setMediaKeys).callCount(1); expect(reqMediaKsAccessSpy).callCount(1); @@ -226,9 +223,6 @@ describe('EMEController', function () { } as any); expect(emePromise).to.be.a('Promise'); - if (!emePromise) { - return; - } return emePromise.finally(() => { expect(reqMediaKsAccessSpy).callCount(1); const args = reqMediaKsAccessSpy.getCall(0) @@ -416,13 +410,6 @@ describe('EMEController', function () { '00000000000000000000000000000000' ], ).to.be.a('Promise'); - if ( - !emeController.keyIdToKeySessionPromise[ - '00000000000000000000000000000000' - ] - ) { - return; - } return emeController.keyIdToKeySessionPromise[ '00000000000000000000000000000000' ].finally(() => { @@ -499,13 +486,6 @@ describe('EMEController', function () { '00000000000000000000000000000000' ], ).to.be.a('Promise'); - if ( - !emeController.keyIdToKeySessionPromise[ - '00000000000000000000000000000000' - ] - ) { - return; - } return emeController.keyIdToKeySessionPromise[ '00000000000000000000000000000000' ] @@ -582,13 +562,6 @@ describe('EMEController', function () { '00000000000000000000000000000000' ], ).to.be.a('Promise'); - if ( - !emeController.keyIdToKeySessionPromise[ - '00000000000000000000000000000000' - ] - ) { - return; - } return emeController.keyIdToKeySessionPromise[ '00000000000000000000000000000000' ] diff --git a/tests/unit/controller/fragment-tracker.ts b/tests/unit/controller/fragment-tracker.ts index d245ef6fb6c..8c0fdf28e7c 100644 --- a/tests/unit/controller/fragment-tracker.ts +++ b/tests/unit/controller/fragment-tracker.ts @@ -608,7 +608,7 @@ function createBufferAppendedData( frag: new Fragment(PlaylistLevelType.MAIN, ''), part: null, parent: PlaylistLevelType.MAIN, - type: audio && video ? 'audiovideo' : video ? 'video' : 'audio', + type: audio ? 'audiovideo' : 'video', timeRanges: { video: createMockBuffer(video), audio: createMockBuffer(audio || video), @@ -655,7 +655,7 @@ function createMockFragment( ): Fragment { const frag = new Fragment(data.type, ''); Object.assign(frag, data); - frag.start = data.startPTS; + frag.setStart(data.startPTS); frag.duration = data.endPTS - data.startPTS; types.forEach((t) => { frag.setElementaryStreamInfo( diff --git a/tests/unit/controller/interstitials-controller.ts b/tests/unit/controller/interstitials-controller.ts index d0ee1b86bbd..2db36842684 100644 --- a/tests/unit/controller/interstitials-controller.ts +++ b/tests/unit/controller/interstitials-controller.ts @@ -36,10 +36,7 @@ class HLSTestPlayer extends Hls { hlsTestable.coreComponents.forEach((component) => component.destroy()); hlsTestable.coreComponents.length = 0; hlsTestable.on(Events.MEDIA_ATTACHING, (t, data) => { - const media = data.media; - if (media) { - media.src = ''; - } + data.media.src = ''; }); hlsTestable.on(Events.MEDIA_DETACHING, () => { const media = hlsTestable.media; @@ -140,7 +137,7 @@ describe('InterstitialsController', function () { 0, null, ); - expect(details?.playlistParsingError).to.equal(null); + expect(details.playlistParsingError).to.equal(null); const attrs = new AttrList({}); const level = new Level({ name: '', @@ -210,9 +207,6 @@ fileSequence4.ts expect(insterstitials.playingIndex).to.equal(0, 'playingIndex'); expect(insterstitials.events).is.an('array').which.has.lengthOf(1); expect(schedule).is.an('array').which.has.lengthOf(2); - if (!insterstitials.events || !schedule) { - return; - } const interstitialEvent = insterstitials.events[0]; expect(interstitialEvent.identifier).to.equal('0'); expect(interstitialEvent.restrictions.jump).to.equal(true); @@ -296,11 +290,8 @@ fileSequence4.ts .is.an('array') .which.has.lengthOf( 7, - `Schedule items: ${schedule?.map((item) => `${item.start}-${item.end}`).join(', ')}`, + `Schedule items: ${schedule.map((item) => `${item.start}-${item.end}`).join(', ')}`, ); - if (!events || !schedule) { - return; - } const eventAssertions = [ { startTime: 0, @@ -478,13 +469,10 @@ fileSequence3.ts const events = insterstitials.events; const schedule = insterstitials.schedule; expect(events).is.an('array').which.has.lengthOf(5); - const scheduleDebugString = `Schedule items: ${schedule?.map((item) => `[${item.event ? 'I' : 'P'}:${item.start}-${item.end}]`).join(', ')}`; + const scheduleDebugString = `Schedule items: ${schedule.map((item) => `[${item.event ? 'I' : 'P'}:${item.start}-${item.end}]`).join(', ')}`; expect(schedule) .is.an('array') .which.has.lengthOf(9, scheduleDebugString); - if (!events || !schedule) { - return; - } [ 'primary', '1', @@ -498,12 +486,12 @@ fileSequence3.ts ].forEach((typeOrIdentifier, i) => { if (typeOrIdentifier === 'primary') { expect( - schedule?.[i], + schedule[i], `Expected to find a primary segment at index ${i}: ${scheduleDebugString}`, ).to.have.property('nextEvent'); } else { expect( - schedule?.[i], + schedule[i], `Expected to find an Interstitial at index ${i}: ${scheduleDebugString}`, ) .to.have.property('event') @@ -725,9 +713,6 @@ fileSequence3.mp4 expect(insterstitials.playingIndex).to.equal(0, 'playingIndex'); expect(insterstitials.events).is.an('array').which.has.lengthOf(2); expect(schedule).is.an('array').which.has.lengthOf(4); - if (!insterstitials.events || !schedule) { - return; - } expect(insterstitials.events[0].identifier).to.equal('ad1'); expect(insterstitials.events[1].identifier).to.equal('ad2'); expect(insterstitials.events[0]).to.equal(schedule[0].event); @@ -827,9 +812,6 @@ fileSequence3.mp4 const schedule = insterstitials.schedule; expect(insterstitials.events).is.an('array').which.has.lengthOf(2); expect(schedule).is.an('array').which.has.lengthOf(2); - if (!insterstitials.events || !schedule) { - return; - } expect(insterstitials.events[0].identifier).to.equal('ad1'); expect(insterstitials.events[1].identifier).to.equal('ad2'); expect(insterstitials.events[0]).to.equal(schedule[0].event); @@ -891,9 +873,6 @@ fileSequence4.ts const events = insterstitials.events; expect(events).is.an('array').which.has.lengthOf(1); expect(schedule).is.an('array').which.has.lengthOf(2); - if (!events || !schedule) { - return; - } expect(events[0]).to.deep.include({ identifier: '0', timelineOccupancy: TimelineOccupancy.Point, @@ -986,9 +965,6 @@ fileSequence4.ts const events = insterstitials.events; expect(events).is.an('array').which.has.lengthOf(4); expect(schedule).is.an('array').which.has.lengthOf(5); - if (!events || !schedule) { - return; - } eventAssertions.forEach((assertions, i) => { expect(events[i].identifier).to.equal('' + i); expect(events[i], `Interstitial Event "${i}"`).to.deep.include( @@ -1118,9 +1094,6 @@ fileSequence5.mp4`; } expect(insterstitials.events).is.an('array').which.has.lengthOf(2); expect(insterstitials.schedule).is.an('array').which.has.lengthOf(4); - if (!insterstitials.events || !insterstitials.schedule) { - return; - } const callsWithPrerollBeforeAttach = getTriggerCalls(); expect(callsWithPrerollBeforeAttach).to.deep.equal( [Events.LEVEL_UPDATED, Events.INTERSTITIALS_UPDATED], @@ -1185,9 +1158,6 @@ fileSequence3.mp4 } expect(insterstitials.events).is.an('array').which.has.lengthOf(1); expect(insterstitials.schedule).is.an('array').which.has.lengthOf(2); - if (!insterstitials.events || !insterstitials.schedule) { - return; - } const callsBeforeAttach = getTriggerCalls(); expect(callsBeforeAttach).to.deep.equal( [ @@ -1255,9 +1225,6 @@ fileSequence3.mp4 } expect(insterstitials.events).is.an('array').which.has.lengthOf(1); expect(insterstitials.schedule).is.an('array').which.has.lengthOf(2); - if (!insterstitials.events || !insterstitials.schedule) { - return; - } const callsBeforeAttach = getTriggerCalls(); expect(callsBeforeAttach).to.deep.equal( [ @@ -1333,9 +1300,6 @@ fileSequence6.mp4`; } expect(insterstitials.events).is.an('array').which.has.lengthOf(1); expect(insterstitials.schedule).is.an('array').which.has.lengthOf(3); - if (!insterstitials.events || !insterstitials.schedule) { - return; - } const eventsBeforeAttach = getTriggerCalls(); expect(eventsBeforeAttach).to.deep.equal( [Events.LEVEL_UPDATED, Events.INTERSTITIALS_UPDATED], @@ -1463,9 +1427,6 @@ fileSequence6.mp4`; } expect(insterstitials.events).is.an('array').which.has.lengthOf(1); expect(insterstitials.schedule).is.an('array').which.has.lengthOf(3); - if (!insterstitials.events || !insterstitials.schedule) { - return; - } const eventsAfterPlaylist = getTriggerCalls(); expect(eventsAfterPlaylist).to.deep.equal( [ @@ -1508,10 +1469,10 @@ fileSequence6.mp4`; insterstitials.skip(); const eventsAfterSkip = getTriggerCalls(); const expectedSkipEvents = [ - Events.INTERSTITIALS_BUFFERED_TO_BOUNDARY, Events.INTERSTITIAL_ASSET_ENDED, Events.INTERSTITIAL_ENDED, Events.INTERSTITIALS_UPDATED, // removed Interstitial with CUE="ONCE" + Events.INTERSTITIALS_BUFFERED_TO_BOUNDARY, Events.MEDIA_ATTACHING, Events.INTERSTITIALS_PRIMARY_RESUMED, ]; @@ -1607,17 +1568,13 @@ fileSequence6.mp4 duration: 15, }); } else if (bufferingIndex === 2) { - // buffered to primary (end of interstitial) + // buffered to primary (interstitial ended) expectIm(primary, e).to.include({ currentTime: 40, bufferedEnd: 40 }); expectIm(integrated, e).to.include({ currentTime: 45, bufferedEnd: 45, }); - expectIm(interstitialPlayer, e).to.include({ - playingIndex: 2, - currentTime: 15, - duration: 15, - }); + expectIm(interstitialPlayer, e).to.be.null; } }); hls.on(Events.INTERSTITIAL_STARTED, (t) => { @@ -1799,9 +1756,6 @@ fileSequence6.mp4 expect(im.events).is.an('array').which.has.lengthOf(1); expect(im.schedule).is.an('array').which.has.lengthOf(3); - if (!im.events || !im.schedule) { - return; - } const eventsBeforeAttach = getTriggerCalls(); expect(eventsBeforeAttach).to.deep.equal( [Events.LEVEL_UPDATED, Events.INTERSTITIALS_UPDATED], @@ -1956,9 +1910,9 @@ fileSequence6.mp4 }); const eventsAfterLastAsset = getTriggerCalls(); const expectedEndLastAssetEvents = [ - Events.INTERSTITIALS_BUFFERED_TO_BOUNDARY, Events.INTERSTITIAL_ASSET_ENDED, Events.INTERSTITIAL_ENDED, + Events.INTERSTITIALS_BUFFERED_TO_BOUNDARY, Events.MEDIA_ATTACHING, Events.INTERSTITIALS_PRIMARY_RESUMED, ]; @@ -2008,9 +1962,6 @@ fileSequence6.mp4`; } expect(insterstitials.events).is.an('array').which.has.lengthOf(1); expect(insterstitials.schedule).is.an('array').which.has.lengthOf(3); - if (!insterstitials.events || !insterstitials.schedule) { - return; - } // Capture asset-list request const loadSpy = sandbox.spy(hls.config.loader.prototype, 'load'); diff --git a/tests/unit/controller/level-controller.ts b/tests/unit/controller/level-controller.ts index 232f1928bc6..0a2b1e660b2 100755 --- a/tests/unit/controller/level-controller.ts +++ b/tests/unit/controller/level-controller.ts @@ -10,6 +10,7 @@ import { PlaylistLevelType } from '../../../src/types/loader'; import { AttrList } from '../../../src/utils/attr-list'; import { getMediaSource } from '../../../src/utils/mediasource-helper'; import HlsMock from '../../mocks/hls.mock'; +import { parsedLevel } from '../utils/mock-level'; import type { Fragment } from '../../../src/loader/fragment'; import type { LevelDetails } from '../../../src/loader/level-details'; import type { ParsedMultivariantPlaylist } from '../../../src/loader/m3u8-parser'; @@ -17,7 +18,6 @@ import type { ManifestLoadedData, ManifestParsedData, } from '../../../src/types/events'; -import type { LevelParsed } from '../../../src/types/level'; import type { PlaylistLoaderContext } from '../../../src/types/loader'; import type { MediaAttributes, @@ -49,18 +49,6 @@ type LevelControllerTestable = Omit & { redundantFailover: (levelIndex: number) => void; }; -function parsedLevel( - options: Partial & { bitrate: number }, -): LevelParsed { - const level: LevelParsed = { - attrs: new AttrList({ BANDWIDTH: options.bitrate }), - bitrate: options.bitrate, - name: '', - url: `${options.bitrate}.m3u8`, - }; - return Object.assign(level, options); -} - function mediaPlaylist(options: Partial): MediaPlaylist { const track: MediaPlaylist = { attrs: new AttrList({}) as MediaAttributes, diff --git a/tests/unit/controller/level-helper.ts b/tests/unit/controller/level-helper.ts index da2973f48ad..e788e4b0571 100644 --- a/tests/unit/controller/level-helper.ts +++ b/tests/unit/controller/level-helper.ts @@ -48,7 +48,7 @@ const generatePlaylist = (sequenceNumbers, offset = 0, duration = 5) => { playlist.fragments = sequenceNumbers.map((n, i) => { const frag = new Fragment(PlaylistLevelType.MAIN, ''); frag.sn = n; - frag.start = i * 5 + offset; + frag.setStart(i * 5 + offset); frag.duration = duration; return frag; }); @@ -67,7 +67,9 @@ const getIteratedSequence = (oldPlaylist, newPlaylist) => { }; const getFragmentSequenceNumbers = (details: LevelDetails) => - details.fragments.map((f) => `${f?.sn}-${f?.cc}`).join(','); + details.fragments + .map((f: MediaFragment | null) => `${f?.sn}-${f?.cc}`) + .join(','); describe('LevelHelper Tests', function () { let sandbox; @@ -204,7 +206,7 @@ describe('LevelHelper Tests', function () { it('matches start when the new playlist starts before the old', function () { const oldPlaylist = generatePlaylist([3, 4, 5]); oldPlaylist.fragments.forEach((f) => { - f.start += 10; + f.addStart(10); }); const newPlaylist = generatePlaylist([1, 2, 3]); mergeDetails(oldPlaylist, newPlaylist); @@ -374,12 +376,12 @@ fileSequence11.ts .which.equals(details.fragments[2].ref) .which.has.property('sn') .which.equals(5); - expect(details.dateRanges.one.startTime).to.equal(4); - expect(details.dateRanges.two.startTime).to.equal(10); - expect(details.dateRanges.three.startTime).to.equal(16); - expect(details.dateRanges.one.tagOrder, 'one.tagOrder').to.equal(0); - expect(details.dateRanges.two.tagOrder, 'two.tagOrder').to.equal(1); - expect(details.dateRanges.three.tagOrder, 'three.tagOrder').to.equal(2); + expect(details.dateRanges.one!.startTime).to.equal(4); + expect(details.dateRanges.two!.startTime).to.equal(10); + expect(details.dateRanges.three!.startTime).to.equal(16); + expect(details.dateRanges.one!.tagOrder, 'one.tagOrder').to.equal(0); + expect(details.dateRanges.two!.tagOrder, 'two.tagOrder').to.equal(1); + expect(details.dateRanges.three!.tagOrder, 'three.tagOrder').to.equal(2); expect( detailsUpdated.hasProgramDateTime, 'detailsUpdated.hasProgramDateTime', @@ -407,25 +409,26 @@ fileSequence11.ts .which.has.property('tagAnchor') .which.has.property('sn') .which.equals(5); - expect(detailsUpdated.dateRanges.one.startTime).to.equal(4); - expect(detailsUpdated.dateRanges.two.startTime).to.equal(10); - expect(detailsUpdated.dateRanges.three.startTime).to.equal(16); - expect(detailsUpdated.dateRanges.four.startTime).to.equal(76); + expect(detailsUpdated.dateRanges.one!.startTime).to.equal(4); + expect(detailsUpdated.dateRanges.two!.startTime).to.equal(10); + expect(detailsUpdated.dateRanges.three!.startTime).to.equal(16); + expect(detailsUpdated.dateRanges.four!.startTime).to.equal(76); expect( - detailsUpdated.dateRanges.one.tagOrder, + detailsUpdated.dateRanges.one!.tagOrder, 'one.tagOrder updated', ).to.equal(0); expect( - detailsUpdated.dateRanges.two.tagOrder, + detailsUpdated.dateRanges.two!.tagOrder, 'two.tagOrder updated', ).to.equal(1); expect( - detailsUpdated.dateRanges.three.tagOrder, + detailsUpdated.dateRanges.three!.tagOrder, 'three.tagOrder updated', ).to.equal(2); - expect(detailsUpdated.dateRanges.four.tagOrder, 'four.tagOrder').to.equal( - 3, - ); + expect( + detailsUpdated.dateRanges.four!.tagOrder, + 'four.tagOrder', + ).to.equal(3); expect(detailsUpdated.playlistParsingError).to.be.null; }); @@ -1096,8 +1099,8 @@ fileSequence18.ts`; Object.keys(details.dateRanges), 'first playlist daterange ids', ).to.have.deep.equal(['d0', 'd1', 'd2', 'd3', 'd4']); - expect(details.dateRanges.d2.startTime).to.equal(2.94); - expect(details.dateRanges.d3.startTime).to.equal(3.94); + expect(details.dateRanges.d2!.startTime).to.equal(2.94); + expect(details.dateRanges.d3!.startTime).to.equal(3.94); expect( detailsUpdated.hasProgramDateTime, 'detailsUpdated.hasProgramDateTime', @@ -1116,8 +1119,8 @@ fileSequence18.ts`; .which.equals(detailsUpdated.fragments[0].ref) .which.has.property('sn') .which.equals(3); - expect(detailsUpdated.dateRanges.d2.startTime).to.equal(2.94); - expect(detailsUpdated.dateRanges.d3.startTime).to.equal(3.94); + expect(detailsUpdated.dateRanges.d2!.startTime).to.equal(2.94); + expect(detailsUpdated.dateRanges.d3!.startTime).to.equal(3.94); // Multiple #EXT-X-SKIP tags are not allowed expect(detailsUpdated.playlistParsingError).to.include({ message: `#EXT-X-SKIP must not appear more than once (#EXT-X-SKIP:SKIPPED-SEGMENTS=2,RECENTLY-REMOVED-DATERANGES="d0")`, diff --git a/tests/unit/controller/stream-controller.ts b/tests/unit/controller/stream-controller.ts index d645a0648aa..8bee6813a4c 100644 --- a/tests/unit/controller/stream-controller.ts +++ b/tests/unit/controller/stream-controller.ts @@ -210,7 +210,7 @@ describe('StreamController', function () { fragPrevious.programDateTime = 1505502671523; fragPrevious.duration = 5.0; fragPrevious.level = 1; - fragPrevious.start = 10.0; + fragPrevious.setStart(10.0); fragPrevious.sn = 2; // Fragment with PDT 1505502671523 in level 1 does not have the same sn as in level 2 where cc is 1 fragPrevious.cc = 0; @@ -274,7 +274,7 @@ describe('StreamController', function () { fragPrevious.programDateTime = 1505502681523; fragPrevious.duration = 5.0; fragPrevious.level = 1; - fragPrevious.start = 15.0; + fragPrevious.setStart(15.0); fragPrevious.sn = 3; streamController['fragPrevious'] = fragPrevious; @@ -475,7 +475,7 @@ describe('StreamController', function () { ) as MediaFragment; firstFrag.duration = 5.0; firstFrag.level = 1; - firstFrag.start = 0; + firstFrag.setStart(0); firstFrag.sn = 1; firstFrag.cc = 0; firstFrag.elementaryStreams.video = { diff --git a/tests/unit/hls.ts b/tests/unit/hls.ts index ae64e5af31b..cf9779e5091 100644 --- a/tests/unit/hls.ts +++ b/tests/unit/hls.ts @@ -57,7 +57,7 @@ describe('Hls', function () { const hls = new Hls({ capLevelOnFPSDrop: true }); expect(hls.media).to.equal(null); const media = document.createElement('video'); - expect(media || null).to.not.equal(null); + expect(media).to.be.an('HTMLVideoElement'); hls.attachMedia(media); expect(hls.media).to.equal(media); detachTest(hls, media, 6); @@ -72,7 +72,7 @@ describe('Hls', function () { }); expect(hls.media).to.equal(null); const media = document.createElement('video'); - expect(media || null).to.not.equal(null); + expect(media).to.be.an('HTMLVideoElement'); hls.attachMedia(media); expect(hls.media).to.equal(media); hls.trigger(Events.MEDIA_ATTACHED, { media }); diff --git a/tests/unit/loader/m3u8-parser.ts b/tests/unit/loader/m3u8-parser.ts index e41bfc7d0ee..1549f308ce7 100644 --- a/tests/unit/loader/m3u8-parser.ts +++ b/tests/unit/loader/m3u8-parser.ts @@ -1996,10 +1996,10 @@ segment.m4s ); expect(details.playlistParsingError).to.be.null; expect(details.dateRangeTagCount).to.equal(4); - expect(details.dateRanges.pre.isInterstitial).to.be.true; - expect(details.dateRanges.mid1.isInterstitial).to.be.true; - expect(details.dateRanges.mid2.isInterstitial).to.be.true; - expect(details.dateRanges.post.isInterstitial).to.be.true; + expect(details.dateRanges.pre!.isInterstitial).to.be.true; + expect(details.dateRanges.mid1!.isInterstitial).to.be.true; + expect(details.dateRanges.mid2!.isInterstitial).to.be.true; + expect(details.dateRanges.post!.isInterstitial).to.be.true; expect(details.dateRanges).to.have.property('pre').which.deep.includes({ tagOrder: 0, }); @@ -2012,15 +2012,15 @@ segment.m4s expect(details.dateRanges).to.have.property('post').which.deep.includes({ tagOrder: 3, }); - expect(details.dateRanges.pre.cue.pre).to.be.true; - expect(details.dateRanges.mid1.cue.once).to.be.true; - expect(details.dateRanges.post.cue.post).to.be.true; - expect(details.dateRanges.post.cue.once).to.be.true; + expect(details.dateRanges.pre!.cue.pre).to.be.true; + expect(details.dateRanges.mid1!.cue.once).to.be.true; + expect(details.dateRanges.post!.cue.post).to.be.true; + expect(details.dateRanges.post!.cue.once).to.be.true; // DateRange start times are mapped to the primary timeline and not changed by CUE Interstitial DURATION - expect(details.dateRanges.pre.startTime).to.equal(-7200); - expect(details.dateRanges.mid1.startTime).to.equal(10); - expect(details.dateRanges.mid2.startTime).to.equal(25); - expect(details.dateRanges.post.startTime).to.equal(0); + expect(details.dateRanges.pre!.startTime).to.equal(-7200); + expect(details.dateRanges.mid1!.startTime).to.equal(10); + expect(details.dateRanges.mid2!.startTime).to.equal(25); + expect(details.dateRanges.post!.startTime).to.equal(0); }); it('ensures DateRanges are mapped to a segment whose TimeRange covers the start date of the DATERANGE tag', function () { @@ -2047,14 +2047,14 @@ segment.m4s null, ); expect(details.playlistParsingError).to.be.null; - expect(details.dateRanges.sooner.isValid).to.equal( + expect(details.dateRanges.sooner!.isValid).to.equal( true, 'is valid DateRange', ); - expect(details.dateRanges.sooner.tagAnchor) + expect(details.dateRanges.sooner!.tagAnchor) .to.have.property('sn') .which.equals(2); - expect(details.dateRanges.sooner.startTime).to.equal(10); + expect(details.dateRanges.sooner!.startTime).to.equal(10); }); it('ensures DateRanges that start before the program are mapped to the first PDT tag', function () { @@ -2081,14 +2081,14 @@ segment.m4s null, ); expect(details.playlistParsingError).to.be.null; - expect(details.dateRanges.earlier.isValid).to.equal( + expect(details.dateRanges.earlier!.isValid).to.equal( true, 'is valid DateRange', ); - expect(details.dateRanges.earlier.tagAnchor) + expect(details.dateRanges.earlier!.tagAnchor) .to.have.property('sn') .which.equals(1); - expect(details.dateRanges.earlier.startTime).to.equal(-10); + expect(details.dateRanges.earlier!.startTime).to.equal(-10); }); it('adds PROGRAM-DATE-TIME and DATERANGE tag text to fragment[].tagList for backwards compatibility', function () { @@ -2758,10 +2758,6 @@ a{$mvpVariable}.mp4 TYPE: 'PART', URI: 'part-5.1.mp4', }); - if (details.partList === null) { - expect(details.partList, 'partList').to.not.equal(null); - return; - } if (!details.renditionReports) { expect(details.renditionReports, 'renditionReports').to.not.be .undefined; @@ -2913,7 +2909,7 @@ a{$bar}.mp4 details, 'Missing preceding EXT-X-DEFINE tag for Variable Reference: "bar"', ); - expect(details.fragments?.[0].relurl).to.equal('a{$bar}.mp4'); + expect(details.fragments[0].relurl).to.equal('a{$bar}.mp4'); }); it('fails to parse Media Playlist when variable reference precedes definition', function () { diff --git a/tests/unit/utils/mediacapabilities-helper.ts b/tests/unit/utils/mediacapabilities-helper.ts index 445c554a59e..c7518b24201 100644 --- a/tests/unit/utils/mediacapabilities-helper.ts +++ b/tests/unit/utils/mediacapabilities-helper.ts @@ -7,6 +7,11 @@ import type { MediaPlaylist, } from '../../../src/types/media-playlist'; +declare const navigator: { + prototype: Navigator; + mediaCapabilities?: MediaCapabilities; +}; + describe('getMediaDecodingInfoPromise', function () { it('adds queries to cache', function () { if (!navigator.mediaCapabilities) { diff --git a/tests/unit/utils/mock-level.ts b/tests/unit/utils/mock-level.ts new file mode 100644 index 00000000000..2b2fd421ef7 --- /dev/null +++ b/tests/unit/utils/mock-level.ts @@ -0,0 +1,15 @@ +import { AttrList } from '../../../src/utils/attr-list'; +import type { LevelParsed } from '../../../src/types/level'; + +export function parsedLevel( + options: Partial & { bitrate: number }, +): LevelParsed { + const { bitrate, height } = options; + const level: LevelParsed = { + attrs: new AttrList({ BANDWIDTH: bitrate }), + bitrate, + name: `${height}-${bitrate}`, + url: `${bitrate}.m3u8`, + }; + return Object.assign(level, options); +} From 84e4fbdc3f755b2fc279741d13cdddde1de2f58b Mon Sep 17 00:00:00 2001 From: Rob Walch Date: Mon, 4 Aug 2025 16:08:08 -0700 Subject: [PATCH 34/52] Improve `recoverMediaError` documentation in API.md (#7447) --- docs/API.md | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/docs/API.md b/docs/API.md index 88938b4077d..9bb2807e443 100644 --- a/docs/API.md +++ b/docs/API.md @@ -350,22 +350,44 @@ hls.on(Hls.Events.ERROR, function (event, data) { #### Fatal Error Recovery -HLS.js provides several methods for attempting playback recover in the event of a decoding error in the HTMLMediaElement: +HLS.js provides methods for attempting playback recover in the event of a decoding error in the HTMLMediaElement: ##### `hls.recoverMediaError()` -Resets the MediaSource and restarts streaming from the last known playhead position. +Resets the MediaSource and restarts streaming from the last known playhead position. This should only be used when the media element is in an error state. +It should not be used in response to non-fatal hls.js error events. ###### Error recovery sample code ```js -hls.on(Hls.Events.ERROR, function (event, data) { +let attemptedErrorRecovery = null; + +video.addEventListener('error', (event) { + const mediaError = event.currentTarget.error; + if (mediaError.code === mediaError.MEDIA_ERR_DECODE) { + const now = Date.now(); + if (!attemptedErrorRecovery || now - attemptedErrorRecovery > 5000) { + attemptedErrorRecovery = now; + hls.recoverMediaError(); + } + } +}); + +hls.on(Hls.Events.ERROR, function (name, data) { + // Special handling is only needed to errors flagged as `fatal`. if (data.fatal) { switch (data.type) { - case Hls.ErrorTypes.MEDIA_ERROR: - console.log('fatal media error encountered, try to recover'); - hls.recoverMediaError(); + case Hls.ErrorTypes.MEDIA_ERROR: { + const now = Date.now(); + if (!attemptedErrorRecovery || now - attemptedErrorRecovery > 5000) { + console.log('Fatal media error encountered (' + video.error + + '), attempting to recover'); + attemptedErrorRecovery = now; + hls.recoverMediaError(); + } else { + console.log('Skipping media error recovery (only ' + (now - attemptedErrorRecovery) + 'ms since last error)'); + } break; + } case Hls.ErrorTypes.NETWORK_ERROR: console.error('fatal network error encountered', data); // All retries and media options have been exhausted. From a37db2fc087d7c8fbd58e460f21639d638ac2e13 Mon Sep 17 00:00:00 2001 From: Rob Walch Date: Wed, 6 Aug 2025 19:19:39 -0700 Subject: [PATCH 35/52] Prevent exception when mapping date ranges that referenced dropped PDT (#7450) --- src/loader/m3u8-parser.ts | 6 ++ tests/unit/controller/level-helper.ts | 113 ++++++++++++++++++++++++++ 2 files changed, 119 insertions(+) diff --git a/src/loader/m3u8-parser.ts b/src/loader/m3u8-parser.ts index 12eb6e92c99..942e4b6ef28 100644 --- a/src/loader/m3u8-parser.ts +++ b/src/loader/m3u8-parser.ts @@ -770,6 +770,9 @@ export function mapDateRanges( const startDateTime = dateRange.startDate.getTime(); dateRange.tagAnchor = lastProgramDateTime.ref; for (let j = programDateTimeCount; j--; ) { + if (programDateTimes[j]?.sn < details.startSN) { + break; + } const fragIndex = findFragmentWithStartDate( details, startDateTime, @@ -802,6 +805,9 @@ function findFragmentWithStartDate( if (startDateTime <= pdtStart + durationBetweenPdt * 1000) { // map to fragment with date-time range const startIndex = programDateTimes[index].sn - details.startSN; + if (startIndex < 0) { + return -1; + } const fragments = details.fragments; if (fragments.length > programDateTimes.length) { const endSegment = diff --git a/tests/unit/controller/level-helper.ts b/tests/unit/controller/level-helper.ts index e788e4b0571..6fd8759a89f 100644 --- a/tests/unit/controller/level-helper.ts +++ b/tests/unit/controller/level-helper.ts @@ -1675,6 +1675,119 @@ video_5432.m4s`; ); }); }); + + it('maps dateranges based on latest EXT-X-PROGRAM-DATE-TIME', function () { + const playlist1 = `#EXTM3U +#EXT-X-VERSION:6 +#EXT-X-MEDIA-SEQUENCE:1 +#EXT-X-TARGETDURATION:6 +#EXT-X-SERVER-CONTROL:CAN-SKIP-UNTIL=9,CAN-SKIP-DATERANGES=YES +#EXT-X-DATERANGE:ID="D0",START-DATE="2025-08-06T15:59:59Z",DURATION=1 +#EXT-X-DATERANGE:ID="D1",START-DATE="2025-08-06T16:00:00Z",DURATION=12,SCTE35-CMD=0x00000000 +#EXT-X-MAP:URI="hls/20821722-video=2499968.m4s" +#EXT-X-PROGRAM-DATE-TIME:2025-08-06T16:00:00Z +#EXTINF:4.0, no desc +1.m4s +#EXTINF:4.0, no desc +2.m4s +#EXT-X-PROGRAM-DATE-TIME:2025-08-06T16:08:00Z +#EXTINF:4.0, no desc +3.m4s +#EXT-X-DATERANGE:ID="D2",START-DATE="2025-08-06T16:00:15Z",PLANNED-DURATION=12,SCTE35-OUT=0x00000000 +#EXTINF:4.0, no desc +4.m4s +#EXTINF:4.0, no desc +5.m4s +#EXT-X-PROGRAM-DATE-TIME:2025-08-06T16:00:20Z +#EXTINF:4.0, no desc +6.m4s +#EXTINF:4.0, no desc +7.m4s +#EXTINF:4.0, no desc +8.m4s +#EXTINF:4.0, no desc +9.m4s`; + // Media sequence increased by one but two segments removed. + const playlist2 = `#EXTM3U +#EXT-X-VERSION:10 +#EXT-X-MEDIA-SEQUENCE:5 +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-TARGETDURATION:6 +#EXT-X-SERVER-CONTROL:CAN-SKIP-UNTIL=9,CAN-SKIP-DATERANGES=YES +#EXT-X-SKIP:SKIPPED-SEGMENTS=4,RECENTLY-REMOVED-DATERANGES="D0" +#EXTINF:4, no desc +9.m4s +#EXTINF:4, no desc +10.m4s +#EXT-X-DATERANGE:ID="D3",START-DATE="2025-08-06T16:00:59.100Z",DURATION=12,SCTE35-CMD=0x00000000`; + const details1 = parseLevelPlaylist(playlist1); + const details2 = parseLevelPlaylist(playlist2); + + expect(details1.playlistParsingError).to.be.null; + expect(details2.playlistParsingError).to.be.null; + + // First playilst details + expect(details1).to.include({ + startSN: 1, + endSN: 9, + totalduration: 36, + dateRangeTagCount: 3, + }); + expect(details1.dateRanges).to.have.keys(['D0', 'D1', 'D2']); + expect(details1.dateRanges.D0).to.include({ + startTime: -1, + tagOrder: 0, + }); + expect(details1.dateRanges.D0?.tagAnchor).to.include({ + sn: 1, + }); + expect(details1.dateRanges.D1).to.include({ + startTime: 0, + tagOrder: 1, + }); + expect(details1.dateRanges.D1?.tagAnchor).to.include({ + sn: 1, // expected to change in details2 after `mergeDetails` when sn: 1 is removed + }); + expect(details1.dateRanges.D2).to.include({ + startTime: 15, + tagOrder: 2, + }); + expect(details1.dateRanges.D2?.tagAnchor).to.include({ + sn: 6, + }); + + // Merged delta playlist with EXT-X-PROGRAM-DATE-TIME removed and DateRange tagAnchors updated + mergeDetails(details1, details2); + + expect(details2).to.include({ + startSN: 5, + endSN: 10, + totalduration: 24, + dateRangeTagCount: 1, + }); + expect(details2.dateRanges).to.have.keys(['D1', 'D2', 'D3']); + expect(details2.dateRanges.D1).to.include({ + startTime: 0, + tagOrder: 1, + }); + expect(details2.dateRanges.D1?.tagAnchor).to.include({ + sn: 6, + }); + expect(details2.dateRanges.D2).to.include({ + startTime: 15, + tagOrder: 2, + }); + expect(details2.dateRanges.D2?.tagAnchor).to.include({ + sn: 6, + }); + expect(details2.dateRanges.D3).to.include({ + startTime: 59.1, + tagOrder: 2, // Note: tagOrder: 3 is expected (following previous/skipped date-ranges), but not critical. + }); + expect(details2.dateRanges.D3?.tagAnchor).to.include({ + sn: 6, + }); + }); }); function parseLevelPlaylist( From 3206822e375ef55c710c7e67a0aaac4679900e04 Mon Sep 17 00:00:00 2001 From: Rob Walch Date: Wed, 6 Aug 2025 17:06:40 -0700 Subject: [PATCH 36/52] Detect unhandled encryption and fallback or error Don't error if mediaKeys are set in case of custom EME integration --- api-extractor/report/hls.js.api.md | 6 + src/controller/audio-stream-controller.ts | 3 + src/controller/base-stream-controller.ts | 35 +++++- src/controller/eme-controller.ts | 130 +++++++++++----------- src/controller/error-controller.ts | 6 +- src/controller/stream-controller.ts | 41 +++---- src/remux/passthrough-remuxer.ts | 58 ++++++---- src/types/buffer.ts | 1 + src/types/remuxer.ts | 1 + src/utils/mp4-tools.ts | 68 +++++------ 10 files changed, 209 insertions(+), 140 deletions(-) diff --git a/api-extractor/report/hls.js.api.md b/api-extractor/report/hls.js.api.md index d1eca8cb450..47f77f71d4b 100644 --- a/api-extractor/report/hls.js.api.md +++ b/api-extractor/report/hls.js.api.md @@ -563,6 +563,8 @@ export class BaseStreamController extends TaskLoop implements NetworkComponentAP // (undocumented) protected transmuxer: TransmuxerInterface | null; // (undocumented) + protected unhandledEncryptionError(initSegment: InitSegmentData, frag: Fragment): boolean; + // (undocumented) protected unregisterListeners(): void; // (undocumented) protected waitForCdnTuneIn(details: LevelDetails): boolean | 0; @@ -579,6 +581,8 @@ export interface BaseTrack { // (undocumented) container: string; // (undocumented) + encrypted?: boolean; + // (undocumented) id: 'audio' | 'main'; // (undocumented) levelCodec?: string; @@ -4358,6 +4362,8 @@ export interface RemuxedTrack { // (undocumented) dropped?: number; // (undocumented) + encrypted?: boolean; + // (undocumented) endDTS: number; // (undocumented) endPTS: number; diff --git a/src/controller/audio-stream-controller.ts b/src/controller/audio-stream-controller.ts index 1e4f60d64d5..53965814b94 100644 --- a/src/controller/audio-stream-controller.ts +++ b/src/controller/audio-stream-controller.ts @@ -884,6 +884,9 @@ class AudioStreamController if (initSegment?.tracks) { const mapFragment = frag.initSegment || frag; + if (this.unhandledEncryptionError(initSegment, frag)) { + return; + } this._bufferInitSegment( level, initSegment.tracks, diff --git a/src/controller/base-stream-controller.ts b/src/controller/base-stream-controller.ts index 1a761b030ea..a1e1e84ebec 100644 --- a/src/controller/base-stream-controller.ts +++ b/src/controller/base-stream-controller.ts @@ -57,7 +57,7 @@ import type { PartsLoadedData, } from '../types/events'; import type { Level } from '../types/level'; -import type { RemuxedTrack } from '../types/remuxer'; +import type { InitSegmentData, RemuxedTrack } from '../types/remuxer'; import type { Bufferable, BufferInfo } from '../utils/buffer-helper'; import type { TimestampOffset } from '../utils/timescale-conversion'; @@ -710,6 +710,39 @@ export default class BaseStreamController this.tick(); } + protected unhandledEncryptionError( + initSegment: InitSegmentData, + frag: Fragment, + ): boolean { + const tracks = initSegment.tracks; + if ( + tracks && + !frag.encrypted && + (tracks.audio?.encrypted || tracks.video?.encrypted) && + (!this.config.emeEnabled || !this.keyLoader.emeController) + ) { + const media = this.media; + const error = new Error( + `Encrypted track with no key in ${this.fragInfo(frag)} (media ${media ? 'attached mediaKeys: ' + media.mediaKeys : 'detached'})`, + ); + this.warn(error.message); + // Ignore if media is detached or mediaKeys are set + if (!media || media.mediaKeys) { + return false; + } + this.hls.trigger(Events.ERROR, { + type: ErrorTypes.KEY_SYSTEM_ERROR, + details: ErrorDetails.KEY_SYSTEM_NO_KEYS, + fatal: false, + error, + frag, + }); + this.resetTransmuxer(); + return true; + } + return false; + } + protected fragContextChanged(frag: Fragment | null) { const { fragCurrent } = this; return ( diff --git a/src/controller/eme-controller.ts b/src/controller/eme-controller.ts index c641f3e367b..331e588942c 100644 --- a/src/controller/eme-controller.ts +++ b/src/controller/eme-controller.ts @@ -577,80 +577,82 @@ class EMEController extends Logger implements ComponentAPI { this.keyFormatPromise = this.getKeyFormatPromise(keyFormats); } - this.keyFormatPromise.then((keySystemFormat) => { - const keySystem = keySystemFormatToKeySystemDomain(keySystemFormat); - if (initDataType !== 'sinf' || keySystem !== KeySystems.FAIRPLAY) { - this.log( - `Ignoring "${event.type}" event with init data type: "${initDataType}" for selected key-system ${keySystem}`, - ); - return; - } - - // Match sinf keyId to playlist skd://keyId= - let keyId: Uint8Array | undefined; - try { - const json = bin2str(new Uint8Array(initData)); - const sinf = base64Decode(JSON.parse(json).sinf); - const tenc = parseSinf(sinf); - if (!tenc) { - throw new Error( - `'schm' box missing or not cbcs/cenc with schi > tenc`, + this.keyFormatPromise + .then((keySystemFormat) => { + const keySystem = keySystemFormatToKeySystemDomain(keySystemFormat); + if (initDataType !== 'sinf' || keySystem !== KeySystems.FAIRPLAY) { + this.log( + `Ignoring "${event.type}" event with init data type: "${initDataType}" for selected key-system ${keySystem}`, ); + return; } - keyId = new Uint8Array(tenc.subarray(8, 24)); - } catch (error) { - this.warn(`${logMessage} Failed to parse sinf: ${error}`); - return; - } - - const keyIdHex = Hex.hexDump(keyId); - const { keyIdToKeySessionPromise, mediaKeySessions } = this; - let keySessionContextPromise = keyIdToKeySessionPromise[keyIdHex]; - for (let i = 0; i < mediaKeySessions.length; i++) { - // Match playlist key - const keyContext = mediaKeySessions[i]; - const decryptdata = keyContext.decryptdata; - if (!decryptdata.keyId) { - continue; + // Match sinf keyId to playlist skd://keyId= + let keyId: Uint8Array | undefined; + try { + const json = bin2str(new Uint8Array(initData)); + const sinf = base64Decode(JSON.parse(json).sinf); + const tenc = parseSinf(sinf); + if (!tenc) { + throw new Error( + `'schm' box missing or not cbcs/cenc with schi > tenc`, + ); + } + keyId = new Uint8Array(tenc.subarray(8, 24)); + } catch (error) { + this.warn(`${logMessage} Failed to parse sinf: ${error}`); + return; } - const oldKeyIdHex = Hex.hexDump(decryptdata.keyId); - if ( - keyIdHex === oldKeyIdHex || - decryptdata.uri.replace(/-/g, '').indexOf(keyIdHex) !== -1 - ) { - keySessionContextPromise = keyIdToKeySessionPromise[oldKeyIdHex]; - if (!keySessionContextPromise) { + + const keyIdHex = Hex.hexDump(keyId); + const { keyIdToKeySessionPromise, mediaKeySessions } = this; + let keySessionContextPromise = keyIdToKeySessionPromise[keyIdHex]; + + for (let i = 0; i < mediaKeySessions.length; i++) { + // Match playlist key + const keyContext = mediaKeySessions[i]; + const decryptdata = keyContext.decryptdata; + if (!decryptdata.keyId) { continue; } - if (decryptdata.pssh) { + const oldKeyIdHex = Hex.hexDump(decryptdata.keyId); + if ( + keyIdHex === oldKeyIdHex || + decryptdata.uri.replace(/-/g, '').indexOf(keyIdHex) !== -1 + ) { + keySessionContextPromise = keyIdToKeySessionPromise[oldKeyIdHex]; + if (!keySessionContextPromise) { + continue; + } + if (decryptdata.pssh) { + break; + } + delete keyIdToKeySessionPromise[oldKeyIdHex]; + decryptdata.pssh = new Uint8Array(initData); + decryptdata.keyId = keyId; + keySessionContextPromise = keyIdToKeySessionPromise[keyIdHex] = + keySessionContextPromise.then(() => { + return this.generateRequestWithPreferredKeySession( + keyContext, + initDataType, + initData, + 'encrypted-event-key-match', + ); + }); + keySessionContextPromise.catch((error) => this.handleError(error)); break; } - delete keyIdToKeySessionPromise[oldKeyIdHex]; - decryptdata.pssh = new Uint8Array(initData); - decryptdata.keyId = keyId; - keySessionContextPromise = keyIdToKeySessionPromise[keyIdHex] = - keySessionContextPromise.then(() => { - return this.generateRequestWithPreferredKeySession( - keyContext, - initDataType, - initData, - 'encrypted-event-key-match', - ); - }); - keySessionContextPromise.catch((error) => this.handleError(error)); - break; } - } - if (!keySessionContextPromise) { - this.handleError( - new Error( - `Key ID ${keyIdHex} not encountered in playlist. Key-system sessions ${mediaKeySessions.length}.`, - ), - ); - } - }); + if (!keySessionContextPromise) { + this.handleError( + new Error( + `Key ID ${keyIdHex} not encountered in playlist. Key-system sessions ${mediaKeySessions.length}.`, + ), + ); + } + }) + .catch((error) => this.handleError(error)); }; private onWaitingForKey = (event: Event) => { diff --git a/src/controller/error-controller.ts b/src/controller/error-controller.ts index 39af1810c0b..e2bbc73d392 100644 --- a/src/controller/error-controller.ts +++ b/src/controller/error-controller.ts @@ -413,10 +413,10 @@ export default class ErrorController )) || (findAudioCodecAlternate && level.audioCodec === levelCandidate.audioCodec) || - (!findAudioCodecAlternate && - level.audioCodec !== levelCandidate.audioCodec) || (findVideoCodecAlternate && - level.codecSet === levelCandidate.codecSet) + level.codecSet === levelCandidate.codecSet) || + (!findAudioCodecAlternate && + level.codecSet !== levelCandidate.codecSet) ) { // For video/audio/subs frag errors find another group ID or fallthrough to redundant fail-over continue; diff --git a/src/controller/stream-controller.ts b/src/controller/stream-controller.ts index 8865b301df0..750cb374c26 100644 --- a/src/controller/stream-controller.ts +++ b/src/controller/stream-controller.ts @@ -345,7 +345,7 @@ export default class StreamController const backtrackSn = (this.backtrackFragment ?? frag).sn as number; const fragIdx = backtrackSn - levelDetails.startSN; const backtrackFrag = levelDetails.fragments[fragIdx - 1]; - if (backtrackFrag && frag.cc === backtrackFrag.cc) { + if ((backtrackFrag as any) && frag.cc === backtrackFrag.cc) { frag = backtrackFrag; this.fragmentTracker.removeFragment(backtrackFrag); } @@ -624,13 +624,13 @@ export default class StreamController // detect if we have different kind of audio codecs used amongst playlists let aac = false; let heaac = false; - data.levels.forEach((level) => { - const codec = level.audioCodec; + for (let i = 0; i < data.levels.length; i++) { + const codec = data.levels[i].audioCodec; if (codec) { aac = aac || codec.indexOf('mp4a.40.2') !== -1; heaac = heaac || codec.indexOf('mp4a.40.5') !== -1; } - }); + } this.audioCodecSwitch = aac && heaac && !changeTypeSupported(); if (this.audioCodecSwitch) { this.log( @@ -777,7 +777,7 @@ export default class StreamController 0, ); - if (!bufferInfo?.buffered?.length) { + if (!bufferInfo.buffered?.length) { media.currentTime = liveSyncPosition; return; } @@ -816,7 +816,7 @@ export default class StreamController return; } const currentLevel = levels[frag.level]; - if (!currentLevel) { + if (!currentLevel as any) { this.warn(`Level ${frag.level} not found on progress`); return; } @@ -907,7 +907,10 @@ export default class StreamController if (fromAltAudio) { this.fragmentTracker.removeAllFragments(); hls.once(Events.BUFFER_FLUSHED, () => { - this.hls?.trigger(Events.AUDIO_TRACK_SWITCHED, data); + if (!this.hls as any) { + return; + } + this.hls.trigger(Events.AUDIO_TRACK_SWITCHED, data); }); hls.trigger(Events.BUFFER_FLUSHING, { startOffset: 0, @@ -1240,18 +1243,17 @@ export default class StreamController this.state = State.PARSING; if (initSegment) { - if (initSegment?.tracks) { + const tracks = initSegment.tracks; + if (tracks) { const mapFragment = frag.initSegment || frag; - this._bufferInitSegment( - level, - initSegment.tracks, - mapFragment, - chunkMeta, - ); + if (this.unhandledEncryptionError(initSegment, frag)) { + return; + } + this._bufferInitSegment(level, tracks, mapFragment, chunkMeta); hls.trigger(Events.FRAG_PARSING_INIT_SEGMENT, { frag: mapFragment, id, - tracks: initSegment.tracks, + tracks, }); } @@ -1260,7 +1262,7 @@ export default class StreamController const initPTS = this.initPTS[frag.cc]; if ( Number.isFinite(baseTime) && - (!initPTS || + ((!initPTS as any) || initPTS.baseTime !== baseTime || initPTS.timescale !== timescale) ) { @@ -1287,7 +1289,8 @@ export default class StreamController } const prevFrag = details.fragments[frag.sn - 1 - details.startSN]; const isFirstFragment = frag.sn === details.startSN; - const isFirstInDiscontinuity = !prevFrag || frag.cc > prevFrag.cc; + const isFirstInDiscontinuity = + (!prevFrag as any) || frag.cc > prevFrag.cc; if (remuxResult.independent !== false) { const { startPTS, endPTS, startDTS, endDTS } = video; if (part) { @@ -1390,7 +1393,7 @@ export default class StreamController this.bufferFragmentData(audio, frag, part, chunkMeta); } - if (details && id3?.samples?.length) { + if (details && id3?.samples.length) { const emittedID3: FragParsingMetadataData = { id, frag, @@ -1534,7 +1537,7 @@ export default class StreamController const trackTypes = Object.keys(tracks); if (trackTypes.length) { this.hls.trigger(Events.BUFFER_CODECS, tracks as BufferCodecsData); - if (!this.hls) { + if (!this.hls as any) { // Exit after fatal tracks error return; } diff --git a/src/remux/passthrough-remuxer.ts b/src/remux/passthrough-remuxer.ts index de243ed10fe..488b504254c 100644 --- a/src/remux/passthrough-remuxer.ts +++ b/src/remux/passthrough-remuxer.ts @@ -68,64 +68,81 @@ class PassThroughRemuxer extends Logger implements Remuxer { } public resetInitSegment( - initSegment: Uint8Array | undefined, + initSegment: Uint8Array | undefined, audioCodec: string | undefined, videoCodec: string | undefined, decryptdata: DecryptData | null, ) { this.audioCodec = audioCodec; this.videoCodec = videoCodec; - this.generateInitSegment(patchEncyptionData(initSegment, decryptdata)); + this.generateInitSegment(initSegment, decryptdata); this.emitInitSegment = true; } - private generateInitSegment(initSegment: Uint8Array | undefined): void { + private generateInitSegment( + initSegment: Uint8Array | undefined, + decryptdata?: DecryptData | null, + ) { let { audioCodec, videoCodec } = this; if (!initSegment?.byteLength) { this.initTracks = undefined; this.initData = undefined; return; } - const initData = (this.initData = parseInitSegment(initSegment)); + const { audio, video } = (this.initData = parseInitSegment(initSegment)); + + if (decryptdata) { + patchEncyptionData(initSegment, decryptdata); + } else { + const eitherTrack = audio || video; + if (eitherTrack?.encrypted) { + this.warn( + `Init segment with encrypted track with has no key ("${eitherTrack.codec}")!`, + ); + } + } // Get codec from initSegment - if (initData.audio) { + if (audio) { audioCodec = getParsedTrackCodec( - initData.audio, + audio, ElementaryStreamTypes.AUDIO, this, ); } - if (initData.video) { + if (video) { videoCodec = getParsedTrackCodec( - initData.video, + video, ElementaryStreamTypes.VIDEO, this, ); } const tracks: TrackSet = {}; - if (initData.audio && initData.video) { + if (audio && video) { tracks.audiovideo = { container: 'video/mp4', codec: audioCodec + ',' + videoCodec, - supplemental: initData.video.supplemental, + supplemental: video.supplemental, + encrypted: video.encrypted, initSegment, id: 'main', }; - } else if (initData.audio) { + } else if (audio) { tracks.audio = { container: 'audio/mp4', codec: audioCodec, + encrypted: audio.encrypted, initSegment, id: 'audio', }; - } else if (initData.video) { + } else if (video) { tracks.video = { container: 'video/mp4', codec: videoCodec, - supplemental: initData.video.supplemental, + supplemental: video.supplemental, + encrypted: video.encrypted, initSegment, id: 'main', }; @@ -161,8 +178,8 @@ class PassThroughRemuxer extends Logger implements Remuxer { // The binary segment data is added to the videoTrack in the mp4demuxer. We don't check to see if the data is only // audio or video (or both); adding it to video was an arbitrary choice. - const data = videoTrack.samples as Uint8Array; - if (!data?.length) { + const data = videoTrack.samples; + if (!data.length) { return result; } @@ -266,9 +283,7 @@ class PassThroughRemuxer extends Logger implements Remuxer { initSegment.trackId = initPTS.trackId; } - const startTime = audioTrack - ? decodeTime - initPTS.baseTime / initPTS.timescale - : (lastEndTime as number); + const startTime = decodeTime - initPTS.baseTime / initPTS.timescale; const endTime = startTime + duration; if (duration > 0) { @@ -290,6 +305,10 @@ class PassThroughRemuxer extends Logger implements Remuxer { type += 'video'; } + const encrypted = + (initData.audio ? initData.audio.encrypted : false) || + (initData.video ? initData.video.encrypted : false); + const track: RemuxedTrack = { data1: data, startPTS: startTime, @@ -301,6 +320,7 @@ class PassThroughRemuxer extends Logger implements Remuxer { hasVideo, nb: 1, dropped: 0, + encrypted, }; result.audio = hasAudio && !hasVideo ? track : undefined; @@ -385,7 +405,7 @@ function getParsedTrackCodec( type: ElementaryStreamTypes.AUDIO | ElementaryStreamTypes.VIDEO, logger: ILogger, ): string { - const parsedCodec = track?.codec; + const parsedCodec = track.codec; if (parsedCodec && parsedCodec.length > 4) { return parsedCodec; } diff --git a/src/types/buffer.ts b/src/types/buffer.ts index 396c739daf9..a5eb5848884 100644 --- a/src/types/buffer.ts +++ b/src/types/buffer.ts @@ -21,6 +21,7 @@ export interface BaseTrack { container: string; codec?: string; supplemental?: string; + encrypted?: boolean; levelCodec?: string; pendingCodec?: string; metadata?: { diff --git a/src/types/remuxer.ts b/src/types/remuxer.ts index 9981456ba53..c1e0a0259de 100644 --- a/src/types/remuxer.ts +++ b/src/types/remuxer.ts @@ -52,6 +52,7 @@ export interface RemuxedTrack { transferredData1?: ArrayBuffer; transferredData2?: ArrayBuffer; dropped?: number; + encrypted?: boolean; } export interface RemuxedMetadata { diff --git a/src/utils/mp4-tools.ts b/src/utils/mp4-tools.ts index faf306c638f..5becb00d177 100644 --- a/src/utils/mp4-tools.ts +++ b/src/utils/mp4-tools.ts @@ -224,6 +224,7 @@ export interface InitDataTrack { timescale: number; id: number; codec: string; + encrypted: boolean; supplemental: string | undefined; } @@ -262,15 +263,15 @@ export function parseInitSegment(initSegment: Uint8Array): InitData { for (let i = 0; i < traks.length; i++) { const trak = traks[i]; const tkhd = findBox(trak, ['tkhd'])[0]; - if (tkhd) { + if (tkhd as any) { let version = tkhd[0]; const trackId = readUint32(tkhd, version === 0 ? 12 : 20); const mdhd = findBox(trak, ['mdia', 'mdhd'])[0]; - if (mdhd) { + if (mdhd as any) { version = mdhd[0]; const timescale = readUint32(mdhd, version === 0 ? 12 : 20); const hdlr = findBox(trak, ['mdia', 'hdlr'])[0]; - if (hdlr) { + if (hdlr as any) { const hdlrType = bin2str(hdlr.subarray(8, 12)); const type: HdlrType | undefined = { soun: ElementaryStreamTypes.AUDIO as const, @@ -324,11 +325,11 @@ function parseStsd(stsd: Uint8Array): StsdData { const sinfs = findBox(encBoxChildren, ['sinf']); sinfs.forEach((sinf) => { const schm = findBox(sinf, ['schm'])[0]; - if (schm) { + if (schm as any) { const scheme = bin2str(schm.subarray(4, 8)); if (scheme === 'cbcs' || scheme === 'cenc') { const frma = findBox(sinf, ['frma'])[0]; - if (frma) { + if (frma as any) { // for encrypted content codec fourCC will be in frma codec = bin2str(frma); } @@ -344,7 +345,7 @@ function parseStsd(stsd: Uint8Array): StsdData { case 'avc4': { // extract profile + compatibility + level out of avcC box const avcCBox = findBox(sampleEntriesEnd, ['avcC'])[0]; - if (avcCBox && avcCBox.length > 3) { + if ((avcCBox as any) && avcCBox.length > 3) { codec += '.' + toHex(avcCBox[1]) + toHex(avcCBox[2]) + toHex(avcCBox[3]); supplemental = parseSupplementalDoViCodec( @@ -357,7 +358,7 @@ function parseStsd(stsd: Uint8Array): StsdData { case 'mp4a': { const codecBox = findBox(sampleEntries, [fourCC])[0]; const esdsBox = findBox(codecBox.subarray(28), ['esds'])[0]; - if (esdsBox && esdsBox.length > 7) { + if ((esdsBox as any) && esdsBox.length > 7) { let i = 4; // ES Descriptor tag if (esdsBox[i++] !== 0x03) { @@ -402,7 +403,7 @@ function parseStsd(stsd: Uint8Array): StsdData { case 'hvc1': case 'hev1': { const hvcCBox = findBox(sampleEntriesEnd, ['hvcC'])[0]; - if (hvcCBox && hvcCBox.length > 12) { + if ((hvcCBox as any) && hvcCBox.length > 12) { const profileByte = hvcCBox[1]; const profileSpace = ['', 'A', 'B', 'C'][profileByte >> 6]; const generalProfileIdc = profileByte & 0x1f; @@ -440,7 +441,7 @@ function parseStsd(stsd: Uint8Array): StsdData { } case 'vp09': { const vpcCBox = findBox(sampleEntriesEnd, ['vpcC'])[0]; - if (vpcCBox && vpcCBox.length > 6) { + if ((vpcCBox as any) && vpcCBox.length > 6) { const profile = vpcCBox[4]; const level = vpcCBox[5]; const bitDepth = (vpcCBox[6] >> 4) & 0x0f; @@ -456,7 +457,7 @@ function parseStsd(stsd: Uint8Array): StsdData { } case 'av01': { const av1CBox = findBox(sampleEntriesEnd, ['av1C'])[0]; - if (av1CBox && av1CBox.length > 2) { + if ((av1CBox as any) && av1CBox.length > 2) { const profile = av1CBox[1] >>> 5; const level = av1CBox[1] & 0x1f; const tierFlag = av1CBox[2] >>> 7 ? 'H' : 'M'; @@ -526,7 +527,7 @@ function parseSupplementalDoViCodec( const dvXCBox = dvvCResult.length ? dvvCResult[0] : findBox(sampleEntriesEnd, ['dvcC'])[0]; // used by DoVi Profiles up to 7 and 20 - if (dvXCBox) { + if (dvXCBox as any) { const doViProfile = (dvXCBox[2] >> 1) & 0x7f; const doViLevel = ((dvXCBox[2] << 5) & 0x20) | ((dvXCBox[3] >> 3) & 0x1f); return ( @@ -564,11 +565,11 @@ function addLeadingZero(num: number): string { } export function patchEncyptionData( - initSegment: Uint8Array | undefined, + initSegment: Uint8Array | undefined, decryptdata: DecryptData | null, -): Uint8Array | undefined { +) { if (!initSegment || !decryptdata) { - return initSegment; + return; } const keyId = decryptdata.keyId; if (keyId && decryptdata.isCommonEncryption) { @@ -606,13 +607,11 @@ export function patchEncyptionData( }); }); } - - return initSegment; } export function parseSinf(sinf: Uint8Array): Uint8Array | null { const schm = findBox(sinf, ['schm'])[0]; - if (schm) { + if (schm as any) { const scheme = bin2str(schm.subarray(4, 8)); if (scheme === 'cbcs' || scheme === 'cenc') { return findBox(sinf, ['schi', 'tenc'])[0]; @@ -664,19 +663,18 @@ export function getSampleData( if (!track) { continue; } - const trackTimes: TrackTimes = - tracks[id] || - (tracks[id] = { - start: NaN, - duration: 0, - sampleCount: 0, - timescale: track.timescale, - type: track.type, - }); + (tracks[id] as any) ||= { + start: NaN, + duration: 0, + sampleCount: 0, + timescale: track.timescale, + type: track.type, + }; + const trackTimes: TrackTimes = tracks[id]; // get start DTS const tfdt = findBox(traf, ['tfdt'])[0]; - if (tfdt) { + if (tfdt as any) { const version = tfdt[0]; let baseTime = readUint32(tfdt, 4); if (version === 1) { @@ -825,7 +823,9 @@ export function getSampleData( } // TODO: Check if the last moof+mdat pair is part of the valid range -export function segmentValidRange(data: Uint8Array): SegmentedRange { +export function segmentValidRange( + data: Uint8Array, +): SegmentedRange { const segmentedRange: SegmentedRange = { valid: null, remainder: null, @@ -844,8 +844,8 @@ export function segmentValidRange(data: Uint8Array): SegmentedRange { } export interface SegmentedRange { - valid: Uint8Array | null; - remainder: Uint8Array | null; + valid: Uint8Array | null; + remainder: Uint8Array | null; } export function appendUint8Array(data1: Uint8Array, data2: Uint8Array) { @@ -892,7 +892,7 @@ export function parseSamples( return result / timescale; })[0]; - if (baseTime !== undefined) { + if ((baseTime as any) !== undefined) { timeOffset = baseTime; } @@ -1331,7 +1331,7 @@ export function mp4pssh( kidCount = new Uint8Array(); } const dataSize = new Uint8Array(4); - if (data && data.byteLength > 0) { + if (data.byteLength > 0) { new DataView(dataSize.buffer).setUint32(0, data.byteLength, false); } return mp4Box( @@ -1346,7 +1346,7 @@ export function mp4pssh( kidCount, kids, dataSize, - data || new Uint8Array(), + data, ); } @@ -1411,7 +1411,7 @@ function parsePssh(view: DataView): PsshData | PsshInvalidResult { if (version === 0) { dataSizeOffset = 28; - } else if (version === 1) { + } else { const kidCounts = view.getUint32(28); if (!kidCounts || length < 32 + kidCounts * 16) { return { offset, size }; From 053c42b36cab2fe188498d4ce7f770b3b13a88d2 Mon Sep 17 00:00:00 2001 From: Rob Walch Date: Wed, 6 Aug 2025 21:25:57 -0700 Subject: [PATCH 37/52] Remove unnecessary conditionals (#7455) Use instance logger in level-helper functions --- api-extractor/report/hls.js.api.md | 2 +- src/controller/base-playlist-controller.ts | 2 +- src/controller/base-stream-controller.ts | 39 ++++++++--------- src/controller/interstitials-schedule.ts | 20 ++++----- src/loader/level-details.ts | 8 ++-- src/utils/level-helper.ts | 45 +++++++++++++------- tests/unit/controller/level-helper.ts | 49 +++++++++++----------- 7 files changed, 88 insertions(+), 77 deletions(-) diff --git a/api-extractor/report/hls.js.api.md b/api-extractor/report/hls.js.api.md index 47f77f71d4b..9532f9b276a 100644 --- a/api-extractor/report/hls.js.api.md +++ b/api-extractor/report/hls.js.api.md @@ -3244,7 +3244,7 @@ export class LevelDetails { // (undocumented) canSkipUntil: number; // (undocumented) - dateRanges: Record; + dateRanges: Record; // (undocumented) dateRangeTagCount: number; // (undocumented) diff --git a/src/controller/base-playlist-controller.ts b/src/controller/base-playlist-controller.ts index 20d41b30ce6..e12ff9d9412 100644 --- a/src/controller/base-playlist-controller.ts +++ b/src/controller/base-playlist-controller.ts @@ -174,7 +174,7 @@ export default class BasePlaylistController details.reloaded(previousDetails); // Merge live playlists to adjust fragment starts and fill in delta playlist skipped segments if (previousDetails && details.fragments.length > 0) { - mergeDetails(previousDetails, details); + mergeDetails(previousDetails, details, this); const error = details.playlistParsingError; if (error) { this.warn(error); diff --git a/src/controller/base-stream-controller.ts b/src/controller/base-stream-controller.ts index a1e1e84ebec..fd902ada72c 100644 --- a/src/controller/base-stream-controller.ts +++ b/src/controller/base-stream-controller.ts @@ -264,7 +264,7 @@ export default class BaseStreamController public getLevelDetails(): LevelDetails | undefined { if (this.levels && this.levelLastLoaded !== null) { - return this.levelLastLoaded?.details; + return this.levelLastLoaded.details; } } @@ -817,7 +817,7 @@ export default class BaseStreamController progressCallback?: FragmentLoadProgressCallback, ): Promise { this.fragCurrent = frag; - const details = level?.details; + const details = level.details; if (!this.levels || !details) { throw new Error( `frag load aborted, missing level${details ? '' : ' detail'}s`, @@ -955,7 +955,7 @@ export default class BaseStreamController this.log( `Loading ${frag.type} sn: ${frag.sn} of ${this.fragInfo(frag, false)}) cc: ${frag.cc} ${ - details ? '[' + details.startSN + '-' + details.endSN + ']' : '' + '[' + details.startSN + '-' + details.endSN + ']' }, target: ${parseFloat(targetBufferTime.toFixed(3))}`, ); // Don't update nextLoadPosition for fragments which are not buffered @@ -970,7 +970,7 @@ export default class BaseStreamController if (dataOnProgress && keyLoadingPromise) { result = keyLoadingPromise .then((keyLoadedData) => { - if (!keyLoadedData || this.fragContextChanged(keyLoadedData?.frag)) { + if (!keyLoadedData || this.fragContextChanged(keyLoadedData.frag)) { return null; } return this.fragmentLoader.load(frag, progressCallback); @@ -987,7 +987,7 @@ export default class BaseStreamController keyLoadingPromise, ]) .then(([fragLoadedData]) => { - if (!dataOnProgress && fragLoadedData && progressCallback) { + if (!dataOnProgress && progressCallback) { progressCallback(fragLoadedData); } return fragLoadedData; @@ -1043,7 +1043,7 @@ export default class BaseStreamController private handleFragLoadError(error: LoadError | Error) { if ('data' in error) { const data = error.data; - if (error.data && data.details === ErrorDetails.INTERNAL_ABORTED) { + if ((data as any) && data.details === ErrorDetails.INTERNAL_ABORTED) { this.handleFragLoadAborted(data.frag, data.part); } else { this.hls.trigger(Events.ERROR, data as ErrorData); @@ -1102,7 +1102,7 @@ export default class BaseStreamController if (!details) { return this.loadingParts; } - if (details?.partList) { + if (details.partList) { // Buffer must be ahead of first part + duration of parts after last segment // and playback must be at or past segment adjacent to part list const firstPart = details.partList[0]; @@ -1156,21 +1156,23 @@ export default class BaseStreamController chunkMeta: ChunkMetadata, noBacktracking?: boolean, ) { - if (!data || this.state !== State.PARSING) { + if (this.state !== State.PARSING) { return; } const { data1, data2 } = data; let buffer = data1; - if (data1 && data2) { + if (data2) { // Combine the moof + mdat so that we buffer with a single append buffer = appendUint8Array(data1, data2); } - if (!buffer?.length) { + if (!buffer.length) { return; } - const offsetTimestamp = this.initPTS[frag.cc]; + const offsetTimestamp = this.initPTS[frag.cc] as + | TimestampOffset + | undefined; const offset = offsetTimestamp ? -offsetTimestamp.baseTime / offsetTimestamp.timescale : undefined; @@ -1297,10 +1299,9 @@ export default class BaseStreamController position: number, playlistType: PlaylistLevelType = PlaylistLevelType.MAIN, ): Fragment | null { - const fragOrPart = this.fragmentTracker?.getAppendedFrag( - position, - playlistType, - ); + const fragOrPart = (this.fragmentTracker as any) + ? this.fragmentTracker.getAppendedFrag(position, playlistType) + : null; if (fragOrPart && 'fragment' in fragOrPart) { return fragOrPart.fragment; } @@ -1504,7 +1505,7 @@ export default class BaseStreamController mapToInitFragWhenRequired(frag: Fragment | null): typeof frag { // If an initSegment is present, it must be buffered first - if (frag?.initSegment && !frag?.initSegment.data && !this.bitrateTest) { + if (frag?.initSegment && !frag.initSegment.data && !this.bitrateTest) { return frag.initSegment; } @@ -1654,7 +1655,6 @@ export default class BaseStreamController ); if ( loadingParts && - fragmentHint && !this.bitrateTest && partList[partList.length - 1].fragment.sn === fragmentHint.sn ) { @@ -1702,11 +1702,11 @@ export default class BaseStreamController frag.sn === fragPrevious.sn && (!loadingParts || partList[0].fragment.sn > frag.sn || - (!levelDetails.live && !loadingParts)) + !levelDetails.live) ) { // Force the next fragment to load if the previous one was already selected. This can occasionally happen with // non-uniform fragment durations - const sameLevel = fragPrevious && frag.level === fragPrevious.level; + const sameLevel = frag.level === fragPrevious.level; if (sameLevel) { const nextFrag = fragments[curSNIdx + 1]; if ( @@ -2074,6 +2074,7 @@ export default class BaseStreamController info.endPTS, info.startDTS, info.endDTS, + this, ); this.hls.trigger(Events.LEVEL_PTS_UPDATED, { details, diff --git a/src/controller/interstitials-schedule.ts b/src/controller/interstitials-schedule.ts index 942bdfb2266..73082915106 100644 --- a/src/controller/interstitials-schedule.ts +++ b/src/controller/interstitials-schedule.ts @@ -62,7 +62,7 @@ type ScheduleUpdateCallback = ( export class InterstitialsSchedule extends Logger { private onScheduleUpdate: ScheduleUpdateCallback; - private eventMap: Record = {}; + private eventMap: Record = {}; public events: InterstitialEvent[] | null = null; public items: InterstitialScheduleItem[] | null = null; public durations: InterstitialScheduleDurations = { @@ -327,7 +327,7 @@ export class InterstitialsSchedule extends Logger { } private parseDateRanges( - dateRanges: Record, + dateRanges: Record, baseData: BaseData, enableAppendInPlace: boolean, ): InterstitialEvent[] { @@ -335,7 +335,7 @@ export class InterstitialsSchedule extends Logger { const ids = Object.keys(dateRanges); for (let i = 0; i < ids.length; i++) { const id = ids[i]; - const dateRange = dateRanges[id]; + const dateRange = dateRanges[id]!; if (dateRange.isInterstitial) { let interstitial = this.eventMap[id]; if (interstitial) { @@ -378,7 +378,8 @@ export class InterstitialsSchedule extends Logger { interstitialEvents.forEach((interstitial, i) => { const preroll = interstitial.cue.pre; const postroll = interstitial.cue.post; - const previousEvent = interstitialEvents[i - 1] || null; + const previousEvent = + (interstitialEvents[i - 1] as InterstitialEvent | undefined) || null; const appendInPlace = interstitial.appendInPlace; const eventStart = postroll ? primaryDuration @@ -619,12 +620,6 @@ export class InterstitialsSchedule extends Logger { ); return false; } - if (!mediaSelection) { - this.log( - `"${interstitial.identifier}" resumption ${resumeTime} can not be aligned with media (none selected)`, - ); - return false; - } const playlists = Object.keys(mediaSelection); return !playlists.some((playlistType) => { const details = mediaSelection[playlistType].details; @@ -672,7 +667,8 @@ export class InterstitialsSchedule extends Logger { let sumDuration = 0; let hasUnknownDuration = false; let hasErrors = false; - interstitial.assetList.forEach((asset, i) => { + for (let i = 0; i < interstitial.assetList.length; i++) { + const asset = interstitial.assetList[i]; const timelineStart = eventStart + sumDuration; asset.startOffset = sumDuration; asset.timelineStart = timelineStart; @@ -680,7 +676,7 @@ export class InterstitialsSchedule extends Logger { hasErrors ||= !!asset.error; const duration = asset.error ? 0 : (asset.duration as number) || 0; sumDuration += duration; - }); + } // Use the sum of known durations when it is greater than the stated duration if (hasUnknownDuration && !hasErrors) { interstitial.duration = Math.max(sumDuration, interstitial.duration); diff --git a/src/loader/level-details.ts b/src/loader/level-details.ts index 43ccef6bd40..ed2d7aba8a0 100644 --- a/src/loader/level-details.ts +++ b/src/loader/level-details.ts @@ -17,7 +17,7 @@ export class LevelDetails { public fragments: MediaFragment[]; public fragmentHint?: MediaFragment; public partList: Part[] | null = null; - public dateRanges: Record; + public dateRanges: Record; public dateRangeTagCount: number = 0; public live: boolean = true; public requestScheduled: number = -1; @@ -91,7 +91,7 @@ export class LevelDetails { get hasProgramDateTime(): boolean { if (this.fragments.length) { return Number.isFinite( - this.fragments[this.fragments.length - 1].programDateTime as number, + this.fragments[this.fragments.length - 1].programDateTime, ); } return false; @@ -126,14 +126,14 @@ export class LevelDetails { } get fragmentEnd(): number { - if (this.fragments?.length) { + if (this.fragments.length) { return this.fragments[this.fragments.length - 1].end; } return 0; } get fragmentStart(): number { - if (this.fragments?.length) { + if (this.fragments.length) { return this.fragments[0].start; } return 0; diff --git a/src/utils/level-helper.ts b/src/utils/level-helper.ts index 0968f3d4d65..7ceff215f30 100644 --- a/src/utils/level-helper.ts +++ b/src/utils/level-helper.ts @@ -2,10 +2,10 @@ * Provides methods dealing with playlist sliding and drift */ -import { logger } from './logger'; import { stringify } from './safe-json-stringify'; import { DateRange } from '../loader/date-range'; import { assignProgramDateTime, mapDateRanges } from '../loader/m3u8-parser'; +import type { ILogger } from './logger'; import type { Fragment, MediaFragment, Part } from '../loader/fragment'; import type { LevelDetails } from '../loader/level-details'; import type { Level } from '../types/level'; @@ -67,6 +67,7 @@ export function updateFragPTSDTS( endPTS: number, startDTS: number, endDTS: number, + logger: ILogger, ): number { const parsedMediaDuration = endPTS - startPTS; if (parsedMediaDuration <= 0) { @@ -81,7 +82,11 @@ export function updateFragPTSDTS( if (Number.isFinite(fragStartPts)) { // delta PTS between audio and video const deltaPTS = Math.abs(fragStartPts - startPTS); - if (!Number.isFinite(frag.deltaPTS as number)) { + if (details && deltaPTS > details.totalduration) { + logger.warn( + `media timestamps and playlist times differ by ${deltaPTS}s for level ${frag.level} ${details.url}`, + ); + } else if (!Number.isFinite(frag.deltaPTS as number)) { frag.deltaPTS = deltaPTS; } else { frag.deltaPTS = Math.max(deltaPTS, frag.deltaPTS as number); @@ -89,11 +94,14 @@ export function updateFragPTSDTS( maxStartPTS = Math.max(startPTS, fragStartPts); startPTS = Math.min(startPTS, fragStartPts); - startDTS = Math.min(startDTS, frag.startDTS as number); + startDTS = + frag.startDTS !== undefined + ? Math.min(startDTS, frag.startDTS) + : startDTS; minEndPTS = Math.min(endPTS, fragEndPts); endPTS = Math.max(endPTS, fragEndPts); - endDTS = Math.max(endDTS, frag.endDTS as number); + endDTS = frag.endDTS !== undefined ? Math.max(endDTS, frag.endDTS) : endDTS; } const drift = startPTS - frag.start; @@ -142,6 +150,7 @@ export function updateFragPTSDTS( export function mergeDetails( oldDetails: LevelDetails, newDetails: LevelDetails, + logger: ILogger, ) { if (oldDetails === newDetails) { return; @@ -223,7 +232,7 @@ export function mergeDetails( if (currentInitSegment) { fragmentsToCheck.forEach((frag) => { if ( - frag && + (frag as any) && (!frag.initSegment || frag.initSegment.relurl === currentInitSegment?.relurl) ) { @@ -233,7 +242,7 @@ export function mergeDetails( } if (newDetails.skippedSegments) { - newDetails.deltaUpdateFailed = newFragments.some((frag) => !frag); + newDetails.deltaUpdateFailed = newFragments.some((frag) => !frag as any); if (newDetails.deltaUpdateFailed) { logger.warn( '[level-helper] Previous playlist missing segments skipped in delta playlist', @@ -247,6 +256,7 @@ export function mergeDetails( newDetails.dateRanges = mergeDateRanges( oldDetails.dateRanges, newDetails, + logger, ); } const programDateTimes = oldDetails.fragments.filter( @@ -294,6 +304,7 @@ export function mergeDetails( PTSFrag.endPTS as number, PTSFrag.startDTS as number, PTSFrag.endDTS as number, + logger, ); } else { // ensure that delta is within oldFragments range @@ -328,9 +339,10 @@ export function mergeDetails( } function mergeDateRanges( - oldDateRanges: Record, + oldDateRanges: Record, newDetails: LevelDetails, -): Record { + logger: ILogger, +): Record { const { dateRanges: deltaDateRanges, recentlyRemovedDateranges } = newDetails; const dateRanges = Object.assign({}, oldDateRanges); if (recentlyRemovedDateranges) { @@ -344,7 +356,7 @@ function mergeDateRanges( Object.keys(deltaDateRanges).forEach((id) => { const mergedDateRange = dateRanges[id]; const dateRange = new DateRange( - deltaDateRanges[id].attr, + deltaDateRanges[id]!.attr, mergedDateRange, ); if (dateRange.isValid) { @@ -355,7 +367,7 @@ function mergeDateRanges( } else { logger.warn( `Ignoring invalid Playlist Delta Update DATERANGE tag: "${stringify( - deltaDateRanges[id].attr, + deltaDateRanges[id]!.attr, )}"`, ); } @@ -375,8 +387,8 @@ export function mapPartIntersection( const oldPart = oldParts[i]; const newPart = newParts[i + delta]; if ( - oldPart && - newPart && + (oldPart as any) && + (newPart as any) && oldPart.index === newPart.index && oldPart.fragment.sn === newPart.fragment.sn ) { @@ -413,11 +425,11 @@ export function mapFragmentIntersection( for (let i = start; i <= end; i++) { const oldFrag = oldFrags[delta + i]; let newFrag = newFrags[i]; - if (skippedSegments && !newFrag && oldFrag) { + if (skippedSegments && (!newFrag as any) && (oldFrag as any)) { // Fill in skipped segments in delta playlist newFrag = newDetails.fragments[i] = oldFrag; } - if (oldFrag && newFrag) { + if ((oldFrag as any) && (newFrag as any)) { intersectionFn(oldFrag, newFrag, i, newFrags); if (oldFrag.url && oldFrag.url !== newFrag.url) { newDetails.playlistParsingError = getSequenceError( @@ -537,8 +549,9 @@ export function getFragmentWithSN( if (!details) { return null; } - let fragment: MediaFragment | undefined = - details.fragments[sn - details.startSN]; + let fragment = details.fragments[sn - details.startSN] as + | MediaFragment + | undefined; if (fragment) { return fragment; } diff --git a/tests/unit/controller/level-helper.ts b/tests/unit/controller/level-helper.ts index 6fd8759a89f..166d9a65006 100644 --- a/tests/unit/controller/level-helper.ts +++ b/tests/unit/controller/level-helper.ts @@ -19,6 +19,7 @@ import { mapPartIntersection, mergeDetails, } from '../../../src/utils/level-helper'; +import { logger } from '../../../src/utils/logger'; import type { MediaFragment } from '../../../src/loader/fragment'; import type { ComponentAPI, @@ -188,7 +189,7 @@ describe('LevelHelper Tests', function () { it('transfers start times where segments overlap, and extrapolates the start of any new segment', function () { const oldPlaylist = generatePlaylist([1, 2, 3, 4]); // start times: 0, 5, 10, 15 const newPlaylist = generatePlaylist([2, 3, 4, 5]); - mergeDetails(oldPlaylist, newPlaylist); + mergeDetails(oldPlaylist, newPlaylist, logger); const actual = newPlaylist.fragments.map((f) => f.start); expect(actual).to.deep.equal([5, 10, 15, 20]); expect(newPlaylist.playlistParsingError).to.be.null; @@ -197,7 +198,7 @@ describe('LevelHelper Tests', function () { it('applies expected sliding when there is no segment overlap', function () { const oldPlaylist = generatePlaylist([1, 2, 3]); const newPlaylist = generatePlaylist([5, 6, 7]); - mergeDetails(oldPlaylist, newPlaylist); + mergeDetails(oldPlaylist, newPlaylist, logger); const actual = newPlaylist.fragments.map((f) => f.start); expect(actual).to.deep.equal([20, 25, 30]); expect(newPlaylist.playlistParsingError).to.be.null; @@ -209,7 +210,7 @@ describe('LevelHelper Tests', function () { f.addStart(10); }); const newPlaylist = generatePlaylist([1, 2, 3]); - mergeDetails(oldPlaylist, newPlaylist); + mergeDetails(oldPlaylist, newPlaylist, logger); const actual = newPlaylist.fragments.map((f) => f.start); expect(actual).to.deep.equal([10, 15, 20]); expect(newPlaylist.playlistParsingError).to.be.null; @@ -223,7 +224,7 @@ describe('LevelHelper Tests', function () { // @ts-ignore newPlaylist.fragments.unshift(null, null, null, null, null, null, null); const merged = generatePlaylist([3, 4, 5, 6, 7, 8, 9, 10, 11, 12], 10); - mergeDetails(oldPlaylist, newPlaylist); + mergeDetails(oldPlaylist, newPlaylist, logger); expect(newPlaylist.deltaUpdateFailed).to.equal(false); expect(newPlaylist.fragments.length).to.equal(merged.fragments.length); newPlaylist.fragments.forEach((frag, i) => { @@ -246,7 +247,7 @@ expect: ${JSON.stringify(merged.fragments[i])}`, newPlaylist.fragments.unshift(null, null, null, null, null); // FIXME: An expected offset of 50 would be preferred, but there is nothing to sync playlist start with const merged = generatePlaylist([10, 11, 12], 0); - mergeDetails(oldPlaylist, newPlaylist); + mergeDetails(oldPlaylist, newPlaylist, logger); expect(newPlaylist.deltaUpdateFailed).to.equal(true); expect(newPlaylist.fragments.length).to.equal(3); newPlaylist.fragments.forEach((frag, i) => { @@ -289,7 +290,7 @@ expect: ${JSON.stringify(merged.fragments[i])}`, newPlaylist.fragmentHint.sn = 5; newPlaylist.fragmentHint.initSegment = newInitSegment; - mergeDetails(oldPlaylist, newPlaylist); + mergeDetails(oldPlaylist, newPlaylist, logger); newPlaylist.fragments.forEach((frag, i) => { expect( @@ -355,7 +356,7 @@ fileSequence11.ts #EXT-X-DATERANGE:ID="four",START-DATE="2024-02-29T12:02:04.000Z"`; const details = parseLevelPlaylist(playlist); const detailsUpdated = parseLevelPlaylist(playlistUpdate); - mergeDetails(details, detailsUpdated); + mergeDetails(details, detailsUpdated, logger); expect(details.hasProgramDateTime, 'details.hasProgramDateTime').to.be .true; expect(details.dateRanges, 'one') @@ -600,7 +601,7 @@ fileSequence12.ts`; updated: false, }); // discontinuity sequence numbers (frag.cc) should be carried over - mergeDetails(details1, details2); + mergeDetails(details1, details2, logger); const mergedSequence1 = getFragmentSequenceNumbers(details2); expect( details2, @@ -627,7 +628,7 @@ fileSequence12.ts`; }); // discontinuity sequence numbers (frag.cc) should be carried over - mergeDetails(details2, details3); + mergeDetails(details2, details3, logger); const mergedSequence2 = getFragmentSequenceNumbers(details3); expect( details3, @@ -654,7 +655,7 @@ fileSequence12.ts`; }); // discontinuity sequence numbers (frag.cc) should be carried over - mergeDetails(details3, details4); + mergeDetails(details3, details4, logger); const mergedSequence3 = getFragmentSequenceNumbers(details4); expect( details4, @@ -808,7 +809,7 @@ fileSequence8.m4s updated: true, }); // discontinuity sequence numbers (frag.cc) should be carried over - mergeDetails(details1, details2); + mergeDetails(details1, details2, logger); const mergedSequence1 = getFragmentSequenceNumbers(details2); expect( details2, @@ -836,7 +837,7 @@ fileSequence8.m4s updated: true, }); // discontinuity sequence numbers (frag.cc) should be carried over - mergeDetails(details1, details3); + mergeDetails(details1, details3, logger); const mergedSequence2 = getFragmentSequenceNumbers(details3); expect( details3, @@ -994,7 +995,7 @@ fileSequence8.m4s updated: true, }); // discontinuity sequence numbers (frag.cc) should be carried over - mergeDetails(details1, details2); + mergeDetails(details1, details2, logger); const mergedSequence1 = getFragmentSequenceNumbers(details2); expect( details2, @@ -1092,7 +1093,7 @@ fileSequence17.ts fileSequence18.ts`; const details = parseLevelPlaylist(playlist); const detailsUpdated = parseLevelPlaylist(playlistUpdate); - mergeDetails(details, detailsUpdated); + mergeDetails(details, detailsUpdated, logger); expect(details.hasProgramDateTime, 'details.hasProgramDateTime').to.be .true; expect( @@ -1139,7 +1140,7 @@ fileSequence6.ts`; const details = parseLevelPlaylist(playlist); addSliding(details, 10); expect(details.fragmentStart).to.equal(10); - mergeDetails(details, details); + mergeDetails(details, details, logger); expect(details.fragmentStart).to.equal(10); }); @@ -1197,7 +1198,7 @@ video_32.m4s`; endCC: 3, }); - mergeDetails(oldPlaylist, newPlaylist); + mergeDetails(oldPlaylist, newPlaylist, logger); expect(newPlaylist.playlistParsingError).to.be.null; expect(newPlaylist).to.include({ @@ -1275,7 +1276,7 @@ getMP4MediaFragment.mp4?FragmentNumber=91343852333398821516816632930230891347979 endCC: 5, }); - mergeDetails(oldPlaylist, newPlaylist); + mergeDetails(oldPlaylist, newPlaylist, logger); expect(newPlaylist.playlistParsingError).to.be.null; expect(newPlaylist).to.include({ @@ -1545,8 +1546,8 @@ audio_5441.m4s`; expect(audioDetails1.totalduration).to.equal(16.019); // Seconds main and audio playlist responses - mergeDetails(mainDetails1, mainDetails2); - mergeDetails(audioDetails1, audioDetails2); + mergeDetails(mainDetails1, mainDetails2, logger); + mergeDetails(audioDetails1, audioDetails2, logger); expect(audioDetails2.alignedSliding).to.be.false; expect(mainDetails2.fragmentStart).to.equal(20.0825); expect(audioDetails2.fragmentStart).to.equal(16.0325); @@ -1578,8 +1579,8 @@ audio_5441.m4s`; expect(audioDetails1.totalduration).to.equal(16.019); // Seconds main and audio playlist responses - mergeDetails(mainDetails1, mainDetails2); - mergeDetails(audioDetails1, audioDetails2); + mergeDetails(mainDetails1, mainDetails2, logger); + mergeDetails(audioDetails1, audioDetails2, logger); expect(audioDetails2.alignedSliding).to.be.false; expect(mainDetails2.fragmentStart).to.equal(20.0825); expect(audioDetails2.fragmentStart).to.equal(16.0325); @@ -1629,7 +1630,7 @@ video_5431.m4s video_5432.m4s`; const details1 = parseLevelPlaylist(playlist1); const details2 = parseLevelPlaylist(playlist2); - mergeDetails(details1, details2); + mergeDetails(details1, details2, logger); expectPlaylistParsingError( details2, 'discontinuity sequence mismatch (31!=32)', @@ -1668,7 +1669,7 @@ video_5431.m4s video_5432.m4s`; const details1 = parseLevelPlaylist(playlist1); const details2 = parseLevelPlaylist(playlist2); - mergeDetails(details1, details2); + mergeDetails(details1, details2, logger); expectPlaylistParsingError( details2, 'media sequence mismatch 5429: http://example.com/video_5430.m4s', @@ -1757,7 +1758,7 @@ video_5432.m4s`; }); // Merged delta playlist with EXT-X-PROGRAM-DATE-TIME removed and DateRange tagAnchors updated - mergeDetails(details1, details2); + mergeDetails(details1, details2, logger); expect(details2).to.include({ startSN: 5, From d2c81f03d8012742994cd425c942eab9b4290490 Mon Sep 17 00:00:00 2001 From: Rob Walch Date: Thu, 7 Aug 2025 00:53:30 -0700 Subject: [PATCH 38/52] Force schedule update on asset or interstitial error (#7451) * Force schedule update on asset or interstitial error (in case playout duration is not changed by error but schedule update is needed) * Handle seeking back in interstitial with faulty assets --- src/controller/interstitial-player.ts | 11 ++++++++++- src/controller/interstitials-controller.ts | 19 +++++++++++++------ src/controller/interstitials-schedule.ts | 2 ++ 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/controller/interstitial-player.ts b/src/controller/interstitial-player.ts index 82fb92d1b27..7a89633bb38 100644 --- a/src/controller/interstitial-player.ts +++ b/src/controller/interstitial-player.ts @@ -87,6 +87,8 @@ export class HlsAssetPlayer { // issue should surface as an INTERSTITIAL_ASSET_ERROR loading the asset. } hls.loadSource(uri); + } else if (hls.levels.length && !(hls as any).started) { + hls.startLoad(-1, true); } } @@ -100,7 +102,7 @@ export class HlsAssetPlayer { if (!media) { return false; } - const duration = this._bufferedEosTime || this.duration; + const duration = Math.min(this._bufferedEosTime || Infinity, this.duration); const start = this.timelineOffset; const bufferInfo = BufferHelper.bufferInfo(media, start, 0); const bufferedEnd = this.getAssetTime(bufferInfo.end); @@ -160,6 +162,13 @@ export class HlsAssetPlayer { if (!duration) { return 0; } + const playoutLimit = this.interstitial.playoutLimit; + if (playoutLimit) { + const assetPlayout = playoutLimit - this.startOffset; + if (assetPlayout > 0 && assetPlayout < duration) { + return assetPlayout; + } + } return duration; } diff --git a/src/controller/interstitials-controller.ts b/src/controller/interstitials-controller.ts index 78a5dbc09ca..e81be3072fd 100644 --- a/src/controller/interstitials-controller.ts +++ b/src/controller/interstitials-controller.ts @@ -905,7 +905,7 @@ export default class InterstitialsController currentTime - diff, ); if (resetCount) { - this.updateSchedule(); + this.updateSchedule(true); } } this.checkBuffer(); @@ -961,6 +961,10 @@ export default class InterstitialsController (backwardSeek && currentTime < start) || currentTime >= start + duration ) { + if (playingItem.event?.appendInPlace) { + this.clearInterstitial(playingItem.event, playingItem); + this.flushFrontBuffer(currentTime); + } this.setScheduleToAssetAtTime(currentTime, playingAsset); } }; @@ -1119,8 +1123,10 @@ export default class InterstitialsController if (!scheduleItems || this.playbackDisabled) { return; } - this.log(`setSchedulePosition ${index}, ${assetListIndex}`); const scheduledItem = index >= 0 ? scheduleItems[index] : null; + this.log( + `setSchedulePosition ${index}, ${assetListIndex} (${scheduledItem ? segmentToString(scheduledItem) : scheduledItem})`, + ); // Cleanup current item / asset const currentItem = this.waitingItem || this.playingItem; const playingLastItem = this.playingLastItem; @@ -1837,12 +1843,12 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli return item && this.schedule ? this.schedule.findItemIndex(item, time) : -1; } - private updateSchedule() { + private updateSchedule(forceUpdate: boolean = false) { const mediaSelection = this.mediaSelection; if (!mediaSelection) { return; } - this.schedule?.updateSchedule(mediaSelection, []); + this.schedule?.updateSchedule(mediaSelection, [], forceUpdate); } // Schedule buffer control @@ -2699,8 +2705,8 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli for (let i = assetListIndex; i < interstitial.assetList.length; i++) { this.resetAssetPlayer(interstitial.assetList[i].identifier); } - this.updateSchedule(); } + this.updateSchedule(true); if (interstitial.error) { this.primaryFallback(interstitial); } else if (playingAsset && playingAsset.identifier === assetId) { @@ -2719,7 +2725,6 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli const flushStart = interstitial.timelineStart; const playingItem = this.effectivePlayingItem; // Update schedule now that interstitial/assets are flagged with `error` for fallback - this.updateSchedule(); if (playingItem) { this.log( `Fallback to primary from event "${interstitial.identifier}" start: ${ @@ -2802,6 +2807,7 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli interstitial.error = new Error( `Interstitial no longer within playback range ${this.timelinePos} ${interstitial}`, ); + this.updateSchedule(true); this.primaryFallback(interstitial); return; } @@ -2837,6 +2843,7 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli case ErrorDetails.ASSET_LIST_LOAD_TIMEOUT: { const interstitial = data.interstitial; if (interstitial) { + this.updateSchedule(true); this.primaryFallback(interstitial); } break; diff --git a/src/controller/interstitials-schedule.ts b/src/controller/interstitials-schedule.ts index 73082915106..5e6e41e9680 100644 --- a/src/controller/interstitials-schedule.ts +++ b/src/controller/interstitials-schedule.ts @@ -303,12 +303,14 @@ export class InterstitialsSchedule extends Logger { public updateSchedule( mediaSelection: MediaSelection, removedInterstitials: InterstitialEvent[] = [], + forceUpdate: boolean = false, ) { const events = this.events || []; if (events.length || removedInterstitials.length || this.length < 2) { const currentItems = this.items; const updatedItems = this.parseSchedule(events, mediaSelection); const updated = + forceUpdate || removedInterstitials.length || currentItems?.length !== updatedItems.length || updatedItems.some((item, i) => { From 59ede999a2c94c46136abbd6197b3fbc9fd0a5d0 Mon Sep 17 00:00:00 2001 From: Rob Walch Date: Wed, 13 Aug 2025 23:21:48 -0700 Subject: [PATCH 39/52] Disable part loading for subtitle playlists (not supported in subtitle-stream-controller, timeline-contoller, fragment-tracker...) Stop gap measure for #7460 --- src/controller/base-stream-controller.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/controller/base-stream-controller.ts b/src/controller/base-stream-controller.ts index fd902ada72c..ee6bb650ded 100644 --- a/src/controller/base-stream-controller.ts +++ b/src/controller/base-stream-controller.ts @@ -876,7 +876,7 @@ export default class BaseStreamController if (this.loadingParts && isMediaFragment(frag)) { const partList = details.partList; if (partList && progressCallback) { - if (targetBufferTime > frag.end && details.fragmentHint) { + if (targetBufferTime > details.fragmentEnd && details.fragmentHint) { frag = details.fragmentHint; } const partIndex = this.getNextPart(partList, frag, targetBufferTime); @@ -1106,6 +1106,10 @@ export default class BaseStreamController // Buffer must be ahead of first part + duration of parts after last segment // and playback must be at or past segment adjacent to part list const firstPart = details.partList[0]; + // Loading of VTT subtitle parts is not implemented in subtitle-stream-controller (#7460) + if (firstPart.fragment.type === PlaylistLevelType.SUBTITLE) { + return false; + } const safePartStart = firstPart.end + (details.fragmentHint?.duration || 0); if (bufferEnd >= safePartStart) { From e3c4f1ddc020bf8934224f6464ca25c0003d3aad Mon Sep 17 00:00:00 2001 From: Rob Walch Date: Wed, 13 Aug 2025 09:59:48 -0700 Subject: [PATCH 40/52] Merge new Date Ranges in Delta Update when last details have none Fixes #7461 --- src/loader/m3u8-parser.ts | 11 +- src/utils/level-helper.ts | 38 +++-- tests/unit/controller/level-helper.ts | 219 ++++++++++++++++++++++++++ 3 files changed, 246 insertions(+), 22 deletions(-) diff --git a/src/loader/m3u8-parser.ts b/src/loader/m3u8-parser.ts index 942e4b6ef28..a7765fdfc76 100644 --- a/src/loader/m3u8-parser.ts +++ b/src/loader/m3u8-parser.ts @@ -758,9 +758,16 @@ export function mapDateRanges( details: LevelDetails, ) { // Make sure DateRanges are mapped to a ProgramDateTime tag that applies a date to a segment that overlaps with its start date - const programDateTimeCount = programDateTimes.length; + let programDateTimeCount = programDateTimes.length; if (!programDateTimeCount) { - return; + if (details.hasProgramDateTime) { + const lastFragment = details.fragments[details.fragments.length - 1]; + programDateTimes.push(lastFragment); + programDateTimeCount++; + } else { + // no segments with EXT-X-PROGRAM-DATE-TIME references in playlist history + return; + } } const lastProgramDateTime = programDateTimes[programDateTimeCount - 1]; const playlistEnd = details.live ? Infinity : details.totalduration; diff --git a/src/utils/level-helper.ts b/src/utils/level-helper.ts index 7ceff215f30..0d68170d868 100644 --- a/src/utils/level-helper.ts +++ b/src/utils/level-helper.ts @@ -352,27 +352,25 @@ function mergeDateRanges( } const mergeIds = Object.keys(dateRanges); const mergeCount = mergeIds.length; - if (mergeCount) { - Object.keys(deltaDateRanges).forEach((id) => { - const mergedDateRange = dateRanges[id]; - const dateRange = new DateRange( - deltaDateRanges[id]!.attr, - mergedDateRange, - ); - if (dateRange.isValid) { - dateRanges[id] = dateRange; - if (!mergedDateRange) { - dateRange.tagOrder += mergeCount; - } - } else { - logger.warn( - `Ignoring invalid Playlist Delta Update DATERANGE tag: "${stringify( - deltaDateRanges[id]!.attr, - )}"`, - ); - } - }); + if (!mergeCount) { + return deltaDateRanges; } + Object.keys(deltaDateRanges).forEach((id) => { + const mergedDateRange = dateRanges[id]; + const dateRange = new DateRange(deltaDateRanges[id]!.attr, mergedDateRange); + if (dateRange.isValid) { + dateRanges[id] = dateRange; + if (!mergedDateRange) { + dateRange.tagOrder += mergeCount; + } + } else { + logger.warn( + `Ignoring invalid Playlist Delta Update DATERANGE tag: "${stringify( + deltaDateRanges[id]!.attr, + )}"`, + ); + } + }); return dateRanges; } diff --git a/tests/unit/controller/level-helper.ts b/tests/unit/controller/level-helper.ts index 166d9a65006..5889314b97c 100644 --- a/tests/unit/controller/level-helper.ts +++ b/tests/unit/controller/level-helper.ts @@ -1789,6 +1789,225 @@ video_5432.m4s`; sn: 6, }); }); + + it('merges new dateranges in delta updates with previous details containing no dateranges', function () { + const playlist1 = `#EXTM3U +#EXT-X-VERSION:6 +#EXT-X-MEDIA-SEQUENCE:1 +#EXT-X-TARGETDURATION:6 +#EXT-X-SERVER-CONTROL:CAN-SKIP-UNTIL=9,CAN-SKIP-DATERANGES=YES +#EXT-X-MAP:URI="hls/20821722-video=2499968.m4s" +#EXT-X-PROGRAM-DATE-TIME:2025-08-06T16:00:00Z +#EXTINF:4.0, no desc +1.m4s +#EXTINF:4.0, no desc +2.m4s +#EXT-X-PROGRAM-DATE-TIME:2025-08-06T16:08:00Z +#EXTINF:4.0, no desc +3.m4s +#EXTINF:4.0, no desc +4.m4s +#EXTINF:4.0, no desc +5.m4s +#EXT-X-PROGRAM-DATE-TIME:2025-08-06T16:00:20Z +#EXTINF:4.0, no desc +6.m4s +#EXTINF:4.0, no desc +7.m4s +#EXTINF:4.0, no desc +8.m4s +#EXTINF:4.0, no desc +9.m4s`; + // Media sequence increased by one but two segments removed. + const playlist2 = `#EXTM3U +#EXT-X-VERSION:10 +#EXT-X-MEDIA-SEQUENCE:5 +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-TARGETDURATION:6 +#EXT-X-SERVER-CONTROL:CAN-SKIP-UNTIL=9,CAN-SKIP-DATERANGES=YES +#EXT-X-SKIP:SKIPPED-SEGMENTS=4,RECENTLY-REMOVED-DATERANGES="D0" +#EXTINF:4, no desc +9.m4s +#EXTINF:4, no desc +10.m4s +#EXT-X-DATERANGE:ID="D3",START-DATE="2025-08-06T16:00:59.100Z",DURATION=12,SCTE35-CMD=0x00000000`; + + const details1 = parseLevelPlaylist(playlist1); + const details2 = parseLevelPlaylist(playlist2); + + expect(details1.playlistParsingError).to.be.null; + expect(details2.playlistParsingError).to.be.null; + + // First playilst details + expect(details1).to.include({ + startSN: 1, + endSN: 9, + totalduration: 36, + dateRangeTagCount: 0, + }); + expect(details1.dateRanges).to.be.empty; + + // Merged delta playlist + mergeDetails(details1, details2, logger); + + expect(details2).to.include({ + startSN: 5, + endSN: 10, + totalduration: 24, + dateRangeTagCount: 1, + }); + expect(details2.dateRanges).to.have.keys(['D3']); + expect(details2.dateRanges.D3).to.include({ + startTime: 59.1, + tagOrder: 0, + }); + expect(details2.dateRanges.D3?.tagAnchor, 'D3?.tagAnchor').to.include({ + sn: 6, + }); + }); + + it('adds and removed dateranges in delta updates with previous details when all previous dateranges are removed', function () { + const playlist1 = `#EXTM3U +#EXT-X-VERSION:6 +#EXT-X-MEDIA-SEQUENCE:1 +#EXT-X-TARGETDURATION:6 +#EXT-X-SERVER-CONTROL:CAN-SKIP-UNTIL=9,CAN-SKIP-DATERANGES=YES +#EXT-X-MAP:URI="hls/20821722-video=2499968.m4s" +#EXT-X-PROGRAM-DATE-TIME:2025-08-06T16:00:00Z +#EXTINF:4.0, no desc +1.m4s +#EXTINF:4.0, no desc +2.m4s +#EXTINF:4.0, no desc +3.m4s +#EXTINF:4.0, no desc +4.m4s +#EXTINF:4.0, no desc +5.m4s +#EXTINF:4.0, no desc +6.m4s +#EXTINF:4.0, no desc +7.m4s +#EXTINF:4.0, no desc +8.m4s +#EXTINF:4.0, no desc +9.m4s +#EXT-X-DATERANGE:ID="D2",START-DATE="2025-08-06T16:00:15Z",PLANNED-DURATION=12,SCTE35-OUT=0x00000000`; + // Media sequence increased by one but two segments removed. + const playlist2 = `#EXTM3U +#EXT-X-VERSION:10 +#EXT-X-MEDIA-SEQUENCE:5 +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-TARGETDURATION:6 +#EXT-X-SERVER-CONTROL:CAN-SKIP-UNTIL=9,CAN-SKIP-DATERANGES=YES +#EXT-X-SKIP:SKIPPED-SEGMENTS=4,RECENTLY-REMOVED-DATERANGES="D2" +#EXTINF:4, no desc +9.m4s +#EXTINF:4, no desc +10.m4s +#EXT-X-DATERANGE:ID="D3",START-DATE="2025-08-06T16:00:59.100Z",DURATION=12,SCTE35-CMD=0x00000000`; + + const playlist3 = `#EXTM3U +#EXT-X-VERSION:10 +#EXT-X-MEDIA-SEQUENCE:6 +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-TARGETDURATION:6 +#EXT-X-SERVER-CONTROL:CAN-SKIP-UNTIL=9,CAN-SKIP-DATERANGES=YES +#EXT-X-SKIP:SKIPPED-SEGMENTS=4,RECENTLY-REMOVED-DATERANGES="D2" +#EXTINF:4, no desc +10.m4s +#EXTINF:4, no desc +11.m4s +#EXT-X-DATERANGE:ID="D3",START-DATE="2025-08-06T16:00:59.100Z",DURATION=12,SCTE35-CMD=0x00000000`; + + const playlist4 = `#EXTM3U +#EXT-X-VERSION:10 +#EXT-X-MEDIA-SEQUENCE:7 +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-TARGETDURATION:6 +#EXT-X-SERVER-CONTROL:CAN-SKIP-UNTIL=9,CAN-SKIP-DATERANGES=YES +#EXT-X-SKIP:SKIPPED-SEGMENTS=4,RECENTLY-REMOVED-DATERANGES="D2 D3" +#EXTINF:4, no desc +11.m4s +#EXTINF:4, no desc +12.m4s +#EXT-X-DATERANGE:ID="D4",START-DATE="2025-08-06T16:01:09.100Z",DURATION=12,SCTE35-CMD=0x00000000`; + + const details1 = parseLevelPlaylist(playlist1); + const details2 = parseLevelPlaylist(playlist2); + const details3 = parseLevelPlaylist(playlist3); + const details4 = parseLevelPlaylist(playlist4); + + expect(details1.playlistParsingError).to.be.null; + expect(details2.playlistParsingError).to.be.null; + expect(details3.playlistParsingError).to.be.null; + expect(details4.playlistParsingError).to.be.null; + + // First playilst details + expect(details1).to.include({ + startSN: 1, + endSN: 9, + totalduration: 36, + dateRangeTagCount: 1, + }); + expect(details1.dateRanges).to.have.keys(['D2']); + + // Merge delta playlist + mergeDetails(details1, details2, logger); + + expect(details2).to.include({ + startSN: 5, + endSN: 10, + totalduration: 24, + dateRangeTagCount: 1, + }); + expect(details2.dateRanges).to.have.keys(['D3']); + expect(details2.dateRanges.D3).to.include({ + startTime: 59.1, + tagOrder: 0, + }); + expect(details2.dateRanges.D3?.tagAnchor, 'D3?.tagAnchor').to.include({ + sn: 1, + }); + + // Merge next delta playlist + mergeDetails(details2, details3, logger); + + expect(details3).to.include({ + startSN: 6, + endSN: 11, + totalduration: 24, + dateRangeTagCount: 1, + }); + expect(details3.dateRanges).to.have.keys(['D3']); + expect(details3.dateRanges.D3).to.include({ + startTime: 59.1, + tagOrder: 0, + }); + // `tagAnchor` moved to last segment when no segments with `rawProgramDateTime` (#EXT-X-PROGRAM-DATE-TIME) remain + expect(details3.dateRanges.D3?.tagAnchor, 'D3?.tagAnchor').to.include({ + sn: 11, + }); + + // Merge next delta playlist + mergeDetails(details3, details4, logger); + + expect(details4).to.include({ + startSN: 7, + endSN: 12, + totalduration: 24, + dateRangeTagCount: 1, + }); + expect(details4.dateRanges).to.have.keys(['D4']); + // `tagAnchor` not inherited in new daterange when no segments with `rawProgramDateTime` (#EXT-X-PROGRAM-DATE-TIME) remain + expect(details4.dateRanges.D4?.tagAnchor, 'D4?.tagAnchor').to.include({ + sn: 12, + }); + expect(details4.dateRanges.D4).to.include({ + startTime: 69.1, + tagOrder: 0, + }); + }); }); function parseLevelPlaylist( From b829dd3b004841b827ecdb5973c73770d5bba854 Mon Sep 17 00:00:00 2001 From: Rob Walch Date: Thu, 14 Aug 2025 08:59:32 -0700 Subject: [PATCH 41/52] Do not treat HTTP status 0 frag load errors as gaps Fixes #7410 --- src/controller/base-stream-controller.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/controller/base-stream-controller.ts b/src/controller/base-stream-controller.ts index ee6bb650ded..04e1c2973cf 100644 --- a/src/controller/base-stream-controller.ts +++ b/src/controller/base-stream-controller.ts @@ -1886,7 +1886,14 @@ export default class BaseStreamController couldRetry && !errorAction.resolved && flags === ErrorActionFlags.MoveAllAlternatesMatchingHost; - if (!retry && noAlternate && isMediaFragment(frag) && !frag.endList) { + const httpStatus = data.response?.code || 0; + if ( + !retry && + noAlternate && + isMediaFragment(frag) && + !frag.endList && + httpStatus !== 0 + ) { this.resetFragmentErrors(filterType); this.treatAsGap(frag); errorAction.resolved = true; From 5ea8f781bef578a697a3548a1ab953f38b06cc9d Mon Sep 17 00:00:00 2001 From: Matt Joiner Date: Fri, 15 Aug 2025 03:00:37 +1000 Subject: [PATCH 42/52] Minor improvements to docs (#7459) --- docs/API.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/API.md b/docs/API.md index 9bb2807e443..0f6e47a0963 100644 --- a/docs/API.md +++ b/docs/API.md @@ -756,10 +756,10 @@ Decreasing this value will mean that each stall will have less affect on `hls.ta (default: `Infinity`) -maximum delay allowed from edge of live, expressed in multiple of `EXT-X-TARGETDURATION`. -if set to 10, the player will seek back to `liveSyncDurationCount` whenever the next fragment to be loaded is older than N-10, N being the last fragment of the live playlist. -If set, this value must be stricly superior to `liveSyncDurationCount` -a value too close from `liveSyncDurationCount` is likely to cause playback stalls. +Maximum delay allowed from edge of live, expressed in multiple of `EXT-X-TARGETDURATION`. +If set to 10, the player will seek back to `liveSyncDurationCount` whenever the next fragment to be loaded is older than N-10, N being the last fragment of the live playlist. +If set, this value must be strictly superior to `liveSyncDurationCount`. +A value too close from `liveSyncDurationCount` is likely to cause playback stalls. ### `liveSyncDuration` @@ -776,7 +776,7 @@ A value too low (inferior to ~3 segment durations) is likely to cause playback s Alternative parameter to `liveMaxLatencyDurationCount`, expressed in seconds vs number of segments. If defined in the configuration object, `liveMaxLatencyDuration` will take precedence over the default `liveMaxLatencyDurationCount`. -If set, this value must be stricly superior to `liveSyncDuration` which must be defined as well. +If set, this value must be strictly superior to `liveSyncDuration` which must be defined as well. You can't define this parameter and either `liveSyncDurationCount` or `liveMaxLatencyDurationCount` in your configuration object at the same time. A value too close from `liveSyncDuration` is likely to cause playback stalls. From b21b6de9f67b02ded0e6dcb6330785ad168bc8e0 Mon Sep 17 00:00:00 2001 From: Rob Walch Date: Thu, 14 Aug 2025 11:39:01 -0700 Subject: [PATCH 43/52] Allow base and query URI differences in segment mismatch error check (#7465) --- src/utils/level-helper.ts | 18 ++++++++++++- tests/unit/controller/level-helper.ts | 37 +++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/utils/level-helper.ts b/src/utils/level-helper.ts index 0d68170d868..467dac78a7a 100644 --- a/src/utils/level-helper.ts +++ b/src/utils/level-helper.ts @@ -429,7 +429,9 @@ export function mapFragmentIntersection( } if ((oldFrag as any) && (newFrag as any)) { intersectionFn(oldFrag, newFrag, i, newFrags); - if (oldFrag.url && oldFrag.url !== newFrag.url) { + const uriBefore = oldFrag.relurl; + const uriAfter = newFrag.relurl; + if (uriBefore && notEqualAfterStrippingQueries(uriBefore, uriAfter)) { newDetails.playlistParsingError = getSequenceError( `media sequence mismatch ${newFrag.sn}:`, oldDetails, @@ -600,3 +602,17 @@ export function reassignFragmentLevelIndexes(levels: Level[]) { }); }); } + +function notEqualAfterStrippingQueries( + uriBefore: string, + uriAfter: string | undefined, +): boolean { + if (uriBefore !== uriAfter && uriAfter) { + return stripQuery(uriBefore) !== stripQuery(uriAfter); + } + return false; +} + +function stripQuery(uri: string): string { + return uri.replace(/\?[^?]*$/, ''); +} diff --git a/tests/unit/controller/level-helper.ts b/tests/unit/controller/level-helper.ts index 5889314b97c..d8f932f8d89 100644 --- a/tests/unit/controller/level-helper.ts +++ b/tests/unit/controller/level-helper.ts @@ -1677,6 +1677,43 @@ video_5432.m4s`; }); }); + it('does not error between updates when only the query part of the URI changes', function () { + const playlist1 = `#EXTM3U +#EXT-X-VERSION:6 +#EXT-X-TARGETDURATION:3 +#EXT-X-MEDIA-SEQUENCE:5428 +#EXT-X-DISCONTINUITY-SEQUENCE:31 +#EXT-X-MAP:URI="video_init.mp4" +#EXTINF:2.000, +video_5428.m4s?t=1 +#EXTINF:2.000, +video_5429.m4s?t=1 +#EXTINF:2.000, +video_5430.m4s?t=1 +#EXTINF:2.000, +video_5431.m4s?t=1`; + // Media sequence increased by one but two segments removed. + const playlist2 = `#EXTM3U +#EXT-X-VERSION:6 +#EXT-X-TARGETDURATION:3 +#EXT-X-MEDIA-SEQUENCE:5429 +#EXT-X-DISCONTINUITY-SEQUENCE:31 +#EXT-X-MAP:URI="video_init.mp4" +#EXTINF:2.000, +video_5429.m4s?t=2 +#EXTINF:2.000, +video_5430.m4s?t=2 +#EXTINF:2.000, +video_5431.m4s?t=2 +#EXTINF:2.000, +video_5432.m4s?t=2`; + const details1 = parseLevelPlaylist(playlist1); + const details2 = parseLevelPlaylist(playlist2); + details2.fragments[0].base.url += '?base=changed'; + mergeDetails(details1, details2, logger); + expect(details2.playlistParsingError).to.be.null; + }); + it('maps dateranges based on latest EXT-X-PROGRAM-DATE-TIME', function () { const playlist1 = `#EXTM3U #EXT-X-VERSION:6 From c64313bd2da3a1b3dd6aecea1e231ed00f4900b6 Mon Sep 17 00:00:00 2001 From: Tristan Matthews Date: Tue, 19 Aug 2025 10:00:08 -0400 Subject: [PATCH 44/52] Fix syntax in `recoverMediaError` example in API.md This also addresses some formatting issues found by prettier. --- docs/API.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/docs/API.md b/docs/API.md index 0f6e47a0963..f1f8067f986 100644 --- a/docs/API.md +++ b/docs/API.md @@ -362,7 +362,7 @@ It should not be used in response to non-fatal hls.js error events. ```js let attemptedErrorRecovery = null; -video.addEventListener('error', (event) { +video.addEventListener('error', (event) => { const mediaError = event.currentTarget.error; if (mediaError.code === mediaError.MEDIA_ERR_DECODE) { const now = Date.now(); @@ -380,11 +380,19 @@ hls.on(Hls.Events.ERROR, function (name, data) { case Hls.ErrorTypes.MEDIA_ERROR: { const now = Date.now(); if (!attemptedErrorRecovery || now - attemptedErrorRecovery > 5000) { - console.log('Fatal media error encountered (' + video.error + + '), attempting to recover'); + console.log( + 'Fatal media error encountered (' + + video.error + + +'), attempting to recover', + ); attemptedErrorRecovery = now; hls.recoverMediaError(); } else { - console.log('Skipping media error recovery (only ' + (now - attemptedErrorRecovery) + 'ms since last error)'); + console.log( + 'Skipping media error recovery (only ' + + (now - attemptedErrorRecovery) + + 'ms since last error)', + ); } break; } From 1e3453e1b1c482392cad2094e83dedafda9bacc8 Mon Sep 17 00:00:00 2001 From: Rob Walch Date: Wed, 20 Aug 2025 16:08:06 -0700 Subject: [PATCH 45/52] Fix interstitial asset events not firing when attaching primary early for playout-limit change (#7467) * Fix interstitial asset events not firing when attaching primary early for playout-limit change * Add log message helper for timeline position updates --- docs/API.md | 4 +++- src/controller/interstitials-controller.ts | 26 +++++++++++++++++----- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/docs/API.md b/docs/API.md index f1f8067f986..359c5ddbd7d 100644 --- a/docs/API.md +++ b/docs/API.md @@ -569,7 +569,9 @@ This configuration will be applied by default to all instances. (default: `false`) -Setting `config.debug = true;` will turn on debug logs on JS console. +Setting `config.debug = true` enables JavaScript debug console logs. Debug mode also disables catching exceptions in even handler callbacks. +In debug mode, when an event listener throws, the exception is not caught. This allows uncaught exeptions to trigger the JavaScript debugger. +In production mode (`config.debug = false`), exceptions that are caught in event handlers are redispatched as errors with `type: OTHER_ERROR, details: INTERNAL_EXCEPTION, error: `. A logger object could also be provided for custom logging: `config.debug = customLogger;`. diff --git a/src/controller/interstitials-controller.ts b/src/controller/interstitials-controller.ts index e81be3072fd..f790422bfd4 100644 --- a/src/controller/interstitials-controller.ts +++ b/src/controller/interstitials-controller.ts @@ -86,6 +86,10 @@ function playWithCatch(media: HTMLMediaElement | null) { }); } +function timelineMessage(label: string, time: number) { + return `[${label}] Advancing timeline position to ${time}`; +} + export default class InterstitialsController extends Logger implements NetworkComponentAPI @@ -281,6 +285,7 @@ export default class InterstitialsController } private clearScheduleState() { + this.log(`clear schedule state`); this.playingItem = this.bufferingItem = this.waitingItem = @@ -308,6 +313,7 @@ export default class InterstitialsController if (this.detachedData) { const player = this.getBufferingPlayer(); if (player) { + this.log(`Removing schedule state for detachedData and ${player}`); this.playingAsset = this.endedAsset = this.bufferingAsset = @@ -1027,6 +1033,7 @@ export default class InterstitialsController const effectivePlayingItem = this.effectivePlayingItem; if (timelinePos === -1) { const startPosition = this.hls.startPosition; + this.log(timelineMessage('checkStart', startPosition)); this.timelinePos = startPosition; if (interstitialEvents.length && interstitialEvents[0].cue.pre) { const index = schedule.findEventIndex(interstitialEvents[0].identifier); @@ -1090,6 +1097,7 @@ export default class InterstitialsController } const resumptionTime = interstitial.resumeTime; if (this.timelinePos < resumptionTime) { + this.log(timelineMessage('advanceAfterAssetEnded', resumptionTime)); this.timelinePos = resumptionTime; if (interstitial.appendInPlace) { this.advanceInPlace(resumptionTime); @@ -1125,7 +1133,7 @@ export default class InterstitialsController } const scheduledItem = index >= 0 ? scheduleItems[index] : null; this.log( - `setSchedulePosition ${index}, ${assetListIndex} (${scheduledItem ? segmentToString(scheduledItem) : scheduledItem})`, + `setSchedulePosition ${index}, ${assetListIndex} (${scheduledItem ? segmentToString(scheduledItem) : scheduledItem}) pos: ${this.timelinePos}`, ); // Cleanup current item / asset const currentItem = this.waitingItem || this.playingItem; @@ -1395,6 +1403,7 @@ export default class InterstitialsController timelinePos >= scheduledItem.end ) { timelinePos = this.getPrimaryResumption(scheduledItem, index); + this.log(timelineMessage('resumePrimary', timelinePos)); this.timelinePos = timelinePos; } this.attachPrimary(timelinePos, scheduledItem); @@ -1475,6 +1484,7 @@ export default class InterstitialsController } if (!skipSeekToStartPosition) { // Set primary position to resume time + this.log(timelineMessage('attachPrimary', timelinePos)); this.timelinePos = timelinePos; this.startLoadingPrimaryAt(timelinePos, skipSeekToStartPosition); } @@ -1762,11 +1772,11 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli if (playingItem) { this.trimInPlace(updatedPlayingItem, playingItem); } - if (bufferingItem) { + if (bufferingItem && updatedBufferingItem !== updatedPlayingItem) { this.trimInPlace(updatedBufferingItem, bufferingItem); } - // Check is buffered to new Interstitial event boundary + // Check if buffered to new Interstitial event boundary // (Live update publishes Interstitial with new segment) this.checkBuffer(); } @@ -1809,7 +1819,11 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli bufferInfo.end > flushStart || (bufferInfo.nextStart || 0) > flushStart ) { - this.attachPrimary(flushStart, null); + this.log( + `trim buffered interstitial ${segmentToString(updatedItem)} (was ${segmentToString(itemBeforeUpdate)})`, + ); + const skipSeekToStartPosition = true; + this.attachPrimary(flushStart, null, skipSeekToStartPosition); this.flushFrontBuffer(flushStart); } } @@ -2486,10 +2500,10 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli ) { const playerIndex = this.getAssetPlayerQueueIndex(assetId); if (playerIndex !== -1) { + const player = this.playerQueue[playerIndex]; this.log( - `clear asset player "${assetId}" toSegment: ${toSegment ? segmentToString(toSegment) : toSegment}`, + `clear ${player} toSegment: ${toSegment ? segmentToString(toSegment) : toSegment}`, ); - const player = this.playerQueue[playerIndex]; this.transferMediaFromPlayer(player, toSegment); this.playerQueue.splice(playerIndex, 1); player.destroy(); From d42eccee09f9383f911c6418caec8930b148a5c1 Mon Sep 17 00:00:00 2001 From: Rob Walch Date: Fri, 22 Aug 2025 12:19:21 -0700 Subject: [PATCH 46/52] Fix exception when seeking to program end --- src/controller/interstitials-controller.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/controller/interstitials-controller.ts b/src/controller/interstitials-controller.ts index f790422bfd4..abe29884b49 100644 --- a/src/controller/interstitials-controller.ts +++ b/src/controller/interstitials-controller.ts @@ -1235,7 +1235,7 @@ export default class InterstitialsController if (!schedule) { return; } - const scheduledItem = index >= 0 ? scheduleItems[index] : null; + const scheduledItem = scheduleItems[index] || null; const media = this.primaryMedia; // Cleanup out of range Interstitials const playerQueue = this.playerQueue; @@ -1354,7 +1354,7 @@ export default class InterstitialsController if (this.shouldPlay) { playWithCatch(player.media); } - } else if (scheduledItem !== null) { + } else if (scheduledItem) { this.resumePrimary(scheduledItem, index, currentItem); if (this.shouldPlay) { playWithCatch(this.hls.media); From 4ebd6d2431db42e57bbfd6ee586d5c70d8ae33c7 Mon Sep 17 00:00:00 2001 From: Rob Walch Date: Fri, 22 Aug 2025 14:55:59 -0700 Subject: [PATCH 47/52] Handle EME key status errors such as "internal-error" and "output-restricted" before appending media (#7414) * Handle EME key status errors before appending segments Fixes #7413 (playback/switching fails on KEY_SYSTEM_STATUS_INTERNAL_ERROR 'internal-error' key status) * Fix handling of one-to-many KEY URI to Key IDs #7413 * Remove levels with "internal-error" key status errors and optimize key-bytes comparison Add "FIXME" comments for future MediaKeySessionContext multi-key handling improvements * Do not throw fatal keyLoadingPromise error when context changes on KEY_LOADING reproducible with `hls.once(Hls.Events.KEY_LOADING, () => hls.removeLevel(hls.loadLevel))` --- api-extractor/report/hls.js.api.md | 20 +- src/config.ts | 4 +- src/controller/base-stream-controller.ts | 22 +- src/controller/eme-controller.ts | 288 +++++++++++------- src/controller/error-controller.ts | 143 ++++++--- src/hls.ts | 2 +- src/loader/key-loader.ts | 85 ++++-- src/loader/level-details.ts | 13 + src/loader/level-key.ts | 15 +- src/types/events.ts | 1 + src/utils/arrays.ts | 22 ++ src/utils/hex.ts | 28 +- src/utils/mediakeys-helper.ts | 11 +- src/utils/mp4-tools.ts | 6 +- .../controller/audio-stream-controller.ts | 2 +- .../unit/controller/base-stream-controller.ts | 2 +- tests/unit/controller/eme-controller.ts | 66 ++-- .../controller/subtitle-stream-controller.ts | 2 +- 18 files changed, 454 insertions(+), 278 deletions(-) create mode 100644 src/utils/arrays.ts diff --git a/api-extractor/report/hls.js.api.md b/api-extractor/report/hls.js.api.md index 9532f9b276a..5511ee3ed90 100644 --- a/api-extractor/report/hls.js.api.md +++ b/api-extractor/report/hls.js.api.md @@ -1189,8 +1189,8 @@ export type EMEControllerConfig = { licenseResponseCallback?: (this: Hls, xhr: XMLHttpRequest, url: string, keyContext: MediaKeySessionContext) => ArrayBuffer; emeEnabled: boolean; widevineLicenseUrl?: string; - drmSystems: DRMSystemsConfiguration; - drmSystemOptions: DRMSystemOptions; + drmSystems: DRMSystemsConfiguration | undefined; + drmSystemOptions: DRMSystemOptions | undefined; requestMediaKeySystemAccessFunc: MediaKeyFunc | null; requireKeySystemAccessOnStart: boolean; }; @@ -1204,9 +1204,11 @@ export const enum ErrorActionFlags { // (undocumented) MoveAllAlternatesMatchingHost = 1, // (undocumented) + MoveAllAlternatesMatchingKey = 4, + // (undocumented) None = 0, // (undocumented) - SwitchToSDR = 4 + SwitchToSDR = 8 } // Warning: (ae-missing-release-tag) "ErrorController" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -1239,6 +1241,8 @@ export interface ErrorData { // (undocumented) context?: PlaylistLoaderContext; // (undocumented) + decryptdata?: LevelKey; + // (undocumented) details: ErrorDetails; // @deprecated (undocumented) err?: { @@ -2981,8 +2985,8 @@ export interface KeyLoadedData { // Warning: (ae-missing-release-tag) "KeyLoader" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export class KeyLoader implements ComponentAPI { - constructor(config: HlsConfig); +export class KeyLoader extends Logger implements ComponentAPI { + constructor(config: HlsConfig, logger: ILogger); // (undocumented) abort(type?: PlaylistLevelType): void; // (undocumented) @@ -2999,10 +3003,6 @@ export class KeyLoader implements ComponentAPI { // (undocumented) emeController: EMEController | null; // (undocumented) - keyUriToKeyInfo: { - [keyuri: string]: KeyLoaderInfo; - }; - // (undocumented) load(frag: Fragment): Promise; // (undocumented) loadClear(loadingFrag: Fragment, encryptedFragments: Fragment[], startFragRequested: boolean): null | Promise; @@ -3278,6 +3278,8 @@ export class LevelDetails { // (undocumented) get fragmentStart(): number; // (undocumented) + hasKey(levelKey: LevelKey): boolean; + // (undocumented) get hasProgramDateTime(): boolean; // (undocumented) hasVariableRefs: boolean; diff --git a/src/config.ts b/src/config.ts index 4627baa5797..7ba0901fb2f 100644 --- a/src/config.ts +++ b/src/config.ts @@ -119,8 +119,8 @@ export type EMEControllerConfig = { ) => ArrayBuffer; emeEnabled: boolean; widevineLicenseUrl?: string; - drmSystems: DRMSystemsConfiguration; - drmSystemOptions: DRMSystemOptions; + drmSystems: DRMSystemsConfiguration | undefined; + drmSystemOptions: DRMSystemOptions | undefined; requestMediaKeySystemAccessFunc: MediaKeyFunc | null; requireKeySystemAccessOnStart: boolean; }; diff --git a/src/controller/base-stream-controller.ts b/src/controller/base-stream-controller.ts index 04e1c2973cf..5ce214dd3b2 100644 --- a/src/controller/base-stream-controller.ts +++ b/src/controller/base-stream-controller.ts @@ -841,10 +841,9 @@ export default class BaseStreamController } }); this.hls.trigger(Events.KEY_LOADING, { frag }); - if (this.fragCurrent === null) { - keyLoadingPromise = Promise.reject( - new Error(`frag load aborted, context changed in KEY_LOADING`), - ); + if ((this.fragCurrent as Fragment | null) === null) { + this.log(`context changed in KEY_LOADING`); + return Promise.resolve(null); } } else if (!frag.encrypted) { keyLoadingPromise = this.keyLoader.loadClear( @@ -1040,11 +1039,16 @@ export default class BaseStreamController ); } - private handleFragLoadError(error: LoadError | Error) { + private handleFragLoadError( + error: LoadError | Error | (Error & { data: ErrorData }), + ) { if ('data' in error) { const data = error.data; - if ((data as any) && data.details === ErrorDetails.INTERNAL_ABORTED) { + if (data.frag && data.details === ErrorDetails.INTERNAL_ABORTED) { this.handleFragLoadAborted(data.frag, data.part); + } else if (data.frag && data.type === ErrorTypes.KEY_SYSTEM_ERROR) { + data.frag.abortRequests(); + this.resetFragmentLoading(data.frag); } else { this.hls.trigger(Events.ERROR, data as ErrorData); } @@ -1826,7 +1830,7 @@ export default class BaseStreamController return pos; } - private handleFragLoadAborted(frag: Fragment, part: Part | undefined) { + private handleFragLoadAborted(frag: Fragment, part: Part | null | undefined) { if ( this.transmuxer && frag.type === this.playlistType && @@ -2033,8 +2037,8 @@ export default class BaseStreamController } protected resetWhenMissingContext(chunkMeta: ChunkMetadata | Fragment) { - this.warn( - `The loading context changed while buffering fragment ${chunkMeta.sn} of ${this.playlistLabel()} ${chunkMeta.level}. This chunk will not be buffered.`, + this.log( + `Loading context changed while buffering sn ${chunkMeta.sn} of ${this.playlistLabel()} ${chunkMeta.level === -1 ? '' : chunkMeta.level}. This chunk will not be buffered.`, ); this.removeUnbufferedFrags(); this.resetStartWhenNotLoaded(this.levelLastLoaded); diff --git a/src/controller/eme-controller.ts b/src/controller/eme-controller.ts index 331e588942c..08a06fb166b 100644 --- a/src/controller/eme-controller.ts +++ b/src/controller/eme-controller.ts @@ -11,7 +11,7 @@ import { addEventListener, removeEventListener, } from '../utils/event-listener-helper'; -import Hex from '../utils/hex'; +import { arrayToHex } from '../utils/hex'; import { Logger } from '../utils/logger'; import { getKeySystemsForConfig, @@ -44,6 +44,7 @@ import type { LoaderContext, } from '../types/loader'; import type { KeySystemFormats } from '../utils/mediakeys-helper'; + interface KeySystemAccessPromises { keySystemAccess: Promise; mediaKeys?: Promise; @@ -54,9 +55,9 @@ interface KeySystemAccessPromises { export interface MediaKeySessionContext { keySystem: KeySystems; mediaKeys: MediaKeys; - decryptdata: LevelKey; + decryptdata: LevelKey; // FIXME: LevelKey has a URI which should be bound to the session, but is dependent one KeyId specifically. Session context should be allowed to adopt multiple level keys. mediaKeysSession: MediaKeySession; - keyStatus: MediaKeyStatus; + keyStatus: MediaKeyStatus; // FIXME: MediaKeySession can manage multiple keys with each with its own status licenseXhr?: XMLHttpRequest; _onmessage?: (this: MediaKeySession, ev: MediaKeyMessageEvent) => any; _onkeystatuseschange?: (this: MediaKeySession, ev: Event) => any; @@ -81,17 +82,18 @@ class EMEController extends Logger implements ComponentAPI { private media: HTMLMediaElement | null = null; private keyFormatPromise: Promise | null = null; private keySystemAccessPromises: { - [keysystem: string]: KeySystemAccessPromises; + [keysystem: string]: KeySystemAccessPromises | undefined; } = {}; private _requestLicenseFailureCount: number = 0; private mediaKeySessions: MediaKeySessionContext[] = []; private keyIdToKeySessionPromise: { - [keyId: string]: Promise; + [keyId: string]: Promise | undefined; } = {}; private mediaKeys: MediaKeys | null = null; private setMediaKeysQueue: Promise[] = EMEController.CDMCleanupPromise ? [EMEController.CDMCleanupPromise] : []; + private bannedKeyIds: { [keyId: string]: MediaKeyStatus | undefined } = {}; constructor(hls: Hls) { super('eme', hls.logger); @@ -111,7 +113,7 @@ class EMEController extends Logger implements ComponentAPI { // @ts-ignore this.hls = this.config = this.keyIdToKeySessionPromise = null; // @ts-ignore - this.onWaitingForKey = null; + this.onMediaEncrypted = this.onWaitingForKey = null; } private registerListeners() { @@ -132,7 +134,7 @@ class EMEController extends Logger implements ComponentAPI { private getLicenseServerUrl(keySystem: KeySystems): string | undefined { const { drmSystems, widevineLicenseUrl } = this.config; - const keySystemConfiguration = drmSystems[keySystem]; + const keySystemConfiguration = drmSystems?.[keySystem]; if (keySystemConfiguration) { return keySystemConfiguration.licenseUrl; @@ -156,7 +158,7 @@ class EMEController extends Logger implements ComponentAPI { private getServerCertificateUrl(keySystem: KeySystems): string | void { const { drmSystems } = this.config; - const keySystemConfiguration = drmSystems[keySystem]; + const keySystemConfiguration = drmSystems?.[keySystem]; if (keySystemConfiguration) { return keySystemConfiguration.serverCertificateUrl; @@ -247,10 +249,9 @@ class EMEController extends Logger implements ComponentAPI { keySystem, audioCodecs, videoCodecs, - this.config.drmSystemOptions, + this.config.drmSystemOptions || {}, ); - const keySystemAccessPromises: KeySystemAccessPromises = - this.keySystemAccessPromises[keySystem]; + let keySystemAccessPromises = this.keySystemAccessPromises[keySystem]; let keySystemAccess = keySystemAccessPromises?.keySystemAccess; if (!keySystemAccess) { this.log( @@ -262,10 +263,11 @@ class EMEController extends Logger implements ComponentAPI { keySystem, mediaKeySystemConfigs, ); - const keySystemAccessPromises: KeySystemAccessPromises = - (this.keySystemAccessPromises[keySystem] = { - keySystemAccess, - }); + const keySystemAccessPromisesNew = (keySystemAccessPromises = + this.keySystemAccessPromises[keySystem] = + { + keySystemAccess, + }) as KeySystemAccessPromises; keySystemAccess.catch((error) => { this.log( `Failed to obtain access to key-system "${keySystem}": ${error}`, @@ -279,11 +281,10 @@ class EMEController extends Logger implements ComponentAPI { const certificateRequest = this.fetchServerCertificate(keySystem); this.log(`Create media-keys for "${keySystem}"`); - keySystemAccessPromises.mediaKeys = mediaKeySystemAccess - .createMediaKeys() - .then((mediaKeys) => { + const mediaKeys = (keySystemAccessPromisesNew.mediaKeys = + mediaKeySystemAccess.createMediaKeys().then((mediaKeys) => { this.log(`Media-keys created for "${keySystem}"`); - keySystemAccessPromises.hasMediaKeys = true; + keySystemAccessPromisesNew.hasMediaKeys = true; return certificateRequest.then((certificate) => { if (certificate) { return this.setMediaKeysServerCertificate( @@ -294,18 +295,18 @@ class EMEController extends Logger implements ComponentAPI { } return mediaKeys; }); - }); + })); - keySystemAccessPromises.mediaKeys.catch((error) => { + mediaKeys.catch((error) => { this.error( `Failed to create media-keys for "${keySystem}"}: ${error}`, ); }); - return keySystemAccessPromises.mediaKeys; + return mediaKeys; }); } - return keySystemAccess.then(() => keySystemAccessPromises.mediaKeys!); + return keySystemAccess.then(() => keySystemAccessPromises!.mediaKeys!); } private createMediaKeySessionContext({ @@ -318,8 +319,8 @@ class EMEController extends Logger implements ComponentAPI { mediaKeys: MediaKeys; }): MediaKeySessionContext { this.log( - `Creating key-system session "${keySystem}" keyId: ${Hex.hexDump( - decryptdata.keyId! || [], + `Creating key-system session "${keySystem}" keyId: ${arrayToHex( + decryptdata.keyId || ([] as number[]), )}`, ); @@ -366,7 +367,7 @@ class EMEController extends Logger implements ComponentAPI { if (decryptdata.keyId === null) { throw new Error('keyId is null'); } - return Hex.hexDump(decryptdata.keyId); + return arrayToHex(decryptdata.keyId); } private updateKeySession( @@ -375,10 +376,10 @@ class EMEController extends Logger implements ComponentAPI { ): Promise { const keySession = mediaKeySessionContext.mediaKeysSession; this.log( - `Updating key-session "${keySession.sessionId}" for keyID ${Hex.hexDump( - mediaKeySessionContext.decryptdata?.keyId! || [], + `Updating key-session "${keySession.sessionId}" for keyId ${arrayToHex( + mediaKeySessionContext.decryptdata.keyId || [], )} - } (data length: ${data ? data.byteLength : data})`, + } (data length: ${data.byteLength})`, ); return keySession.update(data); } @@ -387,7 +388,7 @@ class EMEController extends Logger implements ComponentAPI { return (Object.keys(this.keySystemAccessPromises) as KeySystems[]) .map((keySystem) => ({ keySystem, - hasMediaKeys: this.keySystemAccessPromises[keySystem].hasMediaKeys, + hasMediaKeys: this.keySystemAccessPromises[keySystem]!.hasMediaKeys, })) .filter(({ hasMediaKeys }) => !!hasMediaKeys) .map(({ keySystem }) => keySystemDomainToKeySystemFormat(keySystem)) @@ -451,14 +452,22 @@ class EMEController extends Logger implements ComponentAPI { const decryptdata = data.keyInfo.decryptdata; const keyId = this.getKeyIdString(decryptdata); + const badStatus = this.bannedKeyIds[keyId]; + if (badStatus) { + const error = getKeyStatusError(badStatus, decryptdata); + this.handleError(error, data.frag); + return Promise.reject(error); + } const keyDetails = `(keyId: ${keyId} format: "${decryptdata.keyFormat}" method: ${decryptdata.method} uri: ${decryptdata.uri})`; this.log(`Starting session for key ${keyDetails}`); - let keyContextPromise = this.keyIdToKeySessionPromise[keyId]; + const keyContextPromise = this.keyIdToKeySessionPromise[keyId]; if (!keyContextPromise) { - keyContextPromise = this.getKeySystemForKeyPromise(decryptdata).then( - ({ keySystem, mediaKeys }) => { + const keySessionContextPromise = this.getKeySystemForKeyPromise( + decryptdata, + ) + .then(({ keySystem, mediaKeys }) => { this.throwIfDestroyed(); this.log( `Handle encrypted media sn: ${data.frag.sn} ${data.frag.type}: ${data.frag.level} using key ${keyDetails}`, @@ -472,11 +481,8 @@ class EMEController extends Logger implements ComponentAPI { decryptdata, }); }); - }, - ); - - const keySessionContextPromise = (this.keyIdToKeySessionPromise[keyId] = - keyContextPromise.then((keySessionContext) => { + }) + .then((keySessionContext) => { const scheme = 'cenc'; const initData = decryptdata.pssh ? decryptdata.pssh.buffer : null; return this.generateRequestWithPreferredKeySession( @@ -485,26 +491,34 @@ class EMEController extends Logger implements ComponentAPI { initData, 'playlist-key', ); - })); + }); + + keySessionContextPromise.catch((error) => + this.handleError(error, data.frag), + ); + this.keyIdToKeySessionPromise[keyId] = keySessionContextPromise; - keySessionContextPromise.catch((error) => this.handleError(error)); + return keySessionContextPromise; } return keyContextPromise; } private throwIfDestroyed(message = 'Invalid state'): void | never { - if (!this.hls) { + if (!this.hls as any) { throw new Error('invalid state'); } } - private handleError(error: EMEKeyError | Error) { - if (!this.hls) { + private handleError(error: EMEKeyError | Error, frag?: Fragment) { + if (!this.hls as any) { return; } this.error(error.message); if (error instanceof EMEKeyError) { + if (frag) { + error.data.frag = frag; + } this.hls.trigger(Events.ERROR, error.data); } else { this.hls.trigger(Events.ERROR, { @@ -604,7 +618,7 @@ class EMEController extends Logger implements ComponentAPI { return; } - const keyIdHex = Hex.hexDump(keyId); + const keyIdHex = arrayToHex(keyId); const { keyIdToKeySessionPromise, mediaKeySessions } = this; let keySessionContextPromise = keyIdToKeySessionPromise[keyIdHex]; @@ -615,7 +629,7 @@ class EMEController extends Logger implements ComponentAPI { if (!decryptdata.keyId) { continue; } - const oldKeyIdHex = Hex.hexDump(decryptdata.keyId); + const oldKeyIdHex = arrayToHex(decryptdata.keyId); if ( keyIdHex === oldKeyIdHex || decryptdata.uri.replace(/-/g, '').indexOf(keyIdHex) !== -1 @@ -717,7 +731,7 @@ class EMEController extends Logger implements ComponentAPI { context.decryptdata.pssh = initData ? new Uint8Array(initData) : null; } catch (error) { this.warn(error.message); - if (this.hls?.config.debug) { + if ((this.hls as any) && this.hls.config.debug) { throw error; } } @@ -731,7 +745,7 @@ class EMEController extends Logger implements ComponentAPI { const keyId = this.getKeyIdString(context.decryptdata); this.log( `Generating key-session request for "${reason}": ${keyId} (init data type: ${initDataType} length: ${ - initData ? initData.byteLength : null + initData.byteLength })`, ); @@ -739,7 +753,7 @@ class EMEController extends Logger implements ComponentAPI { const onmessage = (context._onmessage = (event: MediaKeyMessageEvent) => { const keySession = context.mediaKeysSession; - if (!keySession) { + if (!keySession as any) { licenseStatus.emit('error', new Error('invalid state')); return; } @@ -772,16 +786,19 @@ class EMEController extends Logger implements ComponentAPI { event: Event, ) => { const keySession = context.mediaKeysSession; - if (!keySession) { + if (!keySession as any) { licenseStatus.emit('error', new Error('invalid state')); return; } + const initialStatus = context.keyStatus; this.onKeyStatusChange(context); - const keyStatus = context.keyStatus; - licenseStatus.emit('keyStatus', keyStatus); - if (keyStatus === 'expired') { - this.warn(`${context.keySystem} expired for key ${keyId}`); - this.renewKeySession(context); + const status = context.keyStatus; + if (status !== initialStatus) { + licenseStatus.emit('keyStatus', status, context); + if (status === 'expired') { + this.log(`${context.keySystem} expired for key ${keyId}`); + this.renewKeySession(context); + } } }); @@ -796,37 +813,32 @@ class EMEController extends Logger implements ComponentAPI { (resolve: (value?: void) => void, reject) => { licenseStatus.on('error', reject); - licenseStatus.on('keyStatus', (keyStatus) => { - if (keyStatus.startsWith('usable')) { - resolve(); - } else if (keyStatus === 'output-restricted') { - reject( - new EMEKeyError( - { - type: ErrorTypes.KEY_SYSTEM_ERROR, - details: ErrorDetails.KEY_SYSTEM_STATUS_OUTPUT_RESTRICTED, - fatal: false, - }, - 'HDCP level output restricted', - ), - ); - } else if (keyStatus === 'internal-error') { - reject( - new EMEKeyError( - { - type: ErrorTypes.KEY_SYSTEM_ERROR, - details: ErrorDetails.KEY_SYSTEM_STATUS_INTERNAL_ERROR, - fatal: true, - }, - `key status changed to "${keyStatus}"`, - ), - ); - } else if (keyStatus === 'expired') { - reject(new Error('key expired while generating request')); - } else { - this.warn(`unhandled key status change "${keyStatus}"`); - } - }); + licenseStatus.on( + 'keyStatus', + ( + keyStatus: MediaKeyStatus, + { decryptdata }: MediaKeySessionContext, + ) => { + if (keyStatus.startsWith('usable')) { + resolve(); + } else if ( + keyStatus === 'internal-error' || + keyStatus === 'output-restricted' + ) { + reject(getKeyStatusError(keyStatus, decryptdata)); + } else if (keyStatus === 'expired') { + reject( + new Error( + `key expired while generating request (keyId: ${keyId})`, + ), + ); + } else { + this.warn( + `unhandled key status change "${keyStatus}" (keyId: ${keyId})`, + ); + } + }, + ); }, ); @@ -834,7 +846,7 @@ class EMEController extends Logger implements ComponentAPI { .generateRequest(initDataType, initData) .then(() => { this.log( - `Request generated for key-session "${context.mediaKeysSession?.sessionId}" keyId: ${keyId}`, + `Request generated for key-session "${context.mediaKeysSession.sessionId}" keyId: ${keyId}`, ); }) .catch((error) => { @@ -843,6 +855,7 @@ class EMEController extends Logger implements ComponentAPI { type: ErrorTypes.KEY_SYSTEM_ERROR, details: ErrorDetails.KEY_SYSTEM_NO_SESSION, error, + decryptdata: context.decryptdata, fatal: false, }, `Error generating key-session request: ${error}`, @@ -861,6 +874,9 @@ class EMEController extends Logger implements ComponentAPI { } private onKeyStatusChange(mediaKeySessionContext: MediaKeySessionContext) { + const sessionLevelKeyId = arrayToHex( + new Uint8Array(mediaKeySessionContext.decryptdata.keyId || []), + ); mediaKeySessionContext.mediaKeysSession.keyStatuses.forEach( (status: MediaKeyStatus, keyId: BufferSource) => { // keyStatuses.forEach is not standard API so the callback value looks weird on xboxone @@ -870,16 +886,25 @@ class EMEController extends Logger implements ComponentAPI { keyId = status; status = temp; } + const keyIdWithStatusChange = arrayToHex( + 'buffer' in keyId + ? new Uint8Array(keyId.buffer, keyId.byteOffset, keyId.byteLength) + : new Uint8Array(keyId), + ); + + // Error immediately when encountering a key ID with this status again + if (status === 'internal-error') { + this.bannedKeyIds[keyIdWithStatusChange] = status; + } + + // Only acknowledge status changes for level-key ID + const matched = keyIdWithStatusChange === sessionLevelKeyId; this.log( - `key status change "${status}" for keyStatuses keyId: ${Hex.hexDump( - 'buffer' in keyId - ? new Uint8Array(keyId.buffer, keyId.byteOffset, keyId.byteLength) - : new Uint8Array(keyId), - )} session keyId: ${Hex.hexDump( - new Uint8Array(mediaKeySessionContext.decryptdata.keyId || []), - )} uri: ${mediaKeySessionContext.decryptdata.uri}`, + `${matched ? '' : 'un'}matched key status change "${status}" for keyStatuses keyId: ${keyIdWithStatusChange} session keyId: ${sessionLevelKeyId} uri: ${mediaKeySessionContext.decryptdata.uri}`, ); - mediaKeySessionContext.keyStatus = status; + if (matched) { + mediaKeySessionContext.keyStatus = status; + } }, ); } @@ -969,7 +994,7 @@ class EMEController extends Logger implements ComponentAPI { this.log( `setServerCertificate ${ success ? 'success' : 'not supported by CDM' - } (${cert?.byteLength}) on "${keySystem}"`, + } (${cert.byteLength}) on "${keySystem}"`, ); resolve(mediaKeys); }) @@ -1002,8 +1027,9 @@ class EMEController extends Logger implements ComponentAPI { { type: ErrorTypes.KEY_SYSTEM_ERROR, details: ErrorDetails.KEY_SYSTEM_SESSION_UPDATE_FAILED, + decryptdata: context.decryptdata, error, - fatal: true, + fatal: false, }, error.message, ); @@ -1078,7 +1104,7 @@ class EMEController extends Logger implements ComponentAPI { return Promise.resolve() .then(() => { - if (!keysListItem.decryptdata) { + if (!keysListItem.decryptdata as any) { throw new Error('Key removed'); } return licenseXhrSetup.call( @@ -1090,7 +1116,7 @@ class EMEController extends Logger implements ComponentAPI { ); }) .catch((error: Error) => { - if (!keysListItem.decryptdata) { + if (!keysListItem.decryptdata as any) { // Key session removed. Cancel license request. throw error; } @@ -1128,7 +1154,10 @@ class EMEController extends Logger implements ComponentAPI { const xhr = new XMLHttpRequest(); xhr.responseType = 'arraybuffer'; xhr.onreadystatechange = () => { - if (!this.hls || !keySessionContext.mediaKeysSession) { + if ( + (!this.hls as any) || + (!keySessionContext.mediaKeysSession as any) + ) { return reject(new Error('invalid state')); } if (xhr.readyState === 4) { @@ -1167,6 +1196,7 @@ class EMEController extends Logger implements ComponentAPI { { type: ErrorTypes.KEY_SYSTEM_ERROR, details: ErrorDetails.KEY_SYSTEM_LICENSE_REQUEST_FAILED, + decryptdata: keySessionContext.decryptdata, fatal: true, networkDetails: xhr, response: { @@ -1251,6 +1281,7 @@ class EMEController extends Logger implements ComponentAPI { private _clear() { this._requestLicenseFailureCount = 0; this.keyIdToKeySessionPromise = {}; + this.bannedKeyIds = {}; if (!this.mediaKeys && !this.mediaKeySessions.length) { return; } @@ -1269,20 +1300,24 @@ class EMEController extends Logger implements ComponentAPI { this.removeSession(mediaKeySessionContext), ) .concat( - media?.setMediaKeys(null)?.catch((error) => { - this.log(`Could not clear media keys: ${error}`); - this.hls?.trigger(Events.ERROR, { - type: ErrorTypes.OTHER_ERROR, - details: ErrorDetails.KEY_SYSTEM_DESTROY_MEDIA_KEYS_ERROR, - fatal: false, - error: new Error(`Could not clear media keys: ${error}`), - }); - }), + (media?.setMediaKeys(null) as Promise | null)?.catch( + (error) => { + this.log(`Could not clear media keys: ${error}`); + if (!this.hls as any) return; + this.hls.trigger(Events.ERROR, { + type: ErrorTypes.OTHER_ERROR, + details: ErrorDetails.KEY_SYSTEM_DESTROY_MEDIA_KEYS_ERROR, + fatal: false, + error: new Error(`Could not clear media keys: ${error}`), + }); + }, + ), ), ) .catch((error) => { this.log(`Could not close sessions and clear media keys: ${error}`); - this.hls?.trigger(Events.ERROR, { + if (!this.hls as any) return; + this.hls.trigger(Events.ERROR, { type: ErrorTypes.OTHER_ERROR, details: ErrorDetails.KEY_SYSTEM_DESTROY_CLOSE_SESSION_ERROR, fatal: false, @@ -1301,6 +1336,7 @@ class EMEController extends Logger implements ComponentAPI { private onManifestLoading() { this.keyFormatPromise = null; + this.bannedKeyIds = {}; } private onManifestLoaded( @@ -1332,10 +1368,11 @@ class EMEController extends Logger implements ComponentAPI { private removeSession( mediaKeySessionContext: MediaKeySessionContext, ): Promise | void { - const { mediaKeysSession, licenseXhr } = mediaKeySessionContext; - if (mediaKeysSession) { + const { mediaKeysSession, licenseXhr, decryptdata } = + mediaKeySessionContext; + if (mediaKeysSession as MediaKeySession | undefined) { this.log( - `Remove licenses and keys and close session ${mediaKeysSession.sessionId}`, + `Remove licenses and keys and close session "${mediaKeysSession.sessionId}" keyId: ${arrayToHex((decryptdata as LevelKey | undefined)?.keyId || [])}`, ); if (mediaKeySessionContext._onmessage) { mediaKeysSession.removeEventListener( @@ -1376,7 +1413,8 @@ class EMEController extends Logger implements ComponentAPI { return removePromise .catch((error) => { this.log(`Could not remove session: ${error}`); - this.hls?.trigger(Events.ERROR, { + if (!this.hls as any) return; + this.hls.trigger(Events.ERROR, { type: ErrorTypes.OTHER_ERROR, details: ErrorDetails.KEY_SYSTEM_DESTROY_REMOVE_SESSION_ERROR, fatal: false, @@ -1388,7 +1426,8 @@ class EMEController extends Logger implements ComponentAPI { }) .catch((error) => { this.log(`Could not close session: ${error}`); - this.hls?.trigger(Events.ERROR, { + if (!this.hls as any) return; + this.hls.trigger(Events.ERROR, { type: ErrorTypes.OTHER_ERROR, details: ErrorDetails.KEY_SYSTEM_DESTROY_CLOSE_SESSION_ERROR, fatal: false, @@ -1412,4 +1451,25 @@ class EMEKeyError extends Error { } } +function getKeyStatusError( + keyStatus: MediaKeyStatus, + decryptdata: LevelKey, +): EMEKeyError { + const outputRestricted = keyStatus === 'output-restricted'; + const details = outputRestricted + ? ErrorDetails.KEY_SYSTEM_STATUS_OUTPUT_RESTRICTED + : ErrorDetails.KEY_SYSTEM_STATUS_INTERNAL_ERROR; + return new EMEKeyError( + { + type: ErrorTypes.KEY_SYSTEM_ERROR, + details, + fatal: false, + decryptdata, + }, + outputRestricted + ? 'HDCP level output restricted' + : `key status changed to "${keyStatus}"`, + ); +} + export default EMEController; diff --git a/src/controller/error-controller.ts b/src/controller/error-controller.ts index e2bbc73d392..171a3fe1ad6 100644 --- a/src/controller/error-controller.ts +++ b/src/controller/error-controller.ts @@ -9,14 +9,15 @@ import { isTimeoutError, shouldRetry, } from '../utils/error-helper'; +import { arrayToHex } from '../utils/hex'; import { Logger } from '../utils/logger'; import type { RetryConfig } from '../config'; +import type { LevelKey } from '../hls'; import type Hls from '../hls'; import type { Fragment, MediaFragment } from '../loader/fragment'; -import type { LevelDetails } from '../loader/level-details'; import type { NetworkComponentAPI } from '../types/component-api'; import type { ErrorData } from '../types/events'; -import type { HdcpLevel } from '../types/level'; +import type { HdcpLevel, Level } from '../types/level'; export const enum NetworkErrorAction { DoNothing = 0, @@ -30,8 +31,9 @@ export const enum NetworkErrorAction { export const enum ErrorActionFlags { None = 0, MoveAllAlternatesMatchingHost = 1, - MoveAllAlternatesMatchingHDCP = 1 << 1, - SwitchToSDR = 1 << 2, // Reserved for future use + MoveAllAlternatesMatchingHDCP = 2, + MoveAllAlternatesMatchingKey = 4, + SwitchToSDR = 8, } export type IErrorAction = { @@ -43,22 +45,12 @@ export type IErrorAction = { nextAutoLevel?: number; resolved?: boolean; }; - -type PenalizedRendition = { - lastErrorPerfMs: number; - errors: ErrorData[]; - details?: LevelDetails; -}; - -type PenalizedRenditions = { [key: number]: PenalizedRendition }; - export default class ErrorController extends Logger implements NetworkComponentAPI { private readonly hls: Hls; private playlistError: number = 0; - private penalizedRenditions: PenalizedRenditions = {}; constructor(hls: Hls) { super('error-controller', hls.logger); @@ -88,7 +80,6 @@ export default class ErrorController this.unregisterListeners(); // @ts-ignore this.hls = null; - this.penalizedRenditions = {}; } startLoad(startPosition: number): void {} @@ -98,14 +89,42 @@ export default class ErrorController } private getVariantLevelIndex(frag: Fragment | undefined): number { - return frag?.type === PlaylistLevelType.MAIN - ? frag.level - : this.hls.loadLevel; + if (frag?.type === PlaylistLevelType.MAIN) { + return frag.level; + } + return this.getVariantIndex(); + } + + private getVariantIndex(): number { + const hls = this.hls; + const currentLevel = hls.currentLevel; + if (hls.loadLevelObj?.details || currentLevel === -1) { + return hls.loadLevel; + } + return currentLevel; + } + + private variantHasKey( + level: Level | undefined, + keyInError: LevelKey, + ): boolean { + if (level) { + if (level.details?.hasKey(keyInError)) { + return true; + } + const audioGroupsIds = level.audioGroups; + if (audioGroupsIds) { + const audioTracks = this.hls.allAudioTracks.filter( + (track) => audioGroupsIds.indexOf(track.groupId) >= 0, + ); + return audioTracks.some((track) => track.details?.hasKey(keyInError)); + } + } + return false; } private onManifestLoading() { this.playlistError = 0; - this.penalizedRenditions = {}; } private onLevelUpdated() { @@ -201,17 +220,20 @@ export default class ErrorController return; case ErrorDetails.KEY_SYSTEM_STATUS_OUTPUT_RESTRICTED: { - const level = hls.loadLevelObj; - const restrictedHdcpLevel = level?.attrs['HDCP-LEVEL']; - if (restrictedHdcpLevel) { - data.errorAction = { - action: NetworkErrorAction.SendAlternateToPenaltyBox, - flags: ErrorActionFlags.MoveAllAlternatesMatchingHDCP, - hdcpLevel: restrictedHdcpLevel, - }; - } else { - this.keySystemError(data); - } + data.errorAction = { + action: NetworkErrorAction.SendAlternateToPenaltyBox, + flags: ErrorActionFlags.MoveAllAlternatesMatchingHDCP, + }; + } + return; + case ErrorDetails.KEY_SYSTEM_SESSION_UPDATE_FAILED: + case ErrorDetails.KEY_SYSTEM_STATUS_INTERNAL_ERROR: + case ErrorDetails.KEY_SYSTEM_NO_SESSION: + { + data.errorAction = { + action: NetworkErrorAction.SendAlternateToPenaltyBox, + flags: ErrorActionFlags.MoveAllAlternatesMatchingKey, + }; } return; case ErrorDetails.BUFFER_ADD_CODEC_ERROR: @@ -237,17 +259,12 @@ export default class ErrorController } if (data.type === ErrorTypes.KEY_SYSTEM_ERROR) { - this.keySystemError(data); + // Do not retry level. Should be fatal if ErrorDetails.KEY_SYSTEM_ not handled with early return above. + data.levelRetry = false; + data.errorAction = createDoNothingErrorAction(); } } - private keySystemError(data: ErrorData) { - const levelIndex = this.getVariantLevelIndex(data.frag); - // Do not retry level. Escalate to fatal if switching levels fails. - data.levelRetry = false; - data.errorAction = this.getLevelSwitchAction(data, levelIndex); - } - private getPlaylistRetryOrSwitchAction( data: ErrorData, levelIndex: number | null | undefined, @@ -478,21 +495,54 @@ export default class ErrorController if (!errorAction) { return; } - const { flags, hdcpLevel, nextAutoLevel } = errorAction; + const { flags } = errorAction; + const nextAutoLevel = errorAction.nextAutoLevel; switch (flags) { case ErrorActionFlags.None: this.switchLevel(data, nextAutoLevel); break; - case ErrorActionFlags.MoveAllAlternatesMatchingHDCP: - if (hdcpLevel) { - hls.maxHdcpLevel = HdcpLevels[HdcpLevels.indexOf(hdcpLevel) - 1]; + case ErrorActionFlags.MoveAllAlternatesMatchingHDCP: { + const levelIndex = this.getVariantLevelIndex(data.frag); + const level = hls.levels[levelIndex]; + const restrictedHdcpLevel = (level as Level | undefined)?.attrs[ + 'HDCP-LEVEL' + ]; + errorAction.hdcpLevel = restrictedHdcpLevel; + if (restrictedHdcpLevel) { + hls.maxHdcpLevel = + HdcpLevels[HdcpLevels.indexOf(restrictedHdcpLevel) - 1]; errorAction.resolved = true; + this.warn( + `Restricting playback to HDCP-LEVEL of "${hls.maxHdcpLevel}" or lower`, + ); + break; + } + // Fallthrough when no HDCP-LEVEL attribute is found + } + // eslint-disable-next-line no-fallthrough + case ErrorActionFlags.MoveAllAlternatesMatchingKey: { + const levelKey = data.decryptdata; + if (levelKey) { + // Penalize all levels with key + const levels = this.hls.levels; + for (let i = levels.length; i--; ) { + if (this.variantHasKey(levels[i], levelKey)) { + this.log( + `Banned key found in level ${i} (${levels[i].bitrate}bps) or audio group "${levels[i].audioGroups?.join(',')}" (${data.frag?.type} fragment) ${arrayToHex(levelKey.keyId || [])}`, + ); + levels[i].fragmentError++; + levels[i].loadError++; + this.log(`Removing level ${i} with key error (${data.error})`); + this.hls.removeLevel(i); + } + } + if (levels.length) { + errorAction.resolved = true; + } } - this.warn( - `Restricting playback to HDCP-LEVEL of "${hls.maxHdcpLevel}" or lower`, - ); break; + } } // If not resolved by previous actions try to switch to next level if (!errorAction.resolved) { @@ -516,6 +566,9 @@ export default class ErrorController const levels = this.hls.levels; for (let i = levels.length; i--; ) { if (levels[i][`${data.sourceBufferName}Codec`] === codec) { + this.log( + `Removing level ${i} for ${data.details} ("${codec}" not supported)`, + ); this.hls.removeLevel(i); } } diff --git a/src/hls.ts b/src/hls.ts index 718005282f7..b0df771dac2 100644 --- a/src/hls.ts +++ b/src/hls.ts @@ -233,7 +233,7 @@ export default class Hls implements HlsEventEmitter { )); const id3TrackController = new ID3TrackController(this); - const keyLoader = new KeyLoader(this.config); + const keyLoader = new KeyLoader(this.config, this.logger); const streamController = (this.streamController = new StreamController( this, fragmentTracker, diff --git a/src/loader/key-loader.ts b/src/loader/key-loader.ts index 124e07bb20d..80f4e72b8c5 100644 --- a/src/loader/key-loader.ts +++ b/src/loader/key-loader.ts @@ -1,6 +1,8 @@ import { LoadError } from './fragment-loader'; import { ErrorDetails, ErrorTypes } from '../errors'; import { type Fragment, isMediaFragment } from '../loader/fragment'; +import { arrayToHex } from '../utils/hex'; +import { Logger } from '../utils/logger'; import { getKeySystemsForConfig, keySystemFormatToKeySystemDomain, @@ -20,6 +22,7 @@ import type { LoaderStats, PlaylistLevelType, } from '../types/loader'; +import type { ILogger } from '../utils/logger'; import type { KeySystemFormats } from '../utils/mediakeys-helper'; export interface KeyLoaderInfo { @@ -28,18 +31,19 @@ export interface KeyLoaderInfo { loader: Loader | null; mediaKeySessionContext: MediaKeySessionContext | null; } -export default class KeyLoader implements ComponentAPI { +export default class KeyLoader extends Logger implements ComponentAPI { private readonly config: HlsConfig; - public keyUriToKeyInfo: { [keyuri: string]: KeyLoaderInfo } = {}; + private keyIdToKeyInfo: { [keyId: string]: KeyLoaderInfo | undefined } = {}; public emeController: EMEController | null = null; - constructor(config: HlsConfig) { + constructor(config: HlsConfig, logger: ILogger) { + super('key-loader', logger); this.config = config; } abort(type?: PlaylistLevelType) { - for (const uri in this.keyUriToKeyInfo) { - const loader = this.keyUriToKeyInfo[uri].loader; + for (const id in this.keyIdToKeyInfo) { + const loader = this.keyIdToKeyInfo[id]!.loader; if (loader) { if (type && type !== loader.context?.frag.type) { return; @@ -50,27 +54,27 @@ export default class KeyLoader implements ComponentAPI { } detach() { - for (const uri in this.keyUriToKeyInfo) { - const keyInfo = this.keyUriToKeyInfo[uri]; + for (const id in this.keyIdToKeyInfo) { + const keyInfo = this.keyIdToKeyInfo[id]!; // Remove cached EME keys on detach if ( keyInfo.mediaKeySessionContext || keyInfo.decryptdata.isCommonEncryption ) { - delete this.keyUriToKeyInfo[uri]; + delete this.keyIdToKeyInfo[id]; } } } destroy() { this.detach(); - for (const uri in this.keyUriToKeyInfo) { - const loader = this.keyUriToKeyInfo[uri].loader; + for (const id in this.keyIdToKeyInfo) { + const loader = this.keyIdToKeyInfo[id]!.loader; if (loader) { loader.destroy(); } } - this.keyUriToKeyInfo = {}; + this.keyIdToKeyInfo = {}; } createKeyLoadError( @@ -185,22 +189,23 @@ export default class KeyLoader implements ComponentAPI { ), ); } - let keyInfo = this.keyUriToKeyInfo[uri]; + const id = getKeyId(decryptdata); + let keyInfo = this.keyIdToKeyInfo[id]; if (keyInfo?.decryptdata.key) { decryptdata.key = keyInfo.decryptdata.key; return Promise.resolve({ frag, keyInfo }); } - // Return key load promise as long as it does not have a mediakey session with an unusable key status + // Return key load promise once it has a mediakey session with an usable key status if (keyInfo?.keyLoadPromise) { - switch (keyInfo.mediaKeySessionContext?.keyStatus) { - case undefined: - case 'status-pending': + const keyStatus = keyInfo.mediaKeySessionContext?.keyStatus; + switch (keyStatus) { case 'usable': case 'usable-in-future': return keyInfo.keyLoadPromise.then((keyLoadedData) => { // Return the correct fragment with updated decryptdata key and loaded keyInfo - decryptdata.key = keyLoadedData.keyInfo.decryptdata.key; + const { keyInfo } = keyLoadedData; + decryptdata.key = keyInfo.decryptdata.key; return { frag, keyInfo }; }); } @@ -209,7 +214,11 @@ export default class KeyLoader implements ComponentAPI { } // Load the key or return the loading promise - keyInfo = this.keyUriToKeyInfo[uri] = { + this.log( + `Loading key ${arrayToHex(decryptdata.keyId || [])} from ${frag.type} ${frag.level}`, + ); + + keyInfo = this.keyIdToKeyInfo[id] = { decryptdata, keyLoadPromise: null, loader: null, @@ -217,7 +226,6 @@ export default class KeyLoader implements ComponentAPI { }; switch (decryptdata.method) { - case 'ISO-23001-7': case 'SAMPLE-AES': case 'SAMPLE-AES-CENC': case 'SAMPLE-AES-CTR': @@ -248,18 +256,19 @@ export default class KeyLoader implements ComponentAPI { if (this.emeController && this.config.emeEnabled) { const keySessionContextPromise = this.emeController.loadKey(keyLoadedData); - if (keySessionContextPromise) { - return (keyInfo.keyLoadPromise = keySessionContextPromise.then( - (keySessionContext) => { - keyInfo.mediaKeySessionContext = keySessionContext; - return keyLoadedData; - }, - )).catch((error) => { - // Remove promise for license renewal or retry - keyInfo.keyLoadPromise = null; - throw error; - }); - } + return (keyInfo.keyLoadPromise = keySessionContextPromise.then( + (keySessionContext) => { + keyInfo.mediaKeySessionContext = keySessionContext; + return keyLoadedData; + }, + )).catch((error) => { + // Remove promise for license renewal or retry + keyInfo.keyLoadPromise = null; + if (error.data) { + error.data.frag = frag; + } + throw error; + }); } return Promise.resolve(keyLoadedData); } @@ -298,7 +307,8 @@ export default class KeyLoader implements ComponentAPI { networkDetails: any, ) => { const { frag, keyInfo, url: uri } = context; - if (!frag.decryptdata || keyInfo !== this.keyUriToKeyInfo[uri]) { + const id = getKeyId(keyInfo.decryptdata) || uri; + if (!frag.decryptdata || keyInfo !== this.keyIdToKeyInfo[id]) { return reject( this.createKeyLoadError( frag, @@ -383,9 +393,18 @@ export default class KeyLoader implements ComponentAPI { frag.keyLoader = null; keyInfo.loader = null; } - delete this.keyUriToKeyInfo[uri]; + const id = getKeyId(keyInfo.decryptdata) || uri; + delete this.keyIdToKeyInfo[id]; if (loader) { loader.destroy(); } } } + +function getKeyId(decryptdata: LevelKey) { + const keyId = decryptdata.keyId; + if (keyId) { + return arrayToHex(keyId); + } + return decryptdata.uri; +} diff --git a/src/loader/level-details.ts b/src/loader/level-details.ts index ed2d7aba8a0..3c954fe00c2 100644 --- a/src/loader/level-details.ts +++ b/src/loader/level-details.ts @@ -1,7 +1,9 @@ import type { DateRange } from './date-range'; import type { Fragment, MediaFragment, Part } from './fragment'; +import type { LevelKey } from './level-key'; import type { VariableMap } from '../types/level'; import type { AttrList } from '../utils/attr-list'; +import type { KeySystemFormats } from '../utils/mediakeys-helper'; const DEFAULT_TARGET_DURATION = 10; @@ -88,6 +90,17 @@ export class LevelDetails { } } + hasKey(levelKey: LevelKey): boolean { + return this.encryptedFragments.some((frag) => { + let decryptdata = frag.decryptdata; + if (!decryptdata) { + frag.setKeyFormat(levelKey.keyFormat as KeySystemFormats); + decryptdata = frag.decryptdata; + } + return !!decryptdata && levelKey.matches(decryptdata); + }); + } + get hasProgramDateTime(): boolean { if (this.fragments.length) { return Number.isFinite( diff --git a/src/loader/level-key.ts b/src/loader/level-key.ts index 49d4dd925f7..0da3ae32ce0 100644 --- a/src/loader/level-key.ts +++ b/src/loader/level-key.ts @@ -1,3 +1,4 @@ +import { arrayValuesMatch, optionalArrayValuesMatch } from '../utils/arrays'; import { isFullSegmentEncryption } from '../utils/encryption-methods-util'; import { hexToArrayBuffer } from '../utils/hex'; import { convertDataUriToArrayBytes } from '../utils/keysystem-util'; @@ -63,8 +64,9 @@ export class LevelKey implements DecryptData { key.method === this.method && key.encrypted === this.encrypted && key.keyFormat === this.keyFormat && - key.keyFormatVersions.join(',') === this.keyFormatVersions.join(',') && - key.iv?.join(',') === this.iv?.join(',') + arrayValuesMatch(key.keyFormatVersions, this.keyFormatVersions) && + optionalArrayValuesMatch(key.iv, this.iv) && + optionalArrayValuesMatch(key.keyId, this.keyId) ); } @@ -84,12 +86,9 @@ export class LevelKey implements DecryptData { case KeySystemFormats.PLAYREADY: case KeySystemFormats.CLEARKEY: return ( - [ - 'ISO-23001-7', - 'SAMPLE-AES', - 'SAMPLE-AES-CENC', - 'SAMPLE-AES-CTR', - ].indexOf(this.method) !== -1 + ['SAMPLE-AES', 'SAMPLE-AES-CENC', 'SAMPLE-AES-CTR'].indexOf( + this.method, + ) !== -1 ); } } diff --git a/src/types/events.ts b/src/types/events.ts index 975cf49656f..d3f5943dd93 100644 --- a/src/types/events.ts +++ b/src/types/events.ts @@ -319,6 +319,7 @@ export interface ErrorData { bytes?: number; chunkMeta?: ChunkMetadata; context?: PlaylistLoaderContext; + decryptdata?: LevelKey; event?: keyof HlsListeners | 'demuxerWorker'; frag?: Fragment; part?: Part | null; diff --git a/src/utils/arrays.ts b/src/utils/arrays.ts new file mode 100644 index 00000000000..d2272794127 --- /dev/null +++ b/src/utils/arrays.ts @@ -0,0 +1,22 @@ +export function arrayValuesMatch( + a: (string | number)[] | Uint8Array, + b: (string | number)[] | Uint8Array, +): boolean { + if (a.length === b.length) { + return !a.some((value: string | number, i: number) => value !== b[i]); + } + return false; +} + +export function optionalArrayValuesMatch( + a: (string | number)[] | Uint8Array | null | undefined, + b: (string | number)[] | Uint8Array | null | undefined, +): boolean { + if (!a && !b) { + return true; + } + if (!a || !b) { + return false; + } + return arrayValuesMatch(a, b); +} diff --git a/src/utils/hex.ts b/src/utils/hex.ts index a7db9633fea..2bc66b55132 100644 --- a/src/utils/hex.ts +++ b/src/utils/hex.ts @@ -2,20 +2,18 @@ * hex dump helper class */ -const Hex = { - hexDump: function (array: Uint8Array) { - let str = ''; - for (let i = 0; i < array.length; i++) { - let h = array[i].toString(16); - if (h.length < 2) { - h = '0' + h; - } - - str += h; +export function arrayToHex(array: Uint8Array | number[]) { + let str = ''; + for (let i = 0; i < array.length; i++) { + let h = array[i].toString(16); + if (h.length < 2) { + h = '0' + h; } - return str; - }, -}; + + str += h; + } + return str; +} export function hexToArrayBuffer(str: string): ArrayBuffer { return Uint8Array.from( @@ -27,4 +25,8 @@ export function hexToArrayBuffer(str: string): ArrayBuffer { ).buffer; } +const Hex = { + hexDump: arrayToHex, +}; + export default Hex; diff --git a/src/utils/mediakeys-helper.ts b/src/utils/mediakeys-helper.ts index c32f2e9bce0..5d9f5e2f64e 100755 --- a/src/utils/mediakeys-helper.ts +++ b/src/utils/mediakeys-helper.ts @@ -167,13 +167,14 @@ function createMediaKeySystemConfigurations( } export function isPersistentSessionType( - drmSystemOptions: DRMSystemOptions, + drmSystemOptions: DRMSystemOptions | undefined, ): boolean { return ( - drmSystemOptions.sessionType === 'persistent-license' || - !!drmSystemOptions.sessionTypes?.some( - (type) => type === 'persistent-license', - ) + !!drmSystemOptions && + (drmSystemOptions.sessionType === 'persistent-license' || + !!drmSystemOptions.sessionTypes?.some( + (type) => type === 'persistent-license', + )) ); } diff --git a/src/utils/mp4-tools.ts b/src/utils/mp4-tools.ts index 5becb00d177..86d536f7047 100644 --- a/src/utils/mp4-tools.ts +++ b/src/utils/mp4-tools.ts @@ -1,5 +1,5 @@ import { utf8ArrayToStr } from '@svta/common-media-library/utils/utf8ArrayToStr'; -import Hex from './hex'; +import { arrayToHex } from './hex'; import { ElementaryStreamTypes } from '../loader/fragment'; import { logger } from '../utils/logger'; import type { KeySystemIds } from './mediakeys-helper'; @@ -596,7 +596,7 @@ export function patchEncyptionData( logger.log( `[eme] Patching keyId in 'enc${ isAudio ? 'a' : 'v' - }>sinf>>tenc' box: ${Hex.hexDump(tencKeyId)} -> ${Hex.hexDump( + }>sinf>>tenc' box: ${arrayToHex(tencKeyId)} -> ${arrayToHex( keyId, )}`, ); @@ -1401,7 +1401,7 @@ function parsePssh(view: DataView): PsshData | PsshInvalidResult { return { offset, size }; } const buffer = view.buffer; - const systemId = Hex.hexDump( + const systemId = arrayToHex( new Uint8Array(buffer, offset + 12, 16), ) as KeySystemIds; diff --git a/tests/unit/controller/audio-stream-controller.ts b/tests/unit/controller/audio-stream-controller.ts index 2076463f9a3..d1c24b81d68 100644 --- a/tests/unit/controller/audio-stream-controller.ts +++ b/tests/unit/controller/audio-stream-controller.ts @@ -139,7 +139,7 @@ describe('AudioStreamController', function () { sandbox = sinon.createSandbox(); hls = new Hls(); fragmentTracker = new FragmentTracker(hls); - keyLoader = new KeyLoader(hlsDefaultConfig); + keyLoader = new KeyLoader(hlsDefaultConfig, hls.logger); audioStreamController = new AudioStreamController( hls, fragmentTracker, diff --git a/tests/unit/controller/base-stream-controller.ts b/tests/unit/controller/base-stream-controller.ts index 40a3651795c..52aaef8e679 100644 --- a/tests/unit/controller/base-stream-controller.ts +++ b/tests/unit/controller/base-stream-controller.ts @@ -42,7 +42,7 @@ describe('BaseStreamController', function () { baseStreamController = new BaseStreamController( hls, fragmentTracker, - new KeyLoader(hlsDefaultConfig), + new KeyLoader(hlsDefaultConfig, hls.logger), ) as unknown as BaseStreamControllerTestable; bufferInfo = { len: 1, diff --git a/tests/unit/controller/eme-controller.ts b/tests/unit/controller/eme-controller.ts index b94509a3005..d84bb54d215 100644 --- a/tests/unit/controller/eme-controller.ts +++ b/tests/unit/controller/eme-controller.ts @@ -61,7 +61,7 @@ class MediaKeySessionMock extends EventEmitter { messageType: 'license-request', message: new Uint8Array(0), }); - this.keyStatuses.set(new Uint8Array(0), 'usable'); + this.keyStatuses.set(new Uint8Array(16), 'usable'); this.emit('keystatuseschange', {}); }); } @@ -373,51 +373,51 @@ describe('EMEController', function () { drmSystems: { 'com.apple.fps': { serverCertificateUrl: 'https://example.com/certificate.cer', + licenseUrl: 'https://example.com/license', }, }, }); - let xhrInstance; sinonFakeXMLHttpRequestStatic.onCreate = ( xhr: sinon.SinonFakeXMLHttpRequest, ) => { - xhrInstance = xhr; - Promise.resolve().then(() => { - (xhr as any).response = new Uint8Array(); - xhr.respond(200, {}, ''); - }); + self.setTimeout(() => { + xhr.respond(200, {}, 'abcdef'); + }, 0); }; emeController.onMediaAttached(Events.MEDIA_ATTACHED, { media: media as any as HTMLMediaElement, }); - emeController.loadKey({ - frag: {}, - keyInfo: { - decryptdata: { - encrypted: true, - method: 'SAMPLE-AES', - uri: 'data://key-uri', - keyFormatVersions: [1], - keyId: new Uint8Array(16), - pssh: new Uint8Array(16), + return emeController + .loadKey({ + frag: {}, + keyInfo: { + decryptdata: { + encrypted: true, + method: 'SAMPLE-AES', + uri: 'data://key-uri', + keyFormatVersions: [1], + keyId: new Uint8Array(16), + pssh: new Uint8Array(16), + }, }, - }, - } as any); - - expect( - emeController.keyIdToKeySessionPromise[ - '00000000000000000000000000000000' - ], - ).to.be.a('Promise'); - return emeController.keyIdToKeySessionPromise[ - '00000000000000000000000000000000' - ].finally(() => { - expect(mediaKeysSetServerCertificateSpy).to.have.been.calledOnce; - expect(mediaKeysSetServerCertificateSpy).to.have.been.calledWith( - xhrInstance.response, - ); - }); + } as any) + .then(() => { + expect( + emeController.keyIdToKeySessionPromise[ + '00000000000000000000000000000000' + ], + ).to.be.a('Promise'); + return emeController.keyIdToKeySessionPromise[ + '00000000000000000000000000000000' + ].finally(() => { + expect(mediaKeysSetServerCertificateSpy).to.have.been.calledOnce; + expect(mediaKeysSetServerCertificateSpy).to.have.been.calledWith( + sinon.match({ byteLength: 6 }), + ); + }); + }); }); it('should fetch the server certificate and trigger update failed error', function () { diff --git a/tests/unit/controller/subtitle-stream-controller.ts b/tests/unit/controller/subtitle-stream-controller.ts index d0489781814..b70ae6371c8 100644 --- a/tests/unit/controller/subtitle-stream-controller.ts +++ b/tests/unit/controller/subtitle-stream-controller.ts @@ -42,7 +42,7 @@ describe('SubtitleStreamController', function () { hls = new Hls({}); mediaMock.currentTime = 0; fragmentTracker = new FragmentTracker(hls); - keyLoader = new KeyLoader(hls.config); + keyLoader = new KeyLoader(hls.config, hls.logger); subtitleStreamController = new SubtitleStreamController( hls, From b2d73913c1cbb8dd9cf53370582bd00200b62100 Mon Sep 17 00:00:00 2001 From: Rob Walch Date: Fri, 22 Aug 2025 16:13:19 -0700 Subject: [PATCH 48/52] Defer segment requests when network connection is lost (#7476) * Wait to retry fragment request when `navigator.onLine` is false (uses polling for Firefox where "online" event never fires) Fixes #7471 * Only treat no-alternate segment request failures as gaps in live playlists (#7464, #7410) * Retry once per seeking out of current fragment range (even when offline) * Do not exhaust retries in tick loop while seeking * Only schedule immediate tick on seeking when buffer is empty and state is idle (#7472) --- api-extractor/report/hls.js.api.md | 4 +- src/controller/audio-stream-controller.ts | 10 +-- src/controller/base-stream-controller.ts | 76 ++++++++++++++++------- src/controller/stream-controller.ts | 12 +--- src/utils/error-helper.ts | 8 ++- 5 files changed, 65 insertions(+), 45 deletions(-) diff --git a/api-extractor/report/hls.js.api.md b/api-extractor/report/hls.js.api.md index 5511ee3ed90..efcc3d551ee 100644 --- a/api-extractor/report/hls.js.api.md +++ b/api-extractor/report/hls.js.api.md @@ -392,6 +392,8 @@ export class BaseStreamController extends TaskLoop implements NetworkComponentAP // (undocumented) protected checkLiveUpdate(details: LevelDetails): void; // (undocumented) + protected checkRetryDate(): void; + // (undocumented) protected clearTrackerIfNeeded(frag: Fragment): void; // (undocumented) protected config: HlsConfig; @@ -528,8 +530,6 @@ export class BaseStreamController extends TaskLoop implements NetworkComponentAP // (undocumented) protected resetLoadingState(): void; // (undocumented) - protected resetStartWhenNotLoaded(level: Level | null): void; - // (undocumented) protected resetTransmuxer(): void; // (undocumented) protected resetWhenMissingContext(chunkMeta: ChunkMetadata | Fragment): void; diff --git a/src/controller/audio-stream-controller.ts b/src/controller/audio-stream-controller.ts index 53965814b94..d4cc58c5c70 100644 --- a/src/controller/audio-stream-controller.ts +++ b/src/controller/audio-stream-controller.ts @@ -255,15 +255,7 @@ class AudioStreamController break; } case State.FRAG_LOADING_WAITING_RETRY: { - const now = performance.now(); - const retryDate = this.retryDate; - // if current time is gt than retryDate, or if media seeking let's switch to IDLE state to retry loading - if (!retryDate || now >= retryDate || this.media?.seeking) { - const { levels, trackId } = this; - this.log('RetryDate reached, switch back to IDLE state'); - this.resetStartWhenNotLoaded(levels?.[trackId] || null); - this.state = State.IDLE; - } + this.checkRetryDate(); break; } case State.WAITING_INIT_PTS: { diff --git a/src/controller/base-stream-controller.ts b/src/controller/base-stream-controller.ts index 5ce214dd3b2..a26c14380be 100644 --- a/src/controller/base-stream-controller.ts +++ b/src/controller/base-stream-controller.ts @@ -24,7 +24,11 @@ import { getAesModeFromFullSegmentMethod, isFullSegmentEncryption, } from '../utils/encryption-methods-util'; -import { getRetryDelay } from '../utils/error-helper'; +import { getRetryDelay, offlineHttpStatus } from '../utils/error-helper'; +import { + addEventListener, + removeEventListener, +} from '../utils/event-listener-helper'; import { findPart, getFragmentWithSN, @@ -284,10 +288,8 @@ export default class BaseStreamController data: MediaAttachedData, ) { const media = (this.media = this.mediaBuffer = data.media); - media.removeEventListener('seeking', this.onMediaSeeking); - media.removeEventListener('ended', this.onMediaEnded); - media.addEventListener('seeking', this.onMediaSeeking); - media.addEventListener('ended', this.onMediaEnded); + addEventListener(media, 'seeking', this.onMediaSeeking); + addEventListener(media, 'ended', this.onMediaEnded); const config = this.config; if (this.levels && config.autoStartLoad && this.state === State.STOPPED) { this.startLoad(config.startPosition); @@ -309,8 +311,8 @@ export default class BaseStreamController } // remove video listeners - media.removeEventListener('seeking', this.onMediaSeeking); - media.removeEventListener('ended', this.onMediaEnded); + removeEventListener(media, 'seeking', this.onMediaSeeking); + removeEventListener(media, 'ended', this.onMediaEnded); if (this.keyLoader && !transferringMedia) { this.keyLoader.detach(); @@ -424,8 +426,10 @@ export default class BaseStreamController } } - // Async tick to speed up processing - this.tickImmediate(); + if (noFowardBuffer && this.state === State.IDLE) { + // Async tick to speed up processing + this.tickImmediate(); + } }; protected onMediaEnded = () => { @@ -1883,27 +1887,41 @@ export default class BaseStreamController } // keep retrying until the limit will be reached const errorAction = data.errorAction; - const { action, flags, retryCount = 0, retryConfig } = errorAction || {}; - const couldRetry = !!errorAction && !!retryConfig; + if (!errorAction) { + this.state = State.ERROR; + return; + } + const { action, flags, retryCount = 0, retryConfig } = errorAction; + const couldRetry = !!retryConfig; const retry = couldRetry && action === NetworkErrorAction.RetryRequest; const noAlternate = couldRetry && !errorAction.resolved && flags === ErrorActionFlags.MoveAllAlternatesMatchingHost; - const httpStatus = data.response?.code || 0; + const live = this.hls.latestLevelDetails?.live; if ( !retry && noAlternate && isMediaFragment(frag) && !frag.endList && - httpStatus !== 0 + live ) { this.resetFragmentErrors(filterType); this.treatAsGap(frag); errorAction.resolved = true; } else if ((retry || noAlternate) && retryCount < retryConfig.maxNumRetry) { - this.resetStartWhenNotLoaded(this.levelLastLoaded); + const offlineStatus = offlineHttpStatus(data.response?.code); const delay = getRetryDelay(retryConfig, retryCount); + this.resetStartWhenNotLoaded(); + this.retryDate = self.performance.now() + delay; + this.state = State.FRAG_LOADING_WAITING_RETRY; + errorAction.resolved = true; + if (offlineStatus) { + this.log(`Waiting for connection (offline)`); + this.retryDate = Infinity; + data.reason = 'offline'; + return; + } this.warn( `Fragment ${frag.sn} of ${filterType} ${frag.level} errored with ${ data.details @@ -1911,10 +1929,7 @@ export default class BaseStreamController retryConfig.maxNumRetry } in ${delay}ms`, ); - errorAction.resolved = true; - this.retryDate = self.performance.now() + delay; - this.state = State.FRAG_LOADING_WAITING_RETRY; - } else if (retryConfig && errorAction) { + } else if (retryConfig) { this.resetFragmentErrors(filterType); if (retryCount < retryConfig.maxNumRetry) { // Network retry is skipped when level switch is preferred @@ -1939,6 +1954,24 @@ export default class BaseStreamController this.tickImmediate(); } + protected checkRetryDate() { + const now = self.performance.now(); + const retryDate = this.retryDate; + // if current time is gt than retryDate, or if media seeking let's switch to IDLE state to retry loading + const waitingForConnection = retryDate === Infinity; + if ( + !retryDate || + now >= retryDate || + (waitingForConnection && !offlineHttpStatus(0)) + ) { + if (waitingForConnection) { + this.log(`Connection restored (online)`); + } + this.resetStartWhenNotLoaded(); + this.state = State.IDLE; + } + } + protected reduceLengthAndFlushBuffer(data: ErrorData): boolean { // if in appending state if (this.state === State.PARSING || this.state === State.PARSED) { @@ -2018,11 +2051,12 @@ export default class BaseStreamController } } - protected resetStartWhenNotLoaded(level: Level | null): void { + private resetStartWhenNotLoaded() { // if loadedmetadata is not set, it means that first frag request failed // in that case, reset startFragRequested flag if (!this.hls.hasEnoughToStart) { this.startFragRequested = false; + const level = this.levelLastLoaded; const details = level ? level.details : null; if (details?.live) { // Update the start position and return to IDLE to recover live start @@ -2041,7 +2075,7 @@ export default class BaseStreamController `Loading context changed while buffering sn ${chunkMeta.sn} of ${this.playlistLabel()} ${chunkMeta.level === -1 ? '' : chunkMeta.level}. This chunk will not be buffered.`, ); this.removeUnbufferedFrags(); - this.resetStartWhenNotLoaded(this.levelLastLoaded); + this.resetStartWhenNotLoaded(); this.resetLoadingState(); } @@ -2176,7 +2210,7 @@ export default class BaseStreamController this.transmuxer.destroy(); this.transmuxer = null; } - this.resetStartWhenNotLoaded(this.levelLastLoaded); + this.resetStartWhenNotLoaded(); this.resetLoadingState(); } } diff --git a/src/controller/stream-controller.ts b/src/controller/stream-controller.ts index 750cb374c26..f58d05fa040 100644 --- a/src/controller/stream-controller.ts +++ b/src/controller/stream-controller.ts @@ -215,17 +215,7 @@ export default class StreamController break; } case State.FRAG_LOADING_WAITING_RETRY: - { - const now = self.performance.now(); - const retryDate = this.retryDate; - // if current time is gt than retryDate, or if media seeking let's switch to IDLE state to retry loading - if (!retryDate || now >= retryDate || this.media?.seeking) { - const { levels, level } = this; - const currentLevel = levels?.[level]; - this.resetStartWhenNotLoaded(currentLevel || null); - this.state = State.IDLE; - } - } + this.checkRetryDate(); break; default: break; diff --git a/src/utils/error-helper.ts b/src/utils/error-helper.ts index dc1eb18aa5d..b42ac693f83 100644 --- a/src/utils/error-helper.ts +++ b/src/utils/error-helper.ts @@ -71,10 +71,14 @@ export function shouldRetry( : retry; } -export function retryForHttpStatus(httpStatus: number | undefined) { +export function retryForHttpStatus(httpStatus: number | undefined): boolean { // Do not retry on status 4xx, status 0 (CORS error), or undefined (decrypt/gap/parse error) return ( - (httpStatus === 0 && navigator.onLine === false) || + offlineHttpStatus(httpStatus) || (!!httpStatus && (httpStatus < 400 || httpStatus > 499)) ); } + +export function offlineHttpStatus(httpStatus: number | undefined): boolean { + return httpStatus === 0 && navigator.onLine === false; +} From 82e46956dd6b6a2a26fe84a883cdb019391f0b66 Mon Sep 17 00:00:00 2001 From: Rob Walch Date: Sun, 24 Aug 2025 18:42:10 -0700 Subject: [PATCH 49/52] Improve functional test logs (#7481) --- tests/functional/auto/setup.js | 75 ++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 36 deletions(-) diff --git a/tests/functional/auto/setup.js b/tests/functional/auto/setup.js index 0180fe2aa59..b0244d62a7f 100644 --- a/tests/functional/auto/setup.js +++ b/tests/functional/auto/setup.js @@ -492,7 +492,7 @@ function getPageURLComponents() { }; } -describe(`testing hls.js playback in the browser on "${browserDescription}"`, function () { +describe(`Testing hls.js playback in ${browserConfig.name} ${browserConfig.version} ${browserConfig.platform ? browserConfig.platform : ''}`, function () { const failedUrls = {}; before(async function () { @@ -696,46 +696,49 @@ ${data.logs} ) { return; } - it( - `should receive video loadeddata event for ${stream.description}`, - testLoadedData.bind(null, url, config) - ); - if (stream.startSeek && !HlsjsLightBuild) { + describe(`${index + 1}. [${name}]: ${stream.description} (${stream.url})`, function () { it( - `seek back to start and play for ${stream.description}`, - testSeekBackToStart.bind(null, url, config) + `should receive video loadeddata event`, + testLoadedData.bind(null, url, config) ); - } - if (stream.abr) { - it( - `should "smooth switch" to highest level and still play after 2s for ${stream.description}`, - testSmoothSwitch.bind(null, url, config) - ); - } + if (stream.startSeek && !HlsjsLightBuild) { + it( + `seek back to start and play`, + testSeekBackToStart.bind(null, url, config) + ); + } - if (stream.live) { - it( - `should seek near the end and receive video seeked event for ${stream.description}`, - testSeekOnLive.bind(null, url, config) - ); - } else if (!HlsjsLightBuild) { - it( - `should buffer up to maxBufferLength or video.duration for ${stream.description}`, - testIdleBufferLength.bind(null, url, config) - ); - it( - `should play ${stream.description}`, - testIsPlayingVOD.bind(null, url, config) - ); + if (stream.abr) { + it( + `should "smooth switch" to highest level and still play after 2s`, + testSmoothSwitch.bind(null, url, config) + ); + } - it( - `should seek 3s from end and receive video ended event for ${stream.description} with 2 or less buffered ranges`, - testSeekOnVOD.bind(null, url, config) - ); - // TODO: Seeking to or past VOD duration should result in the video ending - // it(`should seek on end and receive video ended event for ${stream.description}`, testSeekEndVOD.bind(null, url)); - } + if (stream.live) { + it( + `should seek near the end and receive video seeked event`, + testSeekOnLive.bind(null, url, config) + ); + } else if (!HlsjsLightBuild) { + it( + `should buffer up to maxBufferLength or video.duration`, + testIdleBufferLength.bind(null, url, config) + ); + it( + `should play ${stream.description}`, + testIsPlayingVOD.bind(null, url, config) + ); + + it( + `should seek 3s from end and receive video ended event with 2 or less buffered ranges`, + testSeekOnVOD.bind(null, url, config) + ); + // TODO: Seeking to or past VOD duration should result in the video ending + // it(`should seek on end and receive video ended event`, testSeekEndVOD.bind(null, url)); + } + }); }); }); From 178333c02286cd8493b9d2077db845959c306657 Mon Sep 17 00:00:00 2001 From: Rob Walch Date: Sun, 24 Aug 2025 19:10:26 -0700 Subject: [PATCH 50/52] Add ESLint syntax rules to restrict async syntax, no-floating-promises, and no-misused-promises (#7480) ESlint error messages explain reasoning --- .eslintrc.js | 45 +++++++++++--- src/controller/abr-controller.ts | 74 ++++++++++++----------- src/controller/buffer-controller.ts | 2 + src/controller/eme-controller.ts | 16 +++-- src/controller/level-controller.ts | 1 + src/controller/stream-controller.ts | 48 +++++++++------ src/demux/sample-aes.ts | 30 +++++---- src/utils/mediacapabilities-helper.ts | 37 +++++++----- tests/unit/controller/eme-controller.ts | 48 +++++++++------ tests/unit/controller/error-controller.ts | 10 ++- 10 files changed, 192 insertions(+), 119 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index efde38431df..b336a58425e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,3 +1,12 @@ +const asyncKeywordConstraintMsg = + 'The async keyword adds a `regenerator` dependency in the hls.js ES5 output not allowed in v1 due to bundle size constraints.'; +const selfVsWindowGlobalMsg = + 'Use `self` instead of `window` to access the global context everywhere (including workers).'; +const arrayFindCompatibilityMsg = + 'Usage of Array find methods is restricted for compatibility.'; +const arrayFindIndexCompatibilityMsg = + 'Usage of Array findIndex methods is restricted for compatibility.'; + module.exports = { env: { browser: true, commonjs: true, es6: true }, globals: { @@ -27,20 +36,13 @@ module.exports = { 2, { name: 'window', - message: - 'Use `self` instead of `window` to access the global context everywhere (including workers).', + message: selfVsWindowGlobalMsg, }, { name: 'SourceBuffer', message: 'Use `self.SourceBuffer`' }, { name: 'setTimeout', message: 'Use `self.setTimeout`' }, { name: 'setInterval', message: 'Use `self.setInterval`' }, ], - 'no-restricted-properties': [ - 2, - { property: 'findIndex' }, // Intended to block usage of Array.prototype.findIndex - { property: 'find' }, // Intended to block usage of Array.prototype.find - ], - 'import/first': 1, 'no-var': 1, 'no-empty': 1, @@ -67,6 +69,31 @@ module.exports = { 'no-unused-vars': 0, 'no-undef': 0, 'no-use-before-define': 'off', + 'no-restricted-syntax': [ + 'error', + { + selector: 'FunctionDeclaration[async=true]', + message: asyncKeywordConstraintMsg, + }, + { + selector: 'ArrowFunctionExpression[async=true]', + message: asyncKeywordConstraintMsg, + }, + { + selector: 'MethodDefinition[value.async=true]', + message: asyncKeywordConstraintMsg, + }, + { + selector: + 'MemberExpression[property.name="find"][object.type="Identifier"]', + message: arrayFindCompatibilityMsg, + }, + { + selector: + 'MemberExpression[property.name="findIndex"][object.type="Identifier"]', + message: arrayFindIndexCompatibilityMsg, + }, + ], 'import/order': [ 'warn', { @@ -98,6 +125,8 @@ module.exports = { '@typescript-eslint/consistent-type-imports': 'error', '@typescript-eslint/no-import-type-side-effects': 'error', '@typescript-eslint/no-restricted-imports': 'error', + '@typescript-eslint/no-floating-promises': 'error', + '@typescript-eslint/no-misused-promises': 'error', }, }, ], diff --git a/src/controller/abr-controller.ts b/src/controller/abr-controller.ts index ae1662f3c4d..afefdea4a44 100644 --- a/src/controller/abr-controller.ts +++ b/src/controller/abr-controller.ts @@ -845,43 +845,49 @@ class AbrController extends Logger implements AbrComponentAPI { mediaCapabilities, this.supportedCache, ); - levelInfo.supportedPromise.then((decodingInfo) => { - if (!this.hls) { - return; - } - levelInfo.supportedResult = decodingInfo; - const levels = this.hls.levels; - const index = levels.indexOf(levelInfo); - if (decodingInfo.error) { - this.warn( - `MediaCapabilities decodingInfo error: "${ - decodingInfo.error - }" for level ${index} ${stringify(decodingInfo)}`, - ); - } else if (!decodingInfo.supported) { - this.warn( - `Unsupported MediaCapabilities decodingInfo result for level ${index} ${stringify( - decodingInfo, - )}`, - ); - if (index > -1 && levels.length > 1) { - this.log(`Removing unsupported level ${index}`); - this.hls.removeLevel(index); - if (this.hls.loadLevel === -1) { - this.hls.nextLoadLevel = 0; + levelInfo.supportedPromise + .then((decodingInfo) => { + if (!this.hls) { + return; + } + levelInfo.supportedResult = decodingInfo; + const levels = this.hls.levels; + const index = levels.indexOf(levelInfo); + if (decodingInfo.error) { + this.warn( + `MediaCapabilities decodingInfo error: "${ + decodingInfo.error + }" for level ${index} ${stringify(decodingInfo)}`, + ); + } else if (!decodingInfo.supported) { + this.warn( + `Unsupported MediaCapabilities decodingInfo result for level ${index} ${stringify( + decodingInfo, + )}`, + ); + if (index > -1 && levels.length > 1) { + this.log(`Removing unsupported level ${index}`); + this.hls.removeLevel(index); + if (this.hls.loadLevel === -1) { + this.hls.nextLoadLevel = 0; + } } + } else if ( + decodingInfo.decodingInfoResults.some( + (info) => + info.smooth === false || info.powerEfficient === false, + ) + ) { + this.log( + `MediaCapabilities decodingInfo for level ${index} not smooth or powerEfficient: ${stringify(decodingInfo)}`, + ); } - } else if ( - decodingInfo.decodingInfoResults.some( - (info) => - info.smooth === false || info.powerEfficient === false, - ) - ) { - this.log( - `MediaCapabilities decodingInfo for level ${index} not smooth or powerEfficient: ${stringify(decodingInfo)}`, + }) + .catch((error) => { + this.warn( + `Error handling MediaCapabilities decodingInfo: ${error}`, ); - } - }); + }); } else { levelInfo.supportedResult = SUPPORTED_INFO_DEFAULT; } diff --git a/src/controller/buffer-controller.ts b/src/controller/buffer-controller.ts index 920e9220534..b13ec5edca5 100755 --- a/src/controller/buffer-controller.ts +++ b/src/controller/buffer-controller.ts @@ -343,6 +343,7 @@ export default class BufferController extends Logger implements ComponentAPI { : null; const trackCount = trackNames ? trackNames.length : 0; const mediaSourceOpenCallback = () => { + // eslint-disable-next-line @typescript-eslint/no-floating-promises Promise.resolve().then(() => { if (this.media && this.mediaSourceOpenOrEnded) { this._onMediaSourceOpen(); @@ -417,6 +418,7 @@ transfer tracks: ${stringify(transferredTracks, (key, value) => (key === 'initSe >; this.sourceBuffers[sbIndex] = sbTuple as any; if (sb.updating && this.operationQueue) { + // eslint-disable-next-line @typescript-eslint/no-floating-promises this.operationQueue.prependBlocker(type); } this.trackSourceBuffer(type, track); diff --git a/src/controller/eme-controller.ts b/src/controller/eme-controller.ts index 08a06fb166b..b7e8488f787 100644 --- a/src/controller/eme-controller.ts +++ b/src/controller/eme-controller.ts @@ -357,6 +357,7 @@ class EMEController extends Logger implements ComponentAPI { } else { this.warn(`Could not renew expired session. Missing pssh initData.`); } + // eslint-disable-next-line @typescript-eslint/no-floating-promises this.removeSession(mediaKeySessionContext); } @@ -407,7 +408,7 @@ class EMEController extends Logger implements ComponentAPI { keySystemsToAttempt: KeySystems[], ): Promise { return new Promise((resolve, reject) => { - return this.getKeySystemSelectionPromise(keySystemsToAttempt) + this.getKeySystemSelectionPromise(keySystemsToAttempt) .then(({ keySystem }) => { const keySystemFormat = keySystemDomainToKeySystemFormat(keySystem); if (keySystemFormat) { @@ -774,7 +775,9 @@ class EMEController extends Logger implements ComponentAPI { }); } else if (messageType === 'license-release') { if (context.keySystem === KeySystems.FAIRPLAY) { + // eslint-disable-next-line @typescript-eslint/no-floating-promises this.updateKeySession(context, strToUtf8array('acknowledged')); + // eslint-disable-next-line @typescript-eslint/no-floating-promises this.removeSession(context); } } else { @@ -864,6 +867,7 @@ class EMEController extends Logger implements ComponentAPI { .then(() => keyUsablePromise) .catch((error) => { licenseStatus.removeAllListeners(); + // eslint-disable-next-line @typescript-eslint/no-floating-promises this.removeSession(context); throw error; }) @@ -1231,8 +1235,8 @@ class EMEController extends Logger implements ComponentAPI { } keySessionContext.licenseXhr = xhr; - this.setupLicenseXHR(xhr, url, keySessionContext, licenseChallenge).then( - ({ xhr, licenseChallenge }) => { + this.setupLicenseXHR(xhr, url, keySessionContext, licenseChallenge) + .then(({ xhr, licenseChallenge }) => { if (keySessionContext.keySystem == KeySystems.PLAYREADY) { licenseChallenge = this.unpackPlayReadyKeyMessage( xhr, @@ -1240,8 +1244,8 @@ class EMEController extends Logger implements ComponentAPI { ); } xhr.send(licenseChallenge); - }, - ); + }) + .catch(reject); }); } @@ -1407,7 +1411,7 @@ class EMEController extends Logger implements ComponentAPI { () => reject(new Error(`MediaKeySession.remove() timeout`)), 8000, ); - mediaKeysSession.remove().then(resolve); + mediaKeysSession.remove().then(resolve).catch(reject); }) : Promise.resolve(); return removePromise diff --git a/src/controller/level-controller.ts b/src/controller/level-controller.ts index 9b33b71b561..c2999e7eaf4 100644 --- a/src/controller/level-controller.ts +++ b/src/controller/level-controller.ts @@ -263,6 +263,7 @@ export default class LevelController extends BasePlaylistController { if (levels.length === 0) { // Dispatch error after MANIFEST_LOADED is done propagating + // eslint-disable-next-line @typescript-eslint/no-floating-promises Promise.resolve().then(() => { if (this.hls) { let message = 'no level with compatible codecs found in manifest'; diff --git a/src/controller/stream-controller.ts b/src/controller/stream-controller.ts index f58d05fa040..2e3402bc5cf 100644 --- a/src/controller/stream-controller.ts +++ b/src/controller/stream-controller.ts @@ -1185,26 +1185,34 @@ export default class StreamController private _loadBitrateTestFrag(fragment: Fragment, level: Level) { fragment.bitrateTest = true; - this._doFragLoad(fragment, level).then((data) => { - const { hls } = this; - const frag = data?.frag; - if (!frag || this.fragContextChanged(frag)) { - return; - } - level.fragmentError = 0; - this.state = State.IDLE; - this.startFragRequested = false; - this.bitrateTest = false; - const stats = frag.stats; - // Bitrate tests fragments are neither parsed nor buffered - stats.parsing.start = - stats.parsing.end = - stats.buffering.start = - stats.buffering.end = - self.performance.now(); - hls.trigger(Events.FRAG_LOADED, data as FragLoadedData); - frag.bitrateTest = false; - }); + this._doFragLoad(fragment, level) + .then((data) => { + const { hls } = this; + const frag = data?.frag; + if (!frag || this.fragContextChanged(frag)) { + return; + } + level.fragmentError = 0; + this.state = State.IDLE; + this.startFragRequested = false; + this.bitrateTest = false; + const stats = frag.stats; + // Bitrate tests fragments are neither parsed nor buffered + stats.parsing.start = + stats.parsing.end = + stats.buffering.start = + stats.buffering.end = + self.performance.now(); + hls.trigger(Events.FRAG_LOADED, data as FragLoadedData); + frag.bitrateTest = false; + }) + .catch((reason) => { + if (this.state === State.STOPPED || this.state === State.ERROR) { + return; + } + this.warn(reason); + this.resetFragmentLoading(fragment); + }); } private _handleTransmuxComplete(transmuxResult: TransmuxerResult) { diff --git a/src/demux/sample-aes.ts b/src/demux/sample-aes.ts index 5267bbccc97..b1ead44700b 100644 --- a/src/demux/sample-aes.ts +++ b/src/demux/sample-aes.ts @@ -56,14 +56,16 @@ class SampleAesDecrypter { encryptedData.byteOffset + encryptedData.length, ); - this.decryptBuffer(encryptedBuffer).then((decryptedBuffer: ArrayBuffer) => { - const decryptedData = new Uint8Array(decryptedBuffer); - curUnit.set(decryptedData, 16); + this.decryptBuffer(encryptedBuffer) + .then((decryptedBuffer: ArrayBuffer) => { + const decryptedData = new Uint8Array(decryptedBuffer); + curUnit.set(decryptedData, 16); - if (!this.decrypter.isSync()) { - this.decryptAacSamples(samples, sampleIndex + 1, callback); - } - }); + if (!this.decrypter.isSync()) { + this.decryptAacSamples(samples, sampleIndex + 1, callback); + } + }) + .catch(callback); } decryptAacSamples( @@ -136,13 +138,15 @@ class SampleAesDecrypter { const decodedData = discardEPB(curUnit.data); const encryptedData = this.getAvcEncryptedData(decodedData); - this.decryptBuffer(encryptedData.buffer).then((decryptedBuffer) => { - curUnit.data = this.getAvcDecryptedUnit(decodedData, decryptedBuffer); + this.decryptBuffer(encryptedData.buffer) + .then((decryptedBuffer) => { + curUnit.data = this.getAvcDecryptedUnit(decodedData, decryptedBuffer); - if (!this.decrypter.isSync()) { - this.decryptAvcSamples(samples, sampleIndex, unitIndex + 1, callback); - } - }); + if (!this.decrypter.isSync()) { + this.decryptAvcSamples(samples, sampleIndex, unitIndex + 1, callback); + } + }) + .catch(callback); } decryptAvcSamples( diff --git a/src/utils/mediacapabilities-helper.ts b/src/utils/mediacapabilities-helper.ts index a61181c25e8..280a30c9561 100644 --- a/src/utils/mediacapabilities-helper.ts +++ b/src/utils/mediacapabilities-helper.ts @@ -16,16 +16,26 @@ export type MediaDecodingInfo = { error?: Error; }; +// @ts-ignore +const supportedResult: MediaCapabilitiesDecodingInfo = { + supported: true, + powerEfficient: true, + smooth: true, + // keySystemAccess: null, +}; + +// @ts-ignore +const unsupportedResult: MediaCapabilitiesDecodingInfo = { + supported: false, + smooth: false, + powerEfficient: false, + // keySystemAccess: null, +}; + export const SUPPORTED_INFO_DEFAULT: MediaDecodingInfo = { supported: true, configurations: [] as MediaDecodingConfiguration[], - decodingInfoResults: [ - { - supported: true, - powerEfficient: true, - smooth: true, - }, - ], + decodingInfoResults: [supportedResult], } as const; export function getUnsupportedResult( @@ -35,13 +45,7 @@ export function getUnsupportedResult( return { supported: false, configurations, - decodingInfoResults: [ - { - supported: false, - smooth: false, - powerEfficient: false, - }, - ], + decodingInfoResults: [unsupportedResult], error, }; } @@ -113,7 +117,10 @@ export function getMediaDecodingInfoPromise( level: Level, audioTracksByGroup: AudioTracksByGroup, mediaCapabilities: MediaCapabilities | undefined, - cache: Record> = {}, + cache: Record< + string, + Promise | undefined + > = {}, ): Promise { const videoCodecs = level.videoCodec; if ((!videoCodecs && !level.audioCodec) || !mediaCapabilities) { diff --git a/tests/unit/controller/eme-controller.ts b/tests/unit/controller/eme-controller.ts index d84bb54d215..52aa6688a7f 100644 --- a/tests/unit/controller/eme-controller.ts +++ b/tests/unit/controller/eme-controller.ts @@ -469,17 +469,21 @@ describe('EMEController', function () { emeController.onMediaAttached(Events.MEDIA_ATTACHED, { media: media as any as HTMLMediaElement, }); - emeController.loadKey({ - frag: {}, - keyInfo: { - decryptdata: { - encrypted: true, - method: 'SAMPLE-AES', - uri: 'data://key-uri', - keyId: new Uint8Array(16), + emeController + .loadKey({ + frag: {}, + keyInfo: { + decryptdata: { + encrypted: true, + method: 'SAMPLE-AES', + uri: 'data://key-uri', + keyId: new Uint8Array(16), + }, }, - }, - } as any); + } as any) + .catch((error) => { + // expected? + }); expect( emeController.keyIdToKeySessionPromise[ @@ -545,17 +549,21 @@ describe('EMEController', function () { emeController.onMediaAttached(Events.MEDIA_ATTACHED, { media: media as any as HTMLMediaElement, }); - emeController.loadKey({ - frag: {}, - keyInfo: { - decryptdata: { - encrypted: true, - method: 'SAMPLE-AES', - uri: 'data://key-uri', - keyId: new Uint8Array(16), + emeController + .loadKey({ + frag: {}, + keyInfo: { + decryptdata: { + encrypted: true, + method: 'SAMPLE-AES', + uri: 'data://key-uri', + keyId: new Uint8Array(16), + }, }, - }, - } as any); + } as any) + .catch((error) => { + // expected? + }); expect( emeController.keyIdToKeySessionPromise[ diff --git a/tests/unit/controller/error-controller.ts b/tests/unit/controller/error-controller.ts index 9df0432a1cc..7ddef8323ac 100644 --- a/tests/unit/controller/error-controller.ts +++ b/tests/unit/controller/error-controller.ts @@ -266,9 +266,10 @@ describe('ErrorController Integration Tests', function () { hls.loadSource('noSegmentsVod.m3u8'); hls.stopLoad.should.have.been.calledOnce; return new Promise((resolve, reject) => { - hls.on(Events.ERROR, (event, data) => - Promise.resolve().then(() => resolve(data)), - ); + hls.on(Events.ERROR, (event, data) => { + // eslint-disable-next-line @typescript-eslint/no-floating-promises + Promise.resolve().then(() => resolve(data)); + }); hls.on(Events.LEVEL_LOADED, () => reject( new Error( @@ -713,6 +714,7 @@ segment.mp4 hls.on(Events.FRAG_LOADING, loadingEventCallback(server, timers)); hls.on(Events.ERROR, (event, data) => { errors.push(data); + // eslint-disable-next-line @typescript-eslint/no-floating-promises Promise.resolve().then(() => timers.tick(2000)); }); return new Promise((resolve, reject) => { @@ -823,6 +825,7 @@ segment.mp4 hls.on(Events.FRAG_LOADING, loadingEventCallback(server, timers)); hls.on(Events.ERROR, (event, data) => { errors.push(data); + // eslint-disable-next-line @typescript-eslint/no-floating-promises Promise.resolve().then(() => timers.tick(2000)); }); return new Promise((resolve, reject) => { @@ -968,6 +971,7 @@ function setupMockServerResponses(server: sinon.SinonFakeServer) { function loadingEventCallback(server, timers) { return (event, data) => { + // eslint-disable-next-line @typescript-eslint/no-floating-promises Promise.resolve().then(() => { server.respond(); }); From f5d3cd1b8d505de6bf3a55360818bf4a74d0f95d Mon Sep 17 00:00:00 2001 From: Hongfei Huang Date: Tue, 26 Aug 2025 11:32:11 +0800 Subject: [PATCH 51/52] chore: 1.6.12 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index e7f9d8bf756..48847b18904 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "hls.js", - "version": "1.6.8", + "version": "1.6.12", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "hls.js", - "version": "1.6.8", + "version": "1.6.12", "license": "Apache-2.0", "devDependencies": { "@babel/core": "7.28.0", diff --git a/package.json b/package.json index 1b3207431b4..fc0cb5d562e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hls.js", - "version": "1.6.8", + "version": "1.6.12", "license": "Apache-2.0", "description": "JavaScript HLS client using MediaSourceExtension", "homepage": "https://github.com/video-dev/hls.js", From f75c23e1f7a8252445c8e1c42d265acdbedb484b Mon Sep 17 00:00:00 2001 From: Hongfei Huang Date: Tue, 26 Aug 2025 15:18:57 +0800 Subject: [PATCH 52/52] Revert "fix: support Widevine and Playready Hardware DRM. (#111)" This reverts commit 9aeacf9228d1753ebc121c72af2e20837c56dc61. --- api-extractor/report/hls.js.api.md | 14 -------------- src/controller/eme-controller.ts | 5 ----- src/events.ts | 7 ------- src/hls.ts | 1 - src/types/events.ts | 5 ----- 5 files changed, 32 deletions(-) diff --git a/api-extractor/report/hls.js.api.md b/api-extractor/report/hls.js.api.md index 49f5a4b429c..efcc3d551ee 100644 --- a/api-extractor/report/hls.js.api.md +++ b/api-extractor/report/hls.js.api.md @@ -1511,8 +1511,6 @@ export enum Events { // (undocumented) KEY_LOADING = "hlsKeyLoading", // (undocumented) - KEY_STATUSES_CHANGED = "hlsKeyStatusesChanged", - // (undocumented) LEVEL_LOADED = "hlsLevelLoaded", // (undocumented) LEVEL_LOADING = "hlsLevelLoading", @@ -2413,8 +2411,6 @@ export interface HlsListeners { // (undocumented) [Events.KEY_LOADING]: (event: Events.KEY_LOADING, data: KeyLoadingData) => void; // (undocumented) - [Events.KEY_STATUSES_CHANGED]: (event: Events.KEY_STATUSES_CHANGED, data: KeyStatusesChangedData) => void; - // (undocumented) [Events.LEVEL_LOADED]: (event: Events.LEVEL_LOADED, data: LevelLoadedData) => void; // (undocumented) [Events.LEVEL_LOADING]: (event: Events.LEVEL_LOADING, data: LevelLoadingData) => void; @@ -3050,16 +3046,6 @@ export interface KeyLoadingData { frag: Fragment; } -// Warning: (ae-missing-release-tag) "KeyStatusesChangedData" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export interface KeyStatusesChangedData { - // (undocumented) - keyStatuses: MediaKeyStatusMap; - // (undocumented) - keySystem: string; -} - // Warning: (ae-missing-release-tag) "KeySystemFormats" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) diff --git a/src/controller/eme-controller.ts b/src/controller/eme-controller.ts index f2ae899098c..b7e8488f787 100644 --- a/src/controller/eme-controller.ts +++ b/src/controller/eme-controller.ts @@ -803,11 +803,6 @@ class EMEController extends Logger implements ComponentAPI { this.renewKeySession(context); } } - - this.hls.trigger(Events.KEY_STATUSES_CHANGED, { - keySystem: context.decryptdata.keyFormat, - keyStatuses: keySession.keyStatuses, - }); }); addEventListener(context.mediaKeysSession, 'message', onmessage); diff --git a/src/events.ts b/src/events.ts index 722749ad054..4a13a65adb9 100644 --- a/src/events.ts +++ b/src/events.ts @@ -40,7 +40,6 @@ import type { InterstitialsUpdatedData, KeyLoadedData, KeyLoadingData, - KeyStatusesChangedData, LevelLoadedData, LevelLoadingData, LevelPTSUpdatedData, @@ -221,8 +220,6 @@ export enum Events { PLAYOUT_LIMIT_REACHED = 'hlsPlayoutLimitReached', // Event DateRange cue "enter" event dispatched EVENT_CUE_ENTER = 'hlsEventCueEnter', - // HD DRM - KEY_STATUSES_CHANGED = 'hlsKeyStatusesChanged', } /** @@ -499,10 +496,6 @@ export interface HlsListeners { data: {}, ) => void; [Events.EVENT_CUE_ENTER]: (event: Events.EVENT_CUE_ENTER, data: {}) => void; - [Events.KEY_STATUSES_CHANGED]: ( - event: Events.KEY_STATUSES_CHANGED, - data: KeyStatusesChangedData, - ) => void; } export interface HlsEventEmitter { on( diff --git a/src/hls.ts b/src/hls.ts index 5ace8d8efb0..b0df771dac2 100644 --- a/src/hls.ts +++ b/src/hls.ts @@ -1417,7 +1417,6 @@ export type { BufferFlushingData, CuesParsedData, ErrorData, - KeyStatusesChangedData, FPSDropData, FPSDropLevelCappingData, FragBufferedData, diff --git a/src/types/events.ts b/src/types/events.ts index af29c26247f..d3f5943dd93 100644 --- a/src/types/events.ts +++ b/src/types/events.ts @@ -252,11 +252,6 @@ export interface LevelPTSUpdatedData { end: number; } -export interface KeyStatusesChangedData { - keySystem: string; - keyStatuses: MediaKeyStatusMap; -} - export interface AudioTrackSwitchingData extends MediaPlaylist {} export interface AudioTrackSwitchedData extends MediaPlaylist {}