From c90d1e9f6559cadff1cc02740bffb923e1a6a847 Mon Sep 17 00:00:00 2001 From: Jameson Date: Wed, 21 Oct 2026 14:54:11 -0600 Subject: [PATCH 1/3] CT-2322 Make the example app's proxy URL configurable at build time The example app hardcoded the SSO proxy at 10.0.2.2:8089 (Android) or localhost:8089 in six places. Move it to src/shared/proxy.ts and let EXPO_PUBLIC_PROXY_BASE_URL override it, so a CI build can point a Sauce Labs device at the proxy through Sauce Connect. Local development keeps the same defaults. Release builds bundle the JS (no Metro), and Android blocks cleartext http in release builds by default, so enable usesCleartextTraffic via expo-build-properties. Only the example app is affected. --- example/app.json | 8 ++++ example/package-lock.json | 64 ++++++++++++++++++++++++++++++++ example/package.json | 5 ++- example/src/app/budgets.tsx | 6 +-- example/src/app/goals.tsx | 6 +-- example/src/app/pulse.tsx | 6 +-- example/src/app/spending.tsx | 6 +-- example/src/app/transactions.tsx | 6 +-- example/src/shared/api.ts | 7 +--- example/src/shared/proxy.ts | 10 +++++ 10 files changed, 97 insertions(+), 27 deletions(-) create mode 100644 example/src/shared/proxy.ts diff --git a/example/app.json b/example/app.json index 89e12a2..d76aa3e 100644 --- a/example/app.json +++ b/example/app.json @@ -45,6 +45,14 @@ "backgroundColor": "#000000" } } + ], + [ + "expo-build-properties", + { + "android": { + "usesCleartextTraffic": true + } + } ] ], "experiments": { diff --git a/example/package-lock.json b/example/package-lock.json index 1e2e9ca..8433dba 100644 --- a/example/package-lock.json +++ b/example/package-lock.json @@ -14,6 +14,7 @@ "@react-navigation/elements": "^2.6.3", "@react-navigation/native": "^7.1.8", "expo": "~54.0.29", + "expo-build-properties": "~1.0.10", "expo-constants": "~18.0.12", "expo-font": "~14.0.10", "expo-haptics": "~15.0.8", @@ -6140,6 +6141,53 @@ "react-native": "*" } }, + "node_modules/expo-build-properties": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/expo-build-properties/-/expo-build-properties-1.0.10.tgz", + "integrity": "sha512-mFCZbrbrv0AP5RB151tAoRzwRJelqM7bCJzCkxpu+owOyH+p/rFC/q7H5q8B9EpVWj8etaIuszR+gKwohpmu1Q==", + "license": "MIT", + "dependencies": { + "ajv": "^8.11.0", + "semver": "^7.6.0" + }, + "peerDependencies": { + "expo": "*" + } + }, + "node_modules/expo-build-properties/node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/expo-build-properties/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/expo-build-properties/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/expo-constants": { "version": "18.0.12", "resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-18.0.12.tgz", @@ -6783,6 +6831,22 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-uri": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.9.tgz", + "integrity": "sha512-hS/0Wt3Ya043oKXs0sIxrMpKQITXhMLIrVAKKXi4gozGC9v7YRWVnXIZzjNPTO02M9BdxLCWqK1yRXgNziVyJQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fb-watchman": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", diff --git a/example/package.json b/example/package.json index 574f289..b38523c 100644 --- a/example/package.json +++ b/example/package.json @@ -12,12 +12,13 @@ "lint": "expo lint" }, "dependencies": { - "@mxenabled/react-native-widget-sdk": "file:..", "@expo/vector-icons": "^15.0.3", + "@mxenabled/react-native-widget-sdk": "file:..", "@react-navigation/bottom-tabs": "^7.4.0", "@react-navigation/elements": "^2.6.3", "@react-navigation/native": "^7.1.8", "expo": "~54.0.29", + "expo-build-properties": "~1.0.10", "expo-constants": "~18.0.12", "expo-font": "~14.0.10", "expo-haptics": "~15.0.8", @@ -33,11 +34,11 @@ "react-dom": "19.1.0", "react-native": "0.81.5", "react-native-gesture-handler": "~2.28.0", - "react-native-webview": "^13.0.0", "react-native-reanimated": "~4.1.1", "react-native-safe-area-context": "~5.6.0", "react-native-screens": "~4.16.0", "react-native-web": "~0.21.0", + "react-native-webview": "^13.0.0", "react-native-worklets": "0.5.1", "react-router-native": "^6.30.0" }, diff --git a/example/src/app/budgets.tsx b/example/src/app/budgets.tsx index 9078e40..95af013 100644 --- a/example/src/app/budgets.tsx +++ b/example/src/app/budgets.tsx @@ -1,11 +1,9 @@ import React from "react" -import { StyleSheet, Platform } from "react-native" +import { StyleSheet } from "react-native" import { SafeAreaView } from "react-native-safe-area-context" import { BudgetsWidget } from "@mxenabled/react-native-widget-sdk" - -const baseUrl = Platform.OS === "android" ? "http://10.0.2.2:8089" : "http://localhost:8089" -const proxy = `${baseUrl}/user/widget_urls` +import { proxy } from "../shared/proxy" const styles = StyleSheet.create({ page: { diff --git a/example/src/app/goals.tsx b/example/src/app/goals.tsx index bbc09f7..6c9a2af 100644 --- a/example/src/app/goals.tsx +++ b/example/src/app/goals.tsx @@ -1,11 +1,9 @@ import React from "react" -import { StyleSheet, Platform } from "react-native" +import { StyleSheet } from "react-native" import { SafeAreaView } from "react-native-safe-area-context" import { GoalsWidget } from "@mxenabled/react-native-widget-sdk" - -const baseUrl = Platform.OS === "android" ? "http://10.0.2.2:8089" : "http://localhost:8089" -const proxy = `${baseUrl}/user/widget_urls` +import { proxy } from "../shared/proxy" const styles = StyleSheet.create({ page: { diff --git a/example/src/app/pulse.tsx b/example/src/app/pulse.tsx index 315ab94..47a01bc 100644 --- a/example/src/app/pulse.tsx +++ b/example/src/app/pulse.tsx @@ -1,10 +1,8 @@ import React from "react" -import { StyleSheet, Platform } from "react-native" +import { StyleSheet } from "react-native" import { SafeAreaView } from "react-native-safe-area-context" import { PulseWidget } from "@mxenabled/react-native-widget-sdk" - -const baseUrl = Platform.OS === "android" ? "http://10.0.2.2:8089" : "http://localhost:8089" -const proxy = `${baseUrl}/user/widget_urls` +import { proxy } from "../shared/proxy" const styles = StyleSheet.create({ page: { diff --git a/example/src/app/spending.tsx b/example/src/app/spending.tsx index f06f8fa..d54c571 100644 --- a/example/src/app/spending.tsx +++ b/example/src/app/spending.tsx @@ -1,10 +1,8 @@ import React from "react" -import { StyleSheet, Platform } from "react-native" +import { StyleSheet } from "react-native" import { SafeAreaView } from "react-native-safe-area-context" import { SpendingWidget } from "@mxenabled/react-native-widget-sdk" - -const baseUrl = Platform.OS === "android" ? "http://10.0.2.2:8089" : "http://localhost:8089" -const proxy = `${baseUrl}/user/widget_urls` +import { proxy } from "../shared/proxy" const styles = StyleSheet.create({ page: { diff --git a/example/src/app/transactions.tsx b/example/src/app/transactions.tsx index 3e7f591..3202e4e 100644 --- a/example/src/app/transactions.tsx +++ b/example/src/app/transactions.tsx @@ -1,10 +1,8 @@ import React from "react" -import { StyleSheet, Platform } from "react-native" +import { StyleSheet } from "react-native" import { SafeAreaView } from "react-native-safe-area-context" import { TransactionsWidget } from "@mxenabled/react-native-widget-sdk" - -const baseUrl = Platform.OS === "android" ? "http://10.0.2.2:8089" : "http://localhost:8089" -const proxy = `${baseUrl}/user/widget_urls` +import { proxy } from "../shared/proxy" const styles = StyleSheet.create({ page: { diff --git a/example/src/shared/api.ts b/example/src/shared/api.ts index 0e22201..f8ae4de 100644 --- a/example/src/shared/api.ts +++ b/example/src/shared/api.ts @@ -1,7 +1,4 @@ -import { Platform } from "react-native" - -const baseUrl = Platform.OS === "android" ? "http://10.0.2.2:8089" : "http://localhost:8089" -const proxyUrl = `${baseUrl}/user/widget_urls` +import { proxy } from "./proxy" interface WidgetUrlResponse { widget_url: { @@ -27,7 +24,7 @@ export const fetchConnectWidgetUrl = async (clientRedirectUrl: string) => { }) try { - const response = await fetch(proxyUrl, { + const response = await fetch(proxy, { body, headers, method, diff --git a/example/src/shared/proxy.ts b/example/src/shared/proxy.ts new file mode 100644 index 0000000..e723c1e --- /dev/null +++ b/example/src/shared/proxy.ts @@ -0,0 +1,10 @@ +import { Platform } from "react-native" + +// The Android emulator reaches the host machine through 10.0.2.2. CI builds set +// EXPO_PUBLIC_PROXY_BASE_URL so a Sauce Labs device can reach the proxy through +// Sauce Connect. Expo inlines EXPO_PUBLIC_* variables when it bundles the JS. +const defaultBaseUrl = Platform.OS === "android" ? "http://10.0.2.2:8089" : "http://localhost:8089" + +export const proxyBaseUrl = process.env.EXPO_PUBLIC_PROXY_BASE_URL || defaultBaseUrl + +export const proxy = `${proxyBaseUrl}/user/widget_urls` From 8fc51425794b5a526d547c90d4e729db6a196654 Mon Sep 17 00:00:00 2001 From: Jameson Date: Wed, 21 Oct 2026 14:58:26 -0600 Subject: [PATCH 2/3] CT-2322 Run the Maestro flows on Sauce Labs in GitHub Actions Add a workflow that builds the example app as a release APK, starts the SSO proxy on the runner, opens a Sauce Connect tunnel and runs example/maestro on a Sauce Labs Android emulator (or a real device when run manually) with maestro-runner, Sauce's community integration for Maestro flows. The APK is built in its own job, which needs no secrets, so build breaks show up on every PR even while the Sauce Labs job is skipped for lack of secrets (forks, Dependabot, or before they're configured). The runner image already has the Android SDK; setup-android@v3 fails on the removed 'tools' package, so it isn't used. The app reaches the proxy through an /etc/hosts alias, because Sauce Connect can't proxy localhost to real devices, with proxyLocalhost: allow because the alias resolves to 127.0.0.1. E2E_TESTING.md lists the required secrets. --- .github/workflows/e2e-sauce-labs.yml | 243 +++++++++++++++++++++++++ E2E_TESTING.md | 52 ++++++ example/sauce/android-emulator.json | 12 ++ example/sauce/android-real-device.json | 9 + 4 files changed, 316 insertions(+) create mode 100644 .github/workflows/e2e-sauce-labs.yml create mode 100644 example/sauce/android-emulator.json create mode 100644 example/sauce/android-real-device.json diff --git a/.github/workflows/e2e-sauce-labs.yml b/.github/workflows/e2e-sauce-labs.yml new file mode 100644 index 0000000..f3b2751 --- /dev/null +++ b/.github/workflows/e2e-sauce-labs.yml @@ -0,0 +1,243 @@ +name: E2E tests (Sauce Labs) + +# Builds the example app as a release APK and runs the Maestro flows in +# example/maestro on a Sauce Labs device with maestro-runner. The SSO proxy +# runs on this runner, and the device reaches it through a Sauce Connect +# tunnel. See E2E_TESTING.md for the required secrets. + +on: + pull_request: + paths: + - "src/**" + - "example/**" + - "package.json" + - "package-lock.json" + - ".github/workflows/e2e-sauce-labs.yml" + workflow_dispatch: + inputs: + target: + description: Sauce Labs device type + type: choice + options: + - android-emulator + - android-real-device + default: android-emulator + +concurrency: + group: e2e-sauce-labs-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +env: + # Sauce Connect can't proxy "localhost" to real devices, so the app talks to + # this alias, which the test job points at 127.0.0.1 in /etc/hosts. + PROXY_HOST: sdk-e2e-proxy + PROXY_PORT: "8089" + +jobs: + check-secrets: + runs-on: ubuntu-latest + outputs: + run: ${{ steps.check.outputs.run }} + steps: + # PRs from forks and Dependabot don't get repository secrets, so skip + # the run instead of failing it. + - id: check + env: + SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }} + SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }} + MX_CLIENT_ID: ${{ secrets.MX_CLIENT_ID }} + MX_API_KEY: ${{ secrets.MX_API_KEY }} + run: | + missing=() + for name in SAUCE_USERNAME SAUCE_ACCESS_KEY MX_CLIENT_ID MX_API_KEY; do + [ -n "${!name}" ] || missing+=("$name") + done + if [ ${#missing[@]} -gt 0 ]; then + echo "::notice::Skipping Sauce Labs E2E tests, missing secrets: ${missing[*]}" + echo "run=false" >> "$GITHUB_OUTPUT" + else + echo "run=true" >> "$GITHUB_OUTPUT" + fi + + # Doesn't need secrets, so it runs on every PR and catches build breaks + # even when the Sauce Labs tests are skipped. + build-android: + runs-on: ubuntu-latest + timeout-minutes: 45 + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + cache-dependency-path: | + package-lock.json + example/package-lock.json + + - uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: "17" + + # ubuntu-latest already has the Android SDK (ANDROID_HOME). Gradle installs + # anything missing, like the NDK version React Native pins. + + - uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: gradle-${{ runner.os }}-${{ hashFiles('example/package-lock.json', 'example/app.json') }} + restore-keys: gradle-${{ runner.os }}- + + - name: Install SDK dependencies + run: npm ci + + - name: Build SDK + run: npm run build + + - name: Install example app dependencies + working-directory: example + run: npm ci + + - name: Build release APK + working-directory: example + env: + EXPO_PUBLIC_PROXY_BASE_URL: http://${{ env.PROXY_HOST }}:${{ env.PROXY_PORT }} + run: | + npx expo prebuild --platform android --no-install + cd android + ./gradlew assembleRelease --no-daemon + + - uses: actions/upload-artifact@v4 + with: + name: example-app-release-apk + path: example/android/app/build/outputs/apk/release/app-release.apk + retention-days: 7 + if-no-files-found: error + + test-android: + needs: [check-secrets, build-android] + if: needs.check-secrets.outputs.run == 'true' + runs-on: ubuntu-latest + timeout-minutes: 45 + + env: + TARGET: ${{ inputs.target || 'android-emulator' }} + SAUCE_REGION: ${{ vars.SAUCE_REGION || 'us-west-1' }} + SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }} + SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }} + MAESTRO_RUNNER_VERSION: 1.1.29 + RUN_NAME: rn-sdk-e2e-${{ github.run_id }}-${{ github.run_attempt }} + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + # For the SSO proxy (@mxenabled/sso-api-proxy, a dev dependency). + - name: Install SDK dependencies + run: npm ci + + - uses: actions/download-artifact@v4 + with: + name: example-app-release-apk + path: ${{ runner.temp }}/apk + + - name: Start SSO proxy + env: + MX_CLIENT_ID: ${{ secrets.MX_CLIENT_ID }} + MX_API_KEY: ${{ secrets.MX_API_KEY }} + MX_API_HOST: ${{ vars.MX_API_HOST || 'https://int-api.mx.com' }} + MX_DEFAULT_USER_GUID: ${{ vars.MX_DEFAULT_USER_GUID || 'USR-e7353471-51d7-447f-b621-db255148a10c' }} + run: | + echo "127.0.0.1 $PROXY_HOST" | sudo tee -a /etc/hosts + # "$RUNNER_TEMP/sso-proxy.log" 2>&1 & + + # Wait until the proxy can create a widget URL, which also checks the credentials. + for _ in $(seq 1 30); do + if curl -sf -o /dev/null -X POST "http://$PROXY_HOST:$PROXY_PORT/user/widget_urls" \ + -H "Content-Type: application/json" \ + -d '{"widget_url":{"widget_type":"connect_widget","is_mobile_webview":true}}'; then + echo "SSO proxy is up" + exit 0 + fi + sleep 2 + done + echo "::error::SSO proxy didn't return a widget URL" + cat "$RUNNER_TEMP/sso-proxy.log" + exit 1 + + - name: Start Sauce Connect tunnel + uses: saucelabs/sauce-connect-action@v3 + with: + username: ${{ secrets.SAUCE_USERNAME }} + accessKey: ${{ secrets.SAUCE_ACCESS_KEY }} + region: ${{ env.SAUCE_REGION }} + tunnelName: ${{ env.RUN_NAME }} + # The proxy alias resolves to 127.0.0.1, which Sauce Connect 5 blocks by default. + proxyLocalhost: allow + + - name: Upload APK to Sauce Labs + id: upload + run: | + app_name="$RUN_NAME.apk" + curl -fsS -u "$SAUCE_USERNAME:$SAUCE_ACCESS_KEY" \ + -X POST "https://api.$SAUCE_REGION.saucelabs.com/v1/storage/upload" \ + -F "payload=@$RUNNER_TEMP/apk/app-release.apk" \ + -F "name=$app_name" \ + -o "$RUNNER_TEMP/upload.json" + echo "file_id=$(jq -r '.item.id' "$RUNNER_TEMP/upload.json")" >> "$GITHUB_OUTPUT" + + jq --arg app "$app_name" --arg build "$RUN_NAME" --arg tunnel "$RUN_NAME" \ + '. + {"appium:app": ("storage:filename=" + $app)} | ."sauce:options" += {build: $build, tunnelName: $tunnel}' \ + "example/sauce/$TARGET.json" > "$RUNNER_TEMP/caps.json" + cat "$RUNNER_TEMP/caps.json" + + - name: Run Maestro flows on Sauce Labs + run: | + # Real devices share the mobile team's concurrency, so use one at a time. + parallel=2 + [ "$TARGET" = "android-real-device" ] && parallel=1 + + npx -y "maestro-runner@$MAESTRO_RUNNER_VERSION" \ + --driver appium \ + --appium-url "https://$SAUCE_USERNAME:$SAUCE_ACCESS_KEY@ondemand.$SAUCE_REGION.saucelabs.com/wd/hub" \ + --caps "$RUNNER_TEMP/caps.json" \ + test --parallel "$parallel" --output maestro-report --flatten example/maestro/ + + - name: Remove credentials from the report + if: always() + run: | + # maestro-runner writes the Appium URL, which includes the access key, + # into its log and session files. Don't publish them. + [ -d maestro-report ] || exit 0 + find maestro-report \( -name 'maestro-runner.log' -o -name '*session*.json' \) -delete + grep -rlF -- "$SAUCE_ACCESS_KEY" maestro-report | xargs -r rm -f + + - name: Upload Maestro report + if: always() + uses: actions/upload-artifact@v4 + with: + name: maestro-report-${{ env.TARGET }} + path: maestro-report/ + retention-days: 14 + if-no-files-found: ignore + + - name: SSO proxy log + if: failure() + run: cat "$RUNNER_TEMP/sso-proxy.log" || true + + - name: Delete APK from Sauce Labs + if: always() && steps.upload.outputs.file_id != '' && steps.upload.outputs.file_id != 'null' + run: | + curl -fsS -o /dev/null -u "$SAUCE_USERNAME:$SAUCE_ACCESS_KEY" \ + -X DELETE "https://api.$SAUCE_REGION.saucelabs.com/v1/storage/files/${{ steps.upload.outputs.file_id }}" || true diff --git a/E2E_TESTING.md b/E2E_TESTING.md index 9658811..2defbcb 100644 --- a/E2E_TESTING.md +++ b/E2E_TESTING.md @@ -21,3 +21,55 @@ We use Maestro for E2E testing. # Run from the example folder npm run e2e ``` + +The example app talks to the SSO proxy started by `npm run dev:dependencies` (see +[DEVELOPMENT.md](DEVELOPMENT.md)). To point it somewhere else, set +`EXPO_PUBLIC_PROXY_BASE_URL` when you build or start the app, for example +`EXPO_PUBLIC_PROXY_BASE_URL=http://192.168.1.20:8089 npm run android:e2e`. + +## CI (Sauce Labs) + +[`.github/workflows/e2e-sauce-labs.yml`](.github/workflows/e2e-sauce-labs.yml) runs the same flows on a +Sauce Labs device for pull requests that touch `src/`, `example/` or the package files. You can also run +it from the Actions tab, where you can choose between an Android emulator and an Android real device. + +What it does: + +1. Builds the SDK and the example app as a release APK, so the JS is bundled and no Metro server is needed. + The app is built with `EXPO_PUBLIC_PROXY_BASE_URL=http://sdk-e2e-proxy:8089`. This job needs no secrets + and always runs. +2. Starts the SSO proxy on the runner, with `sdk-e2e-proxy` pointed at `127.0.0.1` in `/etc/hosts`. +3. Opens a [Sauce Connect](https://docs.saucelabs.com/secure-connections/sauce-connect-5/) tunnel, so the + device's requests to `sdk-e2e-proxy` reach the runner. Sauce Connect can't proxy `localhost` to real + devices, which is why the alias is needed. +4. Uploads the APK to Sauce Labs App Storage, then runs `example/maestro/` with + [maestro-runner](https://docs.saucelabs.com/basics/community-frameworks/maestro/), which runs Maestro + flows through Appium. Device capabilities are in `example/sauce/`. +5. Uploads the HTML and JUnit report as the `maestro-report-*` artifact, with files containing the Sauce + access key removed, and deletes the APK from App Storage. + +Each flow shows up in Sauce Labs under **Automated > Test Results** (emulators) or **Real Devices**, in a +build named `rn-sdk-e2e--`, with video and logs. + +### Required configuration + +Repository secrets (Settings > Secrets and variables > Actions): + +| Secret | Value | +| ------------------ | ----------------------------------------------------------------------------------------------------- | +| `SAUCE_USERNAME` | Sauce Labs username | +| `SAUCE_ACCESS_KEY` | Sauce Labs access key | +| `MX_CLIENT_ID` | Client ID of the E2E test client (see [`.mx-sso-proxy-rc.example.yml`](.mx-sso-proxy-rc.example.yml)) | +| `MX_API_KEY` | API key of the same client | + +If any of them is missing, the workflow still builds the APK but skips the Sauce Labs tests and passes, with +a notice saying which secrets are missing. That is also what happens on pull requests from forks and from +Dependabot, which don't get repository secrets. + +Optional repository variables: + +| Variable | Default | +| ---------------------- | ------------------------------------------ | +| `SAUCE_REGION` | `us-west-1` | +| `MX_API_HOST` | `https://int-api.mx.com` | +| `MX_DEFAULT_USER_GUID` | `USR-e7353471-51d7-447f-b621-db255148a10c` | diff --git a/example/sauce/android-emulator.json b/example/sauce/android-emulator.json new file mode 100644 index 0000000..3b971e6 --- /dev/null +++ b/example/sauce/android-emulator.json @@ -0,0 +1,12 @@ +{ + "platformName": "Android", + "appium:automationName": "UiAutomator2", + "appium:deviceName": "Google Pixel 9 Emulator", + "appium:platformVersion": "16.0", + "appium:appPackage": "com.anonymous.example", + "appium:appActivity": ".MainActivity", + "appium:appWaitActivity": "*", + "sauce:options": { + "appiumVersion": "2.11.0" + } +} diff --git a/example/sauce/android-real-device.json b/example/sauce/android-real-device.json new file mode 100644 index 0000000..cc7a641 --- /dev/null +++ b/example/sauce/android-real-device.json @@ -0,0 +1,9 @@ +{ + "platformName": "Android", + "appium:automationName": "UiAutomator2", + "appium:deviceName": "Google.*|Samsung.*", + "appium:platformVersion": "^1[4-6].*", + "sauce:options": { + "appiumVersion": "latest" + } +} From a7e98f69636053ce7230d4a9323f31af2d1a9382 Mon Sep 17 00:00:00 2001 From: Jameson Date: Wed, 21 Oct 2026 15:30:16 -0600 Subject: [PATCH 3/3] CT-2322 Harden the Sauce Labs workflow after review Credentials - Redact the Sauce access key in the Maestro report instead of deleting files, fail the step if any copy is left, and only upload the report when that step succeeded. The old scrub could fail open and threw away maestro-runner.log, the main debugging file. - Pass the Sauce and MX secrets only to the steps that use them, and pin saucelabs/sauce-connect-action to a SHA since it receives the key. - Print only request lines from the SSO proxy log; its crash output includes the client credentials. - Gitignore every .mx-sso-proxy-rc* except the example file. Reliability - Pin the SSO proxy and maestro-runner in example/sauce/package.json with a lockfile, installed with --ignore-scripts, instead of a full root npm ci plus npx. - Restart the SSO proxy when it exits: version 1.3.4 dies on the first MX API error. Give the readiness probe a per-request timeout. - Only route the proxy alias through the tunnel (tunnelDomains). - Build only the x86_64 and arm64-v8a ABIs; a cold 4-ABI build took 21 minutes in CI. - Run on pushes to v4 and master so the caches warm for PRs; include the SDK build config in the path filter; keep manual runs for different targets from cancelling each other; time-box the Maestro step; turn off maestro-runner telemetry. Docs: how to run it manually (not from the Actions tab until the file is on master), valid SAUCE_REGION values, and a "When it fails" section. --- .github/workflows/e2e-sauce-labs.yml | 135 +- .gitignore | 4 +- E2E_TESTING.md | 82 +- example/sauce/package-lock.json | 2228 ++++++++++++++++++++++++++ example/sauce/package.json | 9 + example/src/shared/proxy.ts | 10 +- 6 files changed, 2400 insertions(+), 68 deletions(-) create mode 100644 example/sauce/package-lock.json create mode 100644 example/sauce/package.json diff --git a/.github/workflows/e2e-sauce-labs.yml b/.github/workflows/e2e-sauce-labs.yml index f3b2751..4a3641f 100644 --- a/.github/workflows/e2e-sauce-labs.yml +++ b/.github/workflows/e2e-sauce-labs.yml @@ -3,16 +3,24 @@ name: E2E tests (Sauce Labs) # Builds the example app as a release APK and runs the Maestro flows in # example/maestro on a Sauce Labs device with maestro-runner. The SSO proxy # runs on this runner, and the device reaches it through a Sauce Connect -# tunnel. See E2E_TESTING.md for the required secrets. +# tunnel. See E2E_TESTING.md for the required secrets and for debugging. on: pull_request: - paths: + paths: &paths - "src/**" - "example/**" - "package.json" - "package-lock.json" + - "vite.config.build.ts" + - "tsconfig.json" + - "babel.config.js" - ".github/workflows/e2e-sauce-labs.yml" + # Runs on the base branches warm the npm and Gradle caches that PR runs can + # read (PR-scoped caches aren't shared between PRs). + push: + branches: [v4, master] + paths: *paths workflow_dispatch: inputs: target: @@ -24,7 +32,7 @@ on: default: android-emulator concurrency: - group: e2e-sauce-labs-${{ github.ref }} + group: e2e-sauce-labs-${{ github.ref }}-${{ inputs.target || 'default' }} cancel-in-progress: true permissions: @@ -43,7 +51,7 @@ jobs: run: ${{ steps.check.outputs.run }} steps: # PRs from forks and Dependabot don't get repository secrets, so skip - # the run instead of failing it. + # the tests instead of failing them. - id: check env: SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }} @@ -62,13 +70,15 @@ jobs: echo "run=true" >> "$GITHUB_OUTPUT" fi - # Doesn't need secrets, so it runs on every PR and catches build breaks - # even when the Sauce Labs tests are skipped. + # Doesn't need secrets, so it runs even when the Sauce Labs tests are + # skipped and still catches build breaks. build-android: runs-on: ubuntu-latest timeout-minutes: 45 steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: actions/setup-node@v4 with: @@ -85,7 +95,6 @@ jobs: # ubuntu-latest already has the Android SDK (ANDROID_HOME). Gradle installs # anything missing, like the NDK version React Native pins. - - uses: actions/cache@v4 with: path: | @@ -111,7 +120,8 @@ jobs: run: | npx expo prebuild --platform android --no-install cd android - ./gradlew assembleRelease --no-daemon + # Sauce emulators are x86_64 and real devices arm64; skip the other ABIs. + ./gradlew assembleRelease --no-daemon -PreactNativeArchitectures=x86_64,arm64-v8a - uses: actions/upload-artifact@v4 with: @@ -126,25 +136,28 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 45 + # The Sauce and MX secrets are passed only to the steps that use them. env: TARGET: ${{ inputs.target || 'android-emulator' }} SAUCE_REGION: ${{ vars.SAUCE_REGION || 'us-west-1' }} - SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }} - SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }} - MAESTRO_RUNNER_VERSION: 1.1.29 RUN_NAME: rn-sdk-e2e-${{ github.run_id }}-${{ github.run_attempt }} + TOOLS: example/sauce/node_modules/.bin steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: actions/setup-node@v4 with: node-version: 20 cache: npm + cache-dependency-path: example/sauce/package-lock.json - # For the SSO proxy (@mxenabled/sso-api-proxy, a dev dependency). - - name: Install SDK dependencies - run: npm ci + # The SSO proxy and maestro-runner, pinned in example/sauce/package-lock.json. + # Neither needs install scripts. + - name: Install E2E tools + run: npm ci --ignore-scripts --prefix example/sauce - uses: actions/download-artifact@v4 with: @@ -159,72 +172,109 @@ jobs: MX_DEFAULT_USER_GUID: ${{ vars.MX_DEFAULT_USER_GUID || 'USR-e7353471-51d7-447f-b621-db255148a10c' }} run: | echo "127.0.0.1 $PROXY_HOST" | sudo tee -a /etc/hosts - # "$RUNNER_TEMP/sso-proxy.log" 2>&1 & + + # The proxy (1.3.4) exits on the first error from the MX API, so keep + # restarting it. "$RUNNER_TEMP/sso-proxy.log" 2>&1 & # Wait until the proxy can create a widget URL, which also checks the credentials. - for _ in $(seq 1 30); do - if curl -sf -o /dev/null -X POST "http://$PROXY_HOST:$PROXY_PORT/user/widget_urls" \ + for _ in $(seq 1 20); do + if curl -sf --max-time 20 -o /dev/null -X POST "http://$PROXY_HOST:$PROXY_PORT/user/widget_urls" \ -H "Content-Type: application/json" \ -d '{"widget_url":{"widget_type":"connect_widget","is_mobile_webview":true}}'; then echo "SSO proxy is up" exit 0 fi - sleep 2 + sleep 3 done - echo "::error::SSO proxy didn't return a widget URL" - cat "$RUNNER_TEMP/sso-proxy.log" + echo "::error::SSO proxy didn't return a widget URL. Check MX_CLIENT_ID, MX_API_KEY and MX_API_HOST; the 'SSO proxy log' step shows the responses." exit 1 - name: Start Sauce Connect tunnel - uses: saucelabs/sauce-connect-action@v3 + # v3; pinned because it receives the access key. + uses: saucelabs/sauce-connect-action@cb88b508c6f9ff4d84490093733315dbd55de022 with: username: ${{ secrets.SAUCE_USERNAME }} accessKey: ${{ secrets.SAUCE_ACCESS_KEY }} region: ${{ env.SAUCE_REGION }} tunnelName: ${{ env.RUN_NAME }} + # Only the proxy goes through the tunnel; the device reaches everything + # else (widgets, OAuth pages) directly. + tunnelDomains: ${{ env.PROXY_HOST }} # The proxy alias resolves to 127.0.0.1, which Sauce Connect 5 blocks by default. proxyLocalhost: allow - name: Upload APK to Sauce Labs id: upload + env: + SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }} + SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }} run: | - app_name="$RUN_NAME.apk" curl -fsS -u "$SAUCE_USERNAME:$SAUCE_ACCESS_KEY" \ -X POST "https://api.$SAUCE_REGION.saucelabs.com/v1/storage/upload" \ -F "payload=@$RUNNER_TEMP/apk/app-release.apk" \ - -F "name=$app_name" \ + -F "name=$RUN_NAME.apk" \ -o "$RUNNER_TEMP/upload.json" - echo "file_id=$(jq -r '.item.id' "$RUNNER_TEMP/upload.json")" >> "$GITHUB_OUTPUT" + file_id=$(jq -er '.item.id' "$RUNNER_TEMP/upload.json") + echo "file_id=$file_id" >> "$GITHUB_OUTPUT" - jq --arg app "$app_name" --arg build "$RUN_NAME" --arg tunnel "$RUN_NAME" \ - '. + {"appium:app": ("storage:filename=" + $app)} | ."sauce:options" += {build: $build, tunnelName: $tunnel}' \ + # Never put credentials in the caps: maestro-runner logs them, and they're printed here. + - name: Write Appium capabilities + run: | + jq --arg run "$RUN_NAME" \ + '. + {"appium:app": ("storage:filename=" + $run + ".apk")} | ."sauce:options" += {build: $run, tunnelName: $run}' \ "example/sauce/$TARGET.json" > "$RUNNER_TEMP/caps.json" cat "$RUNNER_TEMP/caps.json" - name: Run Maestro flows on Sauce Labs + timeout-minutes: 30 + env: + SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }} + SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }} + MAESTRO_RUNNER_NO_TELEMETRY: "1" run: | - # Real devices share the mobile team's concurrency, so use one at a time. + # Real devices share the account's concurrency with the mobile team, so use one. parallel=2 - [ "$TARGET" = "android-real-device" ] && parallel=1 + if [ "$TARGET" = "android-real-device" ]; then + parallel=1 + fi - npx -y "maestro-runner@$MAESTRO_RUNNER_VERSION" \ + "$TOOLS/maestro-runner" \ --driver appium \ --appium-url "https://$SAUCE_USERNAME:$SAUCE_ACCESS_KEY@ondemand.$SAUCE_REGION.saucelabs.com/wd/hub" \ --caps "$RUNNER_TEMP/caps.json" \ test --parallel "$parallel" --output maestro-report --flatten example/maestro/ - - name: Remove credentials from the report + # maestro-runner 1.1.29 writes the Appium URL, access key included, into + # maestro-runner.log. The report is a public artifact, so redact the key + # and refuse to upload if any copy is left. + - name: Redact credentials in the report + id: redact if: always() + env: + SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }} run: | - # maestro-runner writes the Appium URL, which includes the access key, - # into its log and session files. Don't publish them. + set -euo pipefail [ -d maestro-report ] || exit 0 - find maestro-report \( -name 'maestro-runner.log' -o -name '*session*.json' \) -delete - grep -rlF -- "$SAUCE_ACCESS_KEY" maestro-report | xargs -r rm -f + # grep exits 1 when nothing matches, which is fine here. + grep -rlF --null -- "$SAUCE_ACCESS_KEY" maestro-report >"$RUNNER_TEMP/files-with-key" || [ $? -eq 1 ] + xargs -0 -r perl -pi -e 's/\Q$ENV{SAUCE_ACCESS_KEY}\E/***/g' <"$RUNNER_TEMP/files-with-key" + + # Only exit 1 (no match) is clean; 0 (found) and 2 (error) are not. + rc=0 + grep -rqF -- "$SAUCE_ACCESS_KEY" maestro-report || rc=$? + if [ "$rc" -ne 1 ]; then + echo "::error::Couldn't confirm the Sauce access key is gone from the report; not uploading it" + exit 1 + fi - name: Upload Maestro report - if: always() + if: always() && steps.redact.outcome == 'success' uses: actions/upload-artifact@v4 with: name: maestro-report-${{ env.TARGET }} @@ -232,12 +282,19 @@ jobs: retention-days: 14 if-no-files-found: ignore + # Only request lines and status codes: the proxy's crash output includes the client credentials. - name: SSO proxy log if: failure() - run: cat "$RUNNER_TEMP/sso-proxy.log" || true + run: | + grep -E '^(GET|POST|OPTIONS|HEAD) |Running server|restarting|status code [0-9]+' \ + "$RUNNER_TEMP/sso-proxy.log" | tail -n 100 || true - name: Delete APK from Sauce Labs - if: always() && steps.upload.outputs.file_id != '' && steps.upload.outputs.file_id != 'null' + if: always() && steps.upload.outputs.file_id != '' + env: + SAUCE_USERNAME: ${{ secrets.SAUCE_USERNAME }} + SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }} + FILE_ID: ${{ steps.upload.outputs.file_id }} run: | curl -fsS -o /dev/null -u "$SAUCE_USERNAME:$SAUCE_ACCESS_KEY" \ - -X DELETE "https://api.$SAUCE_REGION.saucelabs.com/v1/storage/files/${{ steps.upload.outputs.file_id }}" || true + -X DELETE "https://api.$SAUCE_REGION.saucelabs.com/v1/storage/files/$FILE_ID" || true diff --git a/.gitignore b/.gitignore index a4ae9e2..e78f344 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ TestRnApp_* build coverage dist -.mx-sso-proxy-rc.yaml +# Local SSO proxy config (real credentials). The example file is committed. +.mx-sso-proxy-rc* +!.mx-sso-proxy-rc.example.yml node_modules npm-debug.log diff --git a/E2E_TESTING.md b/E2E_TESTING.md index 2defbcb..b12e5a6 100644 --- a/E2E_TESTING.md +++ b/E2E_TESTING.md @@ -25,31 +25,45 @@ We use Maestro for E2E testing. The example app talks to the SSO proxy started by `npm run dev:dependencies` (see [DEVELOPMENT.md](DEVELOPMENT.md)). To point it somewhere else, set `EXPO_PUBLIC_PROXY_BASE_URL` when you build or start the app, for example -`EXPO_PUBLIC_PROXY_BASE_URL=http://192.168.1.20:8089 npm run android:e2e`. +`EXPO_PUBLIC_PROXY_BASE_URL=http://192.168.1.20:8089 npm run android:e2e`. The value is compiled into the +JS bundle, so after changing it for a release build, clear the caches (`./gradlew clean`, `npx expo start --clear`). ## CI (Sauce Labs) [`.github/workflows/e2e-sauce-labs.yml`](.github/workflows/e2e-sauce-labs.yml) runs the same flows on a -Sauce Labs device for pull requests that touch `src/`, `example/` or the package files. You can also run -it from the Actions tab, where you can choose between an Android emulator and an Android real device. +Sauce Labs Android device. It runs on pull requests and pushes to `v4` and `master` that touch the SDK +(`src/`, its build config, the package files), `example/`, or the workflow itself. What it does: -1. Builds the SDK and the example app as a release APK, so the JS is bundled and no Metro server is needed. - The app is built with `EXPO_PUBLIC_PROXY_BASE_URL=http://sdk-e2e-proxy:8089`. This job needs no secrets - and always runs. -2. Starts the SSO proxy on the runner, with `sdk-e2e-proxy` pointed at `127.0.0.1` in `/etc/hosts`. -3. Opens a [Sauce Connect](https://docs.saucelabs.com/secure-connections/sauce-connect-5/) tunnel, so the - device's requests to `sdk-e2e-proxy` reach the runner. Sauce Connect can't proxy `localhost` to real - devices, which is why the alias is needed. -4. Uploads the APK to Sauce Labs App Storage, then runs `example/maestro/` with - [maestro-runner](https://docs.saucelabs.com/basics/community-frameworks/maestro/), which runs Maestro - flows through Appium. Device capabilities are in `example/sauce/`. -5. Uploads the HTML and JUnit report as the `maestro-report-*` artifact, with files containing the Sauce - access key removed, and deletes the APK from App Storage. - -Each flow shows up in Sauce Labs under **Automated > Test Results** (emulators) or **Real Devices**, in a -build named `rn-sdk-e2e--`, with video and logs. +1. **`build-android`** builds the SDK and the example app as a release APK, with the JS bundled so no Metro + server is needed, and with `EXPO_PUBLIC_PROXY_BASE_URL=http://sdk-e2e-proxy:8089`. The example app allows + cleartext http (`usesCleartextTraffic` in `example/app.json`) so the release build can reach the proxy. + The APK is signed with the debug keystore; it's for testing only. This job needs no secrets, so it runs + even when the tests are skipped. +2. **`test-android`** starts the SSO proxy on the runner, with `sdk-e2e-proxy` pointed at `127.0.0.1` in + `/etc/hosts`. +3. It opens a [Sauce Connect](https://docs.saucelabs.com/secure-connections/sauce-connect-5/) tunnel that + carries only `sdk-e2e-proxy` traffic from the device to the runner. Sauce Connect can't proxy `localhost` + to real devices, which is why the alias is needed. +4. It uploads the APK to Sauce Labs App Storage and runs `example/maestro/` with + [maestro-runner](https://docs.saucelabs.com/basics/community-frameworks/maestro/), a community tool that + runs Maestro flows through Appium. Device capabilities are in `example/sauce/*.json`. +5. It uploads the HTML, JUnit and Allure report as the `maestro-report-` artifact, with the Sauce + access key redacted, and deletes the APK from App Storage. + +The SSO proxy and maestro-runner versions are pinned in `example/sauce/package.json` (and its lockfile). To +upgrade maestro-runner, change the version there and run `npm install` in `example/sauce`. + +### Running it manually + +The workflow can be started by hand, which is also the only way to run it on a real device. GitHub only +offers this once the workflow file is on the default branch (`master`). From then on, use **Actions > E2E +tests (Sauce Labs) > Run workflow**, or: + +``` +gh workflow run e2e-sauce-labs.yml --ref -f target=android-real-device +``` ### Required configuration @@ -57,8 +71,8 @@ Repository secrets (Settings > Secrets and variables > Actions): | Secret | Value | | ------------------ | ----------------------------------------------------------------------------------------------------- | -| `SAUCE_USERNAME` | Sauce Labs username | -| `SAUCE_ACCESS_KEY` | Sauce Labs access key | +| `SAUCE_USERNAME` | Sauce Labs username. Prefer a service user over a personal account. | +| `SAUCE_ACCESS_KEY` | That user's access key | | `MX_CLIENT_ID` | Client ID of the E2E test client (see [`.mx-sso-proxy-rc.example.yml`](.mx-sso-proxy-rc.example.yml)) | | `MX_API_KEY` | API key of the same client | @@ -68,8 +82,26 @@ Dependabot, which don't get repository secrets. Optional repository variables: -| Variable | Default | -| ---------------------- | ------------------------------------------ | -| `SAUCE_REGION` | `us-west-1` | -| `MX_API_HOST` | `https://int-api.mx.com` | -| `MX_DEFAULT_USER_GUID` | `USR-e7353471-51d7-447f-b621-db255148a10c` | +| Variable | Default | Notes | +| ---------------------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- | +| `SAUCE_REGION` | `us-west-1` | The data center of the Sauce account: `us-west-1`, `eu-central-1` or `us-east-4`. Use the full name; `us` or `eu-central` won't work. | +| `MX_API_HOST` | `https://int-api.mx.com` | | +| `MX_DEFAULT_USER_GUID` | `USR-e7353471-51d7-447f-b621-db255148a10c` | The user the proxy creates widget URLs for | + +### When it fails + +- **Which flow failed, and why:** download the `maestro-report-` artifact from the run and open the + HTML report, which has each flow's steps and screenshots. `maestro-runner.log` in the artifact has the + full run. +- **Video and device logs:** in Sauce Labs, open **Automated > Test Results** (emulators) or **Real + Devices**, and filter by the build `rn-sdk-e2e--`. maestro-runner reuses a session for + several flows, so a Sauce job can contain more than one flow; it's named after the first one. Use the + report's timestamps to find a flow in the video. +- **"SSO proxy didn't return a widget URL":** the proxy couldn't get a widget URL from the MX API. Usually + `MX_CLIENT_ID` / `MX_API_KEY` are wrong or rotated, or `MX_API_HOST` is wrong. The **SSO proxy log** step + shows the requests and status codes (the full log isn't printed because it can contain the credentials). +- **Every flow fails at its first widget screen:** the device probably couldn't reach the proxy. Check the + **SSO proxy log** step: if it has no `POST /user/widget_urls` lines from the test run, the requests never + reached the runner (tunnel, alias or cleartext problem). +- **The Maestro step fails before any flow runs:** Sauce couldn't start a session (no free concurrency, an + emulator that didn't boot, a Sauce outage). Re-run the failed jobs. diff --git a/example/sauce/package-lock.json b/example/sauce/package-lock.json new file mode 100644 index 0000000..c969d4a --- /dev/null +++ b/example/sauce/package-lock.json @@ -0,0 +1,2228 @@ +{ + "name": "example-sauce-e2e-tools", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "example-sauce-e2e-tools", + "devDependencies": { + "@mxenabled/sso-api-proxy": "1.3.4", + "maestro-runner": "1.1.29" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@devicelab/maestro-runner-darwin-arm64": { + "version": "1.1.29", + "resolved": "https://registry.npmjs.org/@devicelab/maestro-runner-darwin-arm64/-/maestro-runner-darwin-arm64-1.1.29.tgz", + "integrity": "sha512-dVbIOf6qVQnJcDmCWcb5jDZKpTaBhrmTw9rEeMpTXI9O3z8cduEFHGT7FxKEItA6T3meSvYgJiNTlUocVXn2ww==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@devicelab/maestro-runner-darwin-x64": { + "version": "1.1.29", + "resolved": "https://registry.npmjs.org/@devicelab/maestro-runner-darwin-x64/-/maestro-runner-darwin-x64-1.1.29.tgz", + "integrity": "sha512-MhO/ZiWwrK/K8T39wcRXOS3DvJEcJk+0vNX6j27etYChyo2mjooipxIE8WzILUau4Qcr9Tubra3yacr9dOpbbg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@devicelab/maestro-runner-linux-arm64": { + "version": "1.1.29", + "resolved": "https://registry.npmjs.org/@devicelab/maestro-runner-linux-arm64/-/maestro-runner-linux-arm64-1.1.29.tgz", + "integrity": "sha512-6l11xzwSa8vABHUqeR1ckQ/LkyM5+j7AKSaikaOgFhxEpfca3RRy2zgwNj/e1NGNlDRGZ2z6qYFCNvSkYaFpwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@devicelab/maestro-runner-linux-x64": { + "version": "1.1.29", + "resolved": "https://registry.npmjs.org/@devicelab/maestro-runner-linux-x64/-/maestro-runner-linux-x64-1.1.29.tgz", + "integrity": "sha512-+4aY3o+BPgw8t5xlEF2WhrmwBH25cG5LK3iqlumATnk9fERaZ4OuAuQDMZRqyNWs+/FkbXLR1bcYNIOxUVlq2Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@inquirer/external-editor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", + "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chardet": "^2.1.1", + "iconv-lite": "^0.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/external-editor/node_modules/iconv-lite": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@mxenabled/sso-api-proxy": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@mxenabled/sso-api-proxy/-/sso-api-proxy-1.3.4.tgz", + "integrity": "sha512-Ah/OyppFrKE8d/yoecVK9mzAya3ei416sY/rt4SUCXh7uyL2h6cgjB6SwgzdL5XR8CDnwy+OYW28cV+NylYmTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^9.4.0", + "cosmiconfig": "^7.0.1", + "debug": "^4.4.3", + "dotenv": "^16.0.0", + "express": "^4.18.1", + "express-async-errors": "^3.1.1", + "inquirer": "^8.2.4", + "morgan": "^1.10.0", + "mx-platform-node": "^1.12.1", + "node-fetch": "^3.2.0", + "wrap-text": "^1.0.9" + }, + "bin": { + "mx-sso-proxy": "build/src/cli.js" + } + }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "dev": true, + "license": "MIT" + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.20.0.tgz", + "integrity": "sha512-r8aOh8j9cGKpgQAqpzrUHnSIc6a59Y3Xf/cv8sy1DrHCkZHzQGEuoq1tARk6qSyDdtQGSDgpb9kFlruzPvrgwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.6", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/basic-auth/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "1.20.8", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.8.tgz", + "integrity": "sha512-JNcyFQ64OiijEkPzUBTCe+hyPXUD/3LEldGQ6iF5LR1w00mx9o7xtDWHXBY2iItjdCFGoilOLNQbH943ut7pHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.16.0", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chardet": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.2.0.tgz", + "integrity": "sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA==", + "dev": true, + "license": "MIT" + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 10" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "dev": true, + "license": "MIT" + }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true, + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.22.3", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.3.tgz", + "integrity": "sha512-Bdcs4+3qlpVlx2NRn6fgX2Ue2/gGRaPeawebgclM0ERSCqDpA+owF1fdPwjJUTAJWMTuAaxjDf+hzb0/4eKvvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.5", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.13", + "proxy-addr": "~2.0.7", + "qs": "~6.16.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-async-errors": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/express-async-errors/-/express-async-errors-3.1.1.tgz", + "integrity": "sha512-h6aK1da4tpqWSbyCa3FxB/V6Ehd4EEB15zyQq9qe75OZBp0krinNKuH4rAY+S/U/2I36vdLAUFSjQJ+TFmODng==", + "dev": true, + "license": "ISC", + "peerDependencies": { + "express": "^4.16.2" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/follow-redirects": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.1.tgz", + "integrity": "sha512-FNvFGzoMLWmE6Yj9spb/zjd7yiNCHiAW9/Tg9CXrQ8wuu32HtlJOwWO11OJafl5FfY3DxTdQ0vj42zU1kvv5jg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.4", + "mime-types": "^2.1.35" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/inquirer": { + "version": "8.2.7", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.7.tgz", + "integrity": "sha512-UjOaSel/iddGZJ5xP/Eixh6dY1XghiBw4XK13rCCIJcJfyhhoul/7KhLLUGtebEj6GDYM6Vnx/mVsjx2L/mFIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/external-editor": "^1.0.0", + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^6.0.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/maestro-runner": { + "version": "1.1.29", + "resolved": "https://registry.npmjs.org/maestro-runner/-/maestro-runner-1.1.29.tgz", + "integrity": "sha512-1Qx5KdRXA5acwKiPAO/Tyl54wC6y/iiAhIcpqA1aBPqWCVKf1L7Wxec9w5uaDjt9yS27GmVDOqJIQXqnPDo0Cg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "maestro-runner": "bin/maestro-runner.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@devicelab/maestro-runner-darwin-arm64": "1.1.29", + "@devicelab/maestro-runner-darwin-x64": "1.1.29", + "@devicelab/maestro-runner-linux-arm64": "1.1.29", + "@devicelab/maestro-runner-linux-x64": "1.1.29" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/morgan": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.12.1.tgz", + "integrity": "sha512-tljKC0ex20AjO58Ob/eZ53JloycbVswbVNCHx6V6VLGzqt/w8dIynVGL0G8qVjNKwiA7sYSogCrN6QtJ82IV+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.4.1", + "on-headers": "~1.1.0" + }, + "engines": { + "node": ">= 0.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/morgan/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/morgan/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true, + "license": "ISC" + }, + "node_modules/mx-platform-node": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/mx-platform-node/-/mx-platform-node-1.12.1.tgz", + "integrity": "sha512-+ytWmXJvjy1WwEzTeyNVZHGzio0oq0xcHdIWsh1x+SUSs7P2lvTv7DCCeKDm7HmIwHuZnZKNTkeF1GjsfKCbuw==", + "deprecated": "Breaking changes. Please upgrade to v2.0.0: https://github.com/mxenabled/mx-platform-node#migration-guide", + "dev": true, + "license": "MIT", + "dependencies": { + "axios": "^1.6.8" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/proxy-addr": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.8.tgz", + "integrity": "sha512-5nnx0yGyVUcY6t9RnWcARWtwT9F1D8O9rt08htPvnd49W1IgZtmLkhu9WfMzQj1cFxjHIO6connUNVW5k7AVyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/qs": { + "version": "6.16.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.1.tgz", + "integrity": "sha512-5Px6J7rfHBAGUhNQ3es4YzcOdBBw0SDcVtThJml4RdnKw4tKs2epA0kPZ+K8X+WiHktT93RHVcQ/3ym5aTCgtg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true, + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-text": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/wrap-text/-/wrap-text-1.0.10.tgz", + "integrity": "sha512-WwcKHo8D9ncPFdKDBLmFipyx8J0fVjc1BMzCqVhaSrqw0Ae/hUdsNNo4pKm0OGHuTcLNHznKKQi0hB5jne26ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/yaml": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", + "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + } + } +} diff --git a/example/sauce/package.json b/example/sauce/package.json new file mode 100644 index 0000000..354977c --- /dev/null +++ b/example/sauce/package.json @@ -0,0 +1,9 @@ +{ + "name": "example-sauce-e2e-tools", + "private": true, + "description": "Tools the Sauce Labs E2E workflow runs (see E2E_TESTING.md). Locked so CI installs exactly these.", + "devDependencies": { + "@mxenabled/sso-api-proxy": "1.3.4", + "maestro-runner": "1.1.29" + } +} diff --git a/example/src/shared/proxy.ts b/example/src/shared/proxy.ts index e723c1e..71358de 100644 --- a/example/src/shared/proxy.ts +++ b/example/src/shared/proxy.ts @@ -2,9 +2,13 @@ import { Platform } from "react-native" // The Android emulator reaches the host machine through 10.0.2.2. CI builds set // EXPO_PUBLIC_PROXY_BASE_URL so a Sauce Labs device can reach the proxy through -// Sauce Connect. Expo inlines EXPO_PUBLIC_* variables when it bundles the JS. +// Sauce Connect. +// +// Expo inlines EXPO_PUBLIC_* values into the JS bundle at build time, so: +// - keep the direct `process.env.EXPO_PUBLIC_PROXY_BASE_URL` reference; +// destructuring or `process.env[name]` isn't inlined +// - never put a secret in an EXPO_PUBLIC_* variable; it ends up in the app const defaultBaseUrl = Platform.OS === "android" ? "http://10.0.2.2:8089" : "http://localhost:8089" - -export const proxyBaseUrl = process.env.EXPO_PUBLIC_PROXY_BASE_URL || defaultBaseUrl +const proxyBaseUrl = process.env.EXPO_PUBLIC_PROXY_BASE_URL || defaultBaseUrl export const proxy = `${proxyBaseUrl}/user/widget_urls`