diff --git a/.github/workflows/test-changed-auth.yml b/.github/workflows/test-changed-auth.yml index 67d16551b3e..6d23a74605b 100644 --- a/.github/workflows/test-changed-auth.yml +++ b/.github/workflows/test-changed-auth.yml @@ -102,3 +102,29 @@ jobs: run: xvfb-run yarn test:changed auth env: BROWSERS: 'Firefox' + + test-safari: + name: Test Auth on Safari If Changed + runs-on: macos-latest + + steps: + - name: Checkout Repo + uses: actions/checkout@v4 + with: + # This makes Actions fetch all Git history so run-changed script can diff properly. + fetch-depth: 0 + - name: Set up Node (20) + uses: actions/setup-node@v3 + with: + node-version: 20.x + - name: Test setup and yarn install + run: | + cp config/ci.config.json config/project.json + yarn + - name: build + run: yarn build:changed auth + - name: Run tests on auth changed packages + run: yarn test:changed auth + env: + BROWSERS: 'Safari' + diff --git a/.github/workflows/test-changed-firestore.yml b/.github/workflows/test-changed-firestore.yml index e148d164909..6e747a9f638 100644 --- a/.github/workflows/test-changed-firestore.yml +++ b/.github/workflows/test-changed-firestore.yml @@ -230,6 +230,60 @@ jobs: env: BROWSERS: 'Firefox' EXPERIMENTAL_MODE: true + + compat-test-safari: + name: Test Firestore Compatible on Firefox + # Whatever version of Firefox comes with 22.04 is causing Firefox + # startup to hang when launched by karma. Need to look further into + # why. + runs-on: macos-latest + needs: build + if: ${{ needs.build.outputs.changed == 'true'}} + steps: + - name: Set up Node (20) + uses: actions/setup-node@v3 + with: + node-version: 20.x + - name: Download build archive + uses: actions/download-artifact@v3 + with: + name: build.tar.gz + - name: Unzip build artifact + run: tar xf build.tar.gz + - name: Test setup and yarn install + run: cp config/ci.config.json config/project.json + - name: Run compat tests + run: cd packages/firestore-compat && yarn run test:ci + env: + BROWSERS: 'Safari' + + test-safari: + name: Test Firestore on Safari + strategy: + matrix: + test-name: ["test:browser", "test:lite:browser", "test:browser:prod:nameddb", "test:lite:browser:nameddb"] + runs-on: macos-latest + needs: build + if: ${{ needs.build.outputs.changed == 'true'}} + steps: + - name: Download build archive + uses: actions/download-artifact@v3 + with: + name: build.tar.gz + - name: Unzip build artifact + run: tar xf build.tar.gz + - name: Set up Node (20) + uses: actions/setup-node@v3 + with: + node-version: 20.x + - name: Test setup and yarn install + run: cp config/ci.config.json config/project.json + - name: Run tests + run: cd packages/firestore && yarn run ${{ matrix.test-name }} + env: + BROWSERS: 'Safari' + EXPERIMENTAL_MODE: true + # A job that fails if any required job in the test matrix fails, # to be used as a required check for merging. diff --git a/.github/workflows/test-changed.yml b/.github/workflows/test-changed.yml index 7da82df4a26..f1a982c5d64 100644 --- a/.github/workflows/test-changed.yml +++ b/.github/workflows/test-changed.yml @@ -78,3 +78,28 @@ jobs: run: xvfb-run yarn test:changed core env: BROWSERS: 'Firefox' + + test-safari: + name: Test Packages With Changed Files in Safari + runs-on: macos-latest + + steps: + - name: Checkout Repo + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Set up Node (20) + uses: actions/setup-node@v3 + with: + node-version: 20.x + - name: Test setup and yarn install + run: | + cp config/ci.config.json config/project.json + yarn + - name: build + run: yarn build + - name: Run tests on changed packages + run: npx lerna run test:browser --ignore @firebase/firestore --ignore @firebase/auth --ignore firebase-messaging-integration-test + env: + BROWSERS: 'Safari' + diff --git a/config/karma.base.js b/config/karma.base.js index 9740f844d91..0de2a72fea1 100644 --- a/config/karma.base.js +++ b/config/karma.base.js @@ -20,7 +20,66 @@ const path = require('path'); const webpackTestConfig = require('./webpack.test'); const { argv } = require('yargs'); +function determineBrowsers() { + const supportedBrowsers = ['ChromeHeadless', 'Safari', 'Firefox']; + + if (process.env.BROWSERS) { + const browsers = process.env.BROWSERS.split(','); + + const validBrowsers = browsers.filter(browser => + supportedBrowsers.includes(browser) + ); + if (validBrowsers.length === 0) { + console.error( + `The \'BROWSER\' environment variable was set, but no supported browsers were listed. The supported browsers are ${JSON.stringify( + supportedBrowsers + )}.` + ); + return []; + } + + if (browsers.includes('Safari') && process.platform !== 'darwin') { + console.error( + "The 'BROWSER' environment variable includes 'Safari', which is not supported on this platform. The only supported platform is darwin." + ); + return []; + } + + if (browsers.includes('Safari')) { + console.log( + "\x1b[38;2;255;165;0mSafari browser testing has been enabled. This requires Full Disk Access be granted to the executor. To grant Full Disk Access on macOS > 13, visit 'System Settings' > 'Privacy & Security' > 'Full Disk Access'.\x1b[0m" + ); // Log in orange + } + + return validBrowsers; + } else { + /** + * By default we only run the Chrome tests locally since the Safari launcher has some quirks + * that make local testing annoying: + * - Tabs from previous test runs are restored, causing the tests to be ran once on each tab. + * To prevent this, Safari has to be manually re-opened and then quit after every test run. + * - Full Disk Access has to be manually enabled + * + * Running the browser tests in Chrome should catch most browser bugs. If that's not the case, + * the bugs will be caught when the browser tests are ran on Safari in CI. + */ + console.log( + "The 'BROWSER' environment variable is undefined. Defaulting to 'ChromeHeadless'." + ); + return ['ChromeHeadless']; + } +} + const config = { + // See: https://karma-runner.github.io/6.4/config/plugins.html#loading-plugins + plugins: [ + // We use our own custom Safari launcher plugin since https://github.com/karma-runner/karma-safari-launcher + // does not work and is not maintained. + require('../scripts/ci-test/karmaSafariLauncher.js'), + // Include all other plugins from our npm modules + 'karma-*' + ], + // disable watcher autoWatch: false, @@ -57,10 +116,11 @@ const config = { // changes autoWatch: false, - // start these browsers - // available browser launchers: - // https://npmjs.org/browse/keyword/karma-launcher - browsers: process.env?.BROWSERS?.split(',') ?? ['ChromeHeadless'], + // Browsers to launch for testing + // To use a custom set of browsers, define the BROWSERS environment variable as a comma-seperated list. + // Supported browsers are 'ChromeHeadless', 'Safari', and 'Firefox'. + // See: https://karma-runner.github.io/6.4/config/browsers.html + browsers: determineBrowsers(), webpack: webpackTestConfig, diff --git a/package.json b/package.json index 32d8e78eeb9..083111ff4dc 100644 --- a/package.json +++ b/package.json @@ -120,7 +120,6 @@ "karma-firefox-launcher": "2.1.3", "karma-mocha": "2.0.1", "karma-mocha-reporter": "2.2.5", - "karma-safari-launcher": "1.0.0", "karma-sourcemap-loader": "0.4.0", "karma-spec-reporter": "0.0.36", "karma-summary-reporter": "3.1.1", @@ -159,6 +158,7 @@ "undici": "6.19.7", "watch": "1.0.2", "webpack": "5.76.0", + "rimraf": "6.0.1", "yargs": "17.7.2" } } diff --git a/packages/analytics/testing/integration-tests/integration.ts b/packages/analytics/testing/integration-tests/integration.ts index b920973a79e..cfdfe8d0c79 100644 --- a/packages/analytics/testing/integration-tests/integration.ts +++ b/packages/analytics/testing/integration-tests/integration.ts @@ -56,6 +56,18 @@ async function checkForEventCalls(retryCount = 0): Promise { describe('FirebaseAnalytics Integration Smoke Tests', () => { let app: FirebaseApp; + + // Clear cookies to prevent persistence issues across tests when run in non-headless browsers. + // This is required since gtag/logEvent behaviour is dependent on the session state; if an event has already been logged + // in a session, the event will be sent in the request payload instead of the query string. + beforeEach(() => { + document.cookie.split(';').forEach(cookie => { + document.cookie = cookie + .replace(/^ +/, '') + .replace(/=.*/, '=;expires=' + new Date().toUTCString() + ';path=/'); + }); + }); + describe('Using getAnalytics()', () => { afterEach(() => deleteApp(app)); it('logEvent() sends correct network request.', async () => { diff --git a/packages/auth-compat/karma.conf.js b/packages/auth-compat/karma.conf.js index f3f14e8d1b1..bd29576a4e3 100644 --- a/packages/auth-compat/karma.conf.js +++ b/packages/auth-compat/karma.conf.js @@ -23,7 +23,6 @@ const files = ['src/**/*.test.ts']; module.exports = function (config) { const karmaConfig = Object.assign({}, karmaBase, { - browsers: getTestBrowsers(argv), // files to load into karma files: getTestFiles(), preprocessors: { '**/*.ts': ['webpack', 'sourcemap'] }, @@ -56,14 +55,6 @@ function getTestFiles() { } } -function getTestBrowsers(argv) { - let browsers = ['ChromeHeadless']; - if (process.env?.BROWSERS && argv.unit) { - browsers = process.env?.BROWSERS?.split(','); - } - return browsers; -} - function getClientConfig() { if (!argv.integration) { return {}; diff --git a/packages/auth/karma.conf.js b/packages/auth/karma.conf.js index 1d28c329f55..e57b8fee421 100644 --- a/packages/auth/karma.conf.js +++ b/packages/auth/karma.conf.js @@ -21,7 +21,6 @@ const { argv } = require('yargs'); module.exports = function (config) { const karmaConfig = Object.assign({}, karmaBase, { - browsers: getTestBrowsers(argv), // files to load into karma files: getTestFiles(argv), // frameworks to use @@ -60,7 +59,7 @@ function getTestFiles(argv) { } else if (argv.cordova) { return ['src/platform_cordova/**/*.test.ts']; } else { - // For the catch-all yarn:test, ignore the phone integration test + // For the catch-all yarn browser:test, ignore the phone integration test return [ 'src/**/*.test.ts', 'test/helpers/**/*.test.ts', @@ -71,14 +70,6 @@ function getTestFiles(argv) { } } -function getTestBrowsers(argv) { - let browsers = ['ChromeHeadless']; - if (process.env?.BROWSERS && argv.unit) { - browsers = process.env?.BROWSERS?.split(','); - } - return browsers; -} - function getClientConfig(argv) { if (!argv.local) { return {}; diff --git a/packages/auth/package.json b/packages/auth/package.json index add928154b5..7aaa0e5f84b 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -93,11 +93,12 @@ "build:scripts": "tsc -moduleResolution node --module commonjs scripts/*.ts && ls scripts/*.js | xargs -I % sh -c 'terser % -o %'", "dev": "rollup -c -w", "test": "run-p --npm-path npm lint test:all", - "test:all": "run-p --npm-path npm test:browser:unit test:node:unit test:integration test:browser:integration:prodbackend", - "test:integration": "firebase emulators:exec --project emulatedproject --only auth \"run-s --npm-path npm test:browser:integration:local test:node:integration:local test:webdriver\"", + "test:all": "run-p --npm-path npm test:browser:unit test:node:unit test:integration", + "test:integration": "firebase emulators:exec --project emulatedproject --only auth \"run-s --npm-path npm test:browser:integration:local test:browser:integration:prodbackend test:node:integration:local test:webdriver\"", "test:ci": "node ../../scripts/run_tests_in_ci.js -s test:all", "test:integration:local": "run-s --npm-path npm test:node:integration:local test:browser:integration:local test:webdriver", - "test:browser": "karma start --single-run --local", + "test:browser": "karma start --single-run", + "test:browser:local": "karma start --single-run --local", "test:browser:unit": "karma start --single-run --unit", "test:browser:integration": "karma start --single-run --integration", "test:browser:integration:local": "karma start --single-run --integration --local", diff --git a/scripts/ci-test/karmaSafariLauncher.js b/scripts/ci-test/karmaSafariLauncher.js new file mode 100644 index 00000000000..2b6eab101dc --- /dev/null +++ b/scripts/ci-test/karmaSafariLauncher.js @@ -0,0 +1,102 @@ +/** + * @license + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// See: https://karma-runner.github.io/6.4/dev/plugins.html +const fs = require('fs'); +const os = require('os'); +const path = require('path'); +const rimraf = require('rimraf'); + +var SafariBrowser = function (baseBrowserDecorator) { + baseBrowserDecorator(this); + + // This is the only directory that we can open files from in Safari without causing a + // dialog to open. + // Reading and writing to this directory requires Full Disk access, which can be granted on macOS > 13 at + // 'System Settings' > 'Privacy & Security' > 'Full Disk Access'. + const SAFARI_DATA_DIR = path.join( + os.homedir(), + 'Library/Containers/com.apple.Safari/Data' + ); + const HTML_TRAMPOLINE_PATH = path.join(SAFARI_DATA_DIR, 'redirect.html'); + + function clearSafariData() { + const directoriesToClear = [ + path.join(SAFARI_DATA_DIR, 'Library/Caches'), + path.join(SAFARI_DATA_DIR, 'Library/Cookies'), + path.join(SAFARI_DATA_DIR, 'Library/Safari'), + path.join(SAFARI_DATA_DIR, 'Library/WebKit') + ]; + + directoriesToClear.forEach(dir => { + if (fs.existsSync(dir)) { + rimraf.sync(dir); + } + }); + } + + this._start = function (url) { + var self = this; + + // Clear cookies and other persisted data before starting the browser + clearSafariData(); + + // HTML trampoline to redirect Safari to the URL where the tests are hosted. + // This is necessary because Safari assumes the location we pass as an argument is a path + // in the Safari Data directory. So, if we were to pass 'http://localhost:3000' as an argument, + // Safari would attempt to open + // 'file:///Users//Library/Containers/com.apple.Safari/Data/http:/localhost:3000'. + const htmlTrampoline = ` + + + + + + Redirect + + + +

If you are not redirected automatically, please click here.

+ + + `; + + fs.writeFile(HTML_TRAMPOLINE_PATH, htmlTrampoline, function (err) { + if (err) { + console.error('Error writing file:', err); + return; + } + + // Open the HTMl trampoline in Safari + self._execCommand(self._getCommand(), [HTML_TRAMPOLINE_PATH]); + }); + }; +}; + +SafariBrowser.prototype = { + name: 'Safari', + DEFAULT_CMD: { + darwin: '/Applications/Safari.app/Contents/MacOS/Safari' + }, + ENV_CMD: 'SAFARI_BIN' +}; + +SafariBrowser.$inject = ['baseBrowserDecorator']; + +module.exports = { + 'launcher:Safari': ['type', SafariBrowser] +}; diff --git a/scripts/ci-test/test_changed.ts b/scripts/ci-test/test_changed.ts index ed60cdc8e4a..1ea910de7d1 100644 --- a/scripts/ci-test/test_changed.ts +++ b/scripts/ci-test/test_changed.ts @@ -59,7 +59,12 @@ async function runTests(config: TestConfig) { process.exit(0); } - const lernaCmd = ['lerna', 'run', '--concurrency', '4']; + const lernaCmd = ['lerna', 'run']; + // Since the tests run in Safari are not headless, they share some global state when + // run concurrently, so we should run them serially. + if (process.env?.BROWSERS?.includes('Safari')) { + lernaCmd.push('--concurrency', '1'); + } console.log(chalk`{blue Running tests in:}`); for (const task of testTasks) { if (task.reason === TestReason.Changed) { diff --git a/yarn.lock b/yarn.lock index 477e784871e..0ab24b1dc08 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1773,6 +1773,18 @@ resolved "https://registry.npmjs.org/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz" @@ -2871,6 +2883,11 @@ resolved "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.3.1.tgz" integrity sha512-wU5J8rUoo32oSef/rFpOT1HIjLjAv3qIDHkw1QIhODV3OpAVHi5oVzlouozg9obUmZKtbZ0qUe/m7FP0y0yBzA== +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + "@pnpm/config.env-replace@^1.1.0": version "1.1.0" resolved "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz" @@ -4245,6 +4262,11 @@ ansi-regex@^5.0.0, ansi-regex@^5.0.1: resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== +ansi-regex@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" + integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== + ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz" @@ -4269,6 +4291,11 @@ ansi-styles@^5.0.0: resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + ansi-wrap@0.1.0, ansi-wrap@^0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz" @@ -7096,6 +7123,11 @@ each-props@^1.3.2: is-plain-object "^2.0.1" object.defaults "^1.1.0" +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz" @@ -7172,6 +7204,11 @@ emoji-regex@^8.0.0: resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + emojis-list@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz" @@ -8498,6 +8535,14 @@ foreground-child@^2.0.0: cross-spawn "^7.0.0" signal-exit "^3.0.2" +foreground-child@^3.1.0: + version "3.3.0" + resolved "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz#0ac8644c06e431439f8561db8ecf29a7b5519c77" + integrity sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz" @@ -9056,6 +9101,18 @@ glob@7.2.3, glob@~7.2.0: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^11.0.0: + version "11.0.0" + resolved "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz#6031df0d7b65eaa1ccb9b29b5ced16cea658e77e" + integrity sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g== + dependencies: + foreground-child "^3.1.0" + jackspeak "^4.0.1" + minimatch "^10.0.0" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^2.0.0" + glob@^8.0.1: version "8.1.0" resolved "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz" @@ -10822,6 +10879,15 @@ istextorbinary@^3.0.0: binaryextensions "^2.2.0" textextensions "^3.2.0" +jackspeak@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.1.tgz#9fca4ce961af6083e259c376e9e3541431f5287b" + integrity sha512-cub8rahkh0Q/bw1+GxP7aeSe29hHHn2V4m29nnDlvCdlgU+3UGxkZp7Z53jLUdpX3jdTO0nJZUDl3xvbWc2Xog== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + jasmine-core@~2.8.0: version "2.8.0" resolved "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz" @@ -11332,11 +11398,6 @@ karma-mocha@2.0.1: dependencies: minimist "^1.2.3" -karma-safari-launcher@1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/karma-safari-launcher/-/karma-safari-launcher-1.0.0.tgz" - integrity sha1-lpgqLMR9BmquccVTursoMZEVos4= - karma-sourcemap-loader@0.4.0: version "0.4.0" resolved "https://registry.npmjs.org/karma-sourcemap-loader/-/karma-sourcemap-loader-0.4.0.tgz" @@ -12005,6 +12066,11 @@ lowercase-keys@^2.0.0: resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz" integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== +lru-cache@^11.0.0: + version "11.0.0" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.0.tgz#15d93a196f189034d7166caf9fe55e7384c98a21" + integrity sha512-Qv32eSV1RSCfhY3fpPE2GNZ8jgM9X7rdAfemLWqTUxwiyIC4jJ6Sy0fZ8H+oLWevO6i4/bizg7c8d8i6bxrzbA== + lru-cache@^4.0.1: version "4.1.5" resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz" @@ -12448,6 +12514,13 @@ minimatch@9.0.3: dependencies: brace-expansion "^2.0.1" +minimatch@^10.0.0: + version "10.0.1" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz#ce0521856b453c86e25f2c4c0d03e6ff7ddc440b" + integrity sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ== + dependencies: + brace-expansion "^2.0.1" + minimatch@^3.0.0, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz" @@ -12587,6 +12660,11 @@ minipass@^3.1.6: dependencies: yallist "^4.0.0" +minipass@^7.1.2: + version "7.1.2" + resolved "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + minizlib@^1.3.3: version "1.3.3" resolved "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz" @@ -13729,6 +13807,11 @@ package-hash@^4.0.0: lodash.flattendeep "^4.4.0" release-zalgo "^1.0.0" +package-json-from-dist@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz#e501cd3094b278495eb4258d4c9f6d5ac3019f00" + integrity sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw== + package-name-regex@~2.0.6: version "2.0.6" resolved "https://registry.npmjs.org/package-name-regex/-/package-name-regex-2.0.6.tgz" @@ -13966,6 +14049,14 @@ path-root@^0.1.1: dependencies: path-root-regex "^0.1.0" +path-scurry@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz#9f052289f23ad8bf9397a2a0425e7b8615c58580" + integrity sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg== + dependencies: + lru-cache "^11.0.0" + minipass "^7.1.2" + path-strip-sep@^1.0.17: version "1.0.17" resolved "https://registry.npmjs.org/path-strip-sep/-/path-strip-sep-1.0.17.tgz" @@ -15212,6 +15303,14 @@ rfdc@^1.3.0: resolved "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz" integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== +rimraf@6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz#ffb8ad8844dd60332ab15f52bc104bc3ed71ea4e" + integrity sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A== + dependencies: + glob "^11.0.0" + package-json-from-dist "^1.0.0" + rimraf@^2.2.8, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz" @@ -15780,6 +15879,11 @@ signal-exit@^3.0.7: resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + simple-git@3.24.0: version "3.24.0" resolved "https://registry.npmjs.org/simple-git/-/simple-git-3.24.0.tgz#33a8c88dc6fa74e53eaf3d6bfc27d0182a49ec00" @@ -16308,6 +16412,15 @@ string-argv@~0.3.1: resolved "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz" integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg== +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^1.0.1, string-width@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz" @@ -16343,6 +16456,15 @@ string-width@^3.0.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + string.prototype.padend@^3.0.0: version "3.1.2" resolved "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.2.tgz" @@ -16433,6 +16555,13 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" @@ -16461,6 +16590,13 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" +strip-ansi@^7.0.1: + version "7.1.0" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + strip-bom-string@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz" @@ -18170,6 +18306,15 @@ workerpool@6.2.0: resolved "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz" integrity sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A== +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^2.0.0: version "2.1.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz" @@ -18204,6 +18349,15 @@ wrap-ansi@^7.0.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + wrappy@1: version "1.0.2" resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"