From f4db567267b9a1ac923efba5d1cb584c040db183 Mon Sep 17 00:00:00 2001 From: jjbrunton Date: Sun, 30 Aug 2026 20:12:51 +0100 Subject: [PATCH 1/3] feat(mobile): wire expo-updates to the self-hosted XPREM OTA server Install expo-updates and the eoas CLI, and configure the client against https://ota.apps.jbrunton.co.uk. app.json stays the static source of truth for version, buildNumber, and versionCode, which EAS rewrites during a release build. A new app.config.js spreads it and layers on the updates block, because XPREM selects a release channel from a build-time request header and that value differs per EAS build profile. - updates.url points at the XPREM manifest endpoint, /manifest - runtimeVersion uses the appVersion policy, so the runtime version is the store-visible expo.version and a native change requires a store build - expo-channel-name comes from RELEASE_CHANNEL, set per EAS build profile: development, staging (preview profile), and production - expo-app-id and an empty xprem-branch header are declared at build time, since expo-updates only sends headers that existed when the build was made - code signing uses keyid main and rsa-v1_5-sha256 against a certificate at certs/certificate.pem, skipped when DISABLE_CODE_SIGNING is set so a dev client can run against the packager The XPREM app id and the code-signing certificate are still placeholders pending the update server deployment. The config throws a descriptive error whenever RELEASE_CHANNEL is set while either is unresolved, so a misconfigured build fails at config resolution rather than shipping a client that can never validate a manifest. The native projects are Expo CNG output and are left untouched: the expo-updates config plugin embeds the real app id and certificate contents, so they are regenerated as a reviewed source change once both values land. Co-Authored-By: Claude Fable 5 --- apps/mobile/app.config.js | 110 ++ apps/mobile/certs/.gitignore | 3 + apps/mobile/eas.json | 9 +- apps/mobile/package.json | 8 +- package-lock.json | 2133 +++++++++++++++++++++++++++++++++- 5 files changed, 2224 insertions(+), 39 deletions(-) create mode 100644 apps/mobile/app.config.js create mode 100644 apps/mobile/certs/.gitignore diff --git a/apps/mobile/app.config.js b/apps/mobile/app.config.js new file mode 100644 index 0000000..3bb6bae --- /dev/null +++ b/apps/mobile/app.config.js @@ -0,0 +1,110 @@ +// Dynamic Expo config. +// +// `app.json` stays the static source of truth for everything that is stable +// across environments, including the `version`, `ios.buildNumber`, and +// `android.versionCode` fields that EAS rewrites during a release build. This +// file only layers the over-the-air update configuration on top, because the +// XPREM update server selects a release channel from a build-time request +// header and that value differs per EAS build profile. +// +// See `docs/mobile-ota.md`. + +/* global __dirname */ + +const fs = require('fs'); +const path = require('path'); + +const appJson = require('./app.json'); + +/** Base URL of the self-hosted XPREM update server. */ +const OTA_SERVER_URL = 'https://ota.apps.jbrunton.co.uk'; + +/** XPREM manifest endpoint. XPREM serves the Expo Updates protocol here. */ +const OTA_MANIFEST_URL = `${OTA_SERVER_URL}/manifest`; + +/** + * XPREM application UUID, shown in the XPREM dashboard under App Info. + * PLACEHOLDER until the update server deployment reports the real value. + */ +const OTA_APP_ID_PLACEHOLDER = 'REPLACE_WITH_XPREM_APP_ID'; +const OTA_APP_ID = process.env.XPREM_APP_ID || OTA_APP_ID_PLACEHOLDER; + +/** Public code-signing certificate downloaded from the XPREM dashboard. */ +const CODE_SIGNING_CERTIFICATE_PATH = './certs/certificate.pem'; + +/** Marker string present only in the committed placeholder certificate. */ +const CERTIFICATE_PLACEHOLDER_MARKER = 'XPREM-PLACEHOLDER-CERTIFICATE'; + +/** + * Release channel bound to the build. XPREM maps a channel to a branch server + * side, so a channel can be repointed at a different branch without rebuilding. + * Set per EAS build profile in `eas.json` and exported before `eoas publish`. + */ +const releaseChannel = process.env.RELEASE_CHANNEL || 'development'; + +/** + * Code signing is skipped for local development, where the packager serves the + * bundle directly and no signed manifest exists: + * DISABLE_CODE_SIGNING=1 npx expo start --dev-client + */ +const codeSigningDisabled = Boolean(process.env.DISABLE_CODE_SIGNING); + +function assertUpdateConfigIsReal() { + if (OTA_APP_ID === OTA_APP_ID_PLACEHOLDER) { + throw new Error( + `RELEASE_CHANNEL=${releaseChannel} requires a real XPREM app id. ` + + 'Set XPREM_APP_ID, or replace the placeholder in apps/mobile/app.config.js. ' + + 'See docs/mobile-ota.md.', + ); + } + + const certificatePath = path.join(__dirname, CODE_SIGNING_CERTIFICATE_PATH); + const certificate = fs.existsSync(certificatePath) + ? fs.readFileSync(certificatePath, 'utf8') + : ''; + + if (!certificate || certificate.includes(CERTIFICATE_PLACEHOLDER_MARKER)) { + throw new Error( + `RELEASE_CHANNEL=${releaseChannel} requires the real XPREM code-signing ` + + `certificate at apps/mobile/${CODE_SIGNING_CERTIFICATE_PATH}. ` + + 'Download it from the XPREM dashboard App Info page. See docs/mobile-ota.md.', + ); + } +} + +// A build or publish always names its channel explicitly. Fail loudly there +// rather than shipping a build that can never validate a signed manifest. +if (process.env.RELEASE_CHANNEL) { + assertUpdateConfigIsReal(); +} + +module.exports = () => ({ + ...appJson.expo, + // Updates are gated on the app version. A JavaScript-only change ships over + // the air; anything that changes native code must ship a new store build with + // a bumped `version`, which starts a fresh runtime version on the server. + runtimeVersion: { + policy: 'appVersion', + }, + updates: { + enabled: true, + url: OTA_MANIFEST_URL, + checkAutomatically: 'ON_LOAD', + fallbackToCacheTimeout: 5000, + codeSigningCertificate: codeSigningDisabled + ? undefined + : CODE_SIGNING_CERTIFICATE_PATH, + codeSigningMetadata: codeSigningDisabled + ? undefined + : { keyid: 'main', alg: 'rsa-v1_5-sha256' }, + // expo-updates only sends headers that were declared at build time, so + // every header XPREM may need has to be present here even when empty. + requestHeaders: { + 'expo-channel-name': releaseChannel, + 'expo-app-id': OTA_APP_ID, + // Branch surfing: lets a non-production build be pointed at another + // branch at runtime. Empty means the channel mapping decides. + 'xprem-branch': '', + }, + }, +}); diff --git a/apps/mobile/certs/.gitignore b/apps/mobile/certs/.gitignore new file mode 100644 index 0000000..9c4f928 --- /dev/null +++ b/apps/mobile/certs/.gitignore @@ -0,0 +1,3 @@ +# The update-signing private key belongs to the XPREM server, never to the app. +private-key.pem +*.key diff --git a/apps/mobile/eas.json b/apps/mobile/eas.json index 34d2eb5..b443c6b 100644 --- a/apps/mobile/eas.json +++ b/apps/mobile/eas.json @@ -7,19 +7,24 @@ "development": { "node": "20.19.4", "developmentClient": true, - "distribution": "internal" + "distribution": "internal", + "env": { + "RELEASE_CHANNEL": "development" + } }, "preview": { "node": "20.19.4", "distribution": "internal", "env": { - "EXPO_PUBLIC_API_URL": "https://api.preprod.sauci.app" + "EXPO_PUBLIC_API_URL": "https://api.preprod.sauci.app", + "RELEASE_CHANNEL": "staging" } }, "production": { "node": "20.19.4", "autoIncrement": true, "env": { + "RELEASE_CHANNEL": "production", "EXPO_PUBLIC_API_URL": "https://api.sauci.app", "EXPO_PUBLIC_SUPABASE_URL": "https://ckjcrkjpmhqhiucifukx.supabase.co", "EXPO_PUBLIC_SUPABASE_ANON_KEY": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImNramNya2pwbWhxaGl1Y2lmdWt4Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjcxMDc1ODEsImV4cCI6MjA4MjY4MzU4MX0.7FA_RmFXUjlmZF96O9xlAUfxHrQrSIZcJclNReLL4lI", diff --git a/apps/mobile/package.json b/apps/mobile/package.json index cd4aa7f..89a9661 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -18,7 +18,11 @@ "release:ios:preview": "dotenv -e .env -- eas build --platform ios --profile preview --local", "release:android": "dotenv -e .env -- eas build --platform android --profile production --local", "release:android:preview": "dotenv -e .env -- eas build --platform android --profile preview --local", - "release:android:upload": "bundle exec fastlane android upload" + "release:android:upload": "bundle exec fastlane android upload", + "ota:publish:staging": "RELEASE_CHANNEL=staging eoas publish --branch staging", + "ota:publish:production": "RELEASE_CHANNEL=production eoas publish --branch production", + "ota:rollback": "eoas rollback", + "ota:doctor": "eoas doctor" }, "dependencies": { "@bacons/apple-targets": "^0.1.6", @@ -62,6 +66,7 @@ "expo-secure-store": "~15.0.8", "expo-sharing": "~14.0.8", "expo-status-bar": "~3.0.9", + "expo-updates": "~29.0.20", "react": "19.1.0", "react-dom": "19.1.0", "react-native": "0.81.5", @@ -87,6 +92,7 @@ "@types/react": "~19.1.10", "@types/react-test-renderer": "^19.1.0", "dotenv-cli": "^11.0.0", + "eoas": "^3.1.3", "eslint": "^8.57.0", "eslint-config-expo": "~10.0.0", "jest": "^29.7.0", diff --git a/package-lock.json b/package-lock.json index 4112bb9..494a7c1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -371,6 +371,7 @@ "expo-secure-store": "~15.0.8", "expo-sharing": "~14.0.8", "expo-status-bar": "~3.0.9", + "expo-updates": "~29.0.20", "react": "19.1.0", "react-dom": "19.1.0", "react-native": "0.81.5", @@ -396,6 +397,7 @@ "@types/react": "~19.1.10", "@types/react-test-renderer": "^19.1.0", "dotenv-cli": "^11.0.0", + "eoas": "^3.1.3", "eslint": "^8.57.0", "eslint-config-expo": "~10.0.0", "jest": "^29.7.0", @@ -2186,6 +2188,24 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/preset-flow": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.29.7.tgz", + "integrity": "sha512-KYIRV0BuaN68CDdsqFkAD7MU7yipUqQNuNElwATdxaIdpTjhvtY82QvkBJs7zV3Evxj2jFAAZ1iO8nyy0nhjqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "@babel/plugin-transform-flow-strip-types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/preset-react": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.29.7.tgz", @@ -2225,6 +2245,60 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/register": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.29.7.tgz", + "integrity": "sha512-AMGJoWuES861riy6pcB0fphE1YXybtQnBYQMuIyPv6mKLiosfa79BKTnAOyx215c/3RJPJpdQwoHZ3earVH7AA==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-deep": "^4.0.1", + "find-cache-dir": "^2.0.0", + "make-dir": "^2.1.0", + "pirates": "^4.0.6", + "source-map-support": "^0.5.16" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/register/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@babel/register/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@babel/register/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, "node_modules/@babel/runtime": { "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", @@ -2375,6 +2449,40 @@ "react-native": "*" } }, + "node_modules/@borewit/text-codec": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.2.tgz", + "integrity": "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@clack/core": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@clack/core/-/core-0.5.0.tgz", + "integrity": "sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow==", + "dev": true, + "license": "MIT", + "dependencies": { + "picocolors": "^1.0.0", + "sisteransi": "^1.0.5" + } + }, + "node_modules/@clack/prompts": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-0.11.0.tgz", + "integrity": "sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@clack/core": "0.5.0", + "picocolors": "^1.0.0", + "sisteransi": "^1.0.5" + } + }, "node_modules/@dnd-kit/accessibility": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz", @@ -3838,6 +3946,32 @@ } } }, + "node_modules/@expo/eas-build-job": { + "version": "1.0.165", + "resolved": "https://registry.npmjs.org/@expo/eas-build-job/-/eas-build-job-1.0.165.tgz", + "integrity": "sha512-l4bPVRmc6SlrgiC7AOid6s+o3kyP47x17cGIPQ0d4FOaOxu6pP3bPZ3BuPjts1mfvrKg/c/Phe1fJ1nnNlo3Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@expo/logger": "1.0.117", + "joi": "^17.13.1", + "semver": "^7.6.2", + "zod": "^3.23.8" + } + }, + "node_modules/@expo/eas-build-job/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@expo/env": { "version": "2.0.12", "resolved": "https://registry.npmjs.org/@expo/env/-/env-2.0.12.tgz", @@ -4023,6 +4157,17 @@ "@babel/highlight": "^7.10.4" } }, + "node_modules/@expo/logger": { + "version": "1.0.117", + "resolved": "https://registry.npmjs.org/@expo/logger/-/logger-1.0.117.tgz", + "integrity": "sha512-tuCT5fmjRjED6HYAf4HgE+UbiHT3JYrkUzUv9FC769goxo4LuuewBNQiDUkeecQTha7aTS5aO2YHlfdipI8gzg==", + "dev": true, + "license": "BUSL-1.1", + "dependencies": { + "@types/bunyan": "^1.8.11", + "bunyan": "^1.8.15" + } + }, "node_modules/@expo/metro": { "version": "54.2.0", "resolved": "https://registry.npmjs.org/@expo/metro/-/metro-54.2.0.tgz", @@ -7014,6 +7159,153 @@ "node": ">=12.4.0" } }, + "node_modules/@oclif/core": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@oclif/core/-/core-4.14.0.tgz", + "integrity": "sha512-QCJIZoVJxV7jywgVAUlsQwl3dr3Sa21kfi5z9KrYolbexWJUtFSEtMoNiBWojD05mYycLnB50gp/VxlGdaOCRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^4.3.2", + "ansis": "^3.17.0", + "clean-stack": "^3.0.1", + "cli-spinners": "^2.9.2", + "debug": "^4.4.3", + "ejs": "^6", + "get-package-type": "^0.1.0", + "indent-string": "^4.0.0", + "lilconfig": "^3.1.3", + "minimatch": "^10.2.5", + "semver": "^7.8.1", + "string-width": "^4.2.3", + "supports-color": "^8", + "tinyglobby": "^0.2.17", + "widest-line": "^3.1.0", + "wordwrap": "^1.0.0", + "wrap-ansi": "^7.0.0", + "wsl-utils": "^0.4.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@oclif/core/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@oclif/core/node_modules/brace-expansion": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@oclif/core/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/@oclif/core/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/@oclif/core/node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@oclif/core/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@oclif/core/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/@oclif/core/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/@oclif/core/node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -10991,6 +11283,39 @@ "devOptional": true, "license": "MIT" }, + "node_modules/@tokenizer/inflate": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.2.7.tgz", + "integrity": "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "fflate": "^0.8.2", + "token-types": "^6.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/@tokenizer/inflate/node_modules/fflate": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.3.tgz", + "integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "dev": true, + "license": "MIT" + }, "node_modules/@tootallnate/once": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", @@ -11053,6 +11378,16 @@ "@babel/types": "^7.28.2" } }, + "node_modules/@types/bunyan": { + "version": "1.8.11", + "resolved": "https://registry.npmjs.org/@types/bunyan/-/bunyan-1.8.11.tgz", + "integrity": "sha512-758fRH7umIMk5qt5ELmRMff4mLDlN+xyYzC+dkPTdKwbSkJFvz6xwyScrytPU0QIBbRRwbiE8/BIg8bpajerNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/cacheable-request": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", @@ -11234,22 +11569,6 @@ "form-data": "^4.0.4" } }, - "node_modules/@types/node-fetch/node_modules/form-data": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", - "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/@types/pg": { "version": "8.23.1", "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.23.1.tgz", @@ -12431,6 +12750,16 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/ansis": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/ansis/-/ansis-3.17.0.tgz", + "integrity": "sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + } + }, "node_modules/any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", @@ -12691,6 +13020,19 @@ "node": ">=12" } }, + "node_modules/ast-types": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.16.2.tgz", + "integrity": "sha512-6E6tp3Yruw0qFK56M7UQj26h9eHaHdlHHhc/0JijFkDKy+m7Cbw5INnRvdcxs5vAR5CtN5V+XdjmsB9zyjIVlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/ast-types-flow": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", @@ -13315,6 +13657,25 @@ "esbuild": ">=0.18" } }, + "node_modules/bunyan": { + "version": "1.8.15", + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.15.tgz", + "integrity": "sha512-0tECWShh6wUysgucJcBAoYegf3JJoZWibxdqhTm7OHPeT42qdjkZ29QCMcKwbgU1kiH+auSIasNRXMLWXafXig==", + "dev": true, + "engines": [ + "node >=0.10.0" + ], + "license": "MIT", + "bin": { + "bunyan": "bin/bunyan" + }, + "optionalDependencies": { + "dtrace-provider": "~0.8", + "moment": "^2.19.3", + "mv": "~2", + "safe-json-stringify": "~1" + } + }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -13655,6 +14016,22 @@ "url": "https://polar.sh/cva" } }, + "node_modules/clean-stack": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-3.0.1.tgz", + "integrity": "sha512-lR9wNiMRcVQjSB3a7xXGLuz4cr4wJuuXlaAEbRutGowQTmlp7R72/DOgN21e8jdwblMWl9UOJMJXarX94pzKdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -13737,6 +14114,21 @@ "node": ">=0.8" } }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/clone-response": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", @@ -13850,6 +14242,13 @@ "node": "^12.20.0 || >=14" } }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true, + "license": "MIT" + }, "node_modules/compressible": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", @@ -15360,6 +15759,21 @@ "@esbuild/win32-x64": "0.25.12" } }, + "node_modules/dtrace-provider": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz", + "integrity": "sha512-b7Z7cNtHPhH9EJhNNbbeqTcXB8LGFFZhq1PGgEvpeHlzd36bhbdTWoE/Ba/YguqpBSlAPKnARWhVlhunCMwfxg==", + "dev": true, + "hasInstallScript": true, + "license": "BSD-2-Clause", + "optional": true, + "dependencies": { + "nan": "^2.14.0" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -15404,6 +15818,19 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT" }, + "node_modules/ejs": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-6.0.1.tgz", + "integrity": "sha512-UaaM14yby8U3k02ihS1Bmj5Kz2d7CCQM1scxpgs4Mhkq8F1wR2gl3+Ts4h5Ne4Mnt7M9m4Dw7jsuMr3+xO4vZA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.12.18" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.416", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.416.tgz", @@ -15496,6 +15923,758 @@ "node": ">=4" } }, + "node_modules/eoas": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/eoas/-/eoas-3.1.3.tgz", + "integrity": "sha512-6zLIt3sWkZask+xYUNHl9HOlfA4bhVOy5KzG0Bvp0T3GHIVXTVsNsW/GbuJ9aE3BkuYx64oV2o1FaVsnsXUjIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@clack/prompts": "^0.11.0", + "@expo/code-signing-certificates": "^0.0.5", + "@expo/config": "10.0.11", + "@expo/config-plugins": "9.0.12", + "@expo/eas-build-job": "1.0.165", + "@expo/fingerprint": "^0.11.7", + "@expo/package-manager": "1.7.0", + "@expo/spawn-async": "1.7.2", + "@oclif/core": "^4.2.4", + "@types/node-fetch": "^2.6.12", + "@urql/core": "4.0.11", + "@urql/exchange-retry": "1.2.0", + "better-opn": "3.0.2", + "chalk": "4.1.2", + "eslint": "^8.57.1", + "fast-glob": "3.3.2", + "fetch-retry": "^6.0.0", + "figures": "3.2.0", + "file-type": "^20.0.0", + "form-data": "^4.0.1", + "fs-extra": "11.2.0", + "getenv": "1.0.0", + "graphql": "16.8.1", + "graphql-tag": "^2.12.6", + "https-proxy-agent": "5.0.1", + "ignore": "5.3.0", + "joi": "17.11.0", + "js-yaml": "^5.2.2", + "jscodeshift": "^17.1.2", + "log-symbols": "^4.0.0", + "mime": "3.0.0", + "node-fetch": "^2.6.7", + "prettier": "3.1.1", + "prompts": "^2.4.2", + "recast": "^0.23.9", + "resolve-from": "5.0.0", + "semver": "7.5.4", + "tar": "6.2.1", + "terminal-link": "2.1.1", + "uuid": "9.0.1" + }, + "bin": { + "eoas": "bin/run.js" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eoas/node_modules/@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/highlight": "^7.10.4" + } + }, + "node_modules/eoas/node_modules/@expo/code-signing-certificates": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/@expo/code-signing-certificates/-/code-signing-certificates-0.0.5.tgz", + "integrity": "sha512-BNhXkY1bblxKZpltzAx98G2Egj9g1Q+JRcvR7E99DOj862FTCX+ZPsAUtPTr7aHxwtrL7+fL3r0JSmM9kBm+Bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "node-forge": "^1.2.1", + "nullthrows": "^1.1.1" + } + }, + "node_modules/eoas/node_modules/@expo/config": { + "version": "10.0.11", + "resolved": "https://registry.npmjs.org/@expo/config/-/config-10.0.11.tgz", + "integrity": "sha512-nociJ4zr/NmbVfMNe9j/+zRlt7wz/siISu7PjdWE4WE+elEGxWWxsGzltdJG0llzrM+khx8qUiFK5aiVcdMBww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "~7.10.4", + "@expo/config-plugins": "~9.0.17", + "@expo/config-types": "^52.0.5", + "@expo/json-file": "^9.0.2", + "deepmerge": "^4.3.1", + "getenv": "^1.0.0", + "glob": "^10.4.2", + "require-from-string": "^2.0.2", + "resolve-from": "^5.0.0", + "resolve-workspace-root": "^2.0.0", + "semver": "^7.6.0", + "slugify": "^1.3.4", + "sucrase": "3.35.0" + } + }, + "node_modules/eoas/node_modules/@expo/config-plugins": { + "version": "9.0.12", + "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-9.0.12.tgz", + "integrity": "sha512-/Ko/NM+GzvJyRkq8PITm8ms0KY5v0wmN1OQFYRMkcJqOi3PjlhndW+G6bHpJI9mkQXBaUnHwAiGLqIC3+MQ5Wg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@expo/config-types": "^52.0.0", + "@expo/json-file": "~9.0.0", + "@expo/plist": "^0.2.0", + "@expo/sdk-runtime-versions": "^1.0.0", + "chalk": "^4.1.2", + "debug": "^4.3.5", + "getenv": "^1.0.0", + "glob": "^10.4.2", + "resolve-from": "^5.0.0", + "semver": "^7.5.4", + "slash": "^3.0.0", + "slugify": "^1.6.6", + "xcode": "^3.0.1", + "xml2js": "0.6.0" + } + }, + "node_modules/eoas/node_modules/@expo/config-plugins/node_modules/@expo/json-file": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-9.0.2.tgz", + "integrity": "sha512-yAznIUrybOIWp3Uax7yRflB0xsEpvIwIEqIjao9SGi2Gaa+N0OamWfe0fnXBSWF+2zzF4VvqwT4W5zwelchfgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "~7.10.4", + "json5": "^2.2.3", + "write-file-atomic": "^2.3.0" + } + }, + "node_modules/eoas/node_modules/@expo/config-types": { + "version": "52.0.5", + "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-52.0.5.tgz", + "integrity": "sha512-AMDeuDLHXXqd8W+0zSjIt7f37vUd/BP8p43k68NHpyAvQO+z8mbQZm3cNQVAMySeayK2XoPigAFB1JF2NFajaA==", + "dev": true, + "license": "MIT" + }, + "node_modules/eoas/node_modules/@expo/config/node_modules/@expo/config-plugins": { + "version": "9.0.17", + "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-9.0.17.tgz", + "integrity": "sha512-m24F1COquwOm7PBl5wRbkT9P9DviCXe0D7S7nQsolfbhdCWuvMkfXeoWmgjtdhy7sDlOyIgBrAdnB6MfsWKqIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@expo/config-types": "^52.0.5", + "@expo/json-file": "~9.0.2", + "@expo/plist": "^0.2.2", + "@expo/sdk-runtime-versions": "^1.0.0", + "chalk": "^4.1.2", + "debug": "^4.3.5", + "getenv": "^1.0.0", + "glob": "^10.4.2", + "resolve-from": "^5.0.0", + "semver": "^7.5.4", + "slash": "^3.0.0", + "slugify": "^1.6.6", + "xcode": "^3.0.1", + "xml2js": "0.6.0" + } + }, + "node_modules/eoas/node_modules/@expo/config/node_modules/@expo/json-file": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-9.0.2.tgz", + "integrity": "sha512-yAznIUrybOIWp3Uax7yRflB0xsEpvIwIEqIjao9SGi2Gaa+N0OamWfe0fnXBSWF+2zzF4VvqwT4W5zwelchfgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "~7.10.4", + "json5": "^2.2.3", + "write-file-atomic": "^2.3.0" + } + }, + "node_modules/eoas/node_modules/@expo/config/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eoas/node_modules/@expo/fingerprint": { + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/@expo/fingerprint/-/fingerprint-0.11.11.tgz", + "integrity": "sha512-gNyn1KnAOpEa8gSNsYqXMTcq0fSwqU/vit6fP5863vLSKxHm/dNt/gm/uZJxrRZxKq71KUJWF6I7d3z8qIfq5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@expo/spawn-async": "^1.7.2", + "arg": "^5.0.2", + "chalk": "^4.1.2", + "debug": "^4.3.4", + "find-up": "^5.0.0", + "getenv": "^1.0.0", + "minimatch": "^3.0.4", + "p-limit": "^3.1.0", + "resolve-from": "^5.0.0", + "semver": "^7.6.0" + }, + "bin": { + "fingerprint": "bin/cli.js" + } + }, + "node_modules/eoas/node_modules/@expo/fingerprint/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eoas/node_modules/@expo/json-file": { + "version": "9.1.5", + "resolved": "https://registry.npmjs.org/@expo/json-file/-/json-file-9.1.5.tgz", + "integrity": "sha512-prWBhLUlmcQtvN6Y7BpW2k9zXGd3ySa3R6rAguMJkp1z22nunLN64KYTUWfijFlprFoxm9r2VNnGkcbndAlgKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "~7.10.4", + "json5": "^2.2.3" + } + }, + "node_modules/eoas/node_modules/@expo/package-manager": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@expo/package-manager/-/package-manager-1.7.0.tgz", + "integrity": "sha512-yWn5TIjd42wLHZjNtdZkvCkcxqUGxlI4YHb+bQmgm3tWZ8aBHnLhPb0rgU8+hVHCofmRvVUXfVZv8Uh+kkLXgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@expo/json-file": "^9.0.0", + "@expo/spawn-async": "^1.7.2", + "ansi-regex": "^5.0.0", + "chalk": "^4.0.0", + "find-up": "^5.0.0", + "js-yaml": "^3.13.1", + "micromatch": "^4.0.8", + "npm-package-arg": "^11.0.0", + "ora": "^3.4.0", + "resolve-workspace-root": "^2.0.0", + "split": "^1.0.1", + "sudo-prompt": "9.1.1" + } + }, + "node_modules/eoas/node_modules/@expo/package-manager/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/eoas/node_modules/@expo/package-manager/node_modules/js-yaml": { + "version": "3.15.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.2.tgz", + "integrity": "sha512-6EuL879VkRA+1Cz578mKMiKvjPNEuk6+r1JaFzoSWejZmtf7xWbIyw1e3KkxlkzTIt9Taw6JBhEppG7utc1P+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/eoas/node_modules/@expo/plist": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@expo/plist/-/plist-0.2.2.tgz", + "integrity": "sha512-ZZGvTO6vEWq02UAPs3LIdja+HRO18+LRI5QuDl6Hs3Ps7KX7xU6Y6kjahWKY37Rx2YjNpX07dGpBFzzC+vKa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@xmldom/xmldom": "~0.7.7", + "base64-js": "^1.2.3", + "xmlbuilder": "^14.0.0" + } + }, + "node_modules/eoas/node_modules/@expo/spawn-async": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@expo/spawn-async/-/spawn-async-1.7.2.tgz", + "integrity": "sha512-QdWi16+CHB9JYP7gma19OVVg0BFkvU8zNj9GjWorYI8Iv8FUxjOCcYRuAmX4s/h91e4e7BPsskc8cSrZYho9Ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/eoas/node_modules/@urql/core": { + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/@urql/core/-/core-4.0.11.tgz", + "integrity": "sha512-FFdY97vF5xnUrElcGw9erOLvtu+KGMLfwrLNDfv4IPgdp2IBsiGe+Kb7Aypfd3kH//BETewVSLm3+y2sSzjX6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@0no-co/graphql.web": "^1.0.1", + "wonka": "^6.3.2" + } + }, + "node_modules/eoas/node_modules/@urql/exchange-retry": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@urql/exchange-retry/-/exchange-retry-1.2.0.tgz", + "integrity": "sha512-1O/biKiVhhn0EtvDF4UOvz325K4RrLupfL8rHcmqD2TBLv4qVDWQuzx4JGa1FfqjjRb+C9TNZ6w19f32Mq85Ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@urql/core": ">=4.0.0", + "wonka": "^6.3.2" + } + }, + "node_modules/eoas/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eoas/node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/eoas/node_modules/cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eoas/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/eoas/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/eoas/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/eoas/node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/eoas/node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/eoas/node_modules/getenv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/getenv/-/getenv-1.0.0.tgz", + "integrity": "sha512-7yetJWqbS9sbn0vIfliPsFgoXMKn/YMF+Wuiog97x+urnSRRRZ7xB+uVkwGKzRgq9CDFfMQnE9ruL5DHv9c6Xg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/eoas/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/eoas/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/eoas/node_modules/ignore": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", + "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/eoas/node_modules/joi": { + "version": "17.11.0", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.11.0.tgz", + "integrity": "sha512-NgB+lZLNoqISVy1rZocE9PZI36bL/77ie924Ri43yEvi9GUUMPeyVIr8KdFTMUlby1p0PBYMk9spIxEUQYqrJQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0", + "@hapi/topo": "^5.0.0", + "@sideway/address": "^4.1.3", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, + "node_modules/eoas/node_modules/js-yaml": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.4.1.tgz", + "integrity": "sha512-28R/k+NAjeuf7+CKlTxWZVExJGwVVLwY06DgEnOMz2gEpfNkDcD7QvyiVPT0xy0XXhU8vHsd4Ot42OOPdJG7dQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.mjs" + } + }, + "node_modules/eoas/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eoas/node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/eoas/node_modules/mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/eoas/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/eoas/node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/eoas/node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eoas/node_modules/onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eoas/node_modules/ora": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-3.4.0.tgz", + "integrity": "sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-spinners": "^2.0.0", + "log-symbols": "^2.2.0", + "strip-ansi": "^5.2.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/eoas/node_modules/ora/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eoas/node_modules/ora/node_modules/log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eoas/node_modules/restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eoas/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eoas/node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/eoas/node_modules/strip-ansi/node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/eoas/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/eoas/node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eoas/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/eoas/node_modules/write-file-atomic": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", + "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "node_modules/eoas/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, "node_modules/error-ex": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", @@ -17055,6 +18234,12 @@ "expo": "*" } }, + "node_modules/expo-eas-client": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/expo-eas-client/-/expo-eas-client-1.0.8.tgz", + "integrity": "sha512-5or11NJhSeDoHHI6zyvQDW2cz/yFyE+1Cz8NTs5NK8JzC7J0JrkUgptWtxyfB6Xs/21YRNifd3qgbBN3hfKVgA==", + "license": "MIT" + }, "node_modules/expo-file-system": { "version": "19.0.24", "resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-19.0.24.tgz", @@ -17397,6 +18582,42 @@ "react-native": "*" } }, + "node_modules/expo-structured-headers": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/expo-structured-headers/-/expo-structured-headers-5.0.0.tgz", + "integrity": "sha512-RmrBtnSphk5REmZGV+lcdgdpxyzio5rJw8CXviHE6qH5pKQQ83fhMEcigvrkBdsn2Efw2EODp4Yxl1/fqMvOZw==", + "license": "MIT" + }, + "node_modules/expo-updates": { + "version": "29.0.20", + "resolved": "https://registry.npmjs.org/expo-updates/-/expo-updates-29.0.20.tgz", + "integrity": "sha512-9ChWpBaQ1oIIwRRuhFSpwNOJvXeWyTYawcfDLFV0UZBgOwtxgTGdCYbFm4qHAjptmKAPFHHL9bIDgokgkOW9Sg==", + "license": "MIT", + "dependencies": { + "@expo/code-signing-certificates": "^0.0.6", + "@expo/plist": "^0.4.9", + "@expo/spawn-async": "^1.7.2", + "arg": "4.1.0", + "chalk": "^4.1.2", + "debug": "^4.3.4", + "expo-eas-client": "~1.0.8", + "expo-manifests": "~1.0.11", + "expo-structured-headers": "~5.0.0", + "expo-updates-interface": "~2.0.0", + "getenv": "^2.0.0", + "glob": "^13.0.0", + "ignore": "^5.3.1", + "resolve-from": "^5.0.0" + }, + "bin": { + "expo-updates": "bin/cli.js" + }, + "peerDependencies": { + "expo": "*", + "react": "*", + "react-native": "*" + } + }, "node_modules/expo-updates-interface": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/expo-updates-interface/-/expo-updates-interface-2.0.0.tgz", @@ -17406,6 +18627,119 @@ "expo": "*" } }, + "node_modules/expo-updates/node_modules/@expo/plist": { + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/@expo/plist/-/plist-0.4.9.tgz", + "integrity": "sha512-MPVpmKGfnQEnrCzgxuXcmPP/y/t6AVm+DcSb2Myp21LKWv1N3l8uFxMggesfF4ixAxkRlGmMMx9GyDC9M+XklQ==", + "license": "MIT", + "dependencies": { + "@xmldom/xmldom": "^0.8.8", + "base64-js": "^1.2.3", + "xmlbuilder": "^15.1.1" + } + }, + "node_modules/expo-updates/node_modules/@xmldom/xmldom": { + "version": "0.8.15", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.15.tgz", + "integrity": "sha512-/5NV/vDALVFDXgLmfsy9TRCBlKwO2LNBFzpzvb9iIj+jR+eSc6DLYYvVOdivT/jm7MtU6TebYuRmzEOI7w40UA==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/expo-updates/node_modules/arg": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.0.tgz", + "integrity": "sha512-ZWc51jO3qegGkVh8Hwpv636EkbesNV5ZNQPCtRa+0qytRYPEs9IYT9qITY9buezqUH5uqyzlWLcufrzU2rffdg==", + "license": "MIT" + }, + "node_modules/expo-updates/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/expo-updates/node_modules/brace-expansion": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/expo-updates/node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/expo-updates/node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/expo-updates/node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/expo-updates/node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/expo-updates/node_modules/xmlbuilder": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", + "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", + "license": "MIT", + "engines": { + "node": ">=8.0" + } + }, "node_modules/expo/node_modules/@expo/cli": { "version": "54.0.27", "resolved": "https://registry.npmjs.org/@expo/cli/-/cli-54.0.27.tgz", @@ -18356,12 +19690,45 @@ } } }, + "node_modules/fetch-retry": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/fetch-retry/-/fetch-retry-6.0.0.tgz", + "integrity": "sha512-BUFj1aMubgib37I3v4q78fYo63Po7t4HUPTpQ6/QE6yK6cIQrP+W43FYToeTEyg5m2Y7eFUtijUuAv/PDlWuag==", + "dev": true, + "license": "MIT" + }, "node_modules/fflate": { "version": "0.4.9", "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.9.tgz", "integrity": "sha512-zdxgIEddhfsyCaWpJ2SdXEP8ZMrKJ6+5jl4OupODcywU0IhRk6gdXuVGcPICyfx2H97hVK7xmJtRLPjkxAX8Vw==", "license": "MIT" }, + "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/figures/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/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -18375,6 +19742,25 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/file-type": { + "version": "20.5.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-20.5.0.tgz", + "integrity": "sha512-BfHZtG/l9iMm4Ecianu7P8HRD2tBHLtjXinm4X62XBOYzi7CYA7jyqfJzOvXHqzVrVPYqBo2/GvbARMaaJkKVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tokenizer/inflate": "^0.2.6", + "strtok3": "^10.2.0", + "token-types": "^6.0.0", + "uint8array-extras": "^1.4.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -18441,6 +19827,134 @@ "node": ">= 0.8" } }, + "node_modules/find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/find-cache-dir/node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/find-cache-dir/node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/find-cache-dir/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/find-cache-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-cache-dir/node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/find-cache-dir/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/find-cache-dir/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/find-cache-dir/node_modules/pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/find-cache-dir/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -18567,6 +20081,29 @@ "integrity": "sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw==", "license": "MIT" }, + "node_modules/flow-estree": { + "version": "0.329.0", + "resolved": "https://registry.npmjs.org/flow-estree/-/flow-estree-0.329.0.tgz", + "integrity": "sha512-Stv2+LumW7n6DUrV+KlqB4HikeyQtLFUpd9iVexISblopKiYFjNYMmTGUcN5at8NKEmkhjAV5UGps3Z8QqK1lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/flow-parser": { + "version": "0.329.0", + "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.329.0.tgz", + "integrity": "sha512-skMH3HUOF3+JSlIKt4F/LITfzMGEPpJ0S+Gu7HcmxOjWeYEO5PUZJi5WpLw+L4MZaNcMEkzUtoN+M2fAbiutWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "flow-estree": "0.329.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/fontfaceobserver": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/fontfaceobserver/-/fontfaceobserver-2.3.0.tgz", @@ -18616,6 +20153,22 @@ "url": "https://github.com/sponsors/isaacs" } }, + "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==", + "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/form-data-encoder": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", @@ -18726,6 +20279,39 @@ "node": ">=14.14" } }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs-minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -19059,6 +20645,32 @@ "dev": true, "license": "MIT" }, + "node_modules/graphql": { + "version": "16.8.1", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.1.tgz", + "integrity": "sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, + "node_modules/graphql-tag": { + "version": "2.12.7", + "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.7.tgz", + "integrity": "sha512-xnE/NFzy+0eIesvAsREJZ284zTl/wYuBAvpsFSDhRGRdRHdnE90M21Q3xAWyYInb0J756c6x0pIQ62+vtvOs1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "graphql": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" + } + }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", @@ -19137,9 +20749,9 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "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==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -19842,6 +21454,41 @@ "node": ">=0.10.0" } }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container/node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-interactive": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", @@ -19938,6 +21585,19 @@ "node": ">=8" } }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-potential-custom-element-name": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", @@ -20143,6 +21803,16 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "license": "ISC" }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", @@ -21673,6 +23343,74 @@ "integrity": "sha512-0wM3YBWtYePOjfyXQH5MWQ8H7sdk5EXSwZvmSLKk2RboVQ2Bu239jycHDz5J/8Blf3K0Qnoy2b6xD+z10MFB+Q==", "license": "0BSD" }, + "node_modules/jscodeshift": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/jscodeshift/-/jscodeshift-17.4.0.tgz", + "integrity": "sha512-i3ESKiiTsGynxzTg5BhsZViD0ai72/6SsI1efDZxG6/5KCoElsmquxtyhXK5lpEgoO7MTNpYkjFEdEL97SkBNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.7", + "@babel/parser": "^7.24.7", + "@babel/plugin-transform-class-properties": "^7.24.7", + "@babel/plugin-transform-modules-commonjs": "^7.24.7", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", + "@babel/plugin-transform-optional-chaining": "^7.24.7", + "@babel/plugin-transform-private-methods": "^7.24.7", + "@babel/preset-flow": "^7.24.7", + "@babel/preset-typescript": "^7.24.7", + "@babel/register": "^7.24.6", + "flow-parser": "0.*", + "graceful-fs": "^4.2.4", + "neo-async": "^2.5.0", + "picocolors": "^1.0.1", + "picomatch": "^4.0.2", + "recast": "^0.23.11", + "tmp": "^0.2.3", + "write-file-atomic": "^5.0.1" + }, + "bin": { + "jscodeshift": "bin/jscodeshift.js" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@babel/preset-env": "^7.1.6" + }, + "peerDependenciesMeta": { + "@babel/preset-env": { + "optional": true + } + } + }, + "node_modules/jscodeshift/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jscodeshift/node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/jsdom": { "version": "20.0.3", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", @@ -21719,23 +23457,6 @@ } } }, - "node_modules/jsdom/node_modules/form-data": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", - "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", - "dev": true, - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -21859,6 +23580,16 @@ "json-buffer": "3.0.1" } }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/klaw-sync": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", @@ -23167,6 +24898,17 @@ "dev": true, "license": "MIT" }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": "*" + } + }, "node_modules/motion-dom": { "version": "11.18.1", "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.18.1.tgz", @@ -23188,6 +24930,70 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "node_modules/mv": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", + "integrity": "sha512-at/ZndSy3xEGJ8i0ygALh8ru9qy7gWW1cmkaqBN29JmMlIvM//MEO9y1sk/avxuwnPcfhkejkLsuPxH81BrkSg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "mkdirp": "~0.5.1", + "ncp": "~2.0.0", + "rimraf": "~2.4.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/mv/node_modules/glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "optional": true, + "dependencies": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mv/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mv/node_modules/rimraf": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "integrity": "sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "optional": true, + "dependencies": { + "glob": "^6.0.1" + }, + "bin": { + "rimraf": "bin.js" + } + }, "node_modules/mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", @@ -23199,6 +25005,14 @@ "thenify-all": "^1.0.0" } }, + "node_modules/nan": { + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.28.0.tgz", + "integrity": "sha512-fTsDz99OTq2sVePhGdp4qQhggZFtKr64ZNVyVajRKtMOkJxYekplBh577PiJB12v/D3s2E5cGtOI45LWp6rnLQ==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", @@ -23240,6 +25054,17 @@ "devOptional": true, "license": "MIT" }, + "node_modules/ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==", + "dev": true, + "license": "MIT", + "optional": true, + "bin": { + "ncp": "bin/ncp" + } + }, "node_modules/negotiator": { "version": "0.6.4", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", @@ -23249,6 +25074,13 @@ "node": ">= 0.6" } }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, "node_modules/nested-error-stacks": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.0.1.tgz", @@ -24665,6 +26497,19 @@ "integrity": "sha512-q6LWsLatGYZp5VGBIOvbTj6JBV2nOmC8KvWztXBmwJcfFAzhwKwbOxhUH306XY3CcaZDUlSmSuNPBsCn0bFu+g==", "license": "Apache-2.0" }, + "node_modules/powershell-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.1.0.tgz", + "integrity": "sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/preact": { "version": "10.29.8", "resolved": "https://registry.npmjs.org/preact/-/preact-10.29.8.tgz", @@ -24693,6 +26538,22 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.1.tgz", + "integrity": "sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", @@ -25636,6 +27497,23 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/recast": { + "version": "0.23.21", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.21.tgz", + "integrity": "sha512-mFAyJq9vUbSTARLZUvAEf1z3YxlvAwswbmxMx2mPA/MSm4KmpwvwvhsH/NIrZhyOuwD60Lzyw2qh83uCbgTPYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ast-types": "^0.16.1", + "esprima": "~4.0.0", + "source-map": "~0.6.1", + "tiny-invariant": "^1.3.3", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">= 4" + } + }, "node_modules/recharts": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.7.0.tgz", @@ -26123,6 +28001,14 @@ ], "license": "MIT" }, + "node_modules/safe-json-stringify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", + "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/safe-push-apply": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", @@ -26389,6 +28275,19 @@ "node": ">=10" } }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/shallowequal": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", @@ -26654,6 +28553,19 @@ "source-map": "^0.6.0" } }, + "node_modules/split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, "node_modules/split-on-first": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", @@ -27142,6 +29054,23 @@ ], "license": "MIT" }, + "node_modules/strtok3": { + "version": "10.3.5", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.5.tgz", + "integrity": "sha512-ki4hZQfh5rX0QDLLkOCj+h+CVNkqmp/CMf8v8kZpkNVK6jGQooMytqzLZYUVYIZcFZ6yDB70EfD8POcFXiF5oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tokenizer/token": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/structured-headers": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/structured-headers/-/structured-headers-0.4.1.tgz", @@ -27210,6 +29139,14 @@ "node": ">= 6" } }, + "node_modules/sudo-prompt": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/sudo-prompt/-/sudo-prompt-9.1.1.tgz", + "integrity": "sha512-es33J1g2HjMpyAhz8lOR+ICmXXAqTuKbuXuUWLhOLew20oN9oUCgCJx615U/v7aioZg7IX5lIh9x34vwneu4pA==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT" + }, "node_modules/supabase": { "version": "2.116.0", "resolved": "https://registry.npmjs.org/supabase/-/supabase-2.116.0.tgz", @@ -27520,6 +29457,13 @@ "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==" }, + "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/tiny-invariant": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", @@ -27623,6 +29567,25 @@ "node": ">=0.6" } }, + "node_modules/token-types": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.2.tgz", + "integrity": "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@borewit/text-codec": "^0.2.1", + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/tough-cookie": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", @@ -29079,6 +31042,19 @@ "dev": true, "license": "MIT" }, + "node_modules/uint8array-extras": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz", + "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/unbox-primitive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", @@ -29882,6 +31858,51 @@ "node": ">=8" } }, + "node_modules/widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/widest-line/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/widest-line/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/widest-line/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/wonka": { "version": "6.3.6", "resolved": "https://registry.npmjs.org/wonka/-/wonka-6.3.6.tgz", @@ -29898,6 +31919,13 @@ "node": ">=0.10.0" } }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -30031,6 +32059,39 @@ } } }, + "node_modules/wsl-utils": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.4.0.tgz", + "integrity": "sha512-9YmF+2sFEd+T7TkwlmE337F0IVzfDvDknhtpBQxxXzEOfgPphGlFYpyx0cTuCIFj8/p+sqwBYAeGxOMNSzPPDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0", + "powershell-utils": "^0.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wsl-utils/node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/xcode": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/xcode/-/xcode-3.0.1.tgz", From 0ada812294f24701bd823d074b128f05d8363970 Mon Sep 17 00:00:00 2001 From: jjbrunton Date: Sun, 30 Aug 2026 20:12:51 +0100 Subject: [PATCH 2/3] docs(mobile): document the XPREM over-the-air update flow Add docs/mobile-ota.md covering how OTA works here, the branch and channel model, the appVersion runtime version policy and its store-build rule, the eoas publish commands per channel, both rollback paths, the pending native prebuild, and the exact placeholders the server deployment still has to fill. Index it and cross-link it from the release guide. Co-Authored-By: Claude Fable 5 --- docs/index.md | 1 + docs/mobile-ota.md | 245 +++++++++++++++++++++++++++++++++++++++++++++ docs/releasing.md | 5 + 3 files changed, 251 insertions(+) create mode 100644 docs/mobile-ota.md diff --git a/docs/index.md b/docs/index.md index e8442bd..2744e05 100644 --- a/docs/index.md +++ b/docs/index.md @@ -23,6 +23,7 @@ historical notes. Agents should load only the section relevant to their task. - [Subscriptions](subscription-system.md) - [Redemption codes](product/redemption.md) - [Mobile release process](releasing.md) +- [Mobile over-the-air updates](mobile-ota.md) - [Dares loop](dares-loop.md) - [Couples quiz](quiz.md) diff --git a/docs/mobile-ota.md b/docs/mobile-ota.md new file mode 100644 index 0000000..9123085 --- /dev/null +++ b/docs/mobile-ota.md @@ -0,0 +1,245 @@ +# Mobile over-the-air updates + +Sauci ships JavaScript-only mobile changes over the air with `expo-updates` +against a self-hosted XPREM update server (formerly `expo-open-ota`) at +`https://ota.apps.jbrunton.co.uk`. XPREM speaks the official Expo Updates +protocol, so the client is stock `expo-updates`; only the endpoint, the request +headers, and the code-signing certificate are Sauci specific. + +Store releases still follow [Mobile release process](releasing.md). OTA never +replaces a store build; it only refreshes the JavaScript bundle and assets of an +already installed build. + +## Status + +The client is wired but not yet live. Two values from the server deployment are +still outstanding; see [Pending server values](#pending-server-values). + +## How it works + +1. A build embeds a manifest URL, a release channel, an app id, and a public + code-signing certificate. +2. On launch the app requests `https://ota.apps.jbrunton.co.uk/manifest`, sending + `expo-channel-name`, `expo-app-id`, `xprem-branch`, and the standard + `expo-runtime-version` and `expo-platform` headers. +3. XPREM resolves the channel to a branch, finds the newest update published for + that branch and runtime version, and returns a signed manifest. +4. The client verifies the signature against the embedded certificate, downloads + the bundle, and launches it on the next start. + +### Branches and channels + +XPREM separates storage from delivery. A **branch** groups updates for a release +line. A **release channel** is bound to a build at build time and points at a +branch. Repointing a channel at a different branch promotes or reverts an update +without a rebuild. + +| EAS build profile | `RELEASE_CHANNEL` | Publish branch | +| --- | --- | --- | +| `development` | `development` | `development` | +| `preview` | `staging` | `staging` | +| `production` | `production` | `production` | + +Channels and their branch mappings are created in the XPREM dashboard under +Channels. The client cannot create them. + +### Runtime version + +`apps/mobile/app.config.js` sets: + +```js +runtimeVersion: { policy: 'appVersion' } +``` + +The runtime version is therefore the `expo.version` field in `app.json`, for +example `1.0.6`. An update is only offered to builds whose runtime version +matches exactly. + +The practical rule: **any change that touches native code, native dependencies, +or the `ios/`/`android/` projects requires a new store build with a bumped +`expo.version`.** Bumping `version` starts a fresh runtime version on the server, +so old builds stop receiving updates that assume the new native surface. +JavaScript, asset, and configuration changes that do not alter the native binary +can ship over the air against the current `version`. + +`appVersion` is chosen over `fingerprint` deliberately. Sauci commits its native +projects and ships several config plugins and an Apple widget target, so a +fingerprint would churn on native edits that never reach a device, and it would +be harder to reason about from a store release. `appVersion` ties the runtime +version to the number already visible in App Store Connect and Google Play. + +## Client configuration + +`apps/mobile/app.config.js` is a dynamic Expo config that spreads `app.json` and +layers on the `updates` block. `app.json` remains the static source of truth for +`version`, `ios.buildNumber`, and `android.versionCode`, which EAS rewrites +during a release build. + +Resolved configuration: + +| Key | Value | +| --- | --- | +| `updates.url` | `https://ota.apps.jbrunton.co.uk/manifest` | +| `updates.enabled` | `true` | +| `updates.checkAutomatically` | `ON_LOAD` | +| `updates.fallbackToCacheTimeout` | `5000` | +| `updates.codeSigningCertificate` | `./certs/certificate.pem` | +| `updates.codeSigningMetadata` | `{ keyid: 'main', alg: 'rsa-v1_5-sha256' }` | +| `updates.requestHeaders['expo-channel-name']` | `process.env.RELEASE_CHANNEL`, defaulting to `development` | +| `updates.requestHeaders['expo-app-id']` | `process.env.XPREM_APP_ID` | +| `updates.requestHeaders['xprem-branch']` | `''` | +| `runtimeVersion` | `{ policy: 'appVersion' }` | + +`expo-updates` only sends request headers that were declared at build time, so +`xprem-branch` is declared even though it is empty. Removing it would not disable +branch surfing, it would strip the header from every poll for the life of the +install. + +`RELEASE_CHANNEL` is set per profile in `apps/mobile/eas.json`. The config throws +a descriptive error whenever `RELEASE_CHANNEL` is set but the app id or the +certificate is still a placeholder, so a misconfigured build fails at config +resolution rather than shipping a client that can never validate a manifest. + +### Local development + +The dev server serves bundles directly and no signed manifest exists, so disable +code signing when running a dev client: + +```bash +cd apps/mobile +DISABLE_CODE_SIGNING=1 npx expo start --dev-client +``` + +## Code signing + +XPREM signs every manifest with an RSA private key held by the server. The app +embeds only the matching public certificate. + +- `apps/mobile/certs/certificate.pem` is the public certificate. It is public key + material and is committed. +- `private-key.pem` belongs to the update server only. `apps/mobile/certs/.gitignore` + blocks it. It must never be committed or kept in a developer checkout. + +The keypair is generated once, on the machine that configures the server: + +```bash +npx eoas generate-certs +``` + +The certificate can also be downloaded from the XPREM dashboard App Info page for +the Sauci app. + +## Native projects + +`apps/mobile/ios` and `apps/mobile/android` are committed but generated by Expo +CNG, and `docs/releasing.md` treats `expo prebuild` as a source change rather +than a release step. The native projects currently still carry the +updates-disabled defaults: + +- `ios/Sauci/Supporting/Expo.plist`: `EXUpdatesEnabled` is `false`. +- `android/app/src/main/AndroidManifest.xml`: `expo.modules.updates.ENABLED` is + `false`. + +The `expo-updates` config plugin writes the URL, the resolved runtime version, +the request headers, the code-signing metadata, and the certificate contents into +these files. Those values embed the real app id and certificate, so the native +projects are deliberately left untouched until both land. + +Once the pending values below are in place, regenerate and commit the native +projects as a reviewed source change: + +```bash +cd apps/mobile +RELEASE_CHANNEL=production npx expo prebuild +git diff # expect only expo-updates related native changes +``` + +A build produced before that prebuild will not check for updates. + +## Publishing an update + +Publishing uses the XPREM CLI, `eoas`, pinned as a dev dependency of +`apps/mobile`. Authentication is an app-scoped API token generated in the XPREM +dashboard and supplied as `EOO_TOKEN`; it is a secret and must not be committed. + +```bash +cd apps/mobile + +# Staging: consumed by builds made with the EAS `preview` profile. +EOO_TOKEN= npm run ota:publish:staging + +# Production. +EOO_TOKEN= npm run ota:publish:production +``` + +Both scripts wrap `eoas publish --branch ` with the matching +`RELEASE_CHANNEL`. Useful flags to pass through: + +| Flag | Purpose | +| --- | --- | +| `--platform ios\|android\|all` | Defaults to `all`. | +| `-m ""` | Update description shown in the dashboard. | +| `--rollout-percentage <1-99>` | Progressive rollout to a fraction of devices. | +| `--nonInteractive` | Required in CI. | + +`eoas publish` refuses to run against a dirty git working tree. Commit first +rather than reaching for `--disableRepositoryCheck` or `EAS_NO_VCS=1`; the +published update records the commit it came from. + +Publish only from a commit that has passed `npm run verify:full`. An OTA update +reaches installed devices without store review, so it carries the same risk as a +production deploy and none of the review latency. + +### Verifying a published update + +```bash +curl -sD - "https://ota.apps.jbrunton.co.uk/manifest" \ + -H "expo-app-id: " \ + -H "expo-channel-name: production" \ + -H "expo-runtime-version: 1.0.6" \ + -H "expo-platform: ios" +``` + +## Rollback + +Two mechanisms, in order of preference: + +1. **Repoint the channel.** In the XPREM dashboard, map the affected channel to a + branch holding the last known good update. This takes effect at each app's + next update check and needs no rebuild and no republish. +2. **Roll back from the CLI.** + + ```bash + cd apps/mobile + EOO_TOKEN= npm run ota:rollback + ``` + + `eoas rollback` publishes a rollback directive for a branch and runtime + version, sending clients back to the bundle embedded in the store build. + `eoas republish` can instead re-serve a specific earlier update. + +A bad update that prevents the app from starting cannot be fixed over the air on +devices that never complete a launch. Keep `fallbackToCacheTimeout` short and +treat a startup-path change as store-release risk, not OTA risk. + +## Pending server values + +The client wiring is complete apart from two values owned by the XPREM server +deployment. Both are non-secret. + +| Value | Where it goes | How to obtain it | +| --- | --- | --- | +| XPREM app id (UUID) | `OTA_APP_ID_PLACEHOLDER` in `apps/mobile/app.config.js`, or the `XPREM_APP_ID` env var | XPREM dashboard, App Info page for the Sauci app | +| Code-signing certificate (PEM) | Replace the whole of `apps/mobile/certs/certificate.pem` | XPREM dashboard App Info download, or `npx eoas generate-certs` | + +Also required, outside this repository: + +- An `EOO_TOKEN` publish token per environment, stored as a secret. +- `development`, `staging`, and `production` channels created in the dashboard + and mapped to branches of the same names. +- The `private-key.pem` counterpart of the certificate installed in the server key + store. + +After both values land: replace them, run the prebuild described in +[Native projects](#native-projects), commit the native diff, and cut a new store +build. Only builds produced after that point can receive updates. diff --git a/docs/releasing.md b/docs/releasing.md index abb9f84..faaba30 100644 --- a/docs/releasing.md +++ b/docs/releasing.md @@ -2,6 +2,11 @@ This guide covers local release builds for Android and iOS. +JavaScript-only changes can also ship over the air between store releases. See +[Mobile over-the-air updates](mobile-ota.md). Anything that changes native code +still requires a store build with a bumped `expo.version`, because the OTA +runtime version follows the app version. + ## Prerequisites - **Java JDK 17** - Required for Android Gradle builds From f37673cad70e5de7671c6f81f1e8cd7dfb5e07d9 Mon Sep 17 00:00:00 2001 From: jjbrunton Date: Sun, 30 Aug 2026 20:16:12 +0100 Subject: [PATCH 3/3] feat(mobile): finalize XPREM OTA config with real app id and certificate Server registration is complete: app id 9a66f3c2-fda7-416f-87e2-6db833b32e9d, real code-signing certificate (public half), staging/production channels mapped server-side. Drops the app-id placeholder guard (cert guard stays) and re-includes certificate.pem past the root *.pem ignore rule, which had been silently excluding it. Co-Authored-By: Claude Fable 5 --- apps/mobile/app.config.js | 15 +++-------- apps/mobile/certs/.gitignore | 3 +++ apps/mobile/certs/certificate.pem | 18 +++++++++++++ docs/mobile-ota.md | 44 +++++++++++++++---------------- 4 files changed, 45 insertions(+), 35 deletions(-) create mode 100644 apps/mobile/certs/certificate.pem diff --git a/apps/mobile/app.config.js b/apps/mobile/app.config.js index 3bb6bae..761a5b2 100644 --- a/apps/mobile/app.config.js +++ b/apps/mobile/app.config.js @@ -23,11 +23,10 @@ const OTA_SERVER_URL = 'https://ota.apps.jbrunton.co.uk'; const OTA_MANIFEST_URL = `${OTA_SERVER_URL}/manifest`; /** - * XPREM application UUID, shown in the XPREM dashboard under App Info. - * PLACEHOLDER until the update server deployment reports the real value. + * XPREM application UUID for Sauci, shown in the XPREM dashboard under App + * Info. Not a secret; overridable for pointing a build at another app. */ -const OTA_APP_ID_PLACEHOLDER = 'REPLACE_WITH_XPREM_APP_ID'; -const OTA_APP_ID = process.env.XPREM_APP_ID || OTA_APP_ID_PLACEHOLDER; +const OTA_APP_ID = process.env.XPREM_APP_ID || '9a66f3c2-fda7-416f-87e2-6db833b32e9d'; /** Public code-signing certificate downloaded from the XPREM dashboard. */ const CODE_SIGNING_CERTIFICATE_PATH = './certs/certificate.pem'; @@ -50,14 +49,6 @@ const releaseChannel = process.env.RELEASE_CHANNEL || 'development'; const codeSigningDisabled = Boolean(process.env.DISABLE_CODE_SIGNING); function assertUpdateConfigIsReal() { - if (OTA_APP_ID === OTA_APP_ID_PLACEHOLDER) { - throw new Error( - `RELEASE_CHANNEL=${releaseChannel} requires a real XPREM app id. ` + - 'Set XPREM_APP_ID, or replace the placeholder in apps/mobile/app.config.js. ' + - 'See docs/mobile-ota.md.', - ); - } - const certificatePath = path.join(__dirname, CODE_SIGNING_CERTIFICATE_PATH); const certificate = fs.existsSync(certificatePath) ? fs.readFileSync(certificatePath, 'utf8') diff --git a/apps/mobile/certs/.gitignore b/apps/mobile/certs/.gitignore index 9c4f928..f69292b 100644 --- a/apps/mobile/certs/.gitignore +++ b/apps/mobile/certs/.gitignore @@ -1,3 +1,6 @@ # The update-signing private key belongs to the XPREM server, never to the app. private-key.pem *.key +# The public certificate must be committed despite the root *.pem rule: the +# expo-updates plugin embeds it into every build. +!certificate.pem diff --git a/apps/mobile/certs/certificate.pem b/apps/mobile/certs/certificate.pem new file mode 100644 index 0000000..dc93cd9 --- /dev/null +++ b/apps/mobile/certs/certificate.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIICzTCCAbWgAwIBAgIJAPrPjxnUOGisMA0GCSqGSIb3DQEBCwUAMBAxDjAMBgNV +BAMTBVNhdWNpMB4XDTI2MDgzMDE4MTAzMFoXDTM2MDgzMDE4MTAzMFowEDEOMAwG +A1UEAxMFU2F1Y2kwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDWiUac +8i9bIbNZjv8iunVr6kqlgb9vea98JHz/A83biyszg0+GpmLYsr4zV2F7A6RitiIx +SOML+wVedyD0F2quWccBGXinvMAds0TxgIOjQfP+8+VJ/duDjveHvn+OFW4s7XxW +SY7TK1ufDcPWo/f5XzfqtBveug49zv3/F3MwfmMUbEY/zvH7nayPGJNuSyf3wISn +JIt0BsHNhWykRXCzjaF2fy3nAebOyezbtbapCjUlxPENVtBCzBQ7C9kaQXCBw5mV +hxhJlOxHWXpHJgDLHpev8A/CWQRh67tsevjKi6R6SDlf0R0xSbDJTElx8goWOOQW +VO9aDqI+oH0KpWfnAgMBAAGjKjAoMA4GA1UdDwEB/wQEAwIHgDAWBgNVHSUBAf8E +DDAKBggrBgEFBQcDAzANBgkqhkiG9w0BAQsFAAOCAQEAyUc20bIRCxoBTJg84mPl +2nFDWSaAfzgo0K0b6x2T1suuq4LAOUgzBqJISrQAALd0besK1qUZSAMhjQu1wPZT +MHuWRMxQHfOkyHMYUfOoPMS4bE+4K0WjzfoVt3y1A+oxhcIAQUUGyNgNmebfEN07 +JlX0B8UI8aIMjyfpsIDgZx1piHKwittGy/V4MPOLbmT2BBwS9BELcmWE4CwWbfeo +JM4lXY0hk3M/GTstRYaUKiwBWEU+z1PdAmWAgrtxpVnXN+oaToWh0WbVkbF1K6r5 +3oBGFTL7kT+Wogg9GK04XoAREw0jZJ/k77oJx68lGVBNiB+Ue2KqX0H/0cf2CslY +Ug== +-----END CERTIFICATE----- diff --git a/docs/mobile-ota.md b/docs/mobile-ota.md index 9123085..2c41ec1 100644 --- a/docs/mobile-ota.md +++ b/docs/mobile-ota.md @@ -12,8 +12,10 @@ already installed build. ## Status -The client is wired but not yet live. Two values from the server deployment are -still outstanding; see [Pending server values](#pending-server-values). +The client is wired with the real server values (app id +`9a66f3c2-fda7-416f-87e2-6db833b32e9d` and the Sauci code-signing certificate, +SHA-256 fingerprint beginning `DF:C9:17:A9`). What remains is the native +prebuild and a store build; see [Server registration](#server-registration). ## How it works @@ -96,9 +98,10 @@ branch surfing, it would strip the header from every poll for the life of the install. `RELEASE_CHANNEL` is set per profile in `apps/mobile/eas.json`. The config throws -a descriptive error whenever `RELEASE_CHANNEL` is set but the app id or the -certificate is still a placeholder, so a misconfigured build fails at config -resolution rather than shipping a client that can never validate a manifest. +a descriptive error whenever `RELEASE_CHANNEL` is set but the committed +certificate is missing or is the placeholder, so a misconfigured build fails at +config resolution rather than shipping a client that can never validate a +manifest. ### Local development @@ -145,8 +148,8 @@ the request headers, the code-signing metadata, and the certificate contents int these files. Those values embed the real app id and certificate, so the native projects are deliberately left untouched until both land. -Once the pending values below are in place, regenerate and commit the native -projects as a reviewed source change: +With the real app id and certificate now committed, regenerate and commit the +native projects as a reviewed source change: ```bash cd apps/mobile @@ -222,24 +225,19 @@ A bad update that prevents the app from starting cannot be fixed over the air on devices that never complete a launch. Keep `fallbackToCacheTimeout` short and treat a startup-path change as store-release risk, not OTA risk. -## Pending server values +## Server registration -The client wiring is complete apart from two values owned by the XPREM server -deployment. Both are non-secret. +The Sauci app is registered on the XPREM server; the deployment itself is +documented in the homelab repo (`unraid/docs/xprem-ota.md`). -| Value | Where it goes | How to obtain it | -| --- | --- | --- | -| XPREM app id (UUID) | `OTA_APP_ID_PLACEHOLDER` in `apps/mobile/app.config.js`, or the `XPREM_APP_ID` env var | XPREM dashboard, App Info page for the Sauci app | -| Code-signing certificate (PEM) | Replace the whole of `apps/mobile/certs/certificate.pem` | XPREM dashboard App Info download, or `npx eoas generate-certs` | +| Value | State | +| --- | --- | +| App id | `9a66f3c2-fda7-416f-87e2-6db833b32e9d`, hardcoded in `apps/mobile/app.config.js` (non-secret) | +| Code-signing certificate | Committed at `apps/mobile/certs/certificate.pem` (public half; CN=Sauci, valid to 2036, SHA-256 fingerprint `DF:C9:17:A9:85:02:DD:EB:86:06:2B:64:8D:73:D2:8C:A2:AA:29:F0:AC:62:A8:76:62:A1:6B:A2:42:B0:3D:71`) | +| Signing private key | Never leaves the server: keys mode `database`, sealed in the server's Postgres under its master key | +| Publish token | `XPREM_SAUCI_API_KEY` in the homelab repo `.env`; export as `EOO_TOKEN` when publishing | +| Channels | `staging` and `production` created server-side, mapped 1:1 to branches of the same names. `development` intentionally has no server channel — dev clients run with `DISABLE_CODE_SIGNING=1` against the packager | -Also required, outside this repository: - -- An `EOO_TOKEN` publish token per environment, stored as a secret. -- `development`, `staging`, and `production` channels created in the dashboard - and mapped to branches of the same names. -- The `private-key.pem` counterpart of the certificate installed in the server key - store. - -After both values land: replace them, run the prebuild described in +Remaining before updates flow: run the prebuild described in [Native projects](#native-projects), commit the native diff, and cut a new store build. Only builds produced after that point can receive updates.