diff --git a/oxlint-suppressions.json b/oxlint-suppressions.json index ae3513a254..197bd43b1a 100644 --- a/oxlint-suppressions.json +++ b/oxlint-suppressions.json @@ -1456,11 +1456,6 @@ "count": 1 } }, - "src/state/session/agent.ts": { - "typescript/no-explicit-any": { - "count": 1 - } - }, "src/state/shell/color-mode.tsx": { "typescript/no-floating-promises": { "count": 2 diff --git a/package.json b/package.json index 51febee03a..93d671ff31 100644 --- a/package.json +++ b/package.json @@ -98,6 +98,8 @@ "dependencies": { "@atproto/api": "0.20.39", "@atproto/common-web": "0.5.7", + "@atproto/lex-client": "0.3.0", + "@atproto/lex-password-session": "0.1.8", "@atproto/syntax": "0.7.2", "@bitdrift/react-native": "^0.6.8", "@braintree/sanitize-url": "^6.0.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6ffd6a36f6..b4baf4a374 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -248,51 +248,57 @@ importers: '@atproto/common-web': specifier: 0.5.7 version: 0.5.7 + '@atproto/lex-client': + specifier: 0.3.0 + version: 0.3.0 + '@atproto/lex-password-session': + specifier: 0.1.8 + version: 0.1.8 '@atproto/syntax': specifier: 0.7.2 version: 0.7.2 '@bitdrift/react-native': specifier: ^0.6.8 - version: 0.6.14(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 0.6.14(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) '@braintree/sanitize-url': specifier: ^6.0.2 version: 6.0.4 '@bsky.app/alf': specifier: ^0.1.15 - version: 0.1.15(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 0.1.15(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) '@bsky.app/expo-dynamic-app-icon': specifier: ^1.8.5 - version: 1.8.5(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 1.8.5(83e0f64f4281e4798012214ad5b2d72d) '@bsky.app/expo-guess-language': specifier: ^0.2.8 - version: 0.2.8(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 0.2.8(83e0f64f4281e4798012214ad5b2d72d) '@bsky.app/expo-image-crop-tool': specifier: ^0.5.1 - version: 0.5.1(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 0.5.1(83e0f64f4281e4798012214ad5b2d72d) '@bsky.app/expo-scroll-edge-effect': specifier: ^0.1.9 - version: 0.1.9(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 0.1.9(83e0f64f4281e4798012214ad5b2d72d) '@bsky.app/expo-translate-text': specifier: ^0.2.9 - version: 0.2.9(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 0.2.9(83e0f64f4281e4798012214ad5b2d72d) '@bsky.app/peek-menu': specifier: ^0.3.2 - version: 0.3.2(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 0.3.2(83e0f64f4281e4798012214ad5b2d72d) '@bsky.app/react-native-uitextview': specifier: ^2.7.0 - version: 2.7.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 2.7.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) '@bsky.app/sift': specifier: ^0.3.9 - version: 0.3.9(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 0.3.9(2d5c14016450c7e55c8cd0dffcca6f69) '@bsky.app/tapper': specifier: ^0.6.1 - version: 0.6.1(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)(tlds@1.261.0) + version: 0.6.1(8d6e1ad3cb49b1a6214533c496326d11) '@bsky.app/video': specifier: 0.3.6 - version: 0.3.6(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 0.3.6(83e0f64f4281e4798012214ad5b2d72d) '@bsky.app/video-compressor': specifier: 0.2.0 - version: 0.2.0(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 0.2.0(83e0f64f4281e4798012214ad5b2d72d) '@emoji-mart/data': specifier: ^1.2.1 version: 1.2.1 @@ -304,7 +310,7 @@ importers: version: 0.12.5 '@expo/webpack-config': specifier: ^19.0.1 - version: 19.0.1(clean-css@5.3.3)(csso@5.0.5)(esbuild@0.25.12)(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(html-minifier-terser@6.1.0)(postcss@8.5.14) + version: 19.0.1(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(debug@4.4.3(supports-color@8.1.1))(esbuild@0.25.12)(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1))(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)(supports-color@8.1.1) '@floating-ui/dom': specifier: ^1.6.3 version: 1.7.6 @@ -334,25 +340,25 @@ importers: version: 9.2.7 '@lingui/core': specifier: ^5.9.2 - version: 5.9.5(@lingui/babel-plugin-lingui-macro@5.9.5(@typescript/typescript6@6.0.2)) + version: 5.9.5(@lingui/babel-plugin-lingui-macro@5.9.5(@typescript/typescript6@6.0.2)(supports-color@8.1.1)) '@lingui/react': specifier: ^5.9.2 - version: 5.9.5(@lingui/babel-plugin-lingui-macro@5.9.5(@typescript/typescript6@6.0.2))(react@19.1.0) + version: 5.9.5(@lingui/babel-plugin-lingui-macro@5.9.5(@typescript/typescript6@6.0.2)(supports-color@8.1.1))(react@19.1.0) '@react-native-async-storage/async-storage': specifier: 2.2.0 - version: 2.2.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0)) + version: 2.2.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1)) '@react-navigation/bottom-tabs': specifier: ^7.15.5 - version: 7.16.0(91eab7bd987834c4c877d5cbf9d3796c) + version: 7.16.0(15d086c577324dcb87d77ea7fa3207d2) '@react-navigation/native': specifier: ^7.1.33 - version: 7.2.4(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 7.2.4(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) '@react-navigation/native-stack': specifier: ^7.14.4 - version: 7.15.0(91eab7bd987834c4c877d5cbf9d3796c) + version: 7.15.0(15d086c577324dcb87d77ea7fa3207d2) '@sentry/react-native': specifier: ~8.18.0 - version: 8.18.0(@expo/env@2.0.11)(dotenv@16.6.1)(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 8.18.0(61f03fc01fabd5cbd0e83cc2a57309d9) '@tanstack/query-async-storage-persister': specifier: ^5.96.2 version: 5.100.10 @@ -433,115 +439,115 @@ importers: version: 5.0.4 expo: specifier: 54.0.35 - version: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) expo-age-range: specifier: 0.2.18 - version: 0.2.18(patch_hash=c325225749993424461eeece1d97a99b19c00da2ebc958a73c12e4eca0c39306)(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0)) + version: 0.2.18(patch_hash=c325225749993424461eeece1d97a99b19c00da2ebc958a73c12e4eca0c39306)(83f21d12a6c1aa99b4a65d070875cc68) expo-application: specifier: ~7.0.8 - version: 7.0.8(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)) + version: 7.0.8(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)) expo-asset: specifier: ~12.0.13 - version: 12.0.13(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 12.0.13(93a6abd34ab5ae64aff6a80525489f34) expo-blur: specifier: ~15.0.8 - version: 15.0.8(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 15.0.8(83e0f64f4281e4798012214ad5b2d72d) expo-build-properties: specifier: ~1.0.10 - version: 1.0.10(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)) + version: 1.0.10(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)) expo-camera: specifier: ~17.0.10 - version: 17.0.10(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-web@0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 17.0.10(8d9f18414d5ddcf91a3e8d2e3aa26778) expo-clipboard: specifier: ~8.0.8 - version: 8.0.8(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 8.0.8(83e0f64f4281e4798012214ad5b2d72d) expo-contacts: specifier: ^15.0.10 - version: 15.0.11(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 15.0.11(83e0f64f4281e4798012214ad5b2d72d) expo-dev-client: specifier: ~6.0.20 - version: 6.0.21(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)) + version: 6.0.21(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1))(supports-color@8.1.1) expo-device: specifier: ~8.0.10 - version: 8.0.10(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)) + version: 8.0.10(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)) expo-file-system: specifier: ~19.0.21 - version: 19.0.22(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0)) + version: 19.0.22(83f21d12a6c1aa99b4a65d070875cc68) expo-font: specifier: ~14.0.11 - version: 14.0.11(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 14.0.11(83e0f64f4281e4798012214ad5b2d72d) expo-glass-effect: specifier: 0.1.10 - version: 0.1.10(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 0.1.10(83e0f64f4281e4798012214ad5b2d72d) expo-haptics: specifier: ~15.0.8 - version: 15.0.8(patch_hash=b33910ba2753c4eccdc885b449e6e33aa90b9cefa75ca8639762a26013141410)(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)) + version: 15.0.8(patch_hash=b33910ba2753c4eccdc885b449e6e33aa90b9cefa75ca8639762a26013141410)(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)) expo-image: specifier: ~3.0.11 - version: 3.0.11(patch_hash=6d46ffac33426c5285777da5bf5f88c68c8ce27290595dfa65f38c478930c2c4)(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-web@0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 3.0.11(patch_hash=6d46ffac33426c5285777da5bf5f88c68c8ce27290595dfa65f38c478930c2c4)(8d9f18414d5ddcf91a3e8d2e3aa26778) expo-image-manipulator: specifier: ~14.0.8 - version: 14.0.8(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)) + version: 14.0.8(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)) expo-image-picker: specifier: ~17.0.10 - version: 17.0.11(patch_hash=47b28f6ddb8bcaa6483f8714c82daaa0001122f2b0dac6c05d19e98a61a6ceab)(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)) + version: 17.0.11(patch_hash=47b28f6ddb8bcaa6483f8714c82daaa0001122f2b0dac6c05d19e98a61a6ceab)(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)) expo-intent-launcher: specifier: ~13.0.8 - version: 13.0.8(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)) + version: 13.0.8(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)) expo-keep-awake: specifier: ~15.0.8 - version: 15.0.8(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react@19.1.0) + version: 15.0.8(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) expo-linear-gradient: specifier: ~15.0.8 - version: 15.0.8(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 15.0.8(83e0f64f4281e4798012214ad5b2d72d) expo-linking: specifier: ~8.0.11 - version: 8.0.12(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 8.0.12(93a6abd34ab5ae64aff6a80525489f34) expo-localization: specifier: ~17.0.8 - version: 17.0.8(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react@19.1.0) + version: 17.0.8(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) expo-location: specifier: ~19.0.8 - version: 19.0.8(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)) + version: 19.0.8(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)) expo-media-library: specifier: ~18.2.1 - version: 18.2.1(patch_hash=2b84c17999bb0c977eaef7fa8ac297f7074d91c94fb63726b42d2a277b26b39a)(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0)) + version: 18.2.1(patch_hash=2b84c17999bb0c977eaef7fa8ac297f7074d91c94fb63726b42d2a277b26b39a)(83f21d12a6c1aa99b4a65d070875cc68) expo-notifications: specifier: ~0.32.17 - version: 0.32.17(patch_hash=a45a8dcf3d8c4b5df4ee0e62bc79b755fcf9d28ca51a6f685b85830bf4afc2e5)(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 0.32.17(patch_hash=a45a8dcf3d8c4b5df4ee0e62bc79b755fcf9d28ca51a6f685b85830bf4afc2e5)(93a6abd34ab5ae64aff6a80525489f34) expo-paste-input: specifier: ^0.2.1 - version: 0.2.1(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 0.2.1(83e0f64f4281e4798012214ad5b2d72d) expo-privacy-sensitive: specifier: ^0.2.0 - version: 0.2.0(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 0.2.0(83e0f64f4281e4798012214ad5b2d72d) expo-screen-orientation: specifier: ~9.0.8 - version: 9.0.9(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0)) + version: 9.0.9(83f21d12a6c1aa99b4a65d070875cc68) expo-sharing: specifier: ~14.0.8 - version: 14.0.8(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)) + version: 14.0.8(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)) expo-sms: specifier: ^14.0.7 - version: 14.0.8(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)) + version: 14.0.8(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)) expo-splash-screen: specifier: ~31.0.13 - version: 31.0.13(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)) + version: 31.0.13(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1))(supports-color@8.1.1) expo-system-ui: specifier: ~6.0.9 - version: 6.0.9(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-web@0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0)) + version: 6.0.9(41b38c851305f17c9d2f63b3bbab71a9) expo-updates: specifier: ~29.0.17 - version: 29.0.17(patch_hash=04f28cb005b770e9ae8f0065eab96e43cbb1e58107f5f6ad1bdd18f6deb66487)(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 29.0.17(patch_hash=04f28cb005b770e9ae8f0065eab96e43cbb1e58107f5f6ad1bdd18f6deb66487)(93a6abd34ab5ae64aff6a80525489f34) expo-video: specifier: ~3.0.16 - version: 3.0.16(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 3.0.16(83e0f64f4281e4798012214ad5b2d72d) expo-video-thumbnails: specifier: ^10.0.8 - version: 10.0.8(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)) + version: 10.0.8(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)) expo-web-browser: specifier: ~15.0.10 - version: 15.0.11(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0)) + version: 15.0.11(83f21d12a6c1aa99b4a65d070875cc68) fast-deep-equal: specifier: ^3.1.3 version: 3.1.3 @@ -622,73 +628,73 @@ importers: version: 5.1.2(react-is@19.2.6)(react@19.1.0) react-native: specifier: 0.81.5 - version: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + version: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) react-native-compressor: specifier: 1.13.0 - version: 1.13.0(patch_hash=58379dfaace6ced8590cb341c77f2ca8099dfa8f7df6297032ec51de767a9925)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 1.13.0(patch_hash=58379dfaace6ced8590cb341c77f2ca8099dfa8f7df6297032ec51de767a9925)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) react-native-date-picker: specifier: ^5.0.13 - version: 5.0.13(patch_hash=92943fb79d17d7342a29bbb12b0d8ee3cf6f7bca12ed322dc8d124a3e9fb75bd)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 5.0.13(patch_hash=92943fb79d17d7342a29bbb12b0d8ee3cf6f7bca12ed322dc8d124a3e9fb75bd)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) react-native-device-attest: specifier: ^0.1.6 - version: 0.1.6(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 0.1.6(83e0f64f4281e4798012214ad5b2d72d) react-native-drawer-layout: specifier: ^4.2.3 - version: 4.2.3(patch_hash=74f2c043cc22ab87054f219e7c7373a509b779b18bfa79d27e7d051d73355130)(f8669ab0fde0772e831a7516fba74dd4) + version: 4.2.3(patch_hash=74f2c043cc22ab87054f219e7c7373a509b779b18bfa79d27e7d051d73355130)(80d195d8b17891585aee78a1fb66460a) react-native-edge-to-edge: specifier: ^1.8.1 - version: 1.8.1(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 1.8.1(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) react-native-gesture-handler: specifier: ~2.30.0 - version: 2.30.1(patch_hash=39b9e51b3977fae8ff61764b8c32934fe3fbddaf21587b4661fd73b7c6dd325b)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 2.30.1(patch_hash=39b9e51b3977fae8ff61764b8c32934fe3fbddaf21587b4661fd73b7c6dd325b)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) react-native-keyboard-controller: specifier: ^1.21.8 - version: 1.21.8(patch_hash=ac52bf7502ff3ad34a8594b5e1a916b96bf87a2523b6abc87c31f03607d52271)(react-native-reanimated@4.3.2(patch_hash=ab1c095bc473ec5bfd9ead76e870c566660e355e11c5078cfdeb35922d7b2f05)(react-native-worklets@0.8.3(patch_hash=f7263a59ae1c0170af8dfe9a5704cb374d29214a430b4b2841f9b7886c9ba8ad)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 1.21.8(patch_hash=ac52bf7502ff3ad34a8594b5e1a916b96bf87a2523b6abc87c31f03607d52271)(a7f993fe7a53ab427398397086882821) react-native-mmkv: specifier: ^3.3.3 - version: 3.3.3(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 3.3.3(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) react-native-pager-view: specifier: 6.8.0 - version: 6.8.0(patch_hash=c8316acd33c9aef6531e8c272c2bfab7bd02af1255969eeaab2bad5ab48531cc)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 6.8.0(patch_hash=c8316acd33c9aef6531e8c272c2bfab7bd02af1255969eeaab2bad5ab48531cc)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) react-native-progress: specifier: ^5.0.1 - version: 5.0.1(react-native-svg@15.12.1(patch_hash=ae309e9542214f5ae54935e253dedd236765feedac7be343f81d532d7fe09310)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)) + version: 5.0.1(react-native-svg@15.12.1(patch_hash=ae309e9542214f5ae54935e253dedd236765feedac7be343f81d532d7fe09310)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)) react-native-qrcode-styled: specifier: ^0.3.3 - version: 0.3.3(react-native-svg@15.12.1(patch_hash=ae309e9542214f5ae54935e253dedd236765feedac7be343f81d532d7fe09310)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 0.3.3(react-native-svg@15.12.1(patch_hash=ae309e9542214f5ae54935e253dedd236765feedac7be343f81d532d7fe09310)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) react-native-reanimated: specifier: 4.3.2 - version: 4.3.2(patch_hash=ab1c095bc473ec5bfd9ead76e870c566660e355e11c5078cfdeb35922d7b2f05)(react-native-worklets@0.8.3(patch_hash=f7263a59ae1c0170af8dfe9a5704cb374d29214a430b4b2841f9b7886c9ba8ad)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 4.3.2(patch_hash=ab1c095bc473ec5bfd9ead76e870c566660e355e11c5078cfdeb35922d7b2f05)(react-native-worklets@0.8.3(patch_hash=f7263a59ae1c0170af8dfe9a5704cb374d29214a430b4b2841f9b7886c9ba8ad)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) react-native-safe-area-context: specifier: ~5.6.0 - version: 5.6.2(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 5.6.2(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) react-native-screens: specifier: 4.24.0 - version: 4.24.0(patch_hash=74a74b489f8074a1974c2792c9c97aec30d8e251669aa37fc5c99993bb4a19e6)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 4.24.0(patch_hash=74a74b489f8074a1974c2792c9c97aec30d8e251669aa37fc5c99993bb4a19e6)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) react-native-scroll-forwarder: specifier: link:./modules/react-native-scroll-forwarder version: link:modules/react-native-scroll-forwarder react-native-svg: specifier: 15.12.1 - version: 15.12.1(patch_hash=ae309e9542214f5ae54935e253dedd236765feedac7be343f81d532d7fe09310)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 15.12.1(patch_hash=ae309e9542214f5ae54935e253dedd236765feedac7be343f81d532d7fe09310)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) react-native-uuid: specifier: ^2.0.3 version: 2.0.4 react-native-view-shot: specifier: ^4.0.3 - version: 4.0.3(patch_hash=a2457dc30a82cc8c21f371a6f860d9396d450a2a1b4265b1a4ee13bdb24d3268)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 4.0.3(patch_hash=a2457dc30a82cc8c21f371a6f860d9396d450a2a1b4265b1a4ee13bdb24d3268)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) react-native-web: specifier: ^0.21.0 version: 0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react-native-web-webview: specifier: ^1.0.2 - version: 1.0.2(file-loader@6.2.0(webpack@5.106.2(postcss@8.5.14)))(react-native-web@0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0)) + version: 1.0.2(file-loader@6.2.0(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)))(react-native-web@0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0)) react-native-webview: specifier: ^13.15.0 - version: 13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) react-native-worklets: specifier: 0.8.3 - version: 0.8.3(patch_hash=f7263a59ae1c0170af8dfe9a5704cb374d29214a430b4b2841f9b7886c9ba8ad)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 0.8.3(patch_hash=f7263a59ae1c0170af8dfe9a5704cb374d29214a430b4b2841f9b7886c9ba8ad)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) react-remove-scroll-bar: specifier: ^2.3.8 version: 2.3.8(@types/react@19.1.17)(react@19.1.0) @@ -709,7 +715,7 @@ importers: version: 2.0.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0) sonner-native: specifier: 0.26.4 - version: 0.26.4(c538f2bb3fa64bd80a9a262f75d56c17) + version: 0.26.4(dd52c15d65a2604bcf203dfed917879b) tippy.js: specifier: ^6.3.7 version: 6.3.7 @@ -728,10 +734,10 @@ importers: devDependencies: '@babel/core': specifier: ^7.26.0 - version: 7.29.0 + version: 7.29.0(supports-color@8.1.1) '@babel/preset-env': specifier: ^7.26.0 - version: 7.29.5(@babel/core@7.29.0) + version: 7.29.5(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) '@babel/runtime': specifier: ^7.26.0 version: 7.29.2 @@ -740,25 +746,25 @@ importers: version: 4.14.2 '@lingui/babel-plugin-lingui-macro': specifier: ^5.9.2 - version: 5.9.5(@typescript/typescript6@6.0.2) + version: 5.9.5(@typescript/typescript6@6.0.2)(supports-color@8.1.1) '@lingui/cli': specifier: ^5.9.2 - version: 5.9.5(@typescript/typescript6@6.0.2) + version: 5.9.5(@typescript/typescript6@6.0.2)(supports-color@8.1.1) '@pmmmwh/react-refresh-webpack-plugin': specifier: ^0.5.15 - version: 0.5.17(react-refresh@0.14.2)(type-fest@1.4.0)(webpack-dev-server@4.15.2(webpack@5.106.2(postcss@8.5.14)))(webpack@5.106.2(postcss@8.5.14)) + version: 0.5.17(react-refresh@0.14.2)(type-fest@1.4.0)(webpack-dev-server@4.15.2(debug@4.4.3(supports-color@8.1.1))(supports-color@8.1.1)(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)))(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)) '@react-native/babel-preset': specifier: 0.81.5 - version: 0.81.5(@babel/core@7.29.0) + version: 0.81.5(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) '@react-native/typescript-config': specifier: ^0.81.5 version: 0.81.6 '@sentry/webpack-plugin': specifier: ^3.2.2 - version: 3.6.1(webpack@5.106.2(postcss@8.5.14)) + version: 3.6.1(supports-color@8.1.1)(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)) '@testing-library/react-native': specifier: ^13.2.0 - version: 13.3.3(jest@29.7.0(@types/node@24.12.4))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0) + version: 13.3.3(jest@29.7.0(@types/node@24.12.4)(supports-color@8.1.1))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0) '@types/jest': specifier: 29.5.14 version: 29.5.14 @@ -785,7 +791,7 @@ importers: version: typescript@7.0.2 babel-jest: specifier: ^29.7.0 - version: 29.7.0(@babel/core@7.29.0) + version: 29.7.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) babel-plugin-module-resolver: specifier: ^5.0.2 version: 5.0.3 @@ -794,25 +800,25 @@ importers: version: 19.1.0-rc.3 babel-preset-expo: specifier: ~54.0.10 - version: 54.0.10(@babel/core@7.29.0)(@babel/runtime@7.29.2)(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-refresh@0.14.2) + version: 54.0.10(@babel/core@7.29.0(supports-color@8.1.1))(@babel/runtime@7.29.2)(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1))(react-refresh@0.14.2)(supports-color@8.1.1) eslint-plugin-bsky-internal: specifier: link:eslint version: link:eslint eslint-plugin-react-compiler: specifier: 19.1.0-rc.2 - version: 19.1.0-rc.2(eslint@eslint) + version: 19.1.0-rc.2(eslint@8.57.1(supports-color@8.1.1))(supports-color@8.1.1) eslint-plugin-react-native: specifier: ^5.0.0 - version: 5.0.0(eslint@eslint) + version: 5.0.0(eslint@8.57.1(supports-color@8.1.1)) eslint-plugin-react-native-a11y: specifier: ^3.5.1 - version: 3.5.1(eslint@eslint) + version: 3.5.1(eslint@8.57.1(supports-color@8.1.1)) eslint-plugin-simple-import-sort: specifier: ^13.0.0 - version: 13.0.0(eslint@eslint) + version: 13.0.0(eslint@8.57.1(supports-color@8.1.1)) file-loader: specifier: 6.2.0 - version: 6.2.0(webpack@5.106.2(postcss@8.5.14)) + version: 6.2.0(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)) globals: specifier: ^17.0.0 version: 17.6.0 @@ -824,10 +830,10 @@ importers: version: 3.0.1 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@24.12.4) + version: 29.7.0(@types/node@24.12.4)(supports-color@8.1.1) jest-expo: specifier: ~54.0.17 - version: 54.0.17(@babel/core@7.29.0)(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(jest@29.7.0(@types/node@24.12.4))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + version: 54.0.17(ff9d3150c2730c411fa3a2f331cc40ee) jest-junit: specifier: ^16.0.0 version: 16.0.0 @@ -887,6 +893,10 @@ packages: resolution: {integrity: sha512-2c5C6YV8352JJz9Z5fNfcsstllaKGAb3cQW9aO7unMAGh/FzTGFq+xOwNMW2lTeagO6z530uxYm8AiSRM6O+DQ==} engines: {node: '>=22'} + '@atproto/lex-client@0.3.0': + resolution: {integrity: sha512-P+S3hWdIIOgNTun7J42dvxSyCkQog3Q2ShTjvcwP761TqiRex7zDWaRAaT9gvCy6I4pRuNdBCStysjSMlJeSdg==} + engines: {node: '>=22'} + '@atproto/lex-data@0.1.6': resolution: {integrity: sha512-rvovRrYWDEuerX+ZeQbdI5dKs0bYvuMjhwDOtT+pkOvdY4MkfN41DJCz/RAYVAQ7noPdaalB6pAX9euukheE/w==} engines: {node: '>=22'} @@ -903,6 +913,14 @@ packages: resolution: {integrity: sha512-mvrAd0lbyuecIHjyld8QN6MN6CBf4j0GCxLzegsvLh0SvDf+GbYWklkcQqmITL44yFQOwmA/QNIQj0Uvh7+R/g==} engines: {node: '>=22'} + '@atproto/lex-password-session@0.1.8': + resolution: {integrity: sha512-Zr3HLu6rgadiq2w82VSThOQcNfwTJNlrzlT8n9oBXDBw3CUEq3cTMatzTpdk4HYqv0unaqhaEowcKhJ4eWvSxQ==} + engines: {node: '>=22'} + + '@atproto/lex-schema@0.2.5': + resolution: {integrity: sha512-/tP3dRqaZu0EIoq0BSVzhvFGL6u0uu2KFDV21i73DpFsg6TXOTddKJ9bvgK83XdAJYL7PWi703XsaR31v9u5Qg==} + engines: {node: '>=22'} + '@atproto/lexicon@0.7.11': resolution: {integrity: sha512-ukCZMCWmiu7fAlssBbLs+IMaf2K2R9mADaTKz2jEeC/xeZ3jT77Ka0S2G+ELBB+Dc9ijBACU0iPMQLhquOkuzQ==} engines: {node: '>=22'} @@ -1893,6 +1911,24 @@ packages: cpu: [x64] os: [win32] + '@eslint-community/eslint-utils@4.10.1': + resolution: {integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/eslintrc@2.1.4': + resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@eslint/js@8.57.1': + resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@expo/cli@54.0.25': resolution: {integrity: sha512-WnUqIb8oMBhtwSfIqdCHCzcaDIpLNXItRVd5miuvWi4GO0SGo89PSsAkbVJ+LJgcaY+v5rbgMELJS9I/CqOulA==} hasBin: true @@ -2083,6 +2119,19 @@ packages: resolution: {integrity: sha512-mUaMsgeUTpRIUOTn33EUXHRK6j7pxBjwqH4WpQyq+pukjd1AIzWlEa6w7i6bInJUcweGgP2beXZmaP6b6UPn7A==} engines: {node: '>=10'} + '@humanwhocodes/config-array@0.13.0': + resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==} + engines: {node: '>=10.10.0'} + deprecated: Use @eslint/config-array instead + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/object-schema@2.0.3': + resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} + deprecated: Use @eslint/object-schema instead + '@ide/backoff@1.0.0': resolution: {integrity: sha512-F0YfUDjvT+Mtt/R4xdl2X0EYCHMMiJqNLdxHD++jDT5ydEFIyqbCHh51Qx2E211dgZprPKhV7sHmnXKpLuvc5g==} @@ -3500,6 +3549,9 @@ packages: '@sinonjs/fake-timers@10.3.0': resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + '@tanstack/query-async-storage-persister@5.100.10': resolution: {integrity: sha512-NbxsCI9qyMbEooFnFsAR2dbVQ6cmxZpOWSjLFy62qfMkxXnNjkskqex7YgtzWSaD/ozOOLgrbDiyYWEPYKmMyQ==} @@ -3972,10 +4024,12 @@ packages: '@xmldom/xmldom@0.8.13': resolution: {integrity: sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==} engines: {node: '>=10.0.0'} + deprecated: this version has critical issues, please update to the latest version '@xmldom/xmldom@0.9.10': resolution: {integrity: sha512-A9gOqLdi6cV4ibazAjcQufGj0B1y/vDqYrcuP6d/6x8P27gRS8643Dj9o1dEKtB6O7fwxb2FgBmJS2mX7gpvdw==} engines: {node: '>=14.6'} + deprecated: this version has critical issues, please update to the latest version '@xtuc/ieee754@1.2.0': resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} @@ -4008,6 +4062,11 @@ packages: peerDependencies: acorn: ^8.14.0 + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + acorn-walk@8.3.5: resolution: {integrity: sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==} engines: {node: '>=0.4.0'} @@ -4875,6 +4934,9 @@ packages: resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} engines: {node: '>=4.0.0'} + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + deepmerge@4.3.1: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} @@ -4951,6 +5013,10 @@ packages: resolution: {integrity: sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==} engines: {node: '>=6'} + doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + dom-converter@0.2.0: resolution: {integrity: sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==} @@ -5167,15 +5233,37 @@ packages: resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} engines: {node: '>=8.0.0'} + eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint@8.57.1: + resolution: {integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. + hasBin: true + esm@3.2.25: resolution: {integrity: sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==} engines: {node: '>=6'} + espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + esprima@4.0.1: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} engines: {node: '>=4'} hasBin: true + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} engines: {node: '>=4.0'} @@ -5571,6 +5659,9 @@ packages: fast-json-stringify@6.4.0: resolution: {integrity: sha512-ibRCQ0GZKJIQ+P3Et1h0LhPgp3PMTYk0MH8O+kW3lNYsvmaQww5Nn3f1jf73Q0jR1Yz3a1CDP4/NZD3vOajWJQ==} + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-text-encoding@1.0.6: resolution: {integrity: sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w==} @@ -5602,6 +5693,10 @@ packages: picomatch: optional: true + file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + file-loader@6.2.0: resolution: {integrity: sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==} engines: {node: '>= 10.13.0'} @@ -5646,6 +5741,13 @@ packages: find-yarn-workspace-root@2.0.0: resolution: {integrity: sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==} + flat-cache@3.2.0: + resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} + engines: {node: ^10.12.0 || >=12.0.0} + + flatted@3.4.4: + resolution: {integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==} + flow-enums-runtime@0.0.6: resolution: {integrity: sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw==} @@ -5797,6 +5899,10 @@ packages: engines: {node: '>=16 || 14 >=14.17'} 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 + globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + globals@17.6.0: resolution: {integrity: sha512-sepffkT8stwnIYbsMBpoCHJuJM5l98FUF2AnE07hfvE0m/qp3R586hw4jF4uadbhvg1ooIdzuu7CsfD2jzCaNA==} engines: {node: '>=18'} @@ -5820,6 +5926,9 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + gzip-size@6.0.0: resolution: {integrity: sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==} engines: {node: '>=10'} @@ -6196,6 +6305,10 @@ packages: resolution: {integrity: sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==} engines: {node: '>=6'} + is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + is-plain-obj@2.1.0: resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} engines: {node: '>=8'} @@ -6529,6 +6642,9 @@ packages: engines: {node: '>=6'} hasBin: true + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} @@ -6541,6 +6657,9 @@ packages: json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} @@ -6557,6 +6676,9 @@ packages: resolution: {integrity: sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==} engines: {node: '>=18'} + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + kleur@3.0.3: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} @@ -6575,6 +6697,10 @@ packages: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} engines: {node: '>=6'} + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + libphonenumber-js@1.13.1: resolution: {integrity: sha512-GEw0GLL7YUUA6nv21IsCvVjtI5Ejn84sjbdfQ9KxdbqEVOk1PZh7xejn01EEiniKw+dBeCfim+8MGeuvVuE2BA==} @@ -6703,6 +6829,9 @@ packages: lodash.memoize@4.1.2: resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + lodash.shuffle@4.2.0: resolution: {integrity: sha512-V/rTAABKLFjoecTZjKSv+A1ZomG8hZg8hlgeG6wwQVD9AGv+10zqqSf6mFq2tVA703Zd5R0YhSuSlXA+E/Ei+Q==} @@ -7331,6 +7460,10 @@ packages: resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==} hasBin: true + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + ora@3.4.0: resolution: {integrity: sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg==} engines: {node: '>=6'} @@ -7725,6 +7858,10 @@ packages: resolution: {integrity: sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==} engines: {node: ^10 || ^12 || >=14} + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + prettier@3.8.3: resolution: {integrity: sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==} engines: {node: '>=14'} @@ -8823,6 +8960,9 @@ packages: text-segmentation@1.0.3: resolution: {integrity: sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==} + text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + thenify-all@1.6.0: resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} engines: {node: '>=0.8'} @@ -8902,10 +9042,18 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + type-detect@4.0.8: resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} engines: {node: '>=4'} + type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + type-fest@0.21.3: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} @@ -9265,6 +9413,10 @@ packages: wonka@6.3.6: resolution: {integrity: sha512-MXH+6mDHAZ2GuMpgKS055FR6v0xVP3XwquxIMYXgiW+FejHQlMGlvVRZT4qMCxR+bEo/FCtIdKxwej9WV3YQag==} + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + wrap-ansi@10.0.0: resolution: {integrity: sha512-SGcvg80f0wUy2/fXES19feHMz8E0JoXv2uNgHOu4Dgi2OrCy1lqwFYEJz1BLbDI0exjPMe/ZdzZ/YpGECBG/aQ==} engines: {node: '>=20'} @@ -9441,6 +9593,13 @@ snapshots: '@atproto/syntax': 0.7.4 zod: 3.25.76 + '@atproto/lex-client@0.3.0': + dependencies: + '@atproto/lex-data': 0.1.7 + '@atproto/lex-json': 0.1.6 + '@atproto/lex-schema': 0.2.5 + tslib: 2.8.1 + '@atproto/lex-data@0.1.6': dependencies: multiformats: 13.4.2 @@ -9463,6 +9622,19 @@ snapshots: '@atproto/lex-data': 0.1.7 tslib: 2.8.1 + '@atproto/lex-password-session@0.1.8': + dependencies: + '@atproto/lex-client': 0.3.0 + '@atproto/lex-schema': 0.2.5 + tslib: 2.8.1 + + '@atproto/lex-schema@0.2.5': + dependencies: + '@atproto/lex-data': 0.1.7 + '@atproto/syntax': 0.7.4 + '@standard-schema/spec': 1.1.0 + tslib: 2.8.1 + '@atproto/lexicon@0.7.11': dependencies: '@atproto/common-web': 0.5.9 @@ -9497,20 +9669,20 @@ snapshots: '@babel/compat-data@7.29.3': {} - '@babel/core@7.29.0': + '@babel/core@7.29.0(supports-color@8.1.1)': dependencies: '@babel/code-frame': 7.29.0 '@babel/generator': 7.29.1 '@babel/helper-compilation-targets': 7.28.6 - '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) '@babel/helpers': 7.29.2 '@babel/parser': 7.29.3 '@babel/template': 7.28.6 - '@babel/traverse': 7.29.0 + '@babel/traverse': 7.29.0(supports-color@8.1.1) '@babel/types': 7.29.0 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -9537,32 +9709,32 @@ snapshots: lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-create-class-features-plugin@7.29.3(@babel/core@7.29.0)': + '@babel/helper-create-class-features-plugin@7.29.3(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-member-expression-to-functions': 7.28.5(supports-color@8.1.1) '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/traverse': 7.29.0 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1(supports-color@8.1.1) + '@babel/traverse': 7.29.0(supports-color@8.1.1) semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/helper-create-regexp-features-plugin@7.28.5(@babel/core@7.29.0)': + '@babel/helper-create-regexp-features-plugin@7.28.5(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-annotate-as-pure': 7.27.3 regexpu-core: 6.4.0 semver: 6.3.1 - '@babel/helper-define-polyfill-provider@0.6.8(@babel/core@7.29.0)': + '@babel/helper-define-polyfill-provider@0.6.8(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-compilation-targets': 7.28.6 '@babel/helper-plugin-utils': 7.28.6 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) lodash.debounce: 4.0.8 resolve: 1.22.12 transitivePeerDependencies: @@ -9570,26 +9742,26 @@ snapshots: '@babel/helper-globals@7.28.0': {} - '@babel/helper-member-expression-to-functions@7.28.5': + '@babel/helper-member-expression-to-functions@7.28.5(supports-color@8.1.1)': dependencies: - '@babel/traverse': 7.29.0 + '@babel/traverse': 7.29.0(supports-color@8.1.1) '@babel/types': 7.29.0 transitivePeerDependencies: - supports-color - '@babel/helper-module-imports@7.28.6': + '@babel/helper-module-imports@7.28.6(supports-color@8.1.1)': dependencies: - '@babel/traverse': 7.29.0 + '@babel/traverse': 7.29.0(supports-color@8.1.1) '@babel/types': 7.29.0 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': + '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-module-imports': 7.28.6 + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/helper-module-imports': 7.28.6(supports-color@8.1.1) '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.29.0 + '@babel/traverse': 7.29.0(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -9599,27 +9771,27 @@ snapshots: '@babel/helper-plugin-utils@7.28.6': {} - '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.29.0)': + '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-wrap-function': 7.28.6 - '@babel/traverse': 7.29.0 + '@babel/helper-wrap-function': 7.28.6(supports-color@8.1.1) + '@babel/traverse': 7.29.0(supports-color@8.1.1) transitivePeerDependencies: - supports-color - '@babel/helper-replace-supers@7.28.6(@babel/core@7.29.0)': + '@babel/helper-replace-supers@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/helper-member-expression-to-functions': 7.28.5(supports-color@8.1.1) '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/traverse': 7.29.0 + '@babel/traverse': 7.29.0(supports-color@8.1.1) transitivePeerDependencies: - supports-color - '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + '@babel/helper-skip-transparent-expression-wrappers@7.27.1(supports-color@8.1.1)': dependencies: - '@babel/traverse': 7.29.0 + '@babel/traverse': 7.29.0(supports-color@8.1.1) '@babel/types': 7.29.0 transitivePeerDependencies: - supports-color @@ -9630,10 +9802,10 @@ snapshots: '@babel/helper-validator-option@7.27.1': {} - '@babel/helper-wrap-function@7.28.6': + '@babel/helper-wrap-function@7.28.6(supports-color@8.1.1)': dependencies: '@babel/template': 7.28.6 - '@babel/traverse': 7.29.0 + '@babel/traverse': 7.29.0(supports-color@8.1.1) '@babel/types': 7.29.0 transitivePeerDependencies: - supports-color @@ -9654,696 +9826,696 @@ snapshots: dependencies: '@babel/types': 7.29.0 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5(@babel/core@7.29.0)': + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/traverse': 7.29.0 + '@babel/traverse': 7.29.0(supports-color@8.1.1) transitivePeerDependencies: - supports-color - '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array@7.29.3(@babel/core@7.29.0)': + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array@7.29.3(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1(supports-color@8.1.1) transitivePeerDependencies: - supports-color - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0) + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1(supports-color@8.1.1) + '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) transitivePeerDependencies: - supports-color - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/traverse': 7.29.0 + '@babel/traverse': 7.29.0(supports-color@8.1.1) transitivePeerDependencies: - supports-color - '@babel/plugin-proposal-decorators@7.29.0(@babel/core@7.29.0)': + '@babel/plugin-proposal-decorators@7.29.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-decorators': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-decorators': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1)) transitivePeerDependencies: - supports-color - '@babel/plugin-proposal-export-default-from@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-proposal-export-default-from@7.27.1(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-proposal-private-methods@7.18.6(@babel/core@7.29.0)': + '@babel/plugin-proposal-private-methods@7.18.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 transitivePeerDependencies: - supports-color - '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.0)': + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) - '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.29.0)': + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.29.0)': + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.29.0)': + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.29.0)': + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-decorators@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-syntax-decorators@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.29.0)': + '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-export-default-from@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-syntax-export-default-from@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-flow@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-syntax-flow@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-import-assertions@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-syntax-import-assertions@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-import-attributes@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-syntax-import-attributes@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.29.0)': + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.29.0)': + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.29.0)': + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.29.0)': + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.29.0)': + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.29.0)': + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.29.0)': + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.29.0)': + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.29.0)': + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.29.0)': + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-typescript@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-syntax-typescript@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.29.0)': + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0(supports-color@8.1.1)) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-async-generator-functions@7.29.0(@babel/core@7.29.0)': + '@babel/plugin-transform-async-generator-functions@7.29.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.29.0) - '@babel/traverse': 7.29.0 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/traverse': 7.29.0(supports-color@8.1.1) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-async-to-generator@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-async-to-generator@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-module-imports': 7.28.6 + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/helper-module-imports': 7.28.6(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.29.0) + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-block-scoping@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-block-scoping@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-class-properties@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-class-properties@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-class-static-block@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-class-static-block@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-classes@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-classes@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-compilation-targets': 7.28.6 '@babel/helper-globals': 7.28.0 '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) - '@babel/traverse': 7.29.0 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/traverse': 7.29.0(supports-color@8.1.1) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-computed-properties@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-computed-properties@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 '@babel/template': 7.28.6 - '@babel/plugin-transform-destructuring@7.28.5(@babel/core@7.29.0)': + '@babel/plugin-transform-destructuring@7.28.5(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/traverse': 7.29.0 + '@babel/traverse': 7.29.0(supports-color@8.1.1) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-dotall-regex@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-dotall-regex@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0(supports-color@8.1.1)) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.0(@babel/core@7.29.0)': + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.0(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0(supports-color@8.1.1)) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-explicit-resource-management@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-explicit-resource-management@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0) + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-exponentiation-operator@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-exponentiation-operator@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-flow-strip-types@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-flow-strip-types@7.27.1(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-flow': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-flow': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1)) - '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1(supports-color@8.1.1) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-compilation-targets': 7.28.6 '@babel/helper-plugin-utils': 7.28.6 - '@babel/traverse': 7.29.0 + '@babel/traverse': 7.29.0(supports-color@8.1.1) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-json-strings@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-json-strings@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-literals@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-literals@7.27.1(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-logical-assignment-operators@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-logical-assignment-operators@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-commonjs@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-modules-commonjs@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-systemjs@7.29.4(@babel/core@7.29.0)': + '@babel/plugin-transform-modules-systemjs@7.29.4(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.29.0 + '@babel/traverse': 7.29.0(supports-color@8.1.1) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-named-capturing-groups-regex@7.29.0(@babel/core@7.29.0)': + '@babel/plugin-transform-named-capturing-groups-regex@7.29.0(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0(supports-color@8.1.1)) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-nullish-coalescing-operator@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-nullish-coalescing-operator@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-numeric-separator@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-numeric-separator@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-object-rest-spread@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-object-rest-spread@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-compilation-targets': 7.28.6 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0) - '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.0) - '@babel/traverse': 7.29.0 + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/traverse': 7.29.0(supports-color@8.1.1) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-optional-catch-binding@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-optional-catch-binding@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-optional-chaining@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-optional-chaining@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1(supports-color@8.1.1) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-parameters@7.27.7(@babel/core@7.29.0)': + '@babel/plugin-transform-parameters@7.27.7(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-private-methods@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-private-methods@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-private-property-in-object@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-private-property-in-object@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-property-literals@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-property-literals@7.27.1(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-react-display-name@7.28.0(@babel/core@7.29.0)': + '@babel/plugin-transform-react-display-name@7.28.0(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-react-jsx-development@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-react-jsx-development@7.27.1(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 - '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@7.29.0) + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-react-jsx@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-react-jsx@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-module-imports': 7.28.6 + '@babel/helper-module-imports': 7.28.6(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1)) '@babel/types': 7.29.0 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-pure-annotations@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-react-pure-annotations@7.27.1(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-regenerator@7.29.0(@babel/core@7.29.0)': + '@babel/plugin-transform-regenerator@7.29.0(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-regexp-modifiers@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-regexp-modifiers@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0(supports-color@8.1.1)) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-runtime@7.29.0(@babel/core@7.29.0)': + '@babel/plugin-transform-runtime@7.29.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-module-imports': 7.28.6 + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/helper-module-imports': 7.28.6(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - babel-plugin-polyfill-corejs2: 0.4.17(@babel/core@7.29.0) - babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.29.0) - babel-plugin-polyfill-regenerator: 0.6.8(@babel/core@7.29.0) + babel-plugin-polyfill-corejs2: 0.4.17(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + babel-plugin-polyfill-regenerator: 0.6.8(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-spread@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-spread@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1(supports-color@8.1.1) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-typescript@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-typescript@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0) + '@babel/helper-create-class-features-plugin': 7.29.3(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1(supports-color@8.1.1) + '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1)) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-unicode-property-regex@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-unicode-property-regex@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0(supports-color@8.1.1)) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.29.0)': + '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0(supports-color@8.1.1)) '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-unicode-sets-regex@7.28.6(@babel/core@7.29.0)': + '@babel/plugin-transform-unicode-sets-regex@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0(supports-color@8.1.1)) '@babel/helper-plugin-utils': 7.28.6 - '@babel/preset-env@7.29.5(@babel/core@7.29.0)': + '@babel/preset-env@7.29.5(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: '@babel/compat-data': 7.29.3 - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-compilation-targets': 7.28.6 '@babel/helper-plugin-utils': 7.28.6 '@babel/helper-validator-option': 7.27.1 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.28.5(@babel/core@7.29.0) - '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array': 7.29.3(@babel/core@7.29.0) - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.0) - '@babel/plugin-syntax-import-assertions': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.29.0) - '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-async-generator-functions': 7.29.0(@babel/core@7.29.0) - '@babel/plugin-transform-async-to-generator': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-block-scoping': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-class-static-block': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-classes': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-computed-properties': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0) - '@babel/plugin-transform-dotall-regex': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.29.0(@babel/core@7.29.0) - '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-explicit-resource-management': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-exponentiation-operator': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-json-strings': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-logical-assignment-operators': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-modules-systemjs': 7.29.4(@babel/core@7.29.0) - '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-named-capturing-groups-regex': 7.29.0(@babel/core@7.29.0) - '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-nullish-coalescing-operator': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-numeric-separator': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-object-rest-spread': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-optional-catch-binding': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.0) - '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-private-property-in-object': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-regenerator': 7.29.0(@babel/core@7.29.0) - '@babel/plugin-transform-regexp-modifiers': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-spread': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-typeof-symbol': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-unicode-escapes': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-unicode-property-regex': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-unicode-sets-regex': 7.28.6(@babel/core@7.29.0) - '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.29.0) - babel-plugin-polyfill-corejs2: 0.4.17(@babel/core@7.29.0) - babel-plugin-polyfill-corejs3: 0.14.2(@babel/core@7.29.0) - babel-plugin-polyfill-regenerator: 0.6.8(@babel/core@7.29.0) + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.28.5(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array': 7.29.3(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-import-assertions': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-async-generator-functions': 7.29.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-async-to-generator': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-block-scoping': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-class-static-block': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-classes': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-computed-properties': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-dotall-regex': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.29.0(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-explicit-resource-management': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-exponentiation-operator': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-json-strings': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-logical-assignment-operators': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-modules-systemjs': 7.29.4(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-named-capturing-groups-regex': 7.29.0(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-nullish-coalescing-operator': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-numeric-separator': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-object-rest-spread': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-optional-catch-binding': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-private-property-in-object': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-regenerator': 7.29.0(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-regexp-modifiers': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-spread': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-typeof-symbol': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-unicode-escapes': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-unicode-property-regex': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-unicode-sets-regex': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.29.0(supports-color@8.1.1)) + babel-plugin-polyfill-corejs2: 0.4.17(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + babel-plugin-polyfill-corejs3: 0.14.2(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + babel-plugin-polyfill-regenerator: 0.6.8(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) core-js-compat: 3.49.0 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.29.0)': + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 '@babel/types': 7.29.0 esutils: 2.0.3 - '@babel/preset-react@7.28.5(@babel/core@7.29.0)': + '@babel/preset-react@7.28.5(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 '@babel/helper-validator-option': 7.27.1 - '@babel/plugin-transform-react-display-name': 7.28.0(@babel/core@7.29.0) - '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-react-pure-annotations': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-react-display-name': 7.28.0(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-react-pure-annotations': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) transitivePeerDependencies: - supports-color - '@babel/preset-typescript@7.28.5(@babel/core@7.29.0)': + '@babel/preset-typescript@7.28.5(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 '@babel/helper-validator-option': 7.27.1 - '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -10355,7 +10527,7 @@ snapshots: '@babel/parser': 7.29.3 '@babel/types': 7.29.0 - '@babel/traverse@7.29.0': + '@babel/traverse@7.29.0(supports-color@8.1.1)': dependencies: '@babel/code-frame': 7.29.0 '@babel/generator': 7.29.1 @@ -10363,7 +10535,7 @@ snapshots: '@babel/parser': 7.29.3 '@babel/template': 7.28.6 '@babel/types': 7.29.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -10374,94 +10546,94 @@ snapshots: '@bcoe/v8-coverage@0.2.3': {} - '@bitdrift/react-native@0.6.14(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': + '@bitdrift/react-native@0.6.14(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)': dependencies: - '@expo/config-plugins': 9.1.7 + '@expo/config-plugins': 9.1.7(supports-color@8.1.1) fast-json-stringify: 6.4.0 react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) transitivePeerDependencies: - supports-color '@braintree/sanitize-url@6.0.4': {} - '@bsky.app/alf@0.1.15(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': + '@bsky.app/alf@0.1.15(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)': dependencies: react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) react-responsive: 10.0.1(react@19.1.0) - '@bsky.app/expo-dynamic-app-icon@1.8.5(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': + '@bsky.app/expo-dynamic-app-icon@1.8.5(83e0f64f4281e4798012214ad5b2d72d)': dependencies: '@expo/image-utils': 0.8.12 - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) xcode: 3.0.1 - '@bsky.app/expo-guess-language@0.2.8(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': + '@bsky.app/expo-guess-language@0.2.8(83e0f64f4281e4798012214ad5b2d72d)': dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) lande: 1.0.10 react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) - '@bsky.app/expo-image-crop-tool@0.5.1(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': + '@bsky.app/expo-image-crop-tool@0.5.1(83e0f64f4281e4798012214ad5b2d72d)': dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) - '@bsky.app/expo-scroll-edge-effect@0.1.9(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': + '@bsky.app/expo-scroll-edge-effect@0.1.9(83e0f64f4281e4798012214ad5b2d72d)': dependencies: '@types/jest': 30.0.0 - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) - '@bsky.app/expo-translate-text@0.2.9(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': + '@bsky.app/expo-translate-text@0.2.9(83e0f64f4281e4798012214ad5b2d72d)': dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) - '@bsky.app/peek-menu@0.3.2(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': + '@bsky.app/peek-menu@0.3.2(83e0f64f4281e4798012214ad5b2d72d)': dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) - '@bsky.app/react-native-uitextview@2.7.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': + '@bsky.app/react-native-uitextview@2.7.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)': dependencies: react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) - '@bsky.app/sift@0.3.9(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': + '@bsky.app/sift@0.3.9(2d5c14016450c7e55c8cd0dffcca6f69)': dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) - react-native-safe-area-context: 5.6.2(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) + react-native-safe-area-context: 5.6.2(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) - '@bsky.app/tapper@0.6.1(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)(tlds@1.261.0)': + '@bsky.app/tapper@0.6.1(8d6e1ad3cb49b1a6214533c496326d11)': dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) tlds: 1.261.0 - '@bsky.app/video-compressor@0.2.0(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': + '@bsky.app/video-compressor@0.2.0(83e0f64f4281e4798012214ad5b2d72d)': dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) mediabunny: 1.49.0 react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) - '@bsky.app/video@0.3.6(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': + '@bsky.app/video@0.3.6(83e0f64f4281e4798012214ad5b2d72d)': dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) '@crowdin/cli@4.14.2': dependencies: @@ -10564,27 +10736,50 @@ snapshots: '@esbuild/win32-x64@0.25.12': optional: true - '@expo/cli@54.0.25(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))': + '@eslint-community/eslint-utils@4.10.1(eslint@8.57.1(supports-color@8.1.1))': + dependencies: + eslint: 8.57.1(supports-color@8.1.1) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/eslintrc@2.1.4(supports-color@8.1.1)': + dependencies: + ajv: 6.15.0 + debug: 4.4.3(supports-color@8.1.1) + espree: 9.6.1 + globals: 13.24.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@8.57.1': {} + + '@expo/cli@54.0.25(c1408f750868568d2dc75671b50084a7)': dependencies: '@0no-co/graphql.web': 1.2.0 '@expo/code-signing-certificates': 0.0.6 - '@expo/config': 12.0.13 - '@expo/config-plugins': 54.0.4 - '@expo/devcert': 1.2.1 - '@expo/env': 2.0.11 + '@expo/config': 12.0.13(supports-color@8.1.1) + '@expo/config-plugins': 54.0.4(supports-color@8.1.1) + '@expo/devcert': 1.2.1(supports-color@8.1.1) + '@expo/env': 2.0.11(supports-color@8.1.1) '@expo/image-utils': 0.8.12 '@expo/json-file': 10.2.0 - '@expo/metro': 54.2.0 - '@expo/metro-config': 54.0.16(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)) + '@expo/metro': 54.2.0(supports-color@8.1.1) + '@expo/metro-config': 54.0.16(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1))(supports-color@8.1.1) '@expo/osascript': 2.4.3 '@expo/package-manager': 1.10.5 '@expo/plist': 0.4.9 - '@expo/prebuild-config': 54.0.8(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)) + '@expo/prebuild-config': 54.0.8(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1))(supports-color@8.1.1) '@expo/schema-utils': 0.1.8 '@expo/spawn-async': 1.7.2 '@expo/ws-tunnel': 1.0.6 '@expo/xcpretty': 4.4.4 - '@react-native/dev-middleware': 0.81.5 + '@react-native/dev-middleware': 0.81.5(supports-color@8.1.1) '@urql/core': 5.2.0 '@urql/exchange-retry': 1.3.2(@urql/core@5.2.0) accepts: 1.3.8 @@ -10594,11 +10789,11 @@ snapshots: bplist-parser: 0.3.2 chalk: 4.1.2 ci-info: 3.9.0 - compression: 1.8.1 - connect: 3.7.0 - debug: 4.4.3 + compression: 1.8.1(supports-color@8.1.1) + connect: 3.7.0(supports-color@8.1.1) + debug: 4.4.3(supports-color@8.1.1) env-editor: 0.4.2 - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) expo-server: 1.0.7 freeport-async: 2.0.0 getenv: 2.0.0 @@ -10620,7 +10815,7 @@ snapshots: resolve-from: 5.0.0 resolve.exports: 2.0.3 semver: 7.8.0 - send: 0.19.2 + send: 0.19.2(supports-color@8.1.1) slugify: 1.6.9 source-map-support: 0.5.21 stacktrace-parser: 0.1.11 @@ -10631,7 +10826,7 @@ snapshots: wrap-ansi: 7.0.0 ws: 8.20.1 optionalDependencies: - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) transitivePeerDependencies: - bufferutil - graphql @@ -10642,14 +10837,14 @@ snapshots: dependencies: node-forge: 1.4.0 - '@expo/config-plugins@54.0.4': + '@expo/config-plugins@54.0.4(supports-color@8.1.1)': dependencies: '@expo/config-types': 54.0.10 '@expo/json-file': 10.0.14 '@expo/plist': 0.4.8 '@expo/sdk-runtime-versions': 1.0.0 chalk: 4.1.2 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) getenv: 2.0.0 glob: 13.0.6 resolve-from: 5.0.0 @@ -10661,14 +10856,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@expo/config-plugins@9.1.7': + '@expo/config-plugins@9.1.7(supports-color@8.1.1)': dependencies: '@expo/config-types': 53.0.5 '@expo/json-file': 9.1.5 '@expo/plist': 0.3.5 '@expo/sdk-runtime-versions': 1.0.0 chalk: 4.1.2 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) getenv: 1.0.0 glob: 10.5.0 resolve-from: 5.0.0 @@ -10684,10 +10879,10 @@ snapshots: '@expo/config-types@54.0.10': {} - '@expo/config@12.0.13': + '@expo/config@12.0.13(supports-color@8.1.1)': dependencies: '@babel/code-frame': 7.10.4 - '@expo/config-plugins': 54.0.4 + '@expo/config-plugins': 54.0.4(supports-color@8.1.1) '@expo/config-types': 54.0.10 '@expo/json-file': 10.0.14 deepmerge: 4.3.1 @@ -10702,36 +10897,36 @@ snapshots: transitivePeerDependencies: - supports-color - '@expo/devcert@1.2.1': + '@expo/devcert@1.2.1(supports-color@8.1.1)': dependencies: '@expo/sudo-prompt': 9.3.2 - debug: 3.2.7 + debug: 3.2.7(supports-color@8.1.1) transitivePeerDependencies: - supports-color - '@expo/devtools@0.1.8(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': + '@expo/devtools@0.1.8(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)': dependencies: chalk: 4.1.2 optionalDependencies: react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) - '@expo/env@2.0.11': + '@expo/env@2.0.11(supports-color@8.1.1)': dependencies: chalk: 4.1.2 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) dotenv: 16.4.7 dotenv-expand: 11.0.7 getenv: 2.0.0 transitivePeerDependencies: - supports-color - '@expo/fingerprint@0.15.5': + '@expo/fingerprint@0.15.5(supports-color@8.1.1)': dependencies: '@expo/spawn-async': 1.7.2 arg: 5.0.2 chalk: 4.1.2 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) getenv: 2.0.0 glob: 13.0.6 ignore: 5.3.2 @@ -10774,19 +10969,19 @@ snapshots: '@babel/code-frame': 7.10.4 json5: 2.2.3 - '@expo/metro-config@54.0.16(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))': + '@expo/metro-config@54.0.16(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: '@babel/code-frame': 7.29.0 - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/generator': 7.29.1 - '@expo/config': 12.0.13 - '@expo/env': 2.0.11 + '@expo/config': 12.0.13(supports-color@8.1.1) + '@expo/env': 2.0.11(supports-color@8.1.1) '@expo/json-file': 10.0.16 - '@expo/metro': 54.2.0 + '@expo/metro': 54.2.0(supports-color@8.1.1) '@expo/spawn-async': 1.7.2 browserslist: 4.28.2 chalk: 4.1.2 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) dotenv: 16.4.7 dotenv-expand: 11.0.7 getenv: 2.0.0 @@ -10798,28 +10993,28 @@ snapshots: postcss: 8.4.49 resolve-from: 5.0.0 optionalDependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - '@expo/metro@54.2.0': + '@expo/metro@54.2.0(supports-color@8.1.1)': dependencies: - metro: 0.83.3 - metro-babel-transformer: 0.83.3 - metro-cache: 0.83.3 + metro: 0.83.3(supports-color@8.1.1) + metro-babel-transformer: 0.83.3(supports-color@8.1.1) + metro-cache: 0.83.3(supports-color@8.1.1) metro-cache-key: 0.83.3 - metro-config: 0.83.3 + metro-config: 0.83.3(supports-color@8.1.1) metro-core: 0.83.3 - metro-file-map: 0.83.3 + metro-file-map: 0.83.3(supports-color@8.1.1) metro-minify-terser: 0.83.3 metro-resolver: 0.83.3 metro-runtime: 0.83.3 - metro-source-map: 0.83.3 - metro-symbolicate: 0.83.3 - metro-transform-plugins: 0.83.3 - metro-transform-worker: 0.83.3 + metro-source-map: 0.83.3(supports-color@8.1.1) + metro-symbolicate: 0.83.3(supports-color@8.1.1) + metro-transform-plugins: 0.83.3(supports-color@8.1.1) + metro-transform-worker: 0.83.3(supports-color@8.1.1) transitivePeerDependencies: - bufferutil - supports-color @@ -10856,16 +11051,16 @@ snapshots: base64-js: 1.5.1 xmlbuilder: 15.1.1 - '@expo/prebuild-config@54.0.8(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))': + '@expo/prebuild-config@54.0.8(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@expo/config': 12.0.13 - '@expo/config-plugins': 54.0.4 + '@expo/config': 12.0.13(supports-color@8.1.1) + '@expo/config-plugins': 54.0.4(supports-color@8.1.1) '@expo/config-types': 54.0.10 '@expo/image-utils': 0.8.12 '@expo/json-file': 10.0.14 '@react-native/normalize-colors': 0.81.5 - debug: 4.4.3 - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + debug: 4.4.3(supports-color@8.1.1) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) resolve-from: 5.0.0 semver: 7.8.0 xml2js: 0.6.0 @@ -10882,38 +11077,38 @@ snapshots: '@expo/sudo-prompt@9.3.2': {} - '@expo/vector-icons@15.1.1(expo-font@14.0.12(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': + '@expo/vector-icons@15.1.1(expo-font@14.0.12(83e0f64f4281e4798012214ad5b2d72d))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)': dependencies: - expo-font: 14.0.12(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo-font: 14.0.12(83e0f64f4281e4798012214ad5b2d72d) react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) - '@expo/webpack-config@19.0.1(clean-css@5.3.3)(csso@5.0.5)(esbuild@0.25.12)(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(html-minifier-terser@6.1.0)(postcss@8.5.14)': + '@expo/webpack-config@19.0.1(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(debug@4.4.3(supports-color@8.1.1))(esbuild@0.25.12)(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1))(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 - babel-loader: 8.4.1(@babel/core@7.29.0)(webpack@5.106.2(postcss@8.5.14)) + '@babel/core': 7.29.0(supports-color@8.1.1) + babel-loader: 8.4.1(@babel/core@7.29.0(supports-color@8.1.1))(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)) chalk: 4.1.2 - clean-webpack-plugin: 4.0.0(webpack@5.106.2(postcss@8.5.14)) - copy-webpack-plugin: 10.2.4(webpack@5.106.2(postcss@8.5.14)) - css-loader: 6.11.0(webpack@5.106.2(postcss@8.5.14)) - css-minimizer-webpack-plugin: 3.4.1(clean-css@5.3.3)(csso@5.0.5)(esbuild@0.25.12)(webpack@5.106.2(postcss@8.5.14)) - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - expo-pwa: 0.0.127(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)) + clean-webpack-plugin: 4.0.0(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)) + copy-webpack-plugin: 10.2.4(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)) + css-loader: 6.11.0(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)) + css-minimizer-webpack-plugin: 3.4.1(clean-css@5.3.3)(csso@5.0.5)(esbuild@0.25.12)(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) + expo-pwa: 0.0.127(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)) find-up: 5.0.0 find-yarn-workspace-root: 2.0.0 fs-extra: 11.3.5 getenv: 1.0.0 - html-webpack-plugin: 5.6.7(webpack@5.106.2(postcss@8.5.14)) + html-webpack-plugin: 5.6.7(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)) is-wsl: 2.2.0 - mini-css-extract-plugin: 2.10.2(webpack@5.106.2(postcss@8.5.14)) + mini-css-extract-plugin: 2.10.2(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)) node-html-parser: 5.4.2 semver: 7.5.4 - source-map-loader: 3.0.2(webpack@5.106.2(postcss@8.5.14)) - style-loader: 3.3.4(webpack@5.106.2(postcss@8.5.14)) - terser-webpack-plugin: 5.6.0(clean-css@5.3.3)(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(postcss@8.5.14)(webpack@5.106.2(postcss@8.5.14)) - webpack: 5.106.2(postcss@8.5.14) - webpack-dev-server: 4.15.2(webpack@5.106.2(postcss@8.5.14)) - webpack-manifest-plugin: 4.1.1(webpack@5.106.2(postcss@8.5.14)) + source-map-loader: 3.0.2(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)) + style-loader: 3.3.4(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)) + terser-webpack-plugin: 5.6.0(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)) + webpack: 5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14) + webpack-dev-server: 4.15.2(debug@4.4.3(supports-color@8.1.1))(supports-color@8.1.1)(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)) + webpack-manifest-plugin: 4.1.1(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)) transitivePeerDependencies: - '@minify-html/node' - '@parcel/css' @@ -11006,6 +11201,18 @@ snapshots: dependencies: dom-mutator: 0.6.0 + '@humanwhocodes/config-array@0.13.0(supports-color@8.1.1)': + dependencies: + '@humanwhocodes/object-schema': 2.0.3 + debug: 4.4.3(supports-color@8.1.1) + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/object-schema@2.0.3': {} + '@ide/backoff@1.0.0': {} '@ipld/dag-cbor@9.2.7': @@ -11049,12 +11256,12 @@ snapshots: jest-util: 29.7.0 slash: 3.0.0 - '@jest/core@29.7.0': + '@jest/core@29.7.0(supports-color@8.1.1)': dependencies: '@jest/console': 29.7.0 - '@jest/reporters': 29.7.0 + '@jest/reporters': 29.7.0(supports-color@8.1.1) '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 + '@jest/transform': 29.7.0(supports-color@8.1.1) '@jest/types': 29.6.3 '@types/node': 24.12.4 ansi-escapes: 4.3.2 @@ -11063,15 +11270,15 @@ snapshots: exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@24.12.4) + jest-config: 29.7.0(@types/node@24.12.4)(supports-color@8.1.1) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 jest-resolve: 29.7.0 - jest-resolve-dependencies: 29.7.0 - jest-runner: 29.7.0 - jest-runtime: 29.7.0 - jest-snapshot: 29.7.0 + jest-resolve-dependencies: 29.7.0(supports-color@8.1.1) + jest-runner: 29.7.0(supports-color@8.1.1) + jest-runtime: 29.7.0(supports-color@8.1.1) + jest-snapshot: 29.7.0(supports-color@8.1.1) jest-util: 29.7.0 jest-validate: 29.7.0 jest-watcher: 29.7.0 @@ -11105,10 +11312,10 @@ snapshots: dependencies: '@jest/get-type': 30.1.0 - '@jest/expect@29.7.0': + '@jest/expect@29.7.0(supports-color@8.1.1)': dependencies: expect: 29.7.0 - jest-snapshot: 29.7.0 + jest-snapshot: 29.7.0(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -11123,10 +11330,10 @@ snapshots: '@jest/get-type@30.1.0': {} - '@jest/globals@29.7.0': + '@jest/globals@29.7.0(supports-color@8.1.1)': dependencies: '@jest/environment': 29.7.0 - '@jest/expect': 29.7.0 + '@jest/expect': 29.7.0(supports-color@8.1.1) '@jest/types': 29.6.3 jest-mock: 29.7.0 transitivePeerDependencies: @@ -11137,12 +11344,12 @@ snapshots: '@types/node': 24.12.4 jest-regex-util: 30.4.0 - '@jest/reporters@29.7.0': + '@jest/reporters@29.7.0(supports-color@8.1.1)': dependencies: '@bcoe/v8-coverage': 0.2.3 '@jest/console': 29.7.0 '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 + '@jest/transform': 29.7.0(supports-color@8.1.1) '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.31 '@types/node': 24.12.4 @@ -11152,9 +11359,9 @@ snapshots: glob: 7.2.3 graceful-fs: 4.2.11 istanbul-lib-coverage: 3.2.2 - istanbul-lib-instrument: 6.0.3 + istanbul-lib-instrument: 6.0.3(supports-color@8.1.1) istanbul-lib-report: 3.0.1 - istanbul-lib-source-maps: 4.0.1 + istanbul-lib-source-maps: 4.0.1(supports-color@8.1.1) istanbul-reports: 3.2.0 jest-message-util: 29.7.0 jest-util: 29.7.0 @@ -11194,12 +11401,12 @@ snapshots: jest-haste-map: 29.7.0 slash: 3.0.0 - '@jest/transform@29.7.0': + '@jest/transform@29.7.0(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.31 - babel-plugin-istanbul: 6.1.1 + babel-plugin-istanbul: 6.1.1(supports-color@8.1.1) chalk: 4.1.2 convert-source-map: 2.0.0 fast-json-stable-stringify: 2.1.0 @@ -11261,29 +11468,29 @@ snapshots: '@lingui/babel-plugin-extract-messages@5.9.5': {} - '@lingui/babel-plugin-lingui-macro@5.9.5(@typescript/typescript6@6.0.2)': + '@lingui/babel-plugin-lingui-macro@5.9.5(@typescript/typescript6@6.0.2)(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/runtime': 7.29.2 '@babel/types': 7.29.0 '@lingui/conf': 5.9.5(@typescript/typescript6@6.0.2) - '@lingui/core': 5.9.5(@lingui/babel-plugin-lingui-macro@5.9.5(@typescript/typescript6@6.0.2)) + '@lingui/core': 5.9.5(@lingui/babel-plugin-lingui-macro@5.9.5(@typescript/typescript6@6.0.2)(supports-color@8.1.1)) '@lingui/message-utils': 5.9.5 transitivePeerDependencies: - supports-color - typescript - '@lingui/cli@5.9.5(@typescript/typescript6@6.0.2)': + '@lingui/cli@5.9.5(@typescript/typescript6@6.0.2)(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/generator': 7.29.1 '@babel/parser': 7.29.3 '@babel/runtime': 7.29.2 '@babel/types': 7.29.0 '@lingui/babel-plugin-extract-messages': 5.9.5 - '@lingui/babel-plugin-lingui-macro': 5.9.5(@typescript/typescript6@6.0.2) + '@lingui/babel-plugin-lingui-macro': 5.9.5(@typescript/typescript6@6.0.2)(supports-color@8.1.1) '@lingui/conf': 5.9.5(@typescript/typescript6@6.0.2) - '@lingui/core': 5.9.5(@lingui/babel-plugin-lingui-macro@5.9.5(@typescript/typescript6@6.0.2)) + '@lingui/core': 5.9.5(@lingui/babel-plugin-lingui-macro@5.9.5(@typescript/typescript6@6.0.2)(supports-color@8.1.1)) '@lingui/format-po': 5.9.5(@typescript/typescript6@6.0.2) '@lingui/message-utils': 5.9.5 chokidar: 3.5.1 @@ -11301,7 +11508,7 @@ snapshots: pofile: 1.1.4 pseudolocale: 2.2.0 source-map: 0.7.6 - threads: 1.7.0 + threads: 1.7.0(supports-color@8.1.1) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -11317,12 +11524,12 @@ snapshots: transitivePeerDependencies: - typescript - '@lingui/core@5.9.5(@lingui/babel-plugin-lingui-macro@5.9.5(@typescript/typescript6@6.0.2))': + '@lingui/core@5.9.5(@lingui/babel-plugin-lingui-macro@5.9.5(@typescript/typescript6@6.0.2)(supports-color@8.1.1))': dependencies: '@babel/runtime': 7.29.2 '@lingui/message-utils': 5.9.5 optionalDependencies: - '@lingui/babel-plugin-lingui-macro': 5.9.5(@typescript/typescript6@6.0.2) + '@lingui/babel-plugin-lingui-macro': 5.9.5(@typescript/typescript6@6.0.2)(supports-color@8.1.1) '@lingui/format-po@5.9.5(@typescript/typescript6@6.0.2)': dependencies: @@ -11338,13 +11545,13 @@ snapshots: '@messageformat/parser': 5.1.1 js-sha256: 0.10.1 - '@lingui/react@5.9.5(@lingui/babel-plugin-lingui-macro@5.9.5(@typescript/typescript6@6.0.2))(react@19.1.0)': + '@lingui/react@5.9.5(@lingui/babel-plugin-lingui-macro@5.9.5(@typescript/typescript6@6.0.2)(supports-color@8.1.1))(react@19.1.0)': dependencies: '@babel/runtime': 7.29.2 - '@lingui/core': 5.9.5(@lingui/babel-plugin-lingui-macro@5.9.5(@typescript/typescript6@6.0.2)) + '@lingui/core': 5.9.5(@lingui/babel-plugin-lingui-macro@5.9.5(@typescript/typescript6@6.0.2)(supports-color@8.1.1)) react: 19.1.0 optionalDependencies: - '@lingui/babel-plugin-lingui-macro': 5.9.5(@typescript/typescript6@6.0.2) + '@lingui/babel-plugin-lingui-macro': 5.9.5(@typescript/typescript6@6.0.2)(supports-color@8.1.1) '@messageformat/parser@5.1.1': dependencies: @@ -11440,7 +11647,7 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@pmmmwh/react-refresh-webpack-plugin@0.5.17(react-refresh@0.14.2)(type-fest@1.4.0)(webpack-dev-server@4.15.2(webpack@5.106.2(postcss@8.5.14)))(webpack@5.106.2(postcss@8.5.14))': + '@pmmmwh/react-refresh-webpack-plugin@0.5.17(react-refresh@0.14.2)(type-fest@1.4.0)(webpack-dev-server@4.15.2(debug@4.4.3(supports-color@8.1.1))(supports-color@8.1.1)(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)))(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14))': dependencies: ansi-html: 0.0.9 core-js-pure: 3.49.0 @@ -11450,10 +11657,10 @@ snapshots: react-refresh: 0.14.2 schema-utils: 4.3.3 source-map: 0.7.6 - webpack: 5.106.2(postcss@8.5.14) + webpack: 5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14) optionalDependencies: type-fest: 1.4.0 - webpack-dev-server: 4.15.2(webpack@5.106.2(postcss@8.5.14)) + webpack-dev-server: 4.15.2(debug@4.4.3(supports-color@8.1.1))(supports-color@8.1.1)(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)) '@polka/url@1.0.0-next.29': {} @@ -12206,74 +12413,74 @@ snapshots: '@radix-ui/rect@1.1.1': {} - '@react-native-async-storage/async-storage@2.2.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))': + '@react-native-async-storage/async-storage@2.2.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))': dependencies: merge-options: 3.0.4 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) '@react-native/assets-registry@0.81.5': {} - '@react-native/babel-plugin-codegen@0.81.5(@babel/core@7.29.0)': + '@react-native/babel-plugin-codegen@0.81.5(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/traverse': 7.29.0 - '@react-native/codegen': 0.81.5(@babel/core@7.29.0) + '@babel/traverse': 7.29.0(supports-color@8.1.1) + '@react-native/codegen': 0.81.5(@babel/core@7.29.0(supports-color@8.1.1)) transitivePeerDependencies: - '@babel/core' - supports-color - '@react-native/babel-preset@0.81.5(@babel/core@7.29.0)': + '@react-native/babel-preset@0.81.5(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 - '@babel/plugin-proposal-export-default-from': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-export-default-from': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-async-generator-functions': 7.29.0(@babel/core@7.29.0) - '@babel/plugin-transform-async-to-generator': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-block-scoping': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-classes': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-computed-properties': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0) - '@babel/plugin-transform-flow-strip-types': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-logical-assignment-operators': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-named-capturing-groups-regex': 7.29.0(@babel/core@7.29.0) - '@babel/plugin-transform-nullish-coalescing-operator': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-numeric-separator': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-object-rest-spread': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-optional-catch-binding': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.0) - '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-private-property-in-object': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-react-display-name': 7.28.0(@babel/core@7.29.0) - '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-regenerator': 7.29.0(@babel/core@7.29.0) - '@babel/plugin-transform-runtime': 7.29.0(@babel/core@7.29.0) - '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-spread': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.29.0) + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/plugin-proposal-export-default-from': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-export-default-from': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-async-generator-functions': 7.29.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-async-to-generator': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-block-scoping': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-classes': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-computed-properties': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-flow-strip-types': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-logical-assignment-operators': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-named-capturing-groups-regex': 7.29.0(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-nullish-coalescing-operator': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-numeric-separator': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-object-rest-spread': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-optional-catch-binding': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-private-property-in-object': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-react-display-name': 7.28.0(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-regenerator': 7.29.0(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-runtime': 7.29.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-spread': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) '@babel/template': 7.28.6 - '@react-native/babel-plugin-codegen': 0.81.5(@babel/core@7.29.0) + '@react-native/babel-plugin-codegen': 0.81.5(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) babel-plugin-syntax-hermes-parser: 0.29.1 - babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.29.0) + babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.29.0(supports-color@8.1.1)) react-refresh: 0.14.2 transitivePeerDependencies: - supports-color - '@react-native/codegen@0.81.5(@babel/core@7.29.0)': + '@react-native/codegen@0.81.5(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/parser': 7.29.3 glob: 7.2.3 hermes-parser: 0.29.1 @@ -12281,17 +12488,17 @@ snapshots: nullthrows: 1.1.1 yargs: 17.7.2 - '@react-native/community-cli-plugin@0.81.5(@react-native/metro-config@0.86.0(@babel/core@7.29.0))': + '@react-native/community-cli-plugin@0.81.5(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@react-native/dev-middleware': 0.81.5 - debug: 4.4.3 + '@react-native/dev-middleware': 0.81.5(supports-color@8.1.1) + debug: 4.4.3(supports-color@8.1.1) invariant: 2.2.4 - metro: 0.83.3 - metro-config: 0.83.3 + metro: 0.83.3(supports-color@8.1.1) + metro-config: 0.83.3(supports-color@8.1.1) metro-core: 0.83.3 semver: 7.8.0 optionalDependencies: - '@react-native/metro-config': 0.86.0(@babel/core@7.29.0) + '@react-native/metro-config': 0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) transitivePeerDependencies: - bufferutil - supports-color @@ -12299,18 +12506,18 @@ snapshots: '@react-native/debugger-frontend@0.81.5': {} - '@react-native/dev-middleware@0.81.5': + '@react-native/dev-middleware@0.81.5(supports-color@8.1.1)': dependencies: '@isaacs/ttlcache': 1.4.1 '@react-native/debugger-frontend': 0.81.5 - chrome-launcher: 0.15.2 - chromium-edge-launcher: 0.2.0 - connect: 3.7.0 - debug: 4.4.3 + chrome-launcher: 0.15.2(supports-color@8.1.1) + chromium-edge-launcher: 0.2.0(supports-color@8.1.1) + connect: 3.7.0(supports-color@8.1.1) + debug: 4.4.3(supports-color@8.1.1) invariant: 2.2.4 nullthrows: 1.1.1 open: 7.4.2 - serve-static: 1.16.3 + serve-static: 1.16.3(supports-color@8.1.1) ws: 6.2.3 transitivePeerDependencies: - bufferutil @@ -12323,20 +12530,20 @@ snapshots: '@react-native/js-polyfills@0.86.0': {} - '@react-native/metro-babel-transformer@0.86.0(@babel/core@7.29.0)': + '@react-native/metro-babel-transformer@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 - '@react-native/babel-preset': 0.81.5(@babel/core@7.29.0) + '@babel/core': 7.29.0(supports-color@8.1.1) + '@react-native/babel-preset': 0.81.5(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) hermes-parser: 0.36.0 nullthrows: 1.1.1 transitivePeerDependencies: - supports-color - '@react-native/metro-config@0.86.0(@babel/core@7.29.0)': + '@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: '@react-native/js-polyfills': 0.86.0 - '@react-native/metro-babel-transformer': 0.86.0(@babel/core@7.29.0) - metro-config: 0.84.4 + '@react-native/metro-babel-transformer': 0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + metro-config: 0.84.4(supports-color@8.1.1) metro-runtime: 0.84.4 transitivePeerDependencies: - '@babel/core' @@ -12348,24 +12555,24 @@ snapshots: '@react-native/typescript-config@0.81.6': {} - '@react-native/virtualized-lists@0.81.5(@types/react@19.1.17)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': + '@react-native/virtualized-lists@0.81.5(@types/react@19.1.17)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)': dependencies: invariant: 2.2.4 nullthrows: 1.1.1 react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) optionalDependencies: '@types/react': 19.1.17 - '@react-navigation/bottom-tabs@7.16.0(91eab7bd987834c4c877d5cbf9d3796c)': + '@react-navigation/bottom-tabs@7.16.0(15d086c577324dcb87d77ea7fa3207d2)': dependencies: - '@react-navigation/elements': 2.9.17(@react-navigation/native@7.2.4(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - '@react-navigation/native': 7.2.4(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + '@react-navigation/elements': 2.9.17(0db73217b8c0536280a44f0cfa3926ed) + '@react-navigation/native': 7.2.4(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) color: 4.2.3 react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) - react-native-safe-area-context: 5.6.2(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - react-native-screens: 4.24.0(patch_hash=74a74b489f8074a1974c2792c9c97aec30d8e251669aa37fc5c99993bb4a19e6)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) + react-native-safe-area-context: 5.6.2(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) + react-native-screens: 4.24.0(patch_hash=74a74b489f8074a1974c2792c9c97aec30d8e251669aa37fc5c99993bb4a19e6)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) sf-symbols-typescript: 2.2.0 transitivePeerDependencies: - '@react-native-masked-view/masked-view' @@ -12382,38 +12589,38 @@ snapshots: use-latest-callback: 0.2.6(react@19.1.0) use-sync-external-store: 1.6.0(react@19.1.0) - '@react-navigation/elements@2.9.17(@react-navigation/native@7.2.4(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': + '@react-navigation/elements@2.9.17(0db73217b8c0536280a44f0cfa3926ed)': dependencies: - '@react-navigation/native': 7.2.4(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + '@react-navigation/native': 7.2.4(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) color: 4.2.3 react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) - react-native-safe-area-context: 5.6.2(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) + react-native-safe-area-context: 5.6.2(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) use-latest-callback: 0.2.6(react@19.1.0) use-sync-external-store: 1.6.0(react@19.1.0) - '@react-navigation/native-stack@7.15.0(91eab7bd987834c4c877d5cbf9d3796c)': + '@react-navigation/native-stack@7.15.0(15d086c577324dcb87d77ea7fa3207d2)': dependencies: - '@react-navigation/elements': 2.9.17(@react-navigation/native@7.2.4(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - '@react-navigation/native': 7.2.4(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + '@react-navigation/elements': 2.9.17(0db73217b8c0536280a44f0cfa3926ed) + '@react-navigation/native': 7.2.4(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) color: 4.2.3 react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) - react-native-safe-area-context: 5.6.2(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - react-native-screens: 4.24.0(patch_hash=74a74b489f8074a1974c2792c9c97aec30d8e251669aa37fc5c99993bb4a19e6)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) + react-native-safe-area-context: 5.6.2(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) + react-native-screens: 4.24.0(patch_hash=74a74b489f8074a1974c2792c9c97aec30d8e251669aa37fc5c99993bb4a19e6)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) sf-symbols-typescript: 2.2.0 warn-once: 0.1.1 transitivePeerDependencies: - '@react-native-masked-view/masked-view' - '@react-navigation/native@7.2.4(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': + '@react-navigation/native@7.2.4(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)': dependencies: '@react-navigation/core': 7.17.4(react@19.1.0) escape-string-regexp: 4.0.0 fast-deep-equal: 3.1.3 nanoid: 3.3.12 react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) use-latest-callback: 0.2.6(react@19.1.0) '@react-navigation/routers@7.5.5': @@ -12438,11 +12645,11 @@ snapshots: '@sentry/replay': 10.64.0 '@sentry/replay-canvas': 10.64.0 - '@sentry/bundler-plugin-core@3.6.1': + '@sentry/bundler-plugin-core@3.6.1(supports-color@8.1.1)': dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@sentry/babel-plugin-component-annotate': 3.6.1 - '@sentry/cli': 2.58.5 + '@sentry/cli': 2.58.5(supports-color@8.1.1) dotenv: 16.6.1 find-up: 5.0.0 glob: 9.3.5 @@ -12500,9 +12707,9 @@ snapshots: '@sentry/cli-win32-x64@3.6.0': optional: true - '@sentry/cli@2.58.5': + '@sentry/cli@2.58.5(supports-color@8.1.1)': dependencies: - https-proxy-agent: 5.0.1 + https-proxy-agent: 5.0.1(supports-color@8.1.1) node-fetch: 2.7.0 progress: 2.0.3 proxy-from-env: 1.1.0 @@ -12542,29 +12749,29 @@ snapshots: dependencies: '@sentry/conventions': 0.15.1 - '@sentry/expo-upload-sourcemaps@8.18.0(patch_hash=2368e1e1986165e7ead3c60017b78b2f9ade1b6a6b2f5ad27cd428608c6f12ae)(@expo/env@2.0.11)(dotenv@16.6.1)': + '@sentry/expo-upload-sourcemaps@8.18.0(patch_hash=2368e1e1986165e7ead3c60017b78b2f9ade1b6a6b2f5ad27cd428608c6f12ae)(@expo/env@2.0.11(supports-color@8.1.1))(dotenv@16.6.1)': dependencies: '@sentry/cli': 3.6.0 optionalDependencies: - '@expo/env': 2.0.11 + '@expo/env': 2.0.11(supports-color@8.1.1) dotenv: 16.6.1 '@sentry/feedback@10.64.0': dependencies: '@sentry/core': 10.64.0 - '@sentry/react-native@8.18.0(@expo/env@2.0.11)(dotenv@16.6.1)(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': + '@sentry/react-native@8.18.0(61f03fc01fabd5cbd0e83cc2a57309d9)': dependencies: '@sentry/babel-plugin-component-annotate': 5.3.0 '@sentry/browser': 10.64.0 '@sentry/cli': 3.6.0 '@sentry/core': 10.64.0 - '@sentry/expo-upload-sourcemaps': 8.18.0(patch_hash=2368e1e1986165e7ead3c60017b78b2f9ade1b6a6b2f5ad27cd428608c6f12ae)(@expo/env@2.0.11)(dotenv@16.6.1) + '@sentry/expo-upload-sourcemaps': 8.18.0(patch_hash=2368e1e1986165e7ead3c60017b78b2f9ade1b6a6b2f5ad27cd428608c6f12ae)(@expo/env@2.0.11(supports-color@8.1.1))(dotenv@16.6.1) '@sentry/react': 10.64.0(react@19.1.0) react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) optionalDependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) transitivePeerDependencies: - '@expo/env' - dotenv @@ -12585,12 +12792,12 @@ snapshots: '@sentry/browser-utils': 10.64.0 '@sentry/core': 10.64.0 - '@sentry/webpack-plugin@3.6.1(webpack@5.106.2(postcss@8.5.14))': + '@sentry/webpack-plugin@3.6.1(supports-color@8.1.1)(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14))': dependencies: - '@sentry/bundler-plugin-core': 3.6.1 + '@sentry/bundler-plugin-core': 3.6.1(supports-color@8.1.1) unplugin: 1.0.1 uuid: 9.0.1 - webpack: 5.106.2(postcss@8.5.14) + webpack: 5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14) transitivePeerDependencies: - encoding - supports-color @@ -12607,6 +12814,8 @@ snapshots: dependencies: '@sinonjs/commons': 3.0.1 + '@standard-schema/spec@1.1.0': {} + '@tanstack/query-async-storage-persister@5.100.10': dependencies: '@tanstack/query-core': 5.100.10 @@ -12629,17 +12838,17 @@ snapshots: '@tanstack/query-core': 5.100.10 react: 19.1.0 - '@testing-library/react-native@13.3.3(jest@29.7.0(@types/node@24.12.4))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0)': + '@testing-library/react-native@13.3.3(jest@29.7.0(@types/node@24.12.4)(supports-color@8.1.1))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: jest-matcher-utils: 30.4.1 picocolors: 1.1.1 pretty-format: 30.4.1 react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) react-test-renderer: 19.1.0(react@19.1.0) redent: 3.0.0 optionalDependencies: - jest: 29.7.0(@types/node@24.12.4) + jest: 29.7.0(@types/node@24.12.4)(supports-color@8.1.1) '@tiptap/core@2.27.2(@tiptap/pm@2.27.2)': dependencies: @@ -13139,15 +13348,19 @@ snapshots: dependencies: acorn: 8.16.0 + acorn-jsx@5.3.2(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + acorn-walk@8.3.5: dependencies: acorn: 8.16.0 acorn@8.16.0: {} - agent-base@6.0.2: + agent-base@6.0.2(supports-color@8.1.1): dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -13315,34 +13528,34 @@ snapshots: await-lock@3.0.0: {} - babel-jest@29.7.0(@babel/core@7.29.0): + babel-jest@29.7.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1): dependencies: - '@babel/core': 7.29.0 - '@jest/transform': 29.7.0 + '@babel/core': 7.29.0(supports-color@8.1.1) + '@jest/transform': 29.7.0(supports-color@8.1.1) '@types/babel__core': 7.20.5 - babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.6.3(@babel/core@7.29.0) + babel-plugin-istanbul: 6.1.1(supports-color@8.1.1) + babel-preset-jest: 29.6.3(@babel/core@7.29.0(supports-color@8.1.1)) chalk: 4.1.2 graceful-fs: 4.2.11 slash: 3.0.0 transitivePeerDependencies: - supports-color - babel-loader@8.4.1(@babel/core@7.29.0)(webpack@5.106.2(postcss@8.5.14)): + babel-loader@8.4.1(@babel/core@7.29.0(supports-color@8.1.1))(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)): dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) find-cache-dir: 3.3.2 loader-utils: 2.0.4 make-dir: 3.1.0 schema-utils: 2.7.1 - webpack: 5.106.2(postcss@8.5.14) + webpack: 5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14) - babel-plugin-istanbul@6.1.1: + babel-plugin-istanbul@6.1.1(supports-color@8.1.1): dependencies: '@babel/helper-plugin-utils': 7.28.6 '@istanbuljs/load-nyc-config': 1.1.0 '@istanbuljs/schema': 0.1.6 - istanbul-lib-instrument: 5.2.1 + istanbul-lib-instrument: 5.2.1(supports-color@8.1.1) test-exclude: 6.0.0 transitivePeerDependencies: - supports-color @@ -13362,35 +13575,35 @@ snapshots: reselect: 4.1.8 resolve: 1.22.12 - babel-plugin-polyfill-corejs2@0.4.17(@babel/core@7.29.0): + babel-plugin-polyfill-corejs2@0.4.17(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1): dependencies: '@babel/compat-data': 7.29.3 - '@babel/core': 7.29.0 - '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0) + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) semver: 6.3.1 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-corejs3@0.13.0(@babel/core@7.29.0): + babel-plugin-polyfill-corejs3@0.13.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1): dependencies: - '@babel/core': 7.29.0 - '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0) + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) core-js-compat: 3.49.0 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-corejs3@0.14.2(@babel/core@7.29.0): + babel-plugin-polyfill-corejs3@0.14.2(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1): dependencies: - '@babel/core': 7.29.0 - '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0) + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) core-js-compat: 3.49.0 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-regenerator@0.6.8(@babel/core@7.29.0): + babel-plugin-polyfill-regenerator@0.6.8(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1): dependencies: - '@babel/core': 7.29.0 - '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0) + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -13408,102 +13621,102 @@ snapshots: dependencies: hermes-parser: 0.29.1 - babel-plugin-transform-flow-enums@0.0.2(@babel/core@7.29.0): + babel-plugin-transform-flow-enums@0.0.2(@babel/core@7.29.0(supports-color@8.1.1)): dependencies: - '@babel/plugin-syntax-flow': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-flow': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1)) transitivePeerDependencies: - '@babel/core' babel-plugin-transform-remove-console@6.9.4: {} - babel-preset-current-node-syntax@1.2.0(@babel/core@7.29.0): + babel-preset-current-node-syntax@1.2.0(@babel/core@7.29.0(supports-color@8.1.1)): dependencies: - '@babel/core': 7.29.0 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.29.0) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.29.0) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.29.0) - '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.29.0) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.29.0) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.29.0) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.0) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.29.0) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.29.0) + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.29.0(supports-color@8.1.1)) - babel-preset-expo@54.0.10(@babel/core@7.29.0)(@babel/runtime@7.29.2)(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-refresh@0.14.2): + babel-preset-expo@54.0.10(@babel/core@7.29.0(supports-color@8.1.1))(@babel/runtime@7.29.2)(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1))(react-refresh@0.14.2)(supports-color@8.1.1): dependencies: - '@babel/helper-module-imports': 7.28.6 - '@babel/plugin-proposal-decorators': 7.29.0(@babel/core@7.29.0) - '@babel/plugin-proposal-export-default-from': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-syntax-export-default-from': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-class-static-block': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-flow-strip-types': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-object-rest-spread': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.0) - '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-private-property-in-object': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-runtime': 7.29.0(@babel/core@7.29.0) - '@babel/preset-react': 7.28.5(@babel/core@7.29.0) - '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0) - '@react-native/babel-preset': 0.81.5(@babel/core@7.29.0) + '@babel/helper-module-imports': 7.28.6(supports-color@8.1.1) + '@babel/plugin-proposal-decorators': 7.29.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-proposal-export-default-from': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-export-default-from': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-class-static-block': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-flow-strip-types': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-object-rest-spread': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-private-property-in-object': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-runtime': 7.29.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/preset-react': 7.28.5(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@react-native/babel-preset': 0.81.5(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) babel-plugin-react-compiler: 1.0.0 babel-plugin-react-native-web: 0.21.2 babel-plugin-syntax-hermes-parser: 0.29.1 - babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.29.0) - debug: 4.4.3 + babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.29.0(supports-color@8.1.1)) + debug: 4.4.3(supports-color@8.1.1) react-refresh: 0.14.2 resolve-from: 5.0.0 optionalDependencies: '@babel/runtime': 7.29.2 - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) transitivePeerDependencies: - '@babel/core' - supports-color - babel-preset-expo@54.0.12(@babel/core@7.29.0)(@babel/runtime@7.29.2)(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-refresh@0.14.2): + babel-preset-expo@54.0.12(@babel/core@7.29.0(supports-color@8.1.1))(@babel/runtime@7.29.2)(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1))(react-refresh@0.14.2)(supports-color@8.1.1): dependencies: - '@babel/helper-module-imports': 7.28.6 - '@babel/plugin-proposal-decorators': 7.29.0(@babel/core@7.29.0) - '@babel/plugin-proposal-export-default-from': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-syntax-export-default-from': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-class-static-block': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-flow-strip-types': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-object-rest-spread': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.0) - '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-private-property-in-object': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-runtime': 7.29.0(@babel/core@7.29.0) - '@babel/preset-react': 7.28.5(@babel/core@7.29.0) - '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0) - '@react-native/babel-preset': 0.81.5(@babel/core@7.29.0) + '@babel/helper-module-imports': 7.28.6(supports-color@8.1.1) + '@babel/plugin-proposal-decorators': 7.29.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-proposal-export-default-from': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-export-default-from': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-class-static-block': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-flow-strip-types': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-object-rest-spread': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-private-property-in-object': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-runtime': 7.29.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/preset-react': 7.28.5(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@react-native/babel-preset': 0.81.5(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) babel-plugin-react-compiler: 1.0.0 babel-plugin-react-native-web: 0.21.2 babel-plugin-syntax-hermes-parser: 0.29.1 - babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.29.0) - debug: 4.4.3 + babel-plugin-transform-flow-enums: 0.0.2(@babel/core@7.29.0(supports-color@8.1.1)) + debug: 4.4.3(supports-color@8.1.1) react-refresh: 0.14.2 resolve-from: 5.0.0 optionalDependencies: '@babel/runtime': 7.29.2 - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) transitivePeerDependencies: - '@babel/core' - supports-color - babel-preset-jest@29.6.3(@babel/core@7.29.0): + babel-preset-jest@29.6.3(@babel/core@7.29.0(supports-color@8.1.1)): dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) babel-plugin-jest-hoist: 29.6.3 - babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0(supports-color@8.1.1)) badgin@1.2.3: {} @@ -13543,11 +13756,11 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 - body-parser@1.20.5: + body-parser@1.20.5(supports-color@8.1.1): dependencies: bytes: 3.1.2 content-type: 1.0.5 - debug: 2.6.9 + debug: 2.6.9(supports-color@8.1.1) depd: 2.0.0 destroy: 1.2.0 http-errors: 2.0.1 @@ -13704,23 +13917,23 @@ snapshots: chownr@3.0.0: {} - chrome-launcher@0.15.2: + chrome-launcher@0.15.2(supports-color@8.1.1): dependencies: '@types/node': 24.12.4 escape-string-regexp: 4.0.0 is-wsl: 2.2.0 - lighthouse-logger: 1.4.2 + lighthouse-logger: 1.4.2(supports-color@8.1.1) transitivePeerDependencies: - supports-color chrome-trace-event@1.0.4: {} - chromium-edge-launcher@0.2.0: + chromium-edge-launcher@0.2.0(supports-color@8.1.1): dependencies: '@types/node': 24.12.4 escape-string-regexp: 4.0.0 is-wsl: 2.2.0 - lighthouse-logger: 1.4.2 + lighthouse-logger: 1.4.2(supports-color@8.1.1) mkdirp: 1.0.4 rimraf: 3.0.2 transitivePeerDependencies: @@ -13738,10 +13951,10 @@ snapshots: dependencies: source-map: 0.6.1 - clean-webpack-plugin@4.0.0(webpack@5.106.2(postcss@8.5.14)): + clean-webpack-plugin@4.0.0(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)): dependencies: del: 4.1.1 - webpack: 5.106.2(postcss@8.5.14) + webpack: 5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14) cli-cursor@2.1.0: dependencies: @@ -13840,11 +14053,11 @@ snapshots: dependencies: mime-db: 1.54.0 - compression@1.8.1: + compression@1.8.1(supports-color@8.1.1): dependencies: bytes: 3.1.2 compressible: 2.0.18 - debug: 2.6.9 + debug: 2.6.9(supports-color@8.1.1) negotiator: 0.6.4 on-headers: 1.1.0 safe-buffer: 5.2.1 @@ -13856,10 +14069,10 @@ snapshots: connect-history-api-fallback@2.0.0: {} - connect@3.7.0: + connect@3.7.0(supports-color@8.1.1): dependencies: - debug: 2.6.9 - finalhandler: 1.1.2 + debug: 2.6.9(supports-color@8.1.1) + finalhandler: 1.1.2(supports-color@8.1.1) parseurl: 1.3.3 utils-merge: 1.0.1 transitivePeerDependencies: @@ -13877,7 +14090,7 @@ snapshots: cookie@0.7.2: {} - copy-webpack-plugin@10.2.4(webpack@5.106.2(postcss@8.5.14)): + copy-webpack-plugin@10.2.4(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)): dependencies: fast-glob: 3.3.3 glob-parent: 6.0.2 @@ -13885,7 +14098,7 @@ snapshots: normalize-path: 3.0.0 schema-utils: 4.3.3 serialize-javascript: 6.0.2 - webpack: 5.106.2(postcss@8.5.14) + webpack: 5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14) core-js-compat@3.49.0: dependencies: @@ -13904,13 +14117,13 @@ snapshots: optionalDependencies: typescript: '@typescript/typescript6@6.0.2' - create-jest@29.7.0(@types/node@24.12.4): + create-jest@29.7.0(@types/node@24.12.4)(supports-color@8.1.1): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@24.12.4) + jest-config: 29.7.0(@types/node@24.12.4)(supports-color@8.1.1) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -13945,7 +14158,7 @@ snapshots: dependencies: utrie: 1.0.2 - css-loader@6.11.0(webpack@5.106.2(postcss@8.5.14)): + css-loader@6.11.0(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)): dependencies: icss-utils: 5.1.0(postcss@8.5.14) postcss: 8.5.14 @@ -13956,11 +14169,11 @@ snapshots: postcss-value-parser: 4.2.0 semver: 7.8.0 optionalDependencies: - webpack: 5.106.2(postcss@8.5.14) + webpack: 5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14) css-mediaquery@0.1.2: {} - css-minimizer-webpack-plugin@3.4.1(clean-css@5.3.3)(csso@5.0.5)(esbuild@0.25.12)(webpack@5.106.2(postcss@8.5.14)): + css-minimizer-webpack-plugin@3.4.1(clean-css@5.3.3)(csso@5.0.5)(esbuild@0.25.12)(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)): dependencies: cssnano: 5.1.15(postcss@8.5.14) jest-worker: 27.5.1 @@ -13968,7 +14181,7 @@ snapshots: schema-utils: 4.3.3 serialize-javascript: 6.0.2 source-map: 0.6.1 - webpack: 5.106.2(postcss@8.5.14) + webpack: 5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14) optionalDependencies: clean-css: 5.3.3 csso: 5.0.5 @@ -14101,17 +14314,23 @@ snapshots: debounce@1.2.1: {} - debug@2.6.9: + debug@2.6.9(supports-color@8.1.1): dependencies: ms: 2.0.0 + optionalDependencies: + supports-color: 8.1.1 - debug@3.2.7: + debug@3.2.7(supports-color@8.1.1): dependencies: ms: 2.1.3 + optionalDependencies: + supports-color: 8.1.1 - debug@4.4.3: + debug@4.4.3(supports-color@8.1.1): dependencies: ms: 2.1.3 + optionalDependencies: + supports-color: 8.1.1 decamelize@1.2.0: {} @@ -14123,6 +14342,8 @@ snapshots: deep-extend@0.6.0: {} + deep-is@0.1.4: {} + deepmerge@4.3.1: {} default-gateway@6.0.3: @@ -14187,6 +14408,10 @@ snapshots: dependencies: '@leichtgewicht/ip-codec': 2.0.5 + doctrine@3.0.0: + dependencies: + esutils: 2.0.3 + dom-converter@0.2.0: dependencies: utila: 0.4.0 @@ -14430,46 +14655,106 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-plugin-react-compiler@19.1.0-rc.2(eslint@eslint): + eslint-plugin-react-compiler@19.1.0-rc.2(eslint@8.57.1(supports-color@8.1.1))(supports-color@8.1.1): dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/parser': 7.29.3 - '@babel/plugin-proposal-private-methods': 7.18.6(@babel/core@7.29.0) - eslint: link:eslint + '@babel/plugin-proposal-private-methods': 7.18.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + eslint: 8.57.1(supports-color@8.1.1) hermes-parser: 0.25.1 zod: 3.25.76 zod-validation-error: 3.5.4(zod@3.25.76) transitivePeerDependencies: - supports-color - eslint-plugin-react-native-a11y@3.5.1(eslint@eslint): + eslint-plugin-react-native-a11y@3.5.1(eslint@8.57.1(supports-color@8.1.1)): dependencies: '@babel/runtime': 7.29.2 ast-types-flow: 0.0.7 - eslint: link:eslint + eslint: 8.57.1(supports-color@8.1.1) jsx-ast-utils: 3.3.5 eslint-plugin-react-native-globals@0.1.2: {} - eslint-plugin-react-native@5.0.0(eslint@eslint): + eslint-plugin-react-native@5.0.0(eslint@8.57.1(supports-color@8.1.1)): dependencies: - eslint: link:eslint + eslint: 8.57.1(supports-color@8.1.1) eslint-plugin-react-native-globals: 0.1.2 - eslint-plugin-simple-import-sort@13.0.0(eslint@eslint): + eslint-plugin-simple-import-sort@13.0.0(eslint@8.57.1(supports-color@8.1.1)): dependencies: - eslint: link:eslint + eslint: 8.57.1(supports-color@8.1.1) eslint-scope@5.1.1: dependencies: esrecurse: 4.3.0 estraverse: 4.3.0 + eslint-scope@7.2.2: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint@8.57.1(supports-color@8.1.1): + dependencies: + '@eslint-community/eslint-utils': 4.10.1(eslint@8.57.1(supports-color@8.1.1)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/eslintrc': 2.1.4(supports-color@8.1.1) + '@eslint/js': 8.57.1 + '@humanwhocodes/config-array': 0.13.0(supports-color@8.1.1) + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + '@ungap/structured-clone': 1.3.1 + ajv: 6.15.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3(supports-color@8.1.1) + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.24.0 + graphemer: 1.4.0 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.1 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + esm@3.2.25: optional: true + espree@9.6.1: + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + eslint-visitor-keys: 3.4.3 + esprima@4.0.1: {} + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + esrecurse@4.3.0: dependencies: estraverse: 5.3.0 @@ -14521,208 +14806,208 @@ snapshots: jest-mock: 30.4.1 jest-util: 30.4.1 - expo-age-range@0.2.18(patch_hash=c325225749993424461eeece1d97a99b19c00da2ebc958a73c12e4eca0c39306)(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0)): + expo-age-range@0.2.18(patch_hash=c325225749993424461eeece1d97a99b19c00da2ebc958a73c12e4eca0c39306)(83f21d12a6c1aa99b4a65d070875cc68): dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) - expo-application@7.0.8(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)): + expo-application@7.0.8(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)): dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) - expo-asset@12.0.13(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + expo-asset@12.0.13(93a6abd34ab5ae64aff6a80525489f34): dependencies: '@expo/image-utils': 0.8.12 - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - expo-constants: 18.0.13(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0)) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) + expo-constants: 18.0.13(c1408f750868568d2dc75671b50084a7) react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) transitivePeerDependencies: - supports-color - expo-blur@15.0.8(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + expo-blur@15.0.8(83e0f64f4281e4798012214ad5b2d72d): dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) - expo-build-properties@1.0.10(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)): + expo-build-properties@1.0.10(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)): dependencies: ajv: 8.20.0 - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) semver: 7.8.0 - expo-camera@17.0.10(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-web@0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + expo-camera@17.0.10(8d9f18414d5ddcf91a3e8d2e3aa26778): dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) invariant: 2.2.4 react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) optionalDependencies: react-native-web: 0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - expo-clipboard@8.0.8(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + expo-clipboard@8.0.8(83e0f64f4281e4798012214ad5b2d72d): dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) - expo-constants@18.0.13(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0)): + expo-constants@18.0.13(c1408f750868568d2dc75671b50084a7): dependencies: - '@expo/config': 12.0.13 - '@expo/env': 2.0.11 - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + '@expo/config': 12.0.13(supports-color@8.1.1) + '@expo/env': 2.0.11(supports-color@8.1.1) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) transitivePeerDependencies: - supports-color - expo-contacts@15.0.11(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + expo-contacts@15.0.11(83e0f64f4281e4798012214ad5b2d72d): dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) - expo-dev-client@6.0.21(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)): + expo-dev-client@6.0.21(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1))(supports-color@8.1.1): dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - expo-dev-launcher: 6.0.21(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)) - expo-dev-menu: 7.0.19(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)) - expo-dev-menu-interface: 2.0.0(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)) - expo-manifests: 1.0.11(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)) - expo-updates-interface: 2.0.0(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) + expo-dev-launcher: 6.0.21(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1))(supports-color@8.1.1) + expo-dev-menu: 7.0.19(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)) + expo-dev-menu-interface: 2.0.0(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)) + expo-manifests: 1.0.11(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1))(supports-color@8.1.1) + expo-updates-interface: 2.0.0(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)) transitivePeerDependencies: - supports-color - expo-dev-launcher@6.0.21(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)): + expo-dev-launcher@6.0.21(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1))(supports-color@8.1.1): dependencies: ajv: 8.20.0 - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - expo-dev-menu: 7.0.19(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)) - expo-manifests: 1.0.11(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) + expo-dev-menu: 7.0.19(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)) + expo-manifests: 1.0.11(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1))(supports-color@8.1.1) transitivePeerDependencies: - supports-color - expo-dev-menu-interface@2.0.0(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)): + expo-dev-menu-interface@2.0.0(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)): dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) - expo-dev-menu@7.0.19(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)): + expo-dev-menu@7.0.19(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)): dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - expo-dev-menu-interface: 2.0.0(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) + expo-dev-menu-interface: 2.0.0(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)) - expo-device@8.0.10(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)): + expo-device@8.0.10(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)): dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) ua-parser-js: 0.7.41 expo-eas-client@1.0.8: {} - expo-file-system@19.0.22(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0)): + expo-file-system@19.0.22(83f21d12a6c1aa99b4a65d070875cc68): dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) - expo-file-system@19.0.23(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0)): + expo-file-system@19.0.23(83f21d12a6c1aa99b4a65d070875cc68): dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) - expo-font@14.0.11(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + expo-font@14.0.11(83e0f64f4281e4798012214ad5b2d72d): dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) fontfaceobserver: 2.3.0 react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) - expo-font@14.0.12(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + expo-font@14.0.12(83e0f64f4281e4798012214ad5b2d72d): dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) fontfaceobserver: 2.3.0 react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) - expo-glass-effect@0.1.10(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + expo-glass-effect@0.1.10(83e0f64f4281e4798012214ad5b2d72d): dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) - expo-haptics@15.0.8(patch_hash=b33910ba2753c4eccdc885b449e6e33aa90b9cefa75ca8639762a26013141410)(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)): + expo-haptics@15.0.8(patch_hash=b33910ba2753c4eccdc885b449e6e33aa90b9cefa75ca8639762a26013141410)(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)): dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) - expo-image-loader@6.0.0(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)): + expo-image-loader@6.0.0(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)): dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) - expo-image-manipulator@14.0.8(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)): + expo-image-manipulator@14.0.8(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)): dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - expo-image-loader: 6.0.0(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) + expo-image-loader: 6.0.0(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)) - expo-image-picker@17.0.11(patch_hash=47b28f6ddb8bcaa6483f8714c82daaa0001122f2b0dac6c05d19e98a61a6ceab)(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)): + expo-image-picker@17.0.11(patch_hash=47b28f6ddb8bcaa6483f8714c82daaa0001122f2b0dac6c05d19e98a61a6ceab)(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)): dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - expo-image-loader: 6.0.0(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) + expo-image-loader: 6.0.0(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)) - expo-image@3.0.11(patch_hash=6d46ffac33426c5285777da5bf5f88c68c8ce27290595dfa65f38c478930c2c4)(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-web@0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + expo-image@3.0.11(patch_hash=6d46ffac33426c5285777da5bf5f88c68c8ce27290595dfa65f38c478930c2c4)(8d9f18414d5ddcf91a3e8d2e3aa26778): dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) optionalDependencies: react-native-web: 0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - expo-intent-launcher@13.0.8(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)): + expo-intent-launcher@13.0.8(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)): dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) expo-json-utils@0.15.0: {} - expo-keep-awake@15.0.8(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react@19.1.0): + expo-keep-awake@15.0.8(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1))(react@19.1.0): dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) react: 19.1.0 - expo-linear-gradient@15.0.8(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + expo-linear-gradient@15.0.8(83e0f64f4281e4798012214ad5b2d72d): dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) - expo-linking@8.0.12(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + expo-linking@8.0.12(93a6abd34ab5ae64aff6a80525489f34): dependencies: - expo-constants: 18.0.13(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0)) + expo-constants: 18.0.13(c1408f750868568d2dc75671b50084a7) invariant: 2.2.4 react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) transitivePeerDependencies: - expo - supports-color - expo-localization@17.0.8(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react@19.1.0): + expo-localization@17.0.8(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1))(react@19.1.0): dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) react: 19.1.0 rtl-detect: 1.1.2 - expo-location@19.0.8(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)): + expo-location@19.0.8(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)): dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) - expo-manifests@1.0.11(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)): + expo-manifests@1.0.11(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1))(supports-color@8.1.1): dependencies: - '@expo/config': 12.0.13 - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + '@expo/config': 12.0.13(supports-color@8.1.1) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) expo-json-utils: 0.15.0 transitivePeerDependencies: - supports-color - expo-media-library@18.2.1(patch_hash=2b84c17999bb0c977eaef7fa8ac297f7074d91c94fb63726b42d2a277b26b39a)(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0)): + expo-media-library@18.2.1(patch_hash=2b84c17999bb0c977eaef7fa8ac297f7074d91c94fb63726b42d2a277b26b39a)(83f21d12a6c1aa99b4a65d070875cc68): dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) expo-modules-autolinking@3.0.26: dependencies: @@ -14732,150 +15017,150 @@ snapshots: require-from-string: 2.0.2 resolve-from: 5.0.0 - expo-modules-core@3.0.30(patch_hash=952fb1c8cb6dc8a0d8ef4e1114942a341d89b025e011f1acfff788929997346e)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + expo-modules-core@3.0.30(patch_hash=952fb1c8cb6dc8a0d8ef4e1114942a341d89b025e011f1acfff788929997346e)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0): dependencies: invariant: 2.2.4 react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) - expo-notifications@0.32.17(patch_hash=a45a8dcf3d8c4b5df4ee0e62bc79b755fcf9d28ca51a6f685b85830bf4afc2e5)(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + expo-notifications@0.32.17(patch_hash=a45a8dcf3d8c4b5df4ee0e62bc79b755fcf9d28ca51a6f685b85830bf4afc2e5)(93a6abd34ab5ae64aff6a80525489f34): dependencies: '@expo/image-utils': 0.8.12 '@ide/backoff': 1.0.0 abort-controller: 3.0.0 assert: 2.1.0 badgin: 1.2.3 - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - expo-application: 7.0.8(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)) - expo-constants: 18.0.13(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0)) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) + expo-application: 7.0.8(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)) + expo-constants: 18.0.13(c1408f750868568d2dc75671b50084a7) react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) transitivePeerDependencies: - supports-color - expo-paste-input@0.2.1(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + expo-paste-input@0.2.1(83e0f64f4281e4798012214ad5b2d72d): dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) - expo-privacy-sensitive@0.2.0(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + expo-privacy-sensitive@0.2.0(83e0f64f4281e4798012214ad5b2d72d): dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) - expo-pwa@0.0.127(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)): + expo-pwa@0.0.127(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)): dependencies: '@expo/image-utils': 0.8.12 chalk: 4.1.2 commander: 2.20.0 - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) update-check: 1.5.3 - expo-screen-orientation@9.0.9(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0)): + expo-screen-orientation@9.0.9(83f21d12a6c1aa99b4a65d070875cc68): dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) expo-server@1.0.7: {} - expo-sharing@14.0.8(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)): + expo-sharing@14.0.8(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)): dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) - expo-sms@14.0.8(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)): + expo-sms@14.0.8(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)): dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) - expo-splash-screen@31.0.13(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)): + expo-splash-screen@31.0.13(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1))(supports-color@8.1.1): dependencies: - '@expo/prebuild-config': 54.0.8(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)) - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + '@expo/prebuild-config': 54.0.8(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1))(supports-color@8.1.1) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) transitivePeerDependencies: - supports-color expo-structured-headers@5.0.0: {} - expo-system-ui@6.0.9(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-web@0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0)): + expo-system-ui@6.0.9(41b38c851305f17c9d2f63b3bbab71a9): dependencies: '@react-native/normalize-colors': 0.81.5 - debug: 4.4.3 - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + debug: 4.4.3(supports-color@8.1.1) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) optionalDependencies: react-native-web: 0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) transitivePeerDependencies: - supports-color - expo-updates-interface@2.0.0(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)): + expo-updates-interface@2.0.0(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)): dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) - expo-updates@29.0.17(patch_hash=04f28cb005b770e9ae8f0065eab96e43cbb1e58107f5f6ad1bdd18f6deb66487)(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + expo-updates@29.0.17(patch_hash=04f28cb005b770e9ae8f0065eab96e43cbb1e58107f5f6ad1bdd18f6deb66487)(93a6abd34ab5ae64aff6a80525489f34): dependencies: '@expo/code-signing-certificates': 0.0.6 '@expo/plist': 0.4.8 '@expo/spawn-async': 1.7.2 arg: 4.1.0 chalk: 4.1.2 - debug: 4.4.3 - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + debug: 4.4.3(supports-color@8.1.1) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) expo-eas-client: 1.0.8 - expo-manifests: 1.0.11(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)) + expo-manifests: 1.0.11(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1))(supports-color@8.1.1) expo-structured-headers: 5.0.0 - expo-updates-interface: 2.0.0(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)) + expo-updates-interface: 2.0.0(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)) getenv: 2.0.0 glob: 13.0.6 ignore: 5.3.2 react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) resolve-from: 5.0.0 transitivePeerDependencies: - supports-color - expo-video-thumbnails@10.0.8(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)): + expo-video-thumbnails@10.0.8(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1)): dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) - expo-video@3.0.16(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + expo-video@3.0.16(83e0f64f4281e4798012214ad5b2d72d): dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) - expo-web-browser@15.0.11(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0)): + expo-web-browser@15.0.11(83f21d12a6c1aa99b4a65d070875cc68): dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) - expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1): dependencies: '@babel/runtime': 7.29.2 - '@expo/cli': 54.0.25(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0)) - '@expo/config': 12.0.13 - '@expo/config-plugins': 54.0.4 - '@expo/devtools': 0.1.8(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - '@expo/fingerprint': 0.15.5 - '@expo/metro': 54.2.0 - '@expo/metro-config': 54.0.16(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)) - '@expo/vector-icons': 15.1.1(expo-font@14.0.12(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + '@expo/cli': 54.0.25(c1408f750868568d2dc75671b50084a7) + '@expo/config': 12.0.13(supports-color@8.1.1) + '@expo/config-plugins': 54.0.4(supports-color@8.1.1) + '@expo/devtools': 0.1.8(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) + '@expo/fingerprint': 0.15.5(supports-color@8.1.1) + '@expo/metro': 54.2.0(supports-color@8.1.1) + '@expo/metro-config': 54.0.16(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1))(supports-color@8.1.1) + '@expo/vector-icons': 15.1.1(expo-font@14.0.12(83e0f64f4281e4798012214ad5b2d72d))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) '@ungap/structured-clone': 1.3.1 - babel-preset-expo: 54.0.12(@babel/core@7.29.0)(@babel/runtime@7.29.2)(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-refresh@0.14.2) - expo-asset: 12.0.13(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - expo-constants: 18.0.13(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0)) - expo-file-system: 19.0.23(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0)) - expo-font: 14.0.12(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - expo-keep-awake: 15.0.8(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react@19.1.0) + babel-preset-expo: 54.0.12(@babel/core@7.29.0(supports-color@8.1.1))(@babel/runtime@7.29.2)(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1))(react-refresh@0.14.2)(supports-color@8.1.1) + expo-asset: 12.0.13(93a6abd34ab5ae64aff6a80525489f34) + expo-constants: 18.0.13(c1408f750868568d2dc75671b50084a7) + expo-file-system: 19.0.23(83f21d12a6c1aa99b4a65d070875cc68) + expo-font: 14.0.12(83e0f64f4281e4798012214ad5b2d72d) + expo-keep-awake: 15.0.8(expo@54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) expo-modules-autolinking: 3.0.26 - expo-modules-core: 3.0.30(patch_hash=952fb1c8cb6dc8a0d8ef4e1114942a341d89b025e011f1acfff788929997346e)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo-modules-core: 3.0.30(patch_hash=952fb1c8cb6dc8a0d8ef4e1114942a341d89b025e011f1acfff788929997346e)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) pretty-format: 29.7.0 react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) react-refresh: 0.14.2 whatwg-url-without-unicode: 8.0.0-3 optionalDependencies: - react-native-webview: 13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + react-native-webview: 13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) transitivePeerDependencies: - '@babel/core' - bufferutil @@ -14886,21 +15171,21 @@ snapshots: exponential-backoff@3.1.3: {} - express@4.22.2: + express@4.22.2(supports-color@8.1.1): dependencies: accepts: 1.3.8 array-flatten: 1.1.1 - body-parser: 1.20.5 + body-parser: 1.20.5(supports-color@8.1.1) content-disposition: 0.5.4 content-type: 1.0.5 cookie: 0.7.2 cookie-signature: 1.0.7 - debug: 2.6.9 + debug: 2.6.9(supports-color@8.1.1) depd: 2.0.0 encodeurl: 2.0.0 escape-html: 1.0.3 etag: 1.8.1 - finalhandler: 1.3.2 + finalhandler: 1.3.2(supports-color@8.1.1) fresh: 0.5.2 http-errors: 2.0.1 merge-descriptors: 1.0.3 @@ -14912,8 +15197,8 @@ snapshots: qs: 6.15.1 range-parser: 1.2.1 safe-buffer: 5.2.1 - send: 0.19.2 - serve-static: 1.16.3 + send: 0.19.2(supports-color@8.1.1) + serve-static: 1.16.3(supports-color@8.1.1) setprototypeof: 1.2.0 statuses: 2.0.2 type-is: 1.6.18 @@ -14943,6 +15228,8 @@ snapshots: json-schema-ref-resolver: 3.0.0 rfdc: 1.4.1 + fast-levenshtein@2.0.6: {} + fast-text-encoding@1.0.6: {} fast-uri@3.1.2: {} @@ -14977,11 +15264,15 @@ snapshots: optionalDependencies: picomatch: 4.0.4 - file-loader@6.2.0(webpack@5.106.2(postcss@8.5.14)): + file-entry-cache@6.0.1: + dependencies: + flat-cache: 3.2.0 + + file-loader@6.2.0(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)): dependencies: loader-utils: 2.0.4 schema-utils: 3.3.0 - webpack: 5.106.2(postcss@8.5.14) + webpack: 5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14) fill-range@7.1.1: dependencies: @@ -14989,9 +15280,9 @@ snapshots: filter-obj@1.1.0: {} - finalhandler@1.1.2: + finalhandler@1.1.2(supports-color@8.1.1): dependencies: - debug: 2.6.9 + debug: 2.6.9(supports-color@8.1.1) encodeurl: 1.0.2 escape-html: 1.0.3 on-finished: 2.3.0 @@ -15001,9 +15292,9 @@ snapshots: transitivePeerDependencies: - supports-color - finalhandler@1.3.2: + finalhandler@1.3.2(supports-color@8.1.1): dependencies: - debug: 2.6.9 + debug: 2.6.9(supports-color@8.1.1) encodeurl: 2.0.0 escape-html: 1.0.3 on-finished: 2.4.1 @@ -15041,9 +15332,19 @@ snapshots: dependencies: micromatch: 4.0.8 + flat-cache@3.2.0: + dependencies: + flatted: 3.4.4 + keyv: 4.5.4 + rimraf: 3.0.2 + + flatted@3.4.4: {} + flow-enums-runtime@0.0.6: {} - follow-redirects@1.16.0: {} + follow-redirects@1.16.0(debug@4.4.3(supports-color@8.1.1)): + optionalDependencies: + debug: 4.4.3(supports-color@8.1.1) fontfaceobserver@2.3.0: {} @@ -15190,6 +15491,10 @@ snapshots: minipass: 4.2.8 path-scurry: 1.11.1 + globals@13.24.0: + dependencies: + type-fest: 0.20.2 + globals@17.6.0: {} globalthis@1.0.4: @@ -15218,6 +15523,8 @@ snapshots: graceful-fs@4.2.11: {} + graphemer@1.4.0: {} + gzip-size@6.0.0: dependencies: duplexer: 0.1.2 @@ -15315,7 +15622,7 @@ snapshots: relateurl: 0.2.7 terser: 5.47.1 - html-webpack-plugin@5.6.7(webpack@5.106.2(postcss@8.5.14)): + html-webpack-plugin@5.6.7(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)): dependencies: '@types/html-minifier-terser': 6.1.0 html-minifier-terser: 6.1.0 @@ -15323,7 +15630,7 @@ snapshots: pretty-error: 4.0.0 tapable: 2.3.3 optionalDependencies: - webpack: 5.106.2(postcss@8.5.14) + webpack: 5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14) html2canvas@1.4.1: dependencies: @@ -15357,18 +15664,18 @@ snapshots: http-parser-js@0.5.10: {} - http-proxy-agent@5.0.0: + http-proxy-agent@5.0.0(supports-color@8.1.1): dependencies: '@tootallnate/once': 2.0.1 - agent-base: 6.0.2 - debug: 4.4.3 + agent-base: 6.0.2(supports-color@8.1.1) + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color - http-proxy-middleware@2.0.9(@types/express@4.17.25): + http-proxy-middleware@2.0.9(@types/express@4.17.25)(debug@4.4.3(supports-color@8.1.1)): dependencies: '@types/http-proxy': 1.17.17 - http-proxy: 1.18.1 + http-proxy: 1.18.1(debug@4.4.3(supports-color@8.1.1)) is-glob: 4.0.3 is-plain-obj: 3.0.0 micromatch: 4.0.8 @@ -15377,25 +15684,25 @@ snapshots: transitivePeerDependencies: - debug - http-proxy@1.18.1: + http-proxy@1.18.1(debug@4.4.3(supports-color@8.1.1)): dependencies: eventemitter3: 4.0.7 - follow-redirects: 1.16.0 + follow-redirects: 1.16.0(debug@4.4.3(supports-color@8.1.1)) requires-port: 1.0.0 transitivePeerDependencies: - debug - https-proxy-agent@5.0.1: + https-proxy-agent@5.0.1(supports-color@8.1.1): dependencies: - agent-base: 6.0.2 - debug: 4.4.3 + agent-base: 6.0.2(supports-color@8.1.1) + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color - https-proxy-agent@7.0.6: + https-proxy-agent@7.0.6(supports-color@8.1.1): dependencies: agent-base: 7.1.4 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -15592,6 +15899,8 @@ snapshots: dependencies: path-is-inside: 1.0.2 + is-path-inside@3.0.3: {} + is-plain-obj@2.1.0: {} is-plain-obj@3.0.0: {} @@ -15655,9 +15964,9 @@ snapshots: istanbul-lib-coverage@3.2.2: {} - istanbul-lib-instrument@5.2.1: + istanbul-lib-instrument@5.2.1(supports-color@8.1.1): dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/parser': 7.29.3 '@istanbuljs/schema': 0.1.6 istanbul-lib-coverage: 3.2.2 @@ -15665,9 +15974,9 @@ snapshots: transitivePeerDependencies: - supports-color - istanbul-lib-instrument@6.0.3: + istanbul-lib-instrument@6.0.3(supports-color@8.1.1): dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/parser': 7.29.3 '@istanbuljs/schema': 0.1.6 istanbul-lib-coverage: 3.2.2 @@ -15681,9 +15990,9 @@ snapshots: make-dir: 4.0.0 supports-color: 7.2.0 - istanbul-lib-source-maps@4.0.1: + istanbul-lib-source-maps@4.0.1(supports-color@8.1.1): dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) istanbul-lib-coverage: 3.2.2 source-map: 0.6.1 transitivePeerDependencies: @@ -15710,10 +16019,10 @@ snapshots: jest-util: 29.7.0 p-limit: 3.1.0 - jest-circus@29.7.0: + jest-circus@29.7.0(supports-color@8.1.1): dependencies: '@jest/environment': 29.7.0 - '@jest/expect': 29.7.0 + '@jest/expect': 29.7.0(supports-color@8.1.1) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 '@types/node': 24.12.4 @@ -15724,8 +16033,8 @@ snapshots: jest-each: 29.7.0 jest-matcher-utils: 29.7.0 jest-message-util: 29.7.0 - jest-runtime: 29.7.0 - jest-snapshot: 29.7.0 + jest-runtime: 29.7.0(supports-color@8.1.1) + jest-snapshot: 29.7.0(supports-color@8.1.1) jest-util: 29.7.0 p-limit: 3.1.0 pretty-format: 29.7.0 @@ -15736,16 +16045,16 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@29.7.0(@types/node@24.12.4): + jest-cli@29.7.0(@types/node@24.12.4)(supports-color@8.1.1): dependencies: - '@jest/core': 29.7.0 + '@jest/core': 29.7.0(supports-color@8.1.1) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@24.12.4) + create-jest: 29.7.0(@types/node@24.12.4)(supports-color@8.1.1) exit: 0.1.2 import-local: 3.2.0 - jest-config: 29.7.0(@types/node@24.12.4) + jest-config: 29.7.0(@types/node@24.12.4)(supports-color@8.1.1) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -15755,23 +16064,23 @@ snapshots: - supports-color - ts-node - jest-config@29.7.0(@types/node@24.12.4): + jest-config@29.7.0(@types/node@24.12.4)(supports-color@8.1.1): dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@jest/test-sequencer': 29.7.0 '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.29.0) + babel-jest: 29.7.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) chalk: 4.1.2 ci-info: 3.9.0 deepmerge: 4.3.1 glob: 7.2.3 graceful-fs: 4.2.11 - jest-circus: 29.7.0 + jest-circus: 29.7.0(supports-color@8.1.1) jest-environment-node: 29.7.0 jest-get-type: 29.6.3 jest-regex-util: 29.6.3 jest-resolve: 29.7.0 - jest-runner: 29.7.0 + jest-runner: 29.7.0(supports-color@8.1.1) jest-util: 29.7.0 jest-validate: 29.7.0 micromatch: 4.0.8 @@ -15811,7 +16120,7 @@ snapshots: jest-util: 29.7.0 pretty-format: 29.7.0 - jest-environment-jsdom@29.7.0: + jest-environment-jsdom@29.7.0(supports-color@8.1.1): dependencies: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 @@ -15820,7 +16129,7 @@ snapshots: '@types/node': 24.12.4 jest-mock: 29.7.0 jest-util: 29.7.0 - jsdom: 20.0.3 + jsdom: 20.0.3(supports-color@8.1.1) transitivePeerDependencies: - bufferutil - supports-color @@ -15835,21 +16144,21 @@ snapshots: jest-mock: 29.7.0 jest-util: 29.7.0 - jest-expo@54.0.17(@babel/core@7.29.0)(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(jest@29.7.0(@types/node@24.12.4))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + jest-expo@54.0.17(ff9d3150c2730c411fa3a2f331cc40ee): dependencies: - '@expo/config': 12.0.13 + '@expo/config': 12.0.13(supports-color@8.1.1) '@expo/json-file': 10.0.14 '@jest/create-cache-key-function': 29.7.0 - '@jest/globals': 29.7.0 - babel-jest: 29.7.0(@babel/core@7.29.0) - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - jest-environment-jsdom: 29.7.0 - jest-snapshot: 29.7.0 + '@jest/globals': 29.7.0(supports-color@8.1.1) + babel-jest: 29.7.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) + jest-environment-jsdom: 29.7.0(supports-color@8.1.1) + jest-snapshot: 29.7.0(supports-color@8.1.1) jest-watch-select-projects: 2.0.0 - jest-watch-typeahead: 2.2.1(jest@29.7.0(@types/node@24.12.4)) + jest-watch-typeahead: 2.2.1(jest@29.7.0(@types/node@24.12.4)(supports-color@8.1.1)) json5: 2.2.3 lodash: 4.18.1 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) react-test-renderer: 19.1.0(react@19.1.0) server-only: 0.0.1 stacktrace-js: 2.0.2 @@ -15951,10 +16260,10 @@ snapshots: jest-regex-util@30.4.0: {} - jest-resolve-dependencies@29.7.0: + jest-resolve-dependencies@29.7.0(supports-color@8.1.1): dependencies: jest-regex-util: 29.6.3 - jest-snapshot: 29.7.0 + jest-snapshot: 29.7.0(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -15970,12 +16279,12 @@ snapshots: resolve.exports: 2.0.3 slash: 3.0.0 - jest-runner@29.7.0: + jest-runner@29.7.0(supports-color@8.1.1): dependencies: '@jest/console': 29.7.0 '@jest/environment': 29.7.0 '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 + '@jest/transform': 29.7.0(supports-color@8.1.1) '@jest/types': 29.6.3 '@types/node': 24.12.4 chalk: 4.1.2 @@ -15987,7 +16296,7 @@ snapshots: jest-leak-detector: 29.7.0 jest-message-util: 29.7.0 jest-resolve: 29.7.0 - jest-runtime: 29.7.0 + jest-runtime: 29.7.0(supports-color@8.1.1) jest-util: 29.7.0 jest-watcher: 29.7.0 jest-worker: 29.7.0 @@ -15996,14 +16305,14 @@ snapshots: transitivePeerDependencies: - supports-color - jest-runtime@29.7.0: + jest-runtime@29.7.0(supports-color@8.1.1): dependencies: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 - '@jest/globals': 29.7.0 + '@jest/globals': 29.7.0(supports-color@8.1.1) '@jest/source-map': 29.6.3 '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 + '@jest/transform': 29.7.0(supports-color@8.1.1) '@jest/types': 29.6.3 '@types/node': 24.12.4 chalk: 4.1.2 @@ -16016,24 +16325,24 @@ snapshots: jest-mock: 29.7.0 jest-regex-util: 29.6.3 jest-resolve: 29.7.0 - jest-snapshot: 29.7.0 + jest-snapshot: 29.7.0(supports-color@8.1.1) jest-util: 29.7.0 slash: 3.0.0 strip-bom: 4.0.0 transitivePeerDependencies: - supports-color - jest-snapshot@29.7.0: + jest-snapshot@29.7.0(supports-color@8.1.1): dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/generator': 7.29.1 - '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1)) '@babel/types': 7.29.0 '@jest/expect-utils': 29.7.0 - '@jest/transform': 29.7.0 + '@jest/transform': 29.7.0(supports-color@8.1.1) '@jest/types': 29.6.3 - babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0(supports-color@8.1.1)) chalk: 4.1.2 expect: 29.7.0 graceful-fs: 4.2.11 @@ -16081,11 +16390,11 @@ snapshots: chalk: 3.0.0 prompts: 2.4.2 - jest-watch-typeahead@2.2.1(jest@29.7.0(@types/node@24.12.4)): + jest-watch-typeahead@2.2.1(jest@29.7.0(@types/node@24.12.4)(supports-color@8.1.1)): dependencies: ansi-escapes: 6.2.1 chalk: 4.1.2 - jest: 29.7.0(@types/node@24.12.4) + jest: 29.7.0(@types/node@24.12.4)(supports-color@8.1.1) jest-regex-util: 29.6.3 jest-watcher: 29.7.0 slash: 5.1.0 @@ -16116,12 +16425,12 @@ snapshots: merge-stream: 2.0.0 supports-color: 8.1.1 - jest@29.7.0(@types/node@24.12.4): + jest@29.7.0(@types/node@24.12.4)(supports-color@8.1.1): dependencies: - '@jest/core': 29.7.0 + '@jest/core': 29.7.0(supports-color@8.1.1) '@jest/types': 29.6.3 import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@24.12.4) + jest-cli: 29.7.0(@types/node@24.12.4)(supports-color@8.1.1) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -16149,7 +16458,7 @@ snapshots: jsc-safe-url@0.2.4: {} - jsdom@20.0.3: + jsdom@20.0.3(supports-color@8.1.1): dependencies: abab: 2.0.6 acorn: 8.16.0 @@ -16162,8 +16471,8 @@ snapshots: escodegen: 2.1.0 form-data: 4.0.5 html-encoding-sniffer: 3.0.0 - http-proxy-agent: 5.0.0 - https-proxy-agent: 5.0.1 + http-proxy-agent: 5.0.0(supports-color@8.1.1) + https-proxy-agent: 5.0.1(supports-color@8.1.1) is-potential-custom-element-name: 1.0.1 nwsapi: 2.2.23 parse5: 7.3.0 @@ -16184,6 +16493,8 @@ snapshots: jsesc@3.1.0: {} + json-buffer@3.0.1: {} + json-parse-even-better-errors@2.3.1: {} json-schema-ref-resolver@3.0.0: @@ -16194,6 +16505,8 @@ snapshots: json-schema-traverse@1.0.0: {} + json-stable-stringify-without-jsonify@1.0.1: {} + json5@2.2.3: {} jsonfile@6.2.1: @@ -16211,6 +16524,10 @@ snapshots: jwt-decode@4.0.0: {} + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + kleur@3.0.3: {} lan-network@0.2.1: {} @@ -16226,11 +16543,16 @@ snapshots: leven@3.1.0: {} + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + libphonenumber-js@1.13.1: {} - lighthouse-logger@1.4.2: + lighthouse-logger@1.4.2(supports-color@8.1.1): dependencies: - debug: 2.6.9 + debug: 2.6.9(supports-color@8.1.1) marky: 1.3.0 transitivePeerDependencies: - supports-color @@ -16336,6 +16658,8 @@ snapshots: lodash.memoize@4.1.2: {} + lodash.merge@4.6.2: {} + lodash.shuffle@4.2.0: {} lodash.throttle@4.1.1: {} @@ -16449,18 +16773,18 @@ snapshots: methods@1.1.2: {} - metro-babel-transformer@0.83.3: + metro-babel-transformer@0.83.3(supports-color@8.1.1): dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) flow-enums-runtime: 0.0.6 hermes-parser: 0.32.0 nullthrows: 1.1.1 transitivePeerDependencies: - supports-color - metro-babel-transformer@0.84.4: + metro-babel-transformer@0.84.4(supports-color@8.1.1): dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) flow-enums-runtime: 0.0.6 hermes-parser: 0.35.0 metro-cache-key: 0.84.4 @@ -16476,31 +16800,31 @@ snapshots: dependencies: flow-enums-runtime: 0.0.6 - metro-cache@0.83.3: + metro-cache@0.83.3(supports-color@8.1.1): dependencies: exponential-backoff: 3.1.3 flow-enums-runtime: 0.0.6 - https-proxy-agent: 7.0.6 + https-proxy-agent: 7.0.6(supports-color@8.1.1) metro-core: 0.83.3 transitivePeerDependencies: - supports-color - metro-cache@0.84.4: + metro-cache@0.84.4(supports-color@8.1.1): dependencies: exponential-backoff: 3.1.3 flow-enums-runtime: 0.0.6 - https-proxy-agent: 7.0.6 + https-proxy-agent: 7.0.6(supports-color@8.1.1) metro-core: 0.84.4 transitivePeerDependencies: - supports-color - metro-config@0.83.3: + metro-config@0.83.3(supports-color@8.1.1): dependencies: - connect: 3.7.0 + connect: 3.7.0(supports-color@8.1.1) flow-enums-runtime: 0.0.6 jest-validate: 29.7.0 - metro: 0.83.3 - metro-cache: 0.83.3 + metro: 0.83.3(supports-color@8.1.1) + metro-cache: 0.83.3(supports-color@8.1.1) metro-core: 0.83.3 metro-runtime: 0.83.3 yaml: 2.9.0 @@ -16509,13 +16833,13 @@ snapshots: - supports-color - utf-8-validate - metro-config@0.84.4: + metro-config@0.84.4(supports-color@8.1.1): dependencies: - connect: 3.7.0 + connect: 3.7.0(supports-color@8.1.1) flow-enums-runtime: 0.0.6 jest-validate: 29.7.0 - metro: 0.84.4 - metro-cache: 0.84.4 + metro: 0.84.4(supports-color@8.1.1) + metro-cache: 0.84.4(supports-color@8.1.1) metro-core: 0.84.4 metro-runtime: 0.84.4 yaml: 2.9.0 @@ -16536,9 +16860,9 @@ snapshots: lodash.throttle: 4.1.1 metro-resolver: 0.84.4 - metro-file-map@0.83.3: + metro-file-map@0.83.3(supports-color@8.1.1): dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) fb-watchman: 2.0.2 flow-enums-runtime: 0.0.6 graceful-fs: 4.2.11 @@ -16550,9 +16874,9 @@ snapshots: transitivePeerDependencies: - supports-color - metro-file-map@0.84.4: + metro-file-map@0.84.4(supports-color@8.1.1): dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) fb-watchman: 2.0.2 flow-enums-runtime: 0.0.6 graceful-fs: 4.2.11 @@ -16592,14 +16916,14 @@ snapshots: '@babel/runtime': 7.29.2 flow-enums-runtime: 0.0.6 - metro-source-map@0.83.3: + metro-source-map@0.83.3(supports-color@8.1.1): dependencies: - '@babel/traverse': 7.29.0 - '@babel/traverse--for-generate-function-map': '@babel/traverse@7.29.0' + '@babel/traverse': 7.29.0(supports-color@8.1.1) + '@babel/traverse--for-generate-function-map': '@babel/traverse@7.29.0(supports-color@8.1.1)' '@babel/types': 7.29.0 flow-enums-runtime: 0.0.6 invariant: 2.2.4 - metro-symbolicate: 0.83.3 + metro-symbolicate: 0.83.3(supports-color@8.1.1) nullthrows: 1.1.1 ob1: 0.83.3 source-map: 0.5.7 @@ -16607,13 +16931,13 @@ snapshots: transitivePeerDependencies: - supports-color - metro-source-map@0.84.4: + metro-source-map@0.84.4(supports-color@8.1.1): dependencies: - '@babel/traverse': 7.29.0 + '@babel/traverse': 7.29.0(supports-color@8.1.1) '@babel/types': 7.29.0 flow-enums-runtime: 0.0.6 invariant: 2.2.4 - metro-symbolicate: 0.84.4 + metro-symbolicate: 0.84.4(supports-color@8.1.1) nullthrows: 1.1.1 ob1: 0.84.4 source-map: 0.5.7 @@ -16621,104 +16945,104 @@ snapshots: transitivePeerDependencies: - supports-color - metro-symbolicate@0.83.3: + metro-symbolicate@0.83.3(supports-color@8.1.1): dependencies: flow-enums-runtime: 0.0.6 invariant: 2.2.4 - metro-source-map: 0.83.3 + metro-source-map: 0.83.3(supports-color@8.1.1) nullthrows: 1.1.1 source-map: 0.5.7 vlq: 1.0.1 transitivePeerDependencies: - supports-color - metro-symbolicate@0.84.4: + metro-symbolicate@0.84.4(supports-color@8.1.1): dependencies: flow-enums-runtime: 0.0.6 invariant: 2.2.4 - metro-source-map: 0.84.4 + metro-source-map: 0.84.4(supports-color@8.1.1) nullthrows: 1.1.1 source-map: 0.5.7 vlq: 1.0.1 transitivePeerDependencies: - supports-color - metro-transform-plugins@0.83.3: + metro-transform-plugins@0.83.3(supports-color@8.1.1): dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/generator': 7.29.1 '@babel/template': 7.28.6 - '@babel/traverse': 7.29.0 + '@babel/traverse': 7.29.0(supports-color@8.1.1) flow-enums-runtime: 0.0.6 nullthrows: 1.1.1 transitivePeerDependencies: - supports-color - metro-transform-plugins@0.84.4: + metro-transform-plugins@0.84.4(supports-color@8.1.1): dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/generator': 7.29.1 '@babel/template': 7.28.6 - '@babel/traverse': 7.29.0 + '@babel/traverse': 7.29.0(supports-color@8.1.1) flow-enums-runtime: 0.0.6 nullthrows: 1.1.1 transitivePeerDependencies: - supports-color - metro-transform-worker@0.83.3: + metro-transform-worker@0.83.3(supports-color@8.1.1): dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/generator': 7.29.1 '@babel/parser': 7.29.3 '@babel/types': 7.29.0 flow-enums-runtime: 0.0.6 - metro: 0.83.3 - metro-babel-transformer: 0.83.3 - metro-cache: 0.83.3 + metro: 0.83.3(supports-color@8.1.1) + metro-babel-transformer: 0.83.3(supports-color@8.1.1) + metro-cache: 0.83.3(supports-color@8.1.1) metro-cache-key: 0.83.3 metro-minify-terser: 0.83.3 - metro-source-map: 0.83.3 - metro-transform-plugins: 0.83.3 + metro-source-map: 0.83.3(supports-color@8.1.1) + metro-transform-plugins: 0.83.3(supports-color@8.1.1) nullthrows: 1.1.1 transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - metro-transform-worker@0.84.4: + metro-transform-worker@0.84.4(supports-color@8.1.1): dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/generator': 7.29.1 '@babel/parser': 7.29.3 '@babel/types': 7.29.0 flow-enums-runtime: 0.0.6 - metro: 0.84.4 - metro-babel-transformer: 0.84.4 - metro-cache: 0.84.4 + metro: 0.84.4(supports-color@8.1.1) + metro-babel-transformer: 0.84.4(supports-color@8.1.1) + metro-cache: 0.84.4(supports-color@8.1.1) metro-cache-key: 0.84.4 metro-minify-terser: 0.84.4 - metro-source-map: 0.84.4 - metro-transform-plugins: 0.84.4 + metro-source-map: 0.84.4(supports-color@8.1.1) + metro-transform-plugins: 0.84.4(supports-color@8.1.1) nullthrows: 1.1.1 transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - metro@0.83.3: + metro@0.83.3(supports-color@8.1.1): dependencies: '@babel/code-frame': 7.29.0 - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/generator': 7.29.1 '@babel/parser': 7.29.3 '@babel/template': 7.28.6 - '@babel/traverse': 7.29.0 + '@babel/traverse': 7.29.0(supports-color@8.1.1) '@babel/types': 7.29.0 accepts: 1.3.8 chalk: 4.1.2 ci-info: 2.0.0 - connect: 3.7.0 - debug: 4.4.3 + connect: 3.7.0(supports-color@8.1.1) + debug: 4.4.3(supports-color@8.1.1) error-stack-parser: 2.1.4 flow-enums-runtime: 0.0.6 graceful-fs: 4.2.11 @@ -16728,18 +17052,18 @@ snapshots: jest-worker: 29.7.0 jsc-safe-url: 0.2.4 lodash.throttle: 4.1.1 - metro-babel-transformer: 0.83.3 - metro-cache: 0.83.3 + metro-babel-transformer: 0.83.3(supports-color@8.1.1) + metro-cache: 0.83.3(supports-color@8.1.1) metro-cache-key: 0.83.3 - metro-config: 0.83.3 + metro-config: 0.83.3(supports-color@8.1.1) metro-core: 0.83.3 - metro-file-map: 0.83.3 + metro-file-map: 0.83.3(supports-color@8.1.1) metro-resolver: 0.83.3 metro-runtime: 0.83.3 - metro-source-map: 0.83.3 - metro-symbolicate: 0.83.3 - metro-transform-plugins: 0.83.3 - metro-transform-worker: 0.83.3 + metro-source-map: 0.83.3(supports-color@8.1.1) + metro-symbolicate: 0.83.3(supports-color@8.1.1) + metro-transform-plugins: 0.83.3(supports-color@8.1.1) + metro-transform-worker: 0.83.3(supports-color@8.1.1) mime-types: 2.1.35 nullthrows: 1.1.1 serialize-error: 2.1.0 @@ -16752,19 +17076,19 @@ snapshots: - supports-color - utf-8-validate - metro@0.84.4: + metro@0.84.4(supports-color@8.1.1): dependencies: '@babel/code-frame': 7.29.0 - '@babel/core': 7.29.0 + '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/generator': 7.29.1 '@babel/parser': 7.29.3 '@babel/template': 7.28.6 - '@babel/traverse': 7.29.0 + '@babel/traverse': 7.29.0(supports-color@8.1.1) '@babel/types': 7.29.0 accepts: 2.0.0 ci-info: 2.0.0 - connect: 3.7.0 - debug: 4.4.3 + connect: 3.7.0(supports-color@8.1.1) + debug: 4.4.3(supports-color@8.1.1) error-stack-parser: 2.1.4 flow-enums-runtime: 0.0.6 graceful-fs: 4.2.11 @@ -16774,18 +17098,18 @@ snapshots: jest-worker: 29.7.0 jsc-safe-url: 0.2.4 lodash.throttle: 4.1.1 - metro-babel-transformer: 0.84.4 - metro-cache: 0.84.4 + metro-babel-transformer: 0.84.4(supports-color@8.1.1) + metro-cache: 0.84.4(supports-color@8.1.1) metro-cache-key: 0.84.4 - metro-config: 0.84.4 + metro-config: 0.84.4(supports-color@8.1.1) metro-core: 0.84.4 - metro-file-map: 0.84.4 + metro-file-map: 0.84.4(supports-color@8.1.1) metro-resolver: 0.84.4 metro-runtime: 0.84.4 - metro-source-map: 0.84.4 - metro-symbolicate: 0.84.4 - metro-transform-plugins: 0.84.4 - metro-transform-worker: 0.84.4 + metro-source-map: 0.84.4(supports-color@8.1.1) + metro-symbolicate: 0.84.4(supports-color@8.1.1) + metro-transform-plugins: 0.84.4(supports-color@8.1.1) + metro-transform-worker: 0.84.4(supports-color@8.1.1) mime-types: 3.0.2 nullthrows: 1.1.1 serialize-error: 2.1.0 @@ -16825,11 +17149,11 @@ snapshots: min-indent@1.0.1: {} - mini-css-extract-plugin@2.10.2(webpack@5.106.2(postcss@8.5.14)): + mini-css-extract-plugin@2.10.2(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)): dependencies: schema-utils: 4.3.3 tapable: 2.3.3 - webpack: 5.106.2(postcss@8.5.14) + webpack: 5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14) minimalistic-assert@1.0.1: {} @@ -17023,6 +17347,15 @@ snapshots: opener@1.5.2: {} + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + ora@3.4.0: dependencies: chalk: 2.4.2 @@ -17407,6 +17740,8 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + prelude-ls@1.2.1: {} + prettier@3.8.3: {} pretty-bytes@5.6.0: {} @@ -17729,123 +18064,123 @@ snapshots: react: 19.1.0 react-is: 19.2.6 - react-native-compressor@1.13.0(patch_hash=58379dfaace6ced8590cb341c77f2ca8099dfa8f7df6297032ec51de767a9925)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + react-native-compressor@1.13.0(patch_hash=58379dfaace6ced8590cb341c77f2ca8099dfa8f7df6297032ec51de767a9925)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0): dependencies: react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) - react-native-date-picker@5.0.13(patch_hash=92943fb79d17d7342a29bbb12b0d8ee3cf6f7bca12ed322dc8d124a3e9fb75bd)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + react-native-date-picker@5.0.13(patch_hash=92943fb79d17d7342a29bbb12b0d8ee3cf6f7bca12ed322dc8d124a3e9fb75bd)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0): dependencies: react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) - react-native-device-attest@0.1.6(expo@54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + react-native-device-attest@0.1.6(83e0f64f4281e4798012214ad5b2d72d): dependencies: - expo: 54.0.35(@babel/core@7.29.0)(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + expo: 54.0.35(@babel/core@7.29.0(supports-color@8.1.1))(react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) react-native-dotenv@3.4.11(@babel/runtime@7.29.2): dependencies: '@babel/runtime': 7.29.2 dotenv: 16.6.1 - react-native-drawer-layout@4.2.3(patch_hash=74f2c043cc22ab87054f219e7c7373a509b779b18bfa79d27e7d051d73355130)(f8669ab0fde0772e831a7516fba74dd4): + react-native-drawer-layout@4.2.3(patch_hash=74f2c043cc22ab87054f219e7c7373a509b779b18bfa79d27e7d051d73355130)(80d195d8b17891585aee78a1fb66460a): dependencies: color: 4.2.3 react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) - react-native-gesture-handler: 2.30.1(patch_hash=39b9e51b3977fae8ff61764b8c32934fe3fbddaf21587b4661fd73b7c6dd325b)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - react-native-reanimated: 4.3.2(patch_hash=ab1c095bc473ec5bfd9ead76e870c566660e355e11c5078cfdeb35922d7b2f05)(react-native-worklets@0.8.3(patch_hash=f7263a59ae1c0170af8dfe9a5704cb374d29214a430b4b2841f9b7886c9ba8ad)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) + react-native-gesture-handler: 2.30.1(patch_hash=39b9e51b3977fae8ff61764b8c32934fe3fbddaf21587b4661fd73b7c6dd325b)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) + react-native-reanimated: 4.3.2(patch_hash=ab1c095bc473ec5bfd9ead76e870c566660e355e11c5078cfdeb35922d7b2f05)(react-native-worklets@0.8.3(patch_hash=f7263a59ae1c0170af8dfe9a5704cb374d29214a430b4b2841f9b7886c9ba8ad)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) use-latest-callback: 0.2.6(react@19.1.0) - react-native-edge-to-edge@1.8.1(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + react-native-edge-to-edge@1.8.1(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0): dependencies: react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) - react-native-gesture-handler@2.30.1(patch_hash=39b9e51b3977fae8ff61764b8c32934fe3fbddaf21587b4661fd73b7c6dd325b)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + react-native-gesture-handler@2.30.1(patch_hash=39b9e51b3977fae8ff61764b8c32934fe3fbddaf21587b4661fd73b7c6dd325b)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0): dependencies: '@egjs/hammerjs': 2.0.17 hoist-non-react-statics: 3.3.2 invariant: 2.2.4 react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) - react-native-is-edge-to-edge@1.3.1(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + react-native-is-edge-to-edge@1.3.1(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0): dependencies: react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) - ? react-native-keyboard-controller@1.21.8(patch_hash=ac52bf7502ff3ad34a8594b5e1a916b96bf87a2523b6abc87c31f03607d52271)(react-native-reanimated@4.3.2(patch_hash=ab1c095bc473ec5bfd9ead76e870c566660e355e11c5078cfdeb35922d7b2f05)(react-native-worklets@0.8.3(patch_hash=f7263a59ae1c0170af8dfe9a5704cb374d29214a430b4b2841f9b7886c9ba8ad)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - : dependencies: - react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) - react-native-is-edge-to-edge: 1.3.1(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - react-native-reanimated: 4.3.2(patch_hash=ab1c095bc473ec5bfd9ead76e870c566660e355e11c5078cfdeb35922d7b2f05)(react-native-worklets@0.8.3(patch_hash=f7263a59ae1c0170af8dfe9a5704cb374d29214a430b4b2841f9b7886c9ba8ad)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - - react-native-mmkv@3.3.3(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + react-native-keyboard-controller@1.21.8(patch_hash=ac52bf7502ff3ad34a8594b5e1a916b96bf87a2523b6abc87c31f03607d52271)(a7f993fe7a53ab427398397086882821): dependencies: react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) + react-native-is-edge-to-edge: 1.3.1(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) + react-native-reanimated: 4.3.2(patch_hash=ab1c095bc473ec5bfd9ead76e870c566660e355e11c5078cfdeb35922d7b2f05)(react-native-worklets@0.8.3(patch_hash=f7263a59ae1c0170af8dfe9a5704cb374d29214a430b4b2841f9b7886c9ba8ad)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) - react-native-pager-view@6.8.0(patch_hash=c8316acd33c9aef6531e8c272c2bfab7bd02af1255969eeaab2bad5ab48531cc)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + react-native-mmkv@3.3.3(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0): dependencies: react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) - react-native-progress@5.0.1(react-native-svg@15.12.1(patch_hash=ae309e9542214f5ae54935e253dedd236765feedac7be343f81d532d7fe09310)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)): + react-native-pager-view@6.8.0(patch_hash=c8316acd33c9aef6531e8c272c2bfab7bd02af1255969eeaab2bad5ab48531cc)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0): + dependencies: + react: 19.1.0 + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) + + react-native-progress@5.0.1(react-native-svg@15.12.1(patch_hash=ae309e9542214f5ae54935e253dedd236765feedac7be343f81d532d7fe09310)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)): dependencies: prop-types: 15.8.1 - react-native-svg: 15.12.1(patch_hash=ae309e9542214f5ae54935e253dedd236765feedac7be343f81d532d7fe09310)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + react-native-svg: 15.12.1(patch_hash=ae309e9542214f5ae54935e253dedd236765feedac7be343f81d532d7fe09310)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) - react-native-qrcode-styled@0.3.3(react-native-svg@15.12.1(patch_hash=ae309e9542214f5ae54935e253dedd236765feedac7be343f81d532d7fe09310)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + react-native-qrcode-styled@0.3.3(react-native-svg@15.12.1(patch_hash=ae309e9542214f5ae54935e253dedd236765feedac7be343f81d532d7fe09310)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0): dependencies: qrcode: 1.5.4 react: 19.1.0 react-fast-compare: 3.2.2 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) - react-native-svg: 15.12.1(patch_hash=ae309e9542214f5ae54935e253dedd236765feedac7be343f81d532d7fe09310)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) + react-native-svg: 15.12.1(patch_hash=ae309e9542214f5ae54935e253dedd236765feedac7be343f81d532d7fe09310)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) - react-native-reanimated@4.3.2(patch_hash=ab1c095bc473ec5bfd9ead76e870c566660e355e11c5078cfdeb35922d7b2f05)(react-native-worklets@0.8.3(patch_hash=f7263a59ae1c0170af8dfe9a5704cb374d29214a430b4b2841f9b7886c9ba8ad)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + react-native-reanimated@4.3.2(patch_hash=ab1c095bc473ec5bfd9ead76e870c566660e355e11c5078cfdeb35922d7b2f05)(react-native-worklets@0.8.3(patch_hash=f7263a59ae1c0170af8dfe9a5704cb374d29214a430b4b2841f9b7886c9ba8ad)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0): dependencies: react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) - react-native-is-edge-to-edge: 1.3.1(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - react-native-worklets: 0.8.3(patch_hash=f7263a59ae1c0170af8dfe9a5704cb374d29214a430b4b2841f9b7886c9ba8ad)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) + react-native-is-edge-to-edge: 1.3.1(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) + react-native-worklets: 0.8.3(patch_hash=f7263a59ae1c0170af8dfe9a5704cb374d29214a430b4b2841f9b7886c9ba8ad)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) semver: 7.8.0 - react-native-safe-area-context@5.6.2(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + react-native-safe-area-context@5.6.2(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0): dependencies: react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) - react-native-screens@4.24.0(patch_hash=74a74b489f8074a1974c2792c9c97aec30d8e251669aa37fc5c99993bb4a19e6)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + react-native-screens@4.24.0(patch_hash=74a74b489f8074a1974c2792c9c97aec30d8e251669aa37fc5c99993bb4a19e6)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0): dependencies: react: 19.1.0 react-freeze: 1.0.4(react@19.1.0) - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) warn-once: 0.1.1 - react-native-svg@15.12.1(patch_hash=ae309e9542214f5ae54935e253dedd236765feedac7be343f81d532d7fe09310)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + react-native-svg@15.12.1(patch_hash=ae309e9542214f5ae54935e253dedd236765feedac7be343f81d532d7fe09310)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0): dependencies: css-select: 5.2.2 css-tree: 1.1.3 react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) warn-once: 0.1.1 react-native-uuid@2.0.4: {} - react-native-view-shot@4.0.3(patch_hash=a2457dc30a82cc8c21f371a6f860d9396d450a2a1b4265b1a4ee13bdb24d3268)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + react-native-view-shot@4.0.3(patch_hash=a2457dc30a82cc8c21f371a6f860d9396d450a2a1b4265b1a4ee13bdb24d3268)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0): dependencies: html2canvas: 1.4.1 react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) - react-native-web-webview@1.0.2(file-loader@6.2.0(webpack@5.106.2(postcss@8.5.14)))(react-native-web@0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0)): + react-native-web-webview@1.0.2(file-loader@6.2.0(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)))(react-native-web@0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0)): dependencies: - file-loader: 6.2.0(webpack@5.106.2(postcss@8.5.14)) + file-loader: 6.2.0(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)) qs: 6.15.1 react-native-web: 0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -17864,47 +18199,47 @@ snapshots: transitivePeerDependencies: - encoding - react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + react-native-webview@13.15.0(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0): dependencies: escape-string-regexp: 4.0.0 invariant: 2.2.4 react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) - react-native-worklets@0.8.3(patch_hash=f7263a59ae1c0170af8dfe9a5704cb374d29214a430b4b2841f9b7886c9ba8ad)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0): + react-native-worklets@0.8.3(patch_hash=f7263a59ae1c0170af8dfe9a5704cb374d29214a430b4b2841f9b7886c9ba8ad)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1): dependencies: - '@babel/core': 7.29.0 - '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-classes': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-nullish-coalescing-operator': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0) - '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.29.0) - '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.29.0) - '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0) - '@react-native/metro-config': 0.86.0(@babel/core@7.29.0) + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-classes': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-nullish-coalescing-operator': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) + '@react-native/metro-config': 0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) convert-source-map: 2.0.0 react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) semver: 7.8.0 transitivePeerDependencies: - supports-color - react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0): + react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1): dependencies: '@jest/create-cache-key-function': 29.7.0 '@react-native/assets-registry': 0.81.5 - '@react-native/codegen': 0.81.5(@babel/core@7.29.0) - '@react-native/community-cli-plugin': 0.81.5(@react-native/metro-config@0.86.0(@babel/core@7.29.0)) + '@react-native/codegen': 0.81.5(@babel/core@7.29.0(supports-color@8.1.1)) + '@react-native/community-cli-plugin': 0.81.5(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(supports-color@8.1.1) '@react-native/gradle-plugin': 0.81.5 '@react-native/js-polyfills': 0.81.5 '@react-native/normalize-colors': 0.81.5 - '@react-native/virtualized-lists': 0.81.5(@types/react@19.1.17)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + '@react-native/virtualized-lists': 0.81.5(@types/react@19.1.17)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) abort-controller: 3.0.0 anser: 1.4.10 ansi-regex: 5.0.1 - babel-jest: 29.7.0(@babel/core@7.29.0) + babel-jest: 29.7.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) babel-plugin-syntax-hermes-parser: 0.29.1 base64-js: 1.5.1 commander: 12.1.0 @@ -17914,7 +18249,7 @@ snapshots: jest-environment-node: 29.7.0 memoize-one: 5.2.1 metro-runtime: 0.83.3 - metro-source-map: 0.83.3 + metro-source-map: 0.83.3(supports-color@8.1.1) nullthrows: 1.1.1 pretty-format: 29.7.0 promise: 8.3.0 @@ -18226,9 +18561,9 @@ snapshots: semver@7.8.0: {} - send@0.19.2: + send@0.19.2(supports-color@8.1.1): dependencies: - debug: 2.6.9 + debug: 2.6.9(supports-color@8.1.1) depd: 2.0.0 destroy: 1.2.0 encodeurl: 2.0.0 @@ -18250,11 +18585,11 @@ snapshots: dependencies: randombytes: 2.1.0 - serve-index@1.9.2: + serve-index@1.9.2(supports-color@8.1.1): dependencies: accepts: 1.3.8 batch: 0.6.1 - debug: 2.6.9 + debug: 2.6.9(supports-color@8.1.1) escape-html: 1.0.3 http-errors: 1.8.1 mime-types: 2.1.35 @@ -18262,12 +18597,12 @@ snapshots: transitivePeerDependencies: - supports-color - serve-static@1.16.3: + serve-static@1.16.3(supports-color@8.1.1): dependencies: encodeurl: 2.0.0 escape-html: 1.0.3 parseurl: 1.3.3 - send: 0.19.2 + send: 0.19.2(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -18392,16 +18727,16 @@ snapshots: uuid: 8.3.2 websocket-driver: 0.7.4 - sonner-native@0.26.4(c538f2bb3fa64bd80a9a262f75d56c17): + sonner-native@0.26.4(dd52c15d65a2604bcf203dfed917879b): dependencies: react: 19.1.0 - react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0) - react-native-gesture-handler: 2.30.1(patch_hash=39b9e51b3977fae8ff61764b8c32934fe3fbddaf21587b4661fd73b7c6dd325b)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - react-native-reanimated: 4.3.2(patch_hash=ab1c095bc473ec5bfd9ead76e870c566660e355e11c5078cfdeb35922d7b2f05)(react-native-worklets@0.8.3(patch_hash=f7263a59ae1c0170af8dfe9a5704cb374d29214a430b4b2841f9b7886c9ba8ad)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - react-native-safe-area-context: 5.6.2(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - react-native-screens: 4.24.0(patch_hash=74a74b489f8074a1974c2792c9c97aec30d8e251669aa37fc5c99993bb4a19e6)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - react-native-svg: 15.12.1(patch_hash=ae309e9542214f5ae54935e253dedd236765feedac7be343f81d532d7fe09310)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) - react-native-worklets: 0.8.3(patch_hash=f7263a59ae1c0170af8dfe9a5704cb374d29214a430b4b2841f9b7886c9ba8ad)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0)(@react-native/metro-config@0.86.0(@babel/core@7.29.0))(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) + react-native: 0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1) + react-native-gesture-handler: 2.30.1(patch_hash=39b9e51b3977fae8ff61764b8c32934fe3fbddaf21587b4661fd73b7c6dd325b)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) + react-native-reanimated: 4.3.2(patch_hash=ab1c095bc473ec5bfd9ead76e870c566660e355e11c5078cfdeb35922d7b2f05)(react-native-worklets@0.8.3(patch_hash=f7263a59ae1c0170af8dfe9a5704cb374d29214a430b4b2841f9b7886c9ba8ad)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) + react-native-safe-area-context: 5.6.2(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) + react-native-screens: 4.24.0(patch_hash=74a74b489f8074a1974c2792c9c97aec30d8e251669aa37fc5c99993bb4a19e6)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) + react-native-svg: 15.12.1(patch_hash=ae309e9542214f5ae54935e253dedd236765feedac7be343f81d532d7fe09310)(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0) + react-native-worklets: 0.8.3(patch_hash=f7263a59ae1c0170af8dfe9a5704cb374d29214a430b4b2841f9b7886c9ba8ad)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.81.5(patch_hash=91fd85363059530dea649a5ca6012b933f79c8491737fbfdc685abc727e48403)(@babel/core@7.29.0(supports-color@8.1.1))(@react-native/metro-config@0.86.0(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.1.17)(react@19.1.0)(supports-color@8.1.1))(react@19.1.0)(supports-color@8.1.1) sonner@2.0.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: @@ -18412,12 +18747,12 @@ snapshots: source-map-js@1.2.1: {} - source-map-loader@3.0.2(webpack@5.106.2(postcss@8.5.14)): + source-map-loader@3.0.2(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)): dependencies: abab: 2.0.6 iconv-lite: 0.6.3 source-map-js: 1.2.1 - webpack: 5.106.2(postcss@8.5.14) + webpack: 5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14) source-map-support@0.5.13: dependencies: @@ -18437,9 +18772,9 @@ snapshots: source-map@0.7.6: {} - spdy-transport@3.0.0: + spdy-transport@3.0.0(supports-color@8.1.1): dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) detect-node: 2.1.0 hpack.js: 2.1.6 obuf: 1.1.2 @@ -18448,13 +18783,13 @@ snapshots: transitivePeerDependencies: - supports-color - spdy@4.0.2: + spdy@4.0.2(supports-color@8.1.1): dependencies: - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) handle-thing: 2.0.1 http-deceiver: 1.2.7 select-hose: 2.0.0 - spdy-transport: 3.0.0 + spdy-transport: 3.0.0(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -18594,9 +18929,9 @@ snapshots: structured-headers@0.4.1: {} - style-loader@3.3.4(webpack@5.106.2(postcss@8.5.14)): + style-loader@3.3.4(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)): dependencies: - webpack: 5.106.2(postcss@8.5.14) + webpack: 5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14) stylehacks@5.1.1(postcss@8.5.14): dependencies: @@ -18672,18 +19007,20 @@ snapshots: ansi-escapes: 4.3.2 supports-hyperlinks: 2.3.0 - terser-webpack-plugin@5.6.0(clean-css@5.3.3)(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(postcss@8.5.14)(webpack@5.106.2(postcss@8.5.14)): + terser-webpack-plugin@5.6.0(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 schema-utils: 4.3.3 terser: 5.47.1 - webpack: 5.106.2(postcss@8.5.14) + webpack: 5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14) optionalDependencies: clean-css: 5.3.3 + cssnano: 5.1.15(postcss@8.5.14) csso: 5.0.5 esbuild: 0.25.12 html-minifier-terser: 6.1.0 + lightningcss: 1.32.0 postcss: 8.5.14 terser@5.47.1: @@ -18703,6 +19040,8 @@ snapshots: dependencies: utrie: 1.0.2 + text-table@0.2.0: {} + thenify-all@1.6.0: dependencies: thenify: 3.3.1 @@ -18711,10 +19050,10 @@ snapshots: dependencies: any-promise: 1.3.0 - threads@1.7.0: + threads@1.7.0(supports-color@8.1.1): dependencies: callsites: 3.1.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@8.1.1) is-observable: 2.1.0 observable-fns: 0.6.1 optionalDependencies: @@ -18781,8 +19120,14 @@ snapshots: tslib@2.8.1: {} + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + type-detect@4.0.8: {} + type-fest@0.20.2: {} + type-fest@0.21.3: {} type-fest@0.7.1: {} @@ -19043,16 +19388,16 @@ snapshots: - bufferutil - utf-8-validate - webpack-dev-middleware@5.3.4(webpack@5.106.2(postcss@8.5.14)): + webpack-dev-middleware@5.3.4(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)): dependencies: colorette: 2.0.20 memfs: 3.5.3 mime-types: 2.1.35 range-parser: 1.2.1 schema-utils: 4.3.3 - webpack: 5.106.2(postcss@8.5.14) + webpack: 5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14) - webpack-dev-server@4.15.2(webpack@5.106.2(postcss@8.5.14)): + webpack-dev-server@4.15.2(debug@4.4.3(supports-color@8.1.1))(supports-color@8.1.1)(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)): dependencies: '@types/bonjour': 3.5.13 '@types/connect-history-api-fallback': 1.5.4 @@ -19065,13 +19410,13 @@ snapshots: bonjour-service: 1.3.0 chokidar: 3.6.0 colorette: 2.0.20 - compression: 1.8.1 + compression: 1.8.1(supports-color@8.1.1) connect-history-api-fallback: 2.0.0 default-gateway: 6.0.3 - express: 4.22.2 + express: 4.22.2(supports-color@8.1.1) graceful-fs: 4.2.11 html-entities: 2.6.0 - http-proxy-middleware: 2.0.9(@types/express@4.17.25) + http-proxy-middleware: 2.0.9(@types/express@4.17.25)(debug@4.4.3(supports-color@8.1.1)) ipaddr.js: 2.4.0 launch-editor: 2.13.2 open: 8.4.2 @@ -19079,23 +19424,23 @@ snapshots: rimraf: 3.0.2 schema-utils: 4.3.3 selfsigned: 2.4.1 - serve-index: 1.9.2 + serve-index: 1.9.2(supports-color@8.1.1) sockjs: 0.3.24 - spdy: 4.0.2 - webpack-dev-middleware: 5.3.4(webpack@5.106.2(postcss@8.5.14)) + spdy: 4.0.2(supports-color@8.1.1) + webpack-dev-middleware: 5.3.4(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)) ws: 8.20.1 optionalDependencies: - webpack: 5.106.2(postcss@8.5.14) + webpack: 5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14) transitivePeerDependencies: - bufferutil - debug - supports-color - utf-8-validate - webpack-manifest-plugin@4.1.1(webpack@5.106.2(postcss@8.5.14)): + webpack-manifest-plugin@4.1.1(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)): dependencies: tapable: 2.3.3 - webpack: 5.106.2(postcss@8.5.14) + webpack: 5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14) webpack-sources: 2.3.1 webpack-sources@2.3.1: @@ -19107,7 +19452,7 @@ snapshots: webpack-virtual-modules@0.5.0: {} - webpack@5.106.2(postcss@8.5.14): + webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14): dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.6 @@ -19130,7 +19475,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 4.3.3 tapable: 2.3.3 - terser-webpack-plugin: 5.6.0(clean-css@5.3.3)(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(postcss@8.5.14)(webpack@5.106.2(postcss@8.5.14)) + terser-webpack-plugin: 5.6.0(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)(webpack@5.106.2(clean-css@5.3.3)(cssnano@5.1.15(postcss@8.5.14))(csso@5.0.5)(esbuild@0.25.12)(html-minifier-terser@6.1.0)(lightningcss@1.32.0)(postcss@8.5.14)) watchpack: 2.5.1 webpack-sources: 3.4.1 transitivePeerDependencies: @@ -19228,6 +19573,8 @@ snapshots: wonka@6.3.6: {} + word-wrap@1.2.5: {} + wrap-ansi@10.0.0: dependencies: ansi-styles: 6.2.3 diff --git a/src/components/dialogs/EmailDialog/data/useAccountEmailState.ts b/src/components/dialogs/EmailDialog/data/useAccountEmailState.ts index f25369f8db..b9edd1629d 100644 --- a/src/components/dialogs/EmailDialog/data/useAccountEmailState.ts +++ b/src/components/dialogs/EmailDialog/data/useAccountEmailState.ts @@ -1,7 +1,7 @@ import {useEffect, useMemo, useState} from 'react' import {useQuery} from '@tanstack/react-query' -import {useAgent, useSessionApi} from '#/state/session' +import {useSession, useSessionApi} from '#/state/session' import {emitEmailVerified} from '#/components/dialogs/EmailDialog/events' export type AccountEmailState = { @@ -12,24 +12,29 @@ export type AccountEmailState = { export const accountEmailStateQueryKey = ['accountEmailState'] as const export function useAccountEmailState() { - const agent = useAgent() + /* + * Read from the account rather than the agent's session: `partialRefreshSession` + * patches the email fields on the account in the reducer (`PasswordSession` + * has no setter for them), so the account is the fresh source of truth. + */ + const {currentAccount} = useSession() const {partialRefreshSession} = useSessionApi() const [prevIsEmailVerified, setPrevEmailIsVerified] = useState( - !!agent.session?.emailConfirmed, + !!currentAccount?.emailConfirmed, ) const state: AccountEmailState = useMemo( () => ({ - isEmailVerified: !!agent.session?.emailConfirmed, - email2FAEnabled: !!agent.session?.emailAuthFactor, + isEmailVerified: !!currentAccount?.emailConfirmed, + email2FAEnabled: !!currentAccount?.emailAuthFactor, }), - [agent.session], + [currentAccount], ) /** * Only here to refetch on focus, when necessary */ useQuery({ - enabled: !!agent.session, + enabled: !!currentAccount, /** * Only refetch if the email verification s incomplete. */ diff --git a/src/lib/strings/__tests__/errors.test.ts b/src/lib/strings/__tests__/errors.test.ts new file mode 100644 index 0000000000..03cdd422c8 --- /dev/null +++ b/src/lib/strings/__tests__/errors.test.ts @@ -0,0 +1,126 @@ +import {XRPCError} from '@atproto/api' +import {LexError, XrpcResponseError} from '@atproto/lex-client' +import {beforeAll, describe, expect, it} from '@jest/globals' +import {i18n} from '@lingui/core' + +import {cleanError} from '../errors' + +/* + * `cleanError` returns translated copy, so a locale has to be active. With no + * catalog loaded, Lingui falls back to the source message. + */ +beforeAll(() => { + i18n.loadAndActivate({locale: 'en', messages: {}}) +}) + +/** + * A stand-in for the method schema an `XrpcResponseError` is built against. + * The generated lexicons are not available yet, and `XrpcResponseError` only + * reads `method` back out for `matchesSchemaErrors()`, which these tests never + * call - so a minimal object is enough. + */ +const method = { + nsid: 'com.atproto.server.createAccount', + type: 'procedure', + errors: ['HandleNotAvailable'], +} as unknown as ConstructorParameters[0] + +/** An error as the lex client builds one from a JSON error response body. */ +function xrpcResponseError( + error: string, + message: string, + status: number = 400, +) { + return new XrpcResponseError(method, new Response(null, {status}), { + encoding: 'application/json', + body: {error, message}, + }) +} + +/** + * An error as the lex client builds one from a response with no XRPC error + * payload: the code is derived from the HTTP status, and the message is a + * generic overview of the response. + */ +function xrpcStatusError(status: number) { + return new XrpcResponseError(method, new Response(null, {status}), undefined) +} + +describe('cleanError', () => { + it('surfaces the clean message of a lex error', () => { + const e = xrpcResponseError('HandleNotAvailable', 'Handle already taken') + // The raw stringification is class- and code-prefixed, so it must not leak. + expect(e.toString()).toBe( + 'XrpcResponseError: [HandleNotAvailable] Handle already taken', + ) + expect(cleanError(e)).toBe('Handle already taken') + }) + + it('falls back to the lexicon code when a lex error has no message', () => { + // `Error` defaults an absent message to the empty string. + const e = new LexError('InvalidRequest') + expect(e.toString()).toBe('LexError: [InvalidRequest] ') + expect(cleanError(e)).toBe('InvalidRequest') + }) + + it('matches the upstream-failure branch on a lex error code', () => { + // 502 maps to the space-free `UpstreamFailure` lexicon code. + const e = xrpcStatusError(502) + expect(e.error).toBe('UpstreamFailure') + expect(cleanError(e)).toBe( + 'The server appears to be experiencing issues. Please try again in a few moments.', + ) + }) + + it('matches the upstream-failure branch on a legacy XRPC error', () => { + const e = new XRPCError(502) + expect(cleanError(e)).toBe( + 'The server appears to be experiencing issues. Please try again in a few moments.', + ) + }) + + it('matches NotEnoughResources on both error shapes', () => { + expect(cleanError(xrpcStatusError(503))).toBe( + 'The server appears to be experiencing issues. Please try again in a few moments.', + ) + expect(cleanError(new XRPCError(503))).toBe( + 'The server appears to be experiencing issues. Please try again in a few moments.', + ) + }) + + it('matches the app-password branch on a lex error message', () => { + const e = xrpcResponseError('InvalidToken', 'Bad token scope') + expect(cleanError(e)).toBe( + 'This feature is not available while using an App Password. Please sign in with your main password.', + ) + }) + + it('matches the network-error branch on a lex error message', () => { + const e = xrpcResponseError('InternalServerError', 'Failed to fetch', 500) + expect(cleanError(e)).toBe( + 'Unable to connect. Please check your internet connection and try again.', + ) + }) + + it('surfaces the authentication-required code of a lex error', () => { + /* + * The lex client derives `AuthenticationRequired` from a 401 with no XRPC + * payload, where `@atproto/api` used the spaced "Authentication Required". + * Neither is special-cased in `cleanError`, so what matters is that the + * class- and code-prefixed stringification does not reach the user. + */ + const e = xrpcStatusError(401) + expect(e.error).toBe('AuthenticationRequired') + expect(cleanError(e)).toBe('Upstream server responded with a 401 error') + }) + + it('strips a leading "Error: " from a plain error', () => { + expect(cleanError(new Error('Something broke'))).toBe('Something broke') + }) + + it('passes strings through', () => { + expect(cleanError('Something broke')).toBe('Something broke') + expect(cleanError('')).toBe('') + expect(cleanError(undefined)).toBe('') + }) +}) diff --git a/src/lib/strings/errors.ts b/src/lib/strings/errors.ts index 11e7c11362..4f793ec195 100644 --- a/src/lib/strings/errors.ts +++ b/src/lib/strings/errors.ts @@ -1,17 +1,51 @@ import {XRPCError} from '@atproto/api' +import {LexError} from '@atproto/lex-client' import {t} from '@lingui/core/macro' +/** + * The text to show the user when no special case applies. + * + * A `LexError` stringifies as `Class: [ErrorCode] message` (for example + * `XrpcResponseError: [HandleNotAvailable] Handle already taken`), so its + * `toString()` is never fit for display. Its `message` is the server's + * human-readable text, so prefer that and fall back to the lexicon code for the + * errors that carry no message. + * + * Everything else keeps the historical behaviour of stringifying and dropping a + * leading `Error: `. + */ +function toDisplayString(e: unknown, str: string): string { + if (e instanceof LexError) { + return e.message || e.error + } + if (str.startsWith('Error: ')) { + return str.slice('Error: '.length) + } + return str +} + export function cleanError(e: unknown): string { if (!e) { return '' } + /* + * Match against the full stringification rather than the display text: it + * contains the error name and the lexicon code as well as the message, so a + * single check covers both error shapes. + */ // oxlint-disable-next-line typescript/no-base-to-string const str = typeof e === 'string' ? e : e.toString() if (isNetworkError(str)) { return t`Unable to connect. Please check your internet connection and try again.` } + /* + * `@atproto/api` names these with spaces ("Upstream Failure"); lexicon error + * codes are space-free ("UpstreamFailure"). Match both while the app throws + * both shapes. + */ if ( str.includes('Upstream Failure') || + str.includes('UpstreamFailure') || str.includes('NotEnoughResources') || str.includes('pipethrough network error') ) { @@ -41,10 +75,7 @@ export function cleanError(e: unknown): string { if (str.includes('Unable to resolve handle')) { return t`Unable to resolve handle` } - if (str.startsWith('Error: ')) { - return str.slice('Error: '.length) - } - return str + return toDisplayString(e, str) } const NETWORK_ERRORS = [ diff --git a/src/screens/Login/LoginForm.tsx b/src/screens/Login/LoginForm.tsx index 27361219ec..ad50179bee 100644 --- a/src/screens/Login/LoginForm.tsx +++ b/src/screens/Login/LoginForm.tsx @@ -1,9 +1,7 @@ import {useRef, useState} from 'react' import {Keyboard, type TextInput, View} from 'react-native' -import { - ComAtprotoServerCreateSession, - type ComAtprotoServerDescribeServer, -} from '@atproto/api' +import {type ComAtprotoServerDescribeServer} from '@atproto/api' +import {LexAuthFactorError} from '@atproto/lex-password-session' import {Trans, useLingui} from '@lingui/react/macro' import {DEFAULT_SERVICE, HITSLOP_10, HITSLOP_20} from '#/lib/constants' @@ -142,10 +140,11 @@ export const LoginForm = ({ } catch (err) { const errMsg = String(err) setIsProcessing(false) - if ( - err instanceof - ComAtprotoServerCreateSession.AuthFactorTokenRequiredError - ) { + /* + * `LexAuthFactorError` is what `PasswordSession.login` throws when the + * server demands an email 2FA token. + */ + if (err instanceof LexAuthFactorError) { setIsAuthFactorTokenNeeded(true) } else { onAttemptFailed() diff --git a/src/screens/Signup/state.ts b/src/screens/Signup/state.ts index 95b3185758..33a5d26a64 100644 --- a/src/screens/Signup/state.ts +++ b/src/screens/Signup/state.ts @@ -1,9 +1,7 @@ import {createContext, useCallback, useContext} from 'react' import {LayoutAnimation} from 'react-native' -import { - ComAtprotoServerCreateAccount, - type ComAtprotoServerDescribeServer, -} from '@atproto/api' +import {type ComAtprotoServerDescribeServer} from '@atproto/api' +import {XrpcResponseError} from '@atproto/lex-client' import {useLingui} from '@lingui/react/macro' import * as EmailValidator from 'email-validator' @@ -260,14 +258,35 @@ export const useSignupContext = () => useContext(SignupContext) * failure is unexpected and should be reported to Sentry. */ function classifyExpectedSignupError(e: unknown): string | undefined { - if (e instanceof ComAtprotoServerCreateAccount.InvalidHandleError) - return 'InvalidHandle' - if (e instanceof ComAtprotoServerCreateAccount.HandleNotAvailableError) - return 'HandleNotAvailable' - if (e instanceof ComAtprotoServerCreateAccount.InvalidPasswordError) - return 'InvalidPassword' - if (e instanceof ComAtprotoServerCreateAccount.UnsupportedDomainError) - return 'UnsupportedDomain' + /* + * TODO: `XrpcResponseError.error` is the open `LexErrorCode` union, so these + * codes are compared as plain strings and a typo silently never matches. + * Once the generated lexicons land, replace this (and every multi-code error + * site) with a shared helper that narrows against the method schema: + * + * function matchXrpcError( + * e: unknown, + * method: Main, + * ): InferMethodError | undefined + * + * switch (matchXrpcError(e, com.atproto.server.createAccount)) { + * case 'InvalidHandle': ... + * } + * + * The return type is the method's declared-errors union, so a typo'd case is + * a compile error, and undeclared codes fall through to `undefined`. The + * helper should also match `e.method.nsid` so a declared code from a + * different call cannot match, mirroring the old per-method error classes. + */ + if (e instanceof XrpcResponseError) { + switch (e.error) { + case 'InvalidHandle': + case 'HandleNotAvailable': + case 'InvalidPassword': + case 'UnsupportedDomain': + return e.error + } + } /* the server sends no typed error for this case */ if (String(e).includes('Email already taken')) return 'EmailTaken' if (isNetworkError(e)) return 'NetworkError' @@ -357,7 +376,7 @@ export function useSubmitSignup() { } catch (err) { const e = err as Error let errMsg = e.toString() - if (e instanceof ComAtprotoServerCreateAccount.InvalidInviteCodeError) { + if (e instanceof XrpcResponseError && e.error === 'InvalidInviteCode') { dispatch({ type: 'setError', value: l`Invite code not accepted. Check that you input it correctly and try again.`, diff --git a/src/state/persisted/index.ts b/src/state/persisted/index.ts index 8c043a3420..0659e7817b 100644 --- a/src/state/persisted/index.ts +++ b/src/state/persisted/index.ts @@ -31,6 +31,18 @@ export function get(key: K): Schema[K] { } get satisfies PersistedApi['get'] +/** + * Native is single-instance: there is no other tab that could have written + * newer data behind our back, so the in-memory `_state` is already the truth + * and a synchronous fresh read is impossible anyway (AsyncStorage is async). + * This mirrors {@link get}; the web implementation is the one that actually + * re-reads the store. + */ +export function readLatest(key: K): Schema[K] { + return _state[key] +} +readLatest satisfies PersistedApi['readLatest'] + export async function write( key: K, value: Schema[K], diff --git a/src/state/persisted/index.web.ts b/src/state/persisted/index.web.ts index 35e796810d..a4c5d1b247 100644 --- a/src/state/persisted/index.web.ts +++ b/src/state/persisted/index.web.ts @@ -39,6 +39,29 @@ export function get(key: K): Schema[K] { } get satisfies PersistedApi['get'] +/** + * Force a fresh synchronous re-read of localStorage and return the requested + * key from it, WITHOUT adopting it as `_state`. + * + * This exists for the cross-tab expiry-rescue path. A frozen tab may not have + * processed a queued broadcast yet, so {@link get} (and persisted's in-memory + * `_state`) can be stale even though another tab already wrote healthy tokens + * to storage. Reading through storage directly here is the only way to see the + * true cross-tab-latest tokens on web. + * + * Crucially we do NOT adopt into `_state`. {@link readFromStorage} memoizes by + * raw string and returns the same object reference for unchanged data, so + * adopting here would make the later queued broadcast/storage event for that + * same write see `next === _state` and suppress its listener notification - + * leaving non-current-account changes (removals, other tokens, metadata) stale + * indefinitely. Leaving `_state` alone lets that queued event still fire. + */ +export function readLatest(key: K): Schema[K] { + const next = readFromStorage() + return next?.[key] ?? _state[key] +} +readLatest satisfies PersistedApi['readLatest'] + // eslint-disable-next-line @typescript-eslint/require-await export async function write( key: K, diff --git a/src/state/persisted/types.ts b/src/state/persisted/types.ts index d1fdfc26cb..4b5a29d938 100644 --- a/src/state/persisted/types.ts +++ b/src/state/persisted/types.ts @@ -3,6 +3,15 @@ import {type Schema} from './schema' export type PersistedApi = { init(): Promise get(key: K): Schema[K] + /** + * Like {@link get}, but on web forces a fresh synchronous re-read of the + * backing store before returning (without adopting it as the in-memory + * state). This exists for the cross-tab expiry-rescue path: a frozen tab may + * not have processed a queued broadcast yet, so {@link get} can be stale + * while another tab has already written healthy tokens to storage. On native + * it is identical to {@link get} (single-instance, no other writer). + */ + readLatest(key: K): Schema[K] write(key: K, value: Schema[K]): Promise onUpdate( key: K, diff --git a/src/state/queries/preferences/index.ts b/src/state/queries/preferences/index.ts index 41adfc9a04..a5803a9c7b 100644 --- a/src/state/queries/preferences/index.ts +++ b/src/state/queries/preferences/index.ts @@ -21,7 +21,7 @@ import { } from '#/state/queries/preferences/types' import {createQueryKey} from '#/state/queries/util' import {useAgent} from '#/state/session' -import {saveLabelers} from '#/state/session/agent-config' +import {saveLabelers} from '#/state/session/moderation' import {useAgeAssurance} from '#/ageAssurance' import {makeAgeRestrictedModerationPrefs} from '#/ageAssurance/util' import {useAnalytics} from '#/analytics' @@ -53,7 +53,7 @@ export function usePreferencesQuery() { const res = await agent.getPreferences() // save to local storage to ensure there are labels on initial requests - void saveLabelers( + saveLabelers( agent.did, res.moderationPrefs.labelers.map(l => l.did), ) diff --git a/src/state/session/__tests__/bridge-agent-test.ts b/src/state/session/__tests__/bridge-agent-test.ts new file mode 100644 index 0000000000..cbd606d12a --- /dev/null +++ b/src/state/session/__tests__/bridge-agent-test.ts @@ -0,0 +1,573 @@ +import { + PasswordSession, + type PasswordSessionOptions, + type SessionData, +} from '@atproto/lex-password-session' +import {beforeEach, describe, expect, it, jest} from '@jest/globals' + +jest.mock('#/state/events', () => ({ + emitNetworkConfirmed: jest.fn(), + emitNetworkLost: jest.fn(), +})) + +jest.mock('jwt-decode', () => ({ + jwtDecode() { + return {scope: 'com.atproto.access'} + }, +})) + +import {BskyAppAgent, PasswordSessionManager} from '../bridge-agent' +import {sessionAccountToSessionData} from '../session-data' +import {type SessionAccount} from '../types' +import { + asFetch, + DID, + DIDDOC_PDS_HOST, + HANDLE, + json, + makeAccount, + makeDidDoc, + makeMockFetch, + type MockFetch, + PDS_HOST, + SERVICE, + urlsOf, +} from './mock-fetch' + +/** + * Build the manager + agent pair under test. + * + * The mock fetch is installed in both places it can be reached from: as the + * inner `PasswordSession`'s fetch (the authenticated path) and, via + * `setFetch`, as the manager's own fetch (the unauthenticated bypass path, + * which would otherwise use the real network-aware fetch). + */ +function setup({ + account = makeAccount(), + didDoc, + pdsUrl, + fetchMock = makeMockFetch(), + sessionOptions, +}: { + account?: SessionAccount + didDoc?: SessionData['didDoc'] + pdsUrl?: string + fetchMock?: MockFetch + sessionOptions?: PasswordSessionOptions +} = {}) { + const data: SessionData = { + ...sessionAccountToSessionData(account), + ...(didDoc ? {didDoc} : {}), + } + const inner = new PasswordSession(data, { + fetch: asFetch(fetchMock), + ...sessionOptions, + }) + const manager = new PasswordSessionManager(inner, { + service: account.service, + pdsUrl, + }) + manager.setFetch(asFetch(fetchMock)) + const agent = new BskyAppAgent(manager) + return {inner, manager, agent, fetchMock} +} + +function setupPublic(fetchMock: MockFetch = makeMockFetch()) { + const manager = new PasswordSessionManager(null, {service: SERVICE}) + manager.setFetch(asFetch(fetchMock)) + return {manager, agent: new BskyAppAgent(manager), fetchMock} +} + +describe('PasswordSessionManager getters', () => { + it('reads live SessionData through .session', () => { + const {agent} = setup() + expect(agent.session?.did).toBe(DID) + expect(agent.session?.handle).toBe(HANDLE) + expect(agent.session?.email).toBe('alice@example.com') + expect(agent.session?.emailConfirmed).toBe(true) + expect(agent.did).toBe(DID) + expect(agent.hasSession).toBe(true) + }) + + it('defaults active to true when the payload omits it', () => { + const {agent} = setup({account: makeAccount({active: undefined})}) + expect(agent.session?.active).toBe(true) + }) + + it('exposes serviceUrl from the constructor service', () => { + const {agent} = setup() + expect(agent.serviceUrl.toString()).toBe('https://bsky.social/') + }) + + it('derives pdsUrl/dispatchUrl from the didDoc', () => { + const {agent} = setup({didDoc: makeDidDoc(PDS_HOST)}) + expect(agent.pdsUrl?.toString()).toBe(`${PDS_HOST}/`) + expect(agent.dispatchUrl.toString()).toBe(`${PDS_HOST}/`) + }) + + it('falls back to the stored pdsUrl when there is no didDoc', () => { + const {agent} = setup({pdsUrl: PDS_HOST}) + expect(agent.pdsUrl?.toString()).toBe(`${PDS_HOST}/`) + expect(agent.dispatchUrl.toString()).toBe(`${PDS_HOST}/`) + }) + + it('prefers the didDoc PDS over the stored pdsUrl', () => { + const {agent} = setup({ + didDoc: makeDidDoc(DIDDOC_PDS_HOST), + pdsUrl: PDS_HOST, + }) + expect(agent.pdsUrl?.toString()).toBe(`${DIDDOC_PDS_HOST}/`) + }) + + it('dispatchUrl falls back to serviceUrl with no PDS at all', () => { + const {agent} = setup() + expect(agent.pdsUrl).toBe(undefined) + expect(agent.dispatchUrl.toString()).toBe('https://bsky.social/') + }) + + it('ignores an unparseable stored pdsUrl', () => { + const {agent} = setup({pdsUrl: 'not a url'}) + expect(agent.pdsUrl).toBe(undefined) + expect(agent.dispatchUrl.toString()).toBe('https://bsky.social/') + }) + + it('matches the inner session on didDocs a strict validator would reject', () => { + /* + * No `id` on the document and a non-canonical service `type`: enough for + * isValidDidDoc/getPdsEndpoint to bail, but PasswordSession still routes + * here, so the bridge must agree or dispatchUrl lies about where requests + * go (and service-auth aud gets minted for the wrong host). + */ + const {agent} = setup({ + didDoc: { + service: [ + { + id: '#atproto_pds', + type: 'SomethingElse', + serviceEndpoint: DIDDOC_PDS_HOST, + }, + ], + }, + pdsUrl: PDS_HOST, + }) + expect(agent.pdsUrl?.toString()).toBe(`${DIDDOC_PDS_HOST}/`) + expect(agent.dispatchUrl.toString()).toBe(`${DIDDOC_PDS_HOST}/`) + }) + + it('falls back to the stored pdsUrl when the didDoc has no PDS service', () => { + const {agent} = setup({ + didDoc: { + id: DID, + service: [ + { + id: '#bsky_notif', + type: 'BskyNotificationService', + serviceEndpoint: DIDDOC_PDS_HOST, + }, + ], + }, + pdsUrl: PDS_HOST, + }) + expect(agent.pdsUrl?.toString()).toBe(`${PDS_HOST}/`) + }) + + it('falls back to the stored pdsUrl when the PDS endpoint does not parse', () => { + const {agent} = setup({ + didDoc: { + id: DID, + service: [ + { + id: '#atproto_pds', + type: 'AtprotoPersonalDataServer', + serviceEndpoint: 'not a url', + }, + ], + }, + pdsUrl: PDS_HOST, + }) + expect(agent.pdsUrl?.toString()).toBe(`${PDS_HOST}/`) + }) +}) + +describe('PasswordSessionManager.session identity', () => { + it('is stable across consecutive reads', () => { + const {agent} = setup() + expect(agent.session).toBe(agent.session) + }) + + it('is a new object after a refresh rotates tokens', async () => { + const {agent} = setup() + const before = agent.session + expect(before?.accessJwt).toBe('access-jwt') + await agent.sessionManager.refreshSession() + const after = agent.session + expect(after).not.toBe(before) + expect(after?.accessJwt).toBe('access-jwt-2') + expect(after).toBe(agent.session) + }) + + it('rejects writes to .session', () => { + const {agent} = setup() + expect(() => { + /* the whole point of the accessor: writes must not silently drift */ + agent.sessionManager.session = agent.session + }).toThrow('read-only') + }) + + it('rejects writes to .pdsUrl', () => { + const {agent} = setup() + expect(() => { + agent.sessionManager.pdsUrl = new URL(PDS_HOST) + }).toThrow('read-only') + }) +}) + +describe('PasswordSessionManager.fetchHandler routing', () => { + it('dispatches to the stored PDS before a refresh, then to the didDoc PDS', async () => { + const {manager, fetchMock} = setup({pdsUrl: PDS_HOST}) + + await manager.fetchHandler('/xrpc/app.bsky.actor.getProfile') + expect(urlsOf(fetchMock).at(-1)).toBe( + `${PDS_HOST}/xrpc/app.bsky.actor.getProfile`, + ) + + /* the refresh response carries a didDoc pointing at a different host */ + await manager.refreshSession() + expect(manager.pdsUrl?.toString()).toBe(`${DIDDOC_PDS_HOST}/`) + + await manager.fetchHandler('/xrpc/app.bsky.actor.getProfile') + expect(urlsOf(fetchMock).at(-1)).toBe( + `${DIDDOC_PDS_HOST}/xrpc/app.bsky.actor.getProfile`, + ) + }) + + it('attaches the session bearer token', async () => { + const seen: Headers[] = [] + const fetchMock = makeMockFetch({ + 'app.bsky.actor.getProfile': (_url, init) => { + seen.push(new Headers(init.headers)) + return json({}) + }, + }) + const {manager} = setup({fetchMock}) + await manager.fetchHandler('/xrpc/app.bsky.actor.getProfile') + expect(seen[0].get('authorization')).toBe('Bearer access-jwt') + }) + + it('bypasses the inner session when authorization is pre-set', async () => { + const seen: Headers[] = [] + const fetchMock = makeMockFetch({ + 'com.atproto.server.describeServer': (_url, init) => { + seen.push(new Headers(init.headers)) + return json({}) + }, + }) + const {manager} = setup({fetchMock, pdsUrl: PDS_HOST}) + + /* + * PasswordSession throws TypeError on a pre-set authorization header, so + * this path must never reach it. + */ + await expect( + manager.fetchHandler('/xrpc/com.atproto.server.describeServer', { + headers: {authorization: 'Bearer caller-supplied'}, + }), + ).resolves.toBeDefined() + + expect(seen.length).toBe(1) + /* the caller's header survives, and there is exactly one of them */ + expect(seen[0].get('authorization')).toBe('Bearer caller-supplied') + expect(urlsOf(fetchMock).at(-1)).toBe( + `${PDS_HOST}/xrpc/com.atproto.server.describeServer`, + ) + }) +}) + +describe('BskyAppAgent namespace requests', () => { + it('carries proxy, labeler and bearer headers to the dispatch host', async () => { + const seen: {url: string; headers: Headers}[] = [] + const fetchMock = makeMockFetch({ + 'app.bsky.actor.getProfile': (url, init) => { + seen.push({url, headers: new Headers(init.headers)}) + return json({did: DID, handle: HANDLE}) + }, + }) + const {agent} = setup({fetchMock, pdsUrl: PDS_HOST}) + agent.configureProxy('did:web:api.bsky.app#bsky_appview') + agent.configureLabelers(['did:plc:custom-labeler']) + + /* + * The request headers (what we assert) are captured by the fetch mock + * before the agent parses the response body. Response-body lexicon + * validation can throw in the jest environment (a multiformats CID mock + * quirk unrelated to the header composition under test), so we ignore any + * parse error here. + */ + await agent.app.bsky.actor.getProfile({actor: HANDLE}).catch(() => {}) + + expect(seen.length).toBe(1) + expect(seen[0].url.startsWith(`${PDS_HOST}/xrpc/`)).toBe(true) + expect(seen[0].headers.get('atproto-proxy')).toBe( + 'did:web:api.bsky.app#bsky_appview', + ) + expect(seen[0].headers.get('atproto-accept-labelers')).toContain( + 'did:plc:custom-labeler', + ) + expect(seen[0].headers.get('authorization')).toBe('Bearer access-jwt') + }) +}) + +describe('PasswordSessionManager.refreshSession', () => { + it('returns an old-shaped XRPC envelope with fresh tokens', async () => { + const {manager, fetchMock} = setup() + const res = await manager.refreshSession() + expect(res.success).toBe(true) + expect(res.data.accessJwt).toBe('access-jwt-2') + expect(res.data.refreshJwt).toBe('refresh-jwt-2') + expect(res.data.did).toBe(DID) + expect(res.data.handle).toBe(HANDLE) + expect( + urlsOf(fetchMock).some(u => + u.includes('com.atproto.server.refreshSession'), + ), + ).toBe(true) + }) + + it('throws when there is no live session', async () => { + const {manager} = setupPublic() + await expect(manager.refreshSession()).rejects.toThrow( + 'No session to refresh', + ) + }) +}) + +describe('PasswordSessionManager.resumeSession', () => { + const staleData = { + accessJwt: 'stale-access', + refreshJwt: 'stale-refresh', + handle: 'stale.test', + did: DID, + active: true, + } + + it('ignores its argument and returns fresh tokens from a refresh', async () => { + const {manager} = setup() + const res = await manager.resumeSession(staleData) + expect(res.data.accessJwt).toBe('access-jwt-2') + expect(res.data.refreshJwt).toBe('refresh-jwt-2') + expect(manager.session?.accessJwt).toBe('access-jwt-2') + }) + + it('is reachable through the agent and does not install the stale data', async () => { + const {agent} = setup() + await agent.resumeSession(staleData) + expect(agent.session?.accessJwt).toBe('access-jwt-2') + expect(agent.session?.handle).toBe(HANDLE) + }) +}) + +describe('PasswordSessionManager unsupported methods', () => { + it('refuses login()', async () => { + const {agent} = setup() + await expect( + agent.login({identifier: HANDLE, password: 'hunter2'}), + ).rejects.toThrow('Not supported on PasswordSessionManager') + }) + + it('refuses createAccount()', async () => { + const {agent} = setup() + await expect( + agent.createAccount({handle: HANDLE, email: 'a@b.c', password: 'x'}), + ).rejects.toThrow('Not supported on PasswordSessionManager') + }) +}) + +describe('PasswordSessionManager destroyed inner session', () => { + it('getters return undefined rather than throwing after logout', async () => { + const {agent, inner} = setup() + await agent.logout() + expect(inner.destroyed).toBe(true) + /* PasswordSession.did/.session throw once destroyed; the bridge must not */ + expect(() => agent.did).not.toThrow() + expect(agent.did).toBe(undefined) + expect(agent.session).toBe(undefined) + expect(agent.hasSession).toBe(false) + expect(agent.pdsUrl).toBe(undefined) + }) + + it('logout() is idempotent', async () => { + const {agent} = setup() + await agent.logout() + await expect(agent.logout()).resolves.toBeUndefined() + }) + + it('fetchHandler stops attaching auth once destroyed', async () => { + const seen: Headers[] = [] + const fetchMock = makeMockFetch({ + 'app.bsky.actor.getProfile': (_url, init) => { + seen.push(new Headers(init.headers)) + return json({}) + }, + }) + const {agent, manager} = setup({fetchMock}) + await agent.logout() + await manager.fetchHandler('/xrpc/app.bsky.actor.getProfile') + expect(seen.length).toBe(1) + expect(seen[0].get('authorization')).toBe(null) + }) +}) + +describe('BskyAppAgent.dispose', () => { + let ctx: ReturnType + + beforeEach(() => { + ctx = setup({pdsUrl: PDS_HOST}) + }) + + it('makes the session read as logged out', () => { + expect(ctx.agent.session).toBeDefined() + ctx.agent.dispose() + expect(ctx.agent.session).toBe(undefined) + expect(ctx.agent.did).toBe(undefined) + expect(ctx.agent.pdsUrl).toBe(undefined) + expect(ctx.agent.hasSession).toBe(false) + }) + + it('routes requests through the plain unauthenticated fetch', async () => { + const seen: Headers[] = [] + const fetchMock = makeMockFetch({ + 'app.bsky.actor.getProfile': (_url, init) => { + seen.push(new Headers(init.headers)) + return json({}) + }, + }) + const {agent, manager} = setup({fetchMock, pdsUrl: PDS_HOST}) + agent.dispose() + await manager.fetchHandler('/xrpc/app.bsky.actor.getProfile') + expect(seen.length).toBe(1) + expect(seen[0].get('authorization')).toBe(null) + /* dispatch falls back to the service, since pdsUrl now reads undefined */ + expect(urlsOf(fetchMock).at(-1)).toBe( + `${SERVICE}/xrpc/app.bsky.actor.getProfile`, + ) + }) + + it('leaves refreshSession unusable', async () => { + ctx.agent.dispose() + await expect(ctx.agent.sessionManager.refreshSession()).rejects.toThrow( + 'No session to refresh', + ) + }) +}) + +describe('public PasswordSessionManager (no inner session)', () => { + it('reads as logged out', () => { + const {agent} = setupPublic() + expect(agent.session).toBe(undefined) + expect(agent.did).toBe(undefined) + expect(agent.hasSession).toBe(false) + expect(agent.pdsUrl).toBe(undefined) + expect(agent.dispatchUrl.toString()).toBe('https://bsky.social/') + }) + + it('dispatches to the service unauthenticated', async () => { + const seen: Headers[] = [] + const fetchMock = makeMockFetch({ + 'app.bsky.feed.getFeed': (_url, init) => { + seen.push(new Headers(init.headers)) + return json({}) + }, + }) + const {manager} = setupPublic(fetchMock) + await manager.fetchHandler('/xrpc/app.bsky.feed.getFeed') + expect(seen.length).toBe(1) + expect(seen[0].get('authorization')).toBe(null) + expect(urlsOf(fetchMock).at(-1)).toBe( + `${SERVICE}/xrpc/app.bsky.feed.getFeed`, + ) + }) +}) + +describe('PasswordSession lifecycle over mocked fetch', () => { + it('resume fast path: constructing does not hit the network', () => { + const fetchMock = makeMockFetch() + setup({fetchMock}) + expect(fetchMock.mock.calls.length).toBe(0) + }) + + it('a refresh fires onUpdated with fresh tokens', async () => { + const onUpdated = + jest.fn>() + const {manager} = setup({sessionOptions: {onUpdated}}) + await manager.refreshSession() + expect(onUpdated).toHaveBeenCalledTimes(1) + expect(manager.session?.accessJwt).toBe('access-jwt-2') + }) + + it('onDeleted fires when refresh returns a declared invalid-token error', async () => { + const onDeleted = + jest.fn>() + const onUpdated = + jest.fn>() + const fetchMock = makeMockFetch({ + 'com.atproto.server.refreshSession': () => + json({error: 'ExpiredToken', message: 'Token expired'}, 400), + }) + const {manager} = setup({fetchMock, sessionOptions: {onDeleted, onUpdated}}) + await expect(manager.refreshSession()).rejects.toBeDefined() + expect(onDeleted).toHaveBeenCalledTimes(1) + expect(onUpdated).not.toHaveBeenCalled() + /* and the bridge reads as logged out afterwards */ + expect(manager.session).toBe(undefined) + }) + + it('onUpdateFailure fires on a transient (500) refresh error, session preserved', async () => { + const onDeleted = + jest.fn>() + const onUpdateFailure = + jest.fn>() + const fetchMock = makeMockFetch({ + 'com.atproto.server.refreshSession': () => + json({error: 'InternalServerError'}, 500), + }) + const {manager} = setup({ + fetchMock, + sessionOptions: {onDeleted, onUpdateFailure}, + }) + /* + * PasswordSession.refresh() resolves with the unchanged data here; the + * bridge restores the old CredentialSession contract by rejecting. + */ + await expect(manager.refreshSession()).rejects.toThrow( + 'Failed to refresh session', + ) + expect(onUpdateFailure).toHaveBeenCalledTimes(1) + expect(onDeleted).not.toHaveBeenCalled() + expect(manager.session?.accessJwt).toBe('access-jwt') + }) + + it('rejects on a network error rather than reporting a no-op success', async () => { + const fetchMock = makeMockFetch({ + 'com.atproto.server.refreshSession': () => { + throw new TypeError('Network request failed') + }, + }) + const {manager} = setup({fetchMock}) + await expect(manager.refreshSession()).rejects.toThrow( + 'Failed to refresh session', + ) + /* the session survives, exactly as the old transient-failure path did */ + expect(manager.session?.accessJwt).toBe('access-jwt') + }) + + it('resumeSession rejects on a transient failure too', async () => { + const fetchMock = makeMockFetch({ + 'com.atproto.server.refreshSession': () => + json({error: 'InternalServerError'}, 500), + }) + const {agent} = setup({fetchMock}) + await expect(agent.resumeSession(agent.session!)).rejects.toThrow( + 'Failed to refresh session', + ) + }) +}) diff --git a/src/state/session/__tests__/expiry-rescue-test.ts b/src/state/session/__tests__/expiry-rescue-test.ts new file mode 100644 index 0000000000..c33eb527de --- /dev/null +++ b/src/state/session/__tests__/expiry-rescue-test.ts @@ -0,0 +1,104 @@ +import {describe, expect, it} from '@jest/globals' + +import { + MAX_EXPIRY_RESCUE_GENERATIONS, + pickExpiryRescueCandidate, +} from '../expiry-rescue' +import {type SessionAccount} from '../types' + +function makeAccount(overrides: Partial = {}): SessionAccount { + return { + service: 'https://bsky.social', + did: 'did:plc:example123', + handle: 'alice.test', + email: 'alice@example.com', + emailConfirmed: true, + emailAuthFactor: false, + refreshJwt: 'refresh-jwt', + accessJwt: 'access-jwt', + signupQueued: false, + active: true, + status: undefined, + pdsUrl: undefined, + isSelfHosted: false, + ...overrides, + } +} + +describe('pickExpiryRescueCandidate', () => { + it('picks a candidate whose refreshJwt differs from the dying one', () => { + const fresh = makeAccount({refreshJwt: 'refresh-jwt-2'}) + const picked = pickExpiryRescueCandidate({ + dyingRefreshJwt: 'refresh-jwt-1', + candidates: [fresh], + failedRefreshJwts: new Set(), + }) + expect(picked).toBe(fresh) + }) + + it('rejects a candidate carrying the dying refreshJwt (equally dead)', () => { + const picked = pickExpiryRescueCandidate({ + dyingRefreshJwt: 'refresh-jwt-1', + candidates: [makeAccount({refreshJwt: 'refresh-jwt-1'})], + failedRefreshJwts: new Set(), + }) + expect(picked).toBe(undefined) + }) + + it('rejects a candidate with no refreshJwt', () => { + const picked = pickExpiryRescueCandidate({ + dyingRefreshJwt: 'refresh-jwt-1', + candidates: [makeAccount({refreshJwt: undefined}), undefined], + failedRefreshJwts: new Set(), + }) + expect(picked).toBe(undefined) + }) + + it('rejects a candidate already recorded as failed (loop guard)', () => { + const picked = pickExpiryRescueCandidate({ + dyingRefreshJwt: 'refresh-jwt-1', + candidates: [makeAccount({refreshJwt: 'refresh-jwt-2'})], + failedRefreshJwts: new Set(['refresh-jwt-2']), + }) + expect(picked).toBe(undefined) + }) + + it('tries candidates in order, preferring the first qualifying one', () => { + const persistedCandidate = makeAccount({ + refreshJwt: 'refresh-jwt-persisted', + }) + const reducerCandidate = makeAccount({refreshJwt: 'refresh-jwt-reducer'}) + const picked = pickExpiryRescueCandidate({ + dyingRefreshJwt: 'refresh-jwt-1', + candidates: [persistedCandidate, reducerCandidate], + failedRefreshJwts: new Set(), + }) + expect(picked).toBe(persistedCandidate) + }) + + it('skips an unusable first candidate and falls back to a later one', () => { + const reducerCandidate = makeAccount({refreshJwt: 'refresh-jwt-reducer'}) + const picked = pickExpiryRescueCandidate({ + dyingRefreshJwt: 'refresh-jwt-1', + candidates: [ + makeAccount({refreshJwt: 'refresh-jwt-1'}), + reducerCandidate, + ], + failedRefreshJwts: new Set(), + }) + expect(picked).toBe(reducerCandidate) + }) + + it('gives up once the failed-generation set hits the hard cap', () => { + const failed = new Set() + for (let i = 0; i < MAX_EXPIRY_RESCUE_GENERATIONS; i++) { + failed.add(`refresh-jwt-failed-${i}`) + } + const picked = pickExpiryRescueCandidate({ + dyingRefreshJwt: 'refresh-jwt-dying', + candidates: [makeAccount({refreshJwt: 'refresh-jwt-brand-new'})], + failedRefreshJwts: failed, + }) + expect(picked).toBe(undefined) + }) +}) diff --git a/src/state/session/__tests__/mock-fetch.ts b/src/state/session/__tests__/mock-fetch.ts new file mode 100644 index 0000000000..a107d8dc3c --- /dev/null +++ b/src/state/session/__tests__/mock-fetch.ts @@ -0,0 +1,126 @@ +import {jest} from '@jest/globals' + +import {type SessionAccount} from '../types' + +/* + * Shared fixtures for the suites that drive a real `PasswordSession` over a + * stubbed network. Not a suite itself - the filename deliberately avoids the + * `-test` suffix so jest does not collect it. + */ + +export const DID = 'did:plc:example123' +export const HANDLE = 'alice.test' +export const SERVICE = 'https://bsky.social' +/** A PDS host an account may be pinned to by its stored `pdsUrl`. */ +export const PDS_HOST = 'https://shimeji.us-east.host.bsky.network' +/** A different PDS host, delivered by the didDoc a refresh returns. */ +export const DIDDOC_PDS_HOST = 'https://morel.us-west.host.bsky.network' + +export function json(body: unknown, status = 200) { + return new Response(JSON.stringify(body), { + status, + headers: {'content-type': 'application/json'}, + }) +} + +/** A minimal valid DID document whose only service entry is a PDS. */ +export function makeDidDoc(pdsUrl: string, did: string = DID) { + return { + id: did, + service: [ + { + id: '#atproto_pds', + type: 'AtprotoPersonalDataServer', + serviceEndpoint: pdsUrl, + }, + ], + } +} + +export function makeAccount( + overrides: Partial = {}, +): SessionAccount { + return { + service: SERVICE, + did: DID, + handle: HANDLE, + email: 'alice@example.com', + emailConfirmed: true, + emailAuthFactor: false, + refreshJwt: 'refresh-jwt', + accessJwt: 'access-jwt', + signupQueued: false, + active: true, + status: undefined, + pdsUrl: undefined, + isSelfHosted: false, + ...overrides, + } +} + +/** + * Build a mock `fetch` that returns canned XRPC responses keyed by the last + * path segment (nsid). `refreshSession` returns fresh tokens; `getSession` + * echoes the account; anything else returns an empty 200. + * + * The refresh response carries both `emailConfirmed` and a `didDoc` so the + * library has no reason to make a `getSession` follow-up call, which keeps the + * recorded request list assertable. Its didDoc points at + * {@link DIDDOC_PDS_HOST}, a different host from {@link PDS_HOST}, so PDS + * re-routing after a refresh is observable. + */ +export function makeMockFetch( + overrides: Record< + string, + (url: string, init: RequestInit) => Response | Promise + > = {}, +) { + return jest.fn( + /* + * PasswordSession calls fetch with a URL object (new URL(path, service)); + * asFetch() below widens the mock to the full fetch signature it expects. + */ + async (input: URL | string, init: RequestInit = {}): Promise => { + const url = input instanceof URL ? input.href : input + const nsid = url.split('/xrpc/')[1]?.split('?')[0] + const handler = nsid ? overrides[nsid] : undefined + if (handler) { + return handler(url, init) + } + if (nsid === 'com.atproto.server.refreshSession') { + return json({ + accessJwt: 'access-jwt-2', + refreshJwt: 'refresh-jwt-2', + handle: HANDLE, + did: DID, + email: 'alice@example.com', + emailConfirmed: true, + didDoc: makeDidDoc(DIDDOC_PDS_HOST), + active: true, + }) + } + if (nsid === 'com.atproto.server.getSession') { + return json({ + did: DID, + handle: HANDLE, + email: 'alice@example.com', + emailConfirmed: true, + active: true, + }) + } + return json({}) + }, + ) +} + +export type MockFetch = ReturnType + +/** Cast a jest fetch mock to the `fetch` type PasswordSession options expect. */ +export function asFetch(mock: MockFetch): typeof fetch { + return mock as unknown as typeof fetch +} + +/** The URLs a mock fetch was called with, in order. */ +export function urlsOf(mock: MockFetch): string[] { + return mock.mock.calls.map(c => (c[0] instanceof URL ? c[0].href : c[0])) +} diff --git a/src/state/session/__tests__/provider-abort-test.tsx b/src/state/session/__tests__/provider-abort-test.tsx new file mode 100644 index 0000000000..fdc1585981 --- /dev/null +++ b/src/state/session/__tests__/provider-abort-test.tsx @@ -0,0 +1,145 @@ +import {beforeEach, describe, expect, it, jest} from '@jest/globals' +import {act, render} from '@testing-library/react-native' + +/* + * The provider pulls the whole app shell in through `#/state/util` and the + * account factories. These mocks cut the tree back to the session lifecycle + * itself, which is all these tests drive. + */ +jest.mock('#/state/persisted', () => { + const { + defaults, + }: typeof import('#/state/persisted/schema') = require('#/state/persisted/schema') + return { + defaults, + get: (key: keyof typeof defaults) => defaults[key], + write: () => Promise.resolve(), + readLatest: (key: keyof typeof defaults) => defaults[key], + onUpdate: () => () => {}, + } +}) +jest.mock('#/state/util', () => ({useCloseAllActiveElements: () => () => {}})) +jest.mock('#/components/dialogs/Context', () => ({ + useGlobalDialogsControlContext: () => ({signinDialogControl: {open() {}}}), +})) +jest.mock('#/analytics', () => ({ + AnalyticsContext: ({children}: {children: React.ReactNode}) => children, + useAnalyticsBase: () => ({metric() {}, logger: {debug() {}, error() {}}}), + utils: {accountToSessionMetadata: () => ({}), useMeta: () => undefined}, +})) +jest.mock('#/state/shell/onboarding', () => ({ + useOnboardingDispatch: () => () => {}, +})) +jest.mock('#/ageAssurance/data', () => ({ + clearAgeAssuranceServerDataForAll: () => {}, + clearAgeAssuranceServerDataForDid: () => {}, +})) +jest.mock('#/lib/persisted-query-storage', () => ({ + clearPersistedQueryStorage: () => Promise.resolve(), +})) +jest.mock('#/lib/notifications/notifications', () => ({ + unregisterPushToken: () => Promise.resolve(), +})) +jest.mock('jwt-decode', () => ({jwtDecode: () => ({})})) + +/* + * The factories are stubbed so a test controls exactly when each one resolves, + * which is what lets a second call abort the first while it is in flight. + * `disposeBundle` is spied on rather than replaced wholesale: the rest of + * session-core stays real so the provider's own module graph is unchanged. + */ +const mockLogin = jest.fn<(...args: unknown[]) => Promise>() +const mockCreateAccount = jest.fn<(...args: unknown[]) => Promise>() +const mockDisposeBundle = jest.fn() +jest.mock('../session-core', () => ({ + ...jest.requireActual('../session-core'), + createSessionBundleAndLogin: (...args: unknown[]) => mockLogin(...args), + disposeBundle: (bundle: unknown) => mockDisposeBundle(bundle), +})) +jest.mock('../create-account', () => ({ + createSessionBundleAndCreateAccount: (...args: unknown[]) => + mockCreateAccount(...args), +})) + +import {Provider, useSessionApi} from '#/state/session' +import {type SessionApiContext} from '#/state/session/types' + +/** Render the provider and hand back its api context. */ +function renderProvider(): SessionApiContext { + let api!: SessionApiContext + function Probe() { + api = useSessionApi() + return null + } + render( + + + , + ) + return api +} + +/* + * Every factory returns an ARMED bundle, so a call whose result is thrown away + * because a newer call superseded it must dispose that bundle. Leaving it armed + * leaves a live session auto-refreshing and rotating refresh tokens server-side + * for an account the app is no longer tracking. + */ +describe('superseded session tasks dispose their bundle', () => { + /* + * Without this, a recorded call from an earlier test satisfies a later + * assertion. The tagged bundles below are the other half of that guard: two + * `{}` literals are structurally equal, so `toHaveBeenCalledWith` could not + * tell one test's bundle from the other's even within a cleared mock. + */ + beforeEach(() => { + mockLogin.mockReset() + mockCreateAccount.mockReset() + mockDisposeBundle.mockReset() + }) + + it('disposes the bundle of an aborted login', async () => { + const bundle = {tag: 'login-bundle'} as never + let resolveLogin!: (value: unknown) => void + mockLogin.mockReturnValueOnce( + new Promise(resolve => { + resolveLogin = resolve + }), + ) + const api = renderProvider() + + const superseded = api.login({} as never, 'LoginForm') + /* the second call aborts the first task's signal, and never settles */ + mockLogin.mockReturnValueOnce(new Promise(() => {})) + void api.login({} as never, 'LoginForm') + + await act(async () => { + resolveLogin({bundle, account: {did: 'did:plc:example'}}) + await superseded + }) + + expect(mockDisposeBundle).toHaveBeenCalledWith(bundle) + }) + + it('disposes the bundle of an aborted createAccount', async () => { + const bundle = {tag: 'create-account-bundle'} as never + let resolveCreate!: (value: unknown) => void + mockCreateAccount.mockReturnValueOnce( + new Promise(resolve => { + resolveCreate = resolve + }), + ) + const api = renderProvider() + + const superseded = api.createAccount({} as never, {} as never) + mockCreateAccount.mockReturnValueOnce(new Promise(() => {})) + void api.createAccount({} as never, {} as never) + + await act(async () => { + resolveCreate({bundle, account: {did: 'did:plc:example'}}) + await superseded + }) + + expect(mockDisposeBundle).toHaveBeenCalledWith(bundle) + }) +}) diff --git a/src/state/session/__tests__/provider-session-events-test.tsx b/src/state/session/__tests__/provider-session-events-test.tsx new file mode 100644 index 0000000000..e6a51a5414 --- /dev/null +++ b/src/state/session/__tests__/provider-session-events-test.tsx @@ -0,0 +1,559 @@ +import {type SessionData} from '@atproto/lex-password-session' +import {beforeEach, describe, expect, it, jest} from '@jest/globals' +import {act, render} from '@testing-library/react-native' + +import {type Schema} from '#/state/persisted/schema' +import {type SessionAccount} from '../types' + +/* + * The provider pulls the whole app shell in through `#/state/util` and the + * account factories. These mocks cut the tree back to the session lifecycle + * itself, which is all these tests drive. They mirror provider-abort-test.tsx, + * plus a stateful `#/state/persisted` (this suite drives cross-tab updates and + * the expiry rescue's fresh persisted read) and an observable + * `emitSessionDropped`. + */ +const mockPersisted: {session: Schema['session']; latest: Schema['session']} = { + session: {accounts: [], currentAccount: undefined}, + latest: {accounts: [], currentAccount: undefined}, +} +/* + * Every registered listener is kept, not just the newest. The provider's + * subscription effect re-runs on every state change, so a callback captured + * before a dispatch is exactly the stale-closure case the shouldActivate guard + * exists to catch. + */ +const mockPersistedListeners: ((value: Schema['session']) => void)[] = [] +jest.mock('#/state/persisted', () => { + const { + defaults, + }: typeof import('#/state/persisted/schema') = require('#/state/persisted/schema') + return { + defaults, + get: (key: string) => + key === 'session' + ? mockPersisted.session + : defaults[key as keyof typeof defaults], + readLatest: (key: string) => + key === 'session' + ? mockPersisted.latest + : defaults[key as keyof typeof defaults], + write: () => Promise.resolve(), + onUpdate: (_key: string, cb: (value: Schema['session']) => void) => { + mockPersistedListeners.push(cb) + return () => {} + }, + } +}) +jest.mock('#/state/util', () => ({useCloseAllActiveElements: () => () => {}})) +jest.mock('#/components/dialogs/Context', () => ({ + useGlobalDialogsControlContext: () => ({signinDialogControl: {open() {}}}), +})) +jest.mock('#/analytics', () => ({ + AnalyticsContext: ({children}: {children: React.ReactNode}) => children, + useAnalyticsBase: () => ({metric() {}, logger: {debug() {}, error() {}}}), + utils: {accountToSessionMetadata: () => ({}), useMeta: () => undefined}, +})) +jest.mock('#/state/shell/onboarding', () => ({ + useOnboardingDispatch: () => () => {}, +})) +jest.mock('#/ageAssurance/data', () => ({ + clearAgeAssuranceServerDataForAll: () => {}, + clearAgeAssuranceServerDataForDid: () => {}, +})) +jest.mock('#/lib/persisted-query-storage', () => ({ + clearPersistedQueryStorage: () => Promise.resolve(), +})) +jest.mock('#/lib/notifications/notifications', () => ({ + unregisterPushToken: () => Promise.resolve(), +})) +jest.mock('jwt-decode', () => ({jwtDecode: () => ({})})) + +const mockEmitSessionDropped = jest.fn() +jest.mock('#/state/events', () => ({ + emitSessionDropped: () => mockEmitSessionDropped(), + emitNetworkConfirmed: () => {}, + emitNetworkLost: () => {}, +})) + +/* + * The factories are stubbed so a test controls exactly what each one returns + * and when. `createSessionBundleFromStoredAccount` is stubbed faithfully rather + * than replaced by a constant: it must still consult `shouldActivate` and + * decline to hand back a bundle when the guard rejects, because that decision + * is what these tests observe. Its disposal of a rejected bundle is pinned by + * session-core-test; here we only assert what the provider does with the + * result. + */ +const mockLogin = jest.fn<(...args: unknown[]) => Promise>() +const mockResume = jest.fn<(...args: unknown[]) => Promise>() +const mockDisposeBundle = jest.fn() +type Rebuild = { + account: SessionAccount + shouldActivate: boolean + bundle: FakeBundle +} +const mockRebuilds: Rebuild[] = [] +const mockRebuild = jest.fn( + ( + account: SessionAccount, + _onSessionChange: unknown, + shouldActivate: ( + bundle: unknown, + account: SessionAccount, + ) => boolean = () => true, + ) => { + const bundle = makeBundle(account) + const activated = shouldActivate(bundle, account) + mockRebuilds.push({account, shouldActivate: activated, bundle}) + return activated ? {bundle, account} : undefined + }, +) +jest.mock('../session-core', () => ({ + ...jest.requireActual('../session-core'), + createSessionBundleAndLogin: (...args: unknown[]) => mockLogin(...args), + createSessionBundleAndResume: (...args: unknown[]) => mockResume(...args), + createSessionBundleFromStoredAccount: (...args: unknown[]) => + // @ts-expect-error the stub's arity is checked by its own signature + mockRebuild(...args), + disposeBundle: (bundle: unknown) => mockDisposeBundle(bundle), +})) +jest.mock('../create-account', () => ({ + createSessionBundleAndCreateAccount: () => new Promise(() => {}), +})) + +import {Provider, useSession, useSessionApi} from '#/state/session' +import { + type OnSessionChange, + type SessionBundle, +} from '#/state/session/session-core' +import {type SessionApiContext} from '#/state/session/types' + +const DID = 'did:plc:example123' +const SERVICE = 'https://bsky.social/' + +function makeAccount(overrides: Partial = {}): SessionAccount { + return { + service: SERVICE, + did: DID, + handle: 'alice.test', + email: 'alice@example.com', + emailConfirmed: true, + emailAuthFactor: false, + refreshJwt: 'refresh-jwt-1', + accessJwt: 'access-jwt-1', + signupQueued: false, + active: true, + status: undefined, + pdsUrl: undefined, + isSelfHosted: false, + ...overrides, + } +} + +/* + * The provider only ever reads `bundle.agent` (for context) and + * `bundle.session.destroyed` / `bundle.session.session` (for the cross-tab + * token comparison), and otherwise treats a bundle as an opaque identity. A + * literal with those fields is enough, and keeps a real PasswordSession - with + * its network and refresh machinery - out of a suite about provider dispatch. + */ +type FakeBundle = { + session: {destroyed: boolean; session: SessionData} + agent: object + service: URL +} + +function makeBundle(account: SessionAccount): FakeBundle { + return { + session: { + destroyed: false, + session: { + accessJwt: account.accessJwt ?? '', + refreshJwt: account.refreshJwt ?? '', + /* SessionData types these as branded strings; the values are fixtures */ + handle: account.handle as `${string}.${string}`, + did: account.did as `did:${string}:${string}`, + active: true, + service: account.service, + }, + }, + agent: {}, + service: new URL(account.service), + } +} + +type Harness = { + api: SessionApiContext + /** The provider's own onSessionChange, as handed to a session factory. */ + onSessionChange: OnSessionChange + currentAccount: () => SessionAccount | undefined + hasSession: () => boolean +} + +/** + * Render the provider, log an account in through the stubbed login factory, and + * hand back the api plus the `onSessionChange` the factory received. Firing + * that callback is how a test synthesizes a session event from a live bundle. + */ +async function renderLoggedIn( + account: SessionAccount, + bundle: FakeBundle, +): Promise { + let api!: SessionApiContext + let session!: ReturnType + function Probe() { + api = useSessionApi() + session = useSession() + return null + } + render( + + + , + ) + + let captured!: OnSessionChange + mockLogin.mockImplementationOnce((...args: unknown[]) => { + captured = args[1] as OnSessionChange + return Promise.resolve({bundle, account}) + }) + await act(async () => { + await api.login({} as never, 'LoginForm') + }) + + return { + api, + onSessionChange: captured, + currentAccount: () => session.currentAccount, + hasSession: () => session.hasSession, + } +} + +/** The dying payload PasswordSession threads through its `onDeleted` hook. */ +function dyingData(refreshJwt: string): SessionData { + return { + accessJwt: 'dead-access-jwt', + refreshJwt, + handle: 'alice.test', + did: DID, + active: true, + service: SERVICE, + } +} + +/** The rotated payload PasswordSession threads through its `onUpdated` hook. */ +function refreshedData( + refreshJwt: string, + didDoc?: SessionData['didDoc'], +): SessionData { + return { + accessJwt: 'fresh-access-jwt', + refreshJwt, + handle: 'alice.test', + did: DID, + active: true, + service: SERVICE, + ...(didDoc ? {didDoc} : {}), + } +} + +/** A minimal valid DID document whose only service entry is a PDS. */ +function makeDidDoc(pdsUrl: string): SessionData['didDoc'] { + return { + id: DID, + service: [ + { + id: '#atproto_pds', + type: 'AtprotoPersonalDataServer', + serviceEndpoint: pdsUrl, + }, + ], + } +} + +beforeEach(() => { + mockPersisted.session = {accounts: [], currentAccount: undefined} + mockPersisted.latest = {accounts: [], currentAccount: undefined} + mockPersistedListeners.length = 0 + mockRebuilds.length = 0 + mockLogin.mockReset() + mockResume.mockReset() + mockRebuild.mockClear() + mockDisposeBundle.mockReset() + mockEmitSessionDropped.mockReset() +}) + +/* + * A stale tab can expire a refresh token that another tab has already rotated + * past. Logging every tab out on that event is the known-worst failure in this + * subsystem, so the provider first looks for a newer token generation and + * rebuilds onto it, only falling through to logout when there is nothing left + * to try. + */ +describe('expiry rescue', () => { + it('rebuilds onto a fresher persisted generation instead of logging out', async () => { + const account = makeAccount() + const bundle = makeBundle(account) + const {onSessionChange, hasSession, currentAccount} = await renderLoggedIn( + account, + bundle, + ) + + /* another tab already rotated to generation 2 and wrote it to storage */ + const fresher = makeAccount({ + accessJwt: 'access-jwt-2', + refreshJwt: 'refresh-jwt-2', + }) + mockPersisted.latest = {accounts: [fresher], currentAccount: fresher} + + act(() => { + onSessionChange( + bundle as unknown as SessionBundle, + DID, + 'expired', + dyingData('refresh-jwt-1'), + ) + }) + + /* the rescue rebuilt onto the fresher generation ... */ + expect(mockRebuilds.length).toBe(1) + expect(mockRebuilds[0].account.refreshJwt).toBe('refresh-jwt-2') + /* ... and adopted it, without ever reporting the session as dropped */ + expect(hasSession()).toBe(true) + expect(currentAccount()?.refreshJwt).toBe('refresh-jwt-2') + expect(mockEmitSessionDropped).not.toHaveBeenCalled() + }) + + it('drops the session and logs out when there is no fresher generation', async () => { + const account = makeAccount() + const bundle = makeBundle(account) + const {onSessionChange, hasSession, currentAccount} = await renderLoggedIn( + account, + bundle, + ) + + /* storage agrees the dying token is the newest one anybody has */ + mockPersisted.latest = {accounts: [account], currentAccount: account} + + act(() => { + onSessionChange( + bundle as unknown as SessionBundle, + DID, + 'expired', + dyingData('refresh-jwt-1'), + ) + }) + + expect(mockRebuilds.length).toBe(0) + expect(mockEmitSessionDropped).toHaveBeenCalledTimes(1) + expect(hasSession()).toBe(false) + /* the reducer cleared the dead credentials rather than keeping them */ + expect(currentAccount()).toBe(undefined) + }) + + it('does not retry a generation that already failed', async () => { + const account = makeAccount() + const bundle = makeBundle(account) + const {onSessionChange, hasSession} = await renderLoggedIn(account, bundle) + + const gen2 = makeAccount({ + accessJwt: 'access-jwt-2', + refreshJwt: 'refresh-jwt-2', + }) + mockPersisted.latest = {accounts: [gen2], currentAccount: gen2} + + /* generation 1 dies and is rescued onto generation 2 */ + act(() => { + onSessionChange( + bundle as unknown as SessionBundle, + DID, + 'expired', + dyingData('refresh-jwt-1'), + ) + }) + expect(mockRebuilds.length).toBe(1) + const rescued = mockRebuilds[0].bundle + + /* + * Generation 2 dies too, and a stale tab has meanwhile written generation 1 + * back to storage. It differs from the dying token, so only the record of + * its earlier failure can reject it. + */ + mockPersisted.latest = {accounts: [account], currentAccount: account} + act(() => { + onSessionChange( + rescued as unknown as SessionBundle, + DID, + 'expired', + dyingData('refresh-jwt-2'), + ) + }) + + expect(mockRebuilds.length).toBe(1) + expect(mockEmitSessionDropped).toHaveBeenCalledTimes(1) + expect(hasSession()).toBe(false) + }) +}) + +/* + * A refresh payload only carries a didDoc when the server sends one, but + * `pdsUrl` is never derived from the login service. If the provider does not + * thread the stored value through, an ordinary refresh persists + * `pdsUrl: undefined` and the next cold start routes pre-refresh requests to + * the entryway instead of the account's PDS. + */ +describe('refresh persistence', () => { + const PDS_HOST = 'https://shimeji.us-east.host.bsky.network' + const DIDDOC_PDS_HOST = 'https://morel.us-west.host.bsky.network' + + it('keeps the stored pdsUrl when the refresh carries no didDoc', async () => { + const account = makeAccount({pdsUrl: `${PDS_HOST}/`}) + const bundle = makeBundle(account) + const {onSessionChange, currentAccount} = await renderLoggedIn( + account, + bundle, + ) + + act(() => { + onSessionChange( + bundle as unknown as SessionBundle, + DID, + 'update', + refreshedData('refresh-jwt-2'), + ) + }) + + expect(currentAccount()?.refreshJwt).toBe('refresh-jwt-2') + expect(currentAccount()?.pdsUrl).toBe(`${PDS_HOST}/`) + }) + + it('prefers the didDoc endpoint over the stored pdsUrl', async () => { + const account = makeAccount({pdsUrl: `${PDS_HOST}/`}) + const bundle = makeBundle(account) + const {onSessionChange, currentAccount} = await renderLoggedIn( + account, + bundle, + ) + + act(() => { + onSessionChange( + bundle as unknown as SessionBundle, + DID, + 'update', + refreshedData('refresh-jwt-2', makeDidDoc(DIDDOC_PDS_HOST)), + ) + }) + + expect(currentAccount()?.pdsUrl).toBe(`${DIDDOC_PDS_HOST}/`) + }) +}) + +/** Deliver a cross-tab `persisted` update to the provider's newest listener. */ +function emitSynced(session: Schema['session']) { + mockPersistedListeners[mockPersistedListeners.length - 1](session) +} + +/* + * A `PasswordSession` cannot be patched in place, so adopting tokens another + * tab refreshed means rebuilding the bundle. Doing that for every broadcast + * would churn the agent (and the React tree under it) constantly, so the + * provider rebuilds only when the tokens actually moved, and guards the swap + * against the store having advanced underneath it. + */ +describe('cross-tab sync', () => { + it('short-circuits an update carrying the tokens the live session already has', async () => { + const account = makeAccount() + const bundle = makeBundle(account) + const {hasSession} = await renderLoggedIn(account, bundle) + + act(() => { + emitSynced({accounts: [account], currentAccount: account}) + }) + + /* identical tokens: nothing to adopt, so no rebuild */ + expect(mockRebuilds.length).toBe(0) + expect(hasSession()).toBe(true) + }) + + it('rebuilds onto tokens another tab rotated', async () => { + const account = makeAccount() + const bundle = makeBundle(account) + const {currentAccount} = await renderLoggedIn(account, bundle) + + const rotated = makeAccount({ + accessJwt: 'access-jwt-2', + refreshJwt: 'refresh-jwt-2', + }) + act(() => { + emitSynced({accounts: [rotated], currentAccount: rotated}) + }) + + expect(mockRebuilds.length).toBe(1) + expect(mockRebuilds[0].account.refreshJwt).toBe('refresh-jwt-2') + expect(currentAccount()?.refreshJwt).toBe('refresh-jwt-2') + }) + + it('declines to activate a rebuild once the store has moved past the bundle it was built for', async () => { + const account = makeAccount() + const bundle = makeBundle(account) + await renderLoggedIn(account, bundle) + + const gen2 = makeAccount({ + accessJwt: 'access-jwt-2', + refreshJwt: 'refresh-jwt-2', + }) + const gen3 = makeAccount({ + accessJwt: 'access-jwt-3', + refreshJwt: 'refresh-jwt-3', + }) + + /* + * Two broadcasts land back to back inside one act(), so React does not + * commit (and the effect does not re-subscribe) between them: the second + * runs the listener registered while the ORIGINAL bundle was current, even + * though the store has since advanced to the generation-2 rebuild. + */ + const listener = mockPersistedListeners[mockPersistedListeners.length - 1] + act(() => { + listener({accounts: [gen2], currentAccount: gen2}) + listener({accounts: [gen3], currentAccount: gen3}) + }) + + expect(mockRebuilds.length).toBe(2) + expect(mockRebuilds[0].shouldActivate).toBe(true) + /* the stale closure's bundle is no longer current, so the swap is refused */ + expect(mockRebuilds[1].account.refreshJwt).toBe('refresh-jwt-3') + expect(mockRebuilds[1].shouldActivate).toBe(false) + }) + + it('cancels pending work when another tab logs the account out', async () => { + const account = makeAccount() + const bundle = makeBundle(account) + const {api} = await renderLoggedIn(account, bundle) + + /* a resume is in flight and will resolve only after the cross-tab logout */ + const resumedBundle = makeBundle(account) + let finishResume!: (value: unknown) => void + mockResume.mockReturnValueOnce( + new Promise(resolve => { + finishResume = resolve + }), + ) + const pending = api.resumeSession(account) + + const loggedOut = makeAccount({accessJwt: undefined, refreshJwt: undefined}) + act(() => { + emitSynced({accounts: [loggedOut], currentAccount: loggedOut}) + }) + + await act(async () => { + finishResume({bundle: resumedBundle, account}) + await pending + }) + + /* the superseded resume disposed its bundle rather than signing back in */ + expect(mockDisposeBundle).toHaveBeenCalledWith(resumedBundle) + expect(mockRebuilds.length).toBe(0) + }) +}) diff --git a/src/state/session/__tests__/session-core-test.ts b/src/state/session/__tests__/session-core-test.ts new file mode 100644 index 0000000000..9dbc646c9f --- /dev/null +++ b/src/state/session/__tests__/session-core-test.ts @@ -0,0 +1,1027 @@ +import { + PasswordSession, + type PasswordSessionOptions, + type SessionData, +} from '@atproto/lex-password-session' +import {beforeEach, describe, expect, it, jest} from '@jest/globals' + +import {type SessionAccount} from '../types' + +jest.mock('#/state/events', () => ({ + emitNetworkConfirmed: jest.fn(), + emitNetworkLost: jest.fn(), +})) + +/* + * `makeSessionHooks` reports a throwing `onSessionChange` through the logger + * rather than letting it escape into PasswordSession's session promise. Stub + * the module so that report is assertable (and so nothing reaches the real + * transports). + */ +const mockLoggerError = jest.fn() +jest.mock('#/logger', () => { + const noopLogger = { + error: (...args: unknown[]) => mockLoggerError(...args), + warn: () => {}, + info: () => {}, + log: () => {}, + debug: () => {}, + } + return { + logger: noopLogger, + Logger: { + create: () => noopLogger, + Context: new Proxy({}, {get: (_t, key) => String(key)}), + Level: {}, + }, + } +}) + +/* + * `prefetchAgeAssuranceServerData` is a genuine prep await in each factory + * (moderation config is synchronous, so the AA prefetch is where the factory + * tests inject a mid-prep token rotation). The default is a no-op; + * individual tests install behavior via `mockImplementationOnce`. + */ +const mockPrefetchAgeAssuranceServerData = jest.fn<() => void | Promise>() +jest.mock('#/ageAssurance/data', () => ({ + prefetchAgeAssuranceServerData: () => mockPrefetchAgeAssuranceServerData(), +})) +/* + * The factory tail awaits `features.refresh(...)`; stub the analytics module so + * the factory does not pull GrowthBook (and its native deps) into this + * lightweight suite. + */ +jest.mock('#/analytics', () => ({ + features: {refresh: () => Promise.resolve()}, +})) + +/* + * `configureModerationForAccount` is synchronous (the labeler cache is a local + * MMKV read), so it is not a prep await - but it still runs inside each factory + * with the freshly built bridge agent, before the awaited prep steps. The + * factory tests capture the agent with this mock, then inject a real refresh + * into the awaited AA prefetch so a token rotation happens during prep, before + * arm(). The default is a no-op so other tests are unaffected. + * (jest requires out-of-scope factory references to be `mock`-prefixed.) + */ +const mockConfigureModerationForAccount = + jest.fn<(agent: unknown, account: unknown) => void>() +jest.mock('../moderation', () => ({ + configureModerationForAccount: (agent: unknown, account: unknown) => + mockConfigureModerationForAccount(agent, account), + configureModerationForGuest: () => {}, +})) + +jest.mock('jwt-decode', () => ({ + jwtDecode(token: string) { + if (token === 'queued-access-jwt') { + return {scope: 'com.atproto.signupQueued'} + } + /* + * A far-future exp so isSessionExpired() reads this stored token as still + * valid, which routes resume() through the sync (no-network) fast path. + * That isolates the prep-time refresh as the ONLY token rotation. + */ + if (token === 'valid-access-jwt') { + return {scope: 'com.atproto.access', exp: 4102444800} + } + return {scope: 'com.atproto.access'} + }, +})) + +import {type BskyAppAgent} from '../bridge-agent' +import { + type AtpSessionEvent, + buildBundle, + createSessionBundleFromStoredAccount, + disposeBundle, + finishPreparation, + makeSessionHooks, + registerBundleKillSwitch, + sessionAccountToSessionData, + type SessionBundle, + sessionDataToSessionAccount, +} from '../session-core' +import { + asFetch, + DID, + HANDLE, + makeAccount, + makeDidDoc, + makeMockFetch, + PDS_HOST as PDS_URL, + SERVICE, +} from './mock-fetch' + +function synthDidDoc( + did: string, + pdsUrl: string, +): NonNullable { + return makeDidDoc(pdsUrl, did) +} + +function makeSessionData(overrides: Partial = {}): SessionData { + return { + accessJwt: 'access-jwt', + refreshJwt: 'refresh-jwt', + handle: HANDLE, + did: DID, + email: 'alice@example.com', + emailConfirmed: true, + emailAuthFactor: false, + active: true, + service: 'https://bsky.social', + ...overrides, + } +} + +describe('sessionDataToSessionAccount', () => { + it('returns undefined for a missing session', () => { + expect(sessionDataToSessionAccount(undefined, 'https://bsky.social')).toBe( + undefined, + ) + expect(sessionDataToSessionAccount(null, 'https://bsky.social')).toBe( + undefined, + ) + }) + + it('maps fields for a hosted account (no didDoc)', () => { + const account = sessionDataToSessionAccount( + makeSessionData(), + 'https://bsky.social', + )! + expect(account).toEqual({ + service: 'https://bsky.social/', + did: DID, + handle: HANDLE, + email: 'alice@example.com', + emailConfirmed: true, + emailAuthFactor: false, + refreshJwt: 'refresh-jwt', + accessJwt: 'access-jwt', + signupQueued: false, + active: true, + status: undefined, + pdsUrl: undefined, + isSelfHosted: false, + }) + }) + + it('serializes service as a normalized URL', () => { + const account = sessionDataToSessionAccount( + makeSessionData(), + 'https://bsky.social', + )! + expect(account.service).toBe('https://bsky.social/') + }) + + it('serializes the didDoc PDS endpoint as a normalized URL', () => { + const account = sessionDataToSessionAccount( + makeSessionData({didDoc: synthDidDoc(DID, PDS_URL)}), + 'https://bsky.social', + )! + expect(account.pdsUrl).toBe(`${PDS_URL}/`) + }) + + it('leaves pdsUrl undefined for hosted accounts (no service fallback)', () => { + const account = sessionDataToSessionAccount( + makeSessionData({didDoc: undefined}), + 'https://bsky.social', + )! + expect(account.pdsUrl).toBe(undefined) + }) + + it('retains the stored PDS when a valid didDoc has no PDS service', () => { + const account = sessionDataToSessionAccount( + makeSessionData({didDoc: {id: DID}}), + 'https://bsky.social', + PDS_URL, + )! + expect(account.pdsUrl).toBe(`${PDS_URL}/`) + }) + + it('derives isSelfHosted from the service URL', () => { + const hosted = sessionDataToSessionAccount( + makeSessionData(), + 'https://bsky.social', + )! + expect(hosted.isSelfHosted).toBe(false) + + const selfHosted = sessionDataToSessionAccount( + makeSessionData({service: 'https://pds.example.com'}), + 'https://pds.example.com', + )! + expect(selfHosted.isSelfHosted).toBe(true) + }) + + it('derives signupQueued from the access token scope', () => { + const queued = sessionDataToSessionAccount( + makeSessionData({accessJwt: 'queued-access-jwt'}), + 'https://bsky.social', + )! + expect(queued.signupQueued).toBe(true) + + const notQueued = sessionDataToSessionAccount( + makeSessionData(), + 'https://bsky.social', + )! + expect(notQueued.signupQueued).toBe(false) + }) + + it('coerces missing email flags to false', () => { + const account = sessionDataToSessionAccount( + makeSessionData({ + email: undefined, + emailConfirmed: undefined, + emailAuthFactor: undefined, + }), + 'https://bsky.social', + )! + expect(account.email).toBe(undefined) + expect(account.emailConfirmed).toBe(false) + expect(account.emailAuthFactor).toBe(false) + }) + + it('preserves the exact SessionAccount field order', () => { + /* + * Byte-stability guard: the reducer's JSON.stringify fast path and the + * session test snapshots depend on this exact persisted key order. + */ + const account = sessionDataToSessionAccount( + makeSessionData({didDoc: synthDidDoc(DID, PDS_URL)}), + 'https://bsky.social', + )! + const golden: SessionAccount = { + service: 'https://bsky.social/', + did: DID, + handle: HANDLE, + email: 'alice@example.com', + emailConfirmed: true, + emailAuthFactor: false, + refreshJwt: 'refresh-jwt', + accessJwt: 'access-jwt', + signupQueued: false, + active: true, + status: undefined, + pdsUrl: `${PDS_URL}/`, + isSelfHosted: false, + } + expect(Object.keys(account)).toEqual(Object.keys(golden)) + expect(JSON.stringify(account)).toBe(JSON.stringify(golden)) + }) +}) + +describe('sessionAccountToSessionData', () => { + const baseAccount: SessionAccount = { + service: 'https://bsky.social/', + did: DID, + handle: HANDLE, + email: 'alice@example.com', + emailConfirmed: true, + emailAuthFactor: false, + refreshJwt: 'refresh-jwt', + accessJwt: 'access-jwt', + signupQueued: false, + active: true, + status: undefined, + pdsUrl: undefined, + isSelfHosted: false, + } + + it('maps fields with empty-string token fallbacks and active default', () => { + const data = sessionAccountToSessionData({ + ...baseAccount, + accessJwt: undefined, + refreshJwt: undefined, + active: undefined, + }) + expect(data.accessJwt).toBe('') + expect(data.refreshJwt).toBe('') + expect(data.active).toBe(true) + expect(data.did).toBe(DID) + expect(data.handle).toBe(HANDLE) + expect(data.service).toBe('https://bsky.social/') + }) + + it('omits didDoc when the account has no stored pdsUrl', () => { + const data = sessionAccountToSessionData(baseAccount) + expect('didDoc' in data).toBe(false) + }) + + it('does not synthesize protocol data from a stored pdsUrl', () => { + const data = sessionAccountToSessionData({ + ...baseAccount, + pdsUrl: `${PDS_URL}/`, + }) + expect('didDoc' in data).toBe(false) + }) + + it('round-trips account -> SessionData -> account preserving all fields', () => { + const withPds: SessionAccount = { + ...baseAccount, + pdsUrl: `${PDS_URL}/`, + } + for (const account of [baseAccount, withPds]) { + const data = sessionAccountToSessionData(account) + const roundTripped = sessionDataToSessionAccount( + data, + account.service, + account.pdsUrl, + )! + expect(roundTripped).toEqual(account) + expect(JSON.stringify(roundTripped)).toBe(JSON.stringify(account)) + } + }) + + it('round-trips signupQueued via the access token scope', () => { + const queued: SessionAccount = { + ...baseAccount, + accessJwt: 'queued-access-jwt', + signupQueued: true, + } + const roundTripped = sessionDataToSessionAccount( + sessionAccountToSessionData(queued), + queued.service, + )! + expect(roundTripped.signupQueued).toBe(true) + expect(roundTripped).toEqual(queued) + }) + + it('round-trips a self-hosted account', () => { + const selfHosted: SessionAccount = { + ...baseAccount, + service: 'https://pds.example.com/', + pdsUrl: 'https://pds.example.com/', + isSelfHosted: true, + } + const roundTripped = sessionDataToSessionAccount( + sessionAccountToSessionData(selfHosted), + selfHosted.service, + selfHosted.pdsUrl, + )! + expect(roundTripped).toEqual(selfHosted) + }) +}) + +describe('createSessionBundleFromStoredAccount', () => { + it('builds a bridge agent over one session', () => { + const result = createSessionBundleFromStoredAccount( + makeAccount(), + jest.fn(), + )! + + /* the agent reads its identity straight through the shared session */ + expect(result.bundle.agent.session?.accessJwt).toBe('access-jwt') + expect(result.bundle.agent.did).toBe(DID) + expect(result.bundle.agent.sessionManager.session).toBe( + result.bundle.agent.session, + ) + expect(result.bundle.service.toString()).toBe(`${SERVICE}/`) + disposeBundle(result.bundle) + /* disposal detaches the agent from the session */ + expect(result.bundle.agent.session).toBe(undefined) + }) + + it('disposes a bundle rejected by the activation guard', async () => { + const onSessionChange = jest.fn() + let rejectedBundle: SessionBundle | undefined + const result = createSessionBundleFromStoredAccount( + makeAccount(), + onSessionChange, + bundle => { + rejectedBundle = bundle + return false + }, + ) + + expect(result).toBeUndefined() + await expect( + rejectedBundle!.session.fetchHandler('/xrpc/test', {}), + ).rejects.toThrow('session disposed') + expect(onSessionChange).not.toHaveBeenCalled() + }) +}) + +describe('makeSessionHooks arm-latch + event mapping', () => { + /* + * The hooks read neither `this` (the PasswordSession) nor their data + * argument, so we invoke them with empty stand-ins cast to the declared + * parameter types. This keeps the test focused on the arm-latch + event + * mapping. + */ + const fakeSession = {} as PasswordSession + const fakeData = {} as SessionData + + function setup() { + const onSessionChange = + jest.fn< + ( + bundle: SessionBundle, + did: string, + event: AtpSessionEvent, + sessionData?: SessionData, + ) => void + >() + /* the hook only passes this through by identity; a stub bundle suffices */ + const bundle = {} as SessionBundle + const hooks = makeSessionHooks({ + onSessionChange, + getBundle: () => bundle, + getDid: () => DID, + }) + return {onSessionChange, bundle, hooks} + } + + it('swallows events before arm()', () => { + const {onSessionChange, hooks} = setup() + void hooks.onUpdated?.call(fakeSession, fakeData) + expect(onSessionChange).not.toHaveBeenCalled() + }) + + it("maps onUpdated -> 'update' after arm(), passing the bundle + payload through", () => { + const {onSessionChange, bundle, hooks} = setup() + hooks.arm() + void hooks.onUpdated?.call(fakeSession, fakeData) + expect(onSessionChange).toHaveBeenCalledTimes(1) + expect(onSessionChange.mock.calls[0][0]).toBe(bundle) + expect(onSessionChange.mock.calls[0][1]).toBe(DID) + expect(onSessionChange.mock.calls[0][2]).toBe('update') + /* the fresh SessionData the library delivers is threaded through verbatim */ + expect(onSessionChange.mock.calls[0][3]).toBe(fakeData) + }) + + it("maps onDeleted -> 'expired' after arm()", () => { + const {onSessionChange, hooks} = setup() + hooks.arm() + void hooks.onDeleted?.call(fakeSession, fakeData) + expect(onSessionChange.mock.calls[0][2]).toBe('expired') + }) + + it("maps onUpdateFailure -> 'network-error' after arm()", () => { + const {onSessionChange, hooks} = setup() + hooks.arm() + void hooks.onUpdateFailure?.call( + fakeSession, + fakeData, + {} as Parameters>[1], + ) + expect(onSessionChange.mock.calls[0][2]).toBe('network-error') + }) + + it("threads the dying session payload on the 'expired' path", () => { + /* + * onDeleted maps to 'expired' AND threads the dying SessionData through + * (the library hands onDeleted the session being destroyed, before it nulls + * its internal state). The provider reads the dying refreshJwt from this + * payload to drive the cross-tab expiry rescue. The provider still guards + * `refreshedAccount` on `event === 'update' && sessionData`, so the payload + * on 'expired' does NOT produce a refreshedAccount (reducer still clears + * tokens + logs out when no rescue applies) - see the provider test below. + */ + const {onSessionChange, hooks} = setup() + hooks.arm() + void hooks.onDeleted?.call(fakeSession, fakeData) + expect(onSessionChange.mock.calls[0][2]).toBe('expired') + expect(onSessionChange.mock.calls[0][3]).toBe(fakeData) + }) +}) + +/* + * The exact derivation from the provider's onSessionChange (index.tsx). Pinned + * here because payload threading and this mapping together read tokens from + * the delivered payload on 'update' and force + * undefined on the drop paths so the reducer logs the user out. + */ +function deriveRefreshedAccount( + event: AtpSessionEvent, + sessionData?: SessionData, +): SessionAccount | undefined { + return event === 'update' && sessionData + ? sessionDataToSessionAccount(sessionData, sessionData.service) + : undefined +} + +/* + * `PasswordSession` fires onUpdated with the fresh session BEFORE committing it + * internally, so the live getter is still stale at hook time. + */ +describe('session-hook payload threading (pre-commit ordering)', () => { + it('delivers the NEW tokens via the payload even though the live getter is still pre-commit stale', async () => { + const fetchMock = makeMockFetch() + let session!: PasswordSession + let liveGetterAtHookTime: SessionAccount | undefined + let refreshedAccountAtHookTime: SessionAccount | undefined + const onSessionChange = jest.fn( + ( + _bundle: SessionBundle, + _did: string, + event: AtpSessionEvent, + sessionData?: SessionData, + ) => { + /* Capture the live getter to demonstrate its pre-commit state. */ + liveGetterAtHookTime = sessionDataToSessionAccount( + session.session, + session.session.service, + ) + /* Derive fresh data from the delivered payload. */ + refreshedAccountAtHookTime = deriveRefreshedAccount(event, sessionData) + }, + ) + const hooks = makeSessionHooks({ + onSessionChange, + getBundle: () => ({}) as SessionBundle, + getDid: () => DID, + }) + session = new PasswordSession(sessionAccountToSessionData(makeAccount()), { + ...hooks, + fetch: asFetch(fetchMock), + }) + hooks.arm() + + await session.refresh() + + /* at hook time the live getter still held the previous tokens */ + expect(liveGetterAtHookTime?.accessJwt).toBe('access-jwt') + /* the payload already contains the fresh tokens */ + expect(refreshedAccountAtHookTime?.accessJwt).toBe('access-jwt-2') + expect(refreshedAccountAtHookTime?.refreshJwt).toBe('refresh-jwt-2') + /* and the session does eventually commit those same tokens */ + expect(session.session.accessJwt).toBe('access-jwt-2') + }) + + it("yields refreshedAccount === undefined on the 'expired' path (forces logout)", async () => { + const fetchMock = makeMockFetch({ + 'com.atproto.server.refreshSession': () => + new Response( + JSON.stringify({error: 'ExpiredToken', message: 'Token expired'}), + {status: 400, headers: {'content-type': 'application/json'}}, + ), + }) + let refreshedAccountAtHookTime: SessionAccount | undefined = makeAccount() + let observedEvent: AtpSessionEvent | undefined + let observedSessionData: SessionData | undefined + const onSessionChange = jest.fn( + ( + _bundle: SessionBundle, + _did: string, + event: AtpSessionEvent, + sessionData?: SessionData, + ) => { + observedEvent = event + observedSessionData = sessionData + refreshedAccountAtHookTime = deriveRefreshedAccount(event, sessionData) + }, + ) + const hooks = makeSessionHooks({ + onSessionChange, + getBundle: () => ({}) as SessionBundle, + getDid: () => DID, + }) + const session = new PasswordSession( + sessionAccountToSessionData(makeAccount()), + {...hooks, fetch: asFetch(fetchMock)}, + ) + hooks.arm() + + await expect(session.refresh()).rejects.toBeDefined() + + expect(observedEvent).toBe('expired') + /* + * The dying SessionData IS threaded on 'expired' (its refreshJwt drives the + * provider's cross-tab rescue), but it does NOT become a refreshedAccount: + * deriveRefreshedAccount only maps the 'update' path, so the reducer still + * sees `undefined` and logs out when no rescue applies. + */ + expect(observedSessionData?.refreshJwt).toBe('refresh-jwt') + expect(refreshedAccountAtHookTime).toBe(undefined) + }) +}) + +/* + * `PasswordSession` exposes no local + * destroy, so disposeBundle neutralizes the session by tripping the flag inside + * the injected fetch - after disposal every request (direct or auto-refresh, + * which shares this same captured fetch) throws before touching the network. + */ +describe('disposeBundle kill-switch', () => { + it('the injected fetch throws after disposeBundle', () => { + const hooks = makeSessionHooks({ + onSessionChange: jest.fn(), + getBundle: () => ({}) as SessionBundle, + getDid: () => DID, + }) + /* the injected fetch is the kill-switch wrapper makeSessionHooks bakes in */ + const injectedFetch = hooks.fetch! + + /* + * A live session is required for disposeBundle to act (it early-returns on + * a null/destroyed session). + */ + const session = new PasswordSession( + sessionAccountToSessionData(makeAccount()), + {...hooks}, + ) + const bundle = buildBundle(session) + registerBundleKillSwitch(bundle, hooks.kill) + + /* + * Before disposal the wrapper does NOT throw synchronously - it delegates + * to the async networkAwareFetch and returns a promise. Swallow that + * promise's rejection (the real network is unavailable under jest); we only + * care that no synchronous throw happened here. + */ + const pending = injectedFetch('https://bsky.social/xrpc/x') + expect(pending).toBeInstanceOf(Promise) + void pending.catch(() => {}) + + disposeBundle(bundle) + + /* after disposal every call through the injected fetch throws */ + expect(() => injectedFetch('https://bsky.social/xrpc/x')).toThrow( + 'session disposed', + ) + }) +}) + +/* + * PasswordSession lifecycle over a mocked fetch, covering the resume fast path + * and the hooks that makeSessionHooks maps into reducer events. + */ +describe('PasswordSession lifecycle over mocked fetch', () => { + it('resume fast path: constructing does not hit the network', () => { + const fetchMock = makeMockFetch() + /* not expired -> new PasswordSession(...) with no refresh */ + void new PasswordSession(sessionAccountToSessionData(makeAccount()), { + fetch: asFetch(fetchMock), + }) + expect(fetchMock.mock.calls.length).toBe(0) + }) + + it('resume network path fires onUpdated with fresh tokens', async () => { + const fetchMock = makeMockFetch() + const onUpdated = + jest.fn>() + const session = await PasswordSession.resume( + sessionAccountToSessionData(makeAccount()), + {fetch: asFetch(fetchMock), onUpdated}, + ) + expect(onUpdated).toHaveBeenCalledTimes(1) + expect(session.session.accessJwt).toBe('access-jwt-2') + }) + + it('onDeleted fires when refresh returns a declared invalid-token error', async () => { + const onDeleted = + jest.fn>() + const onUpdated = + jest.fn>() + const fetchMock = makeMockFetch({ + 'com.atproto.server.refreshSession': () => + new Response( + JSON.stringify({error: 'ExpiredToken', message: 'Token expired'}), + {status: 400, headers: {'content-type': 'application/json'}}, + ), + }) + const session = new PasswordSession( + sessionAccountToSessionData(makeAccount()), + {fetch: asFetch(fetchMock), onDeleted, onUpdated}, + ) + await expect(session.refresh()).rejects.toBeDefined() + expect(onDeleted).toHaveBeenCalledTimes(1) + expect(onUpdated).not.toHaveBeenCalled() + }) + + it('onUpdateFailure fires on a transient (500) refresh error, session preserved', async () => { + const onDeleted = + jest.fn>() + const onUpdateFailure = + jest.fn>() + const fetchMock = makeMockFetch({ + 'com.atproto.server.refreshSession': () => + new Response(JSON.stringify({error: 'InternalServerError'}), { + status: 500, + headers: {'content-type': 'application/json'}, + }), + }) + const session = new PasswordSession( + sessionAccountToSessionData(makeAccount()), + { + fetch: asFetch(fetchMock), + onDeleted, + onUpdateFailure, + }, + ) + await session.refresh() + expect(onUpdateFailure).toHaveBeenCalledTimes(1) + expect(onDeleted).not.toHaveBeenCalled() + /* session data is preserved (still the original tokens) */ + expect(session.session.accessJwt).toBe('access-jwt') + }) +}) + +/* + * A refresh through a bundle's armed hooks: the app has no `refreshSession` api + * of its own, so every rotation the reducer sees originates here, in + * `PasswordSession.refresh()`. On success the armed hooks dispatch exactly one + * 'update' event and the post-refresh snapshot carries the rotated tokens; + * rejections propagate to the caller. + */ +describe('PasswordSession.refresh through armed hooks', () => { + it('refresh() resolves updated data and the armed hooks dispatch exactly one update', async () => { + const fetchMock = makeMockFetch() + const onSessionChange = + jest.fn< + (bundle: SessionBundle, did: string, event: AtpSessionEvent) => void + >() + const bundle = {} as SessionBundle + const hooks = makeSessionHooks({ + onSessionChange, + getBundle: () => bundle, + getDid: () => DID, + }) + /* + * makeSessionHooks bakes in networkAwareFetch (the real global fetch); + * override it with the mock while keeping the arm-latched callbacks (they + * close over the same `armed` flag, so hooks.arm() below still applies). + */ + const session = new PasswordSession( + sessionAccountToSessionData(makeAccount()), + {...hooks, fetch: asFetch(fetchMock)}, + ) + hooks.arm() + + await session.refresh() + + /* the refreshed tokens are live on the session */ + expect(session.session.accessJwt).toBe('access-jwt-2') + /* exactly one 'update' event reached the reducer via the armed hooks */ + expect(onSessionChange).toHaveBeenCalledTimes(1) + expect(onSessionChange.mock.calls[0][2]).toBe('update') + + /* the post-refresh SessionAccount snapshot carries the rotated tokens */ + const snapshot = sessionDataToSessionAccount( + session.session, + session.session.service, + )! + expect(snapshot.accessJwt).toBe('access-jwt-2') + expect(snapshot.refreshJwt).toBe('refresh-jwt-2') + }) + + it('propagates a rejection from refresh() (invalid session)', async () => { + const fetchMock = makeMockFetch({ + 'com.atproto.server.refreshSession': () => + new Response( + JSON.stringify({error: 'ExpiredToken', message: 'Token expired'}), + {status: 400, headers: {'content-type': 'application/json'}}, + ), + }) + const session = new PasswordSession( + sessionAccountToSessionData(makeAccount()), + {fetch: asFetch(fetchMock)}, + ) + await expect(session.refresh()).rejects.toBeDefined() + }) +}) + +/** + * Load a fresh factory graph whose network leaf captures `fetch`. + * + * `session-core`'s network leaf reads `globalThis.fetch` at module load, and + * that captured fetch is what `PasswordSession`'s auto-refresh routes through, + * so the module has to be re-required after the override is in place. + */ +async function withFreshFactory( + fetch: typeof globalThis.fetch, + run: (core: typeof import('../session-core')) => Promise, +) { + const realFetch = globalThis.fetch + globalThis.fetch = fetch + try { + await jest.isolateModulesAsync(async () => { + const core = + require('../session-core') as typeof import('../session-core') + await run(core) + }) + } finally { + globalThis.fetch = realFetch + } +} + +/* + * The resume/login factories must snapshot the returned account after + * the prep awaits, not before. A 401 during prep triggers PasswordSession's + * internal auto-refresh (rotating BOTH tokens and firing an onUpdated the + * disarmed latch drops); an early snapshot would persist the stale refreshJwt, + * which is dead on the next cold start. + * + * We simulate the mid-prep rotation by capturing the bundle from the mocked + * `configureModerationForAccount` and making the mocked + * `prefetchAgeAssuranceServerData` (a genuine prep await in each factory) run a + * real `session.refresh()`. The factory itself is re-required inside + * `jest.isolateModulesAsync` AFTER overriding `globalThis.fetch`, because + * the network leaf captures `globalThis.fetch` at module load - and that + * captured fetch is what PasswordSession's auto-refresh routes through. + */ +describe('factory account snapshot after preparation', () => { + beforeEach(() => { + mockConfigureModerationForAccount.mockReset() + mockPrefetchAgeAssuranceServerData.mockReset() + }) + + it('resume: returned account carries the tokens rotated DURING prep', async () => { + /* + * A refresh mid-prep rotates the session to access-jwt-2/refresh-jwt-2. + * `valid-access-jwt` decodes as non-expired, so resume() takes the sync + * fast path and the only refresh is the one prep triggers. The bundle is + * captured from the (synchronous) moderation call, and the rotation is + * injected into the awaited AA prefetch. + */ + let capturedAgent: BskyAppAgent | undefined + mockConfigureModerationForAccount.mockImplementationOnce( + (agent: unknown) => { + capturedAgent = agent as BskyAppAgent + }, + ) + mockPrefetchAgeAssuranceServerData.mockImplementationOnce(async () => { + /* routes through the bridge into the shared PasswordSession's refresh */ + await capturedAgent!.sessionManager.refreshSession() + }) + const fetchMock = makeMockFetch() + + await withFreshFactory(asFetch(fetchMock), async core => { + const {account, bundle} = await core.createSessionBundleAndResume( + makeAccount({accessJwt: 'valid-access-jwt'}), + jest.fn(), + ) + /* the moderation prep step ran the refresh */ + expect(mockConfigureModerationForAccount).toHaveBeenCalledTimes(1) + /* the RETURNED account carries the POST-prep (rotated) tokens */ + expect(account.accessJwt).toBe('access-jwt-2') + expect(account.refreshJwt).toBe('refresh-jwt-2') + /* and it matches the session's committed state */ + expect(bundle.session.session.accessJwt).toBe('access-jwt-2') + }) + }) + + it('resume: returned account falls back to the stored account when the fast path yields no live token change', async () => { + /* + * With no mid-prep refresh, the snapshot still reflects the valid stored + * tokens. + */ + mockConfigureModerationForAccount.mockReturnValueOnce(undefined) + const fetchMock = makeMockFetch() + + await withFreshFactory(asFetch(fetchMock), async core => { + const {account} = await core.createSessionBundleAndResume( + makeAccount({accessJwt: 'valid-access-jwt'}), + jest.fn(), + ) + expect(account.accessJwt).toBe('valid-access-jwt') + expect(account.refreshJwt).toBe('refresh-jwt') + }) + }) +}) + +/* + * Preparation is the window between building the bundle and arming its hooks. + * The session is live but its events are swallowed, so a session that dies in + * here would otherwise leave the app holding a bundle that looks signed in and + * can only make unauthenticated requests, with nothing left to log it out. + * Both failure modes must therefore dispose the bundle and reject. + */ +describe('a session destroyed or rejected during preparation', () => { + beforeEach(() => { + mockConfigureModerationForAccount.mockReset() + mockPrefetchAgeAssuranceServerData.mockReset() + }) + + it('resume: rejects with the revoked-during-prep error and disposes the bundle', async () => { + /* + * A revoked refresh token: the session's own refresh during prep gets a + * declared invalid-token error, which destroys it. `logout()` is the only + * way to drive a session to `destroyed` from outside, and it takes the same + * `deleteSession` -> onDeleted -> destroyed path the real 401 rescue does. + */ + let capturedAgent: BskyAppAgent | undefined + mockConfigureModerationForAccount.mockImplementationOnce( + (agent: unknown) => { + capturedAgent = agent as BskyAppAgent + }, + ) + mockPrefetchAgeAssuranceServerData.mockImplementationOnce(async () => { + await capturedAgent!.logout() + }) + const fetchMock = makeMockFetch() + + await withFreshFactory(asFetch(fetchMock), async core => { + /* + * The clean error, not PasswordSession's opaque `Logged out` getter + * throw, which is what a naive `snapshot()` on a destroyed session would + * surface. + */ + await expect( + core.createSessionBundleAndResume( + makeAccount({accessJwt: 'valid-access-jwt'}), + jest.fn(), + ), + ).rejects.toThrow('Session was revoked while it was being prepared') + + /* the bundle the caller never received reads as logged out */ + expect(capturedAgent!.session).toBe(undefined) + }) + }) + + it('resume: a prep rejection propagates and disposes the bundle', async () => { + let capturedAgent: BskyAppAgent | undefined + mockConfigureModerationForAccount.mockImplementationOnce( + (agent: unknown) => { + capturedAgent = agent as BskyAppAgent + }, + ) + mockPrefetchAgeAssuranceServerData.mockImplementationOnce(() => + Promise.reject(new Error('prefetch blew up')), + ) + const fetchMock = makeMockFetch() + + await withFreshFactory(asFetch(fetchMock), async core => { + await expect( + core.createSessionBundleAndResume( + makeAccount({accessJwt: 'valid-access-jwt'}), + jest.fn(), + ), + ).rejects.toThrow('prefetch blew up') + + /* the still-live session was disposed rather than left refreshing */ + expect(capturedAgent!.session).toBe(undefined) + }) + }) + + it('finishPreparation disposes and rethrows without running the snapshot', async () => { + const hooks = makeSessionHooks({ + onSessionChange: jest.fn(), + getBundle: () => bundle, + getDid: () => DID, + }) + const session = new PasswordSession( + sessionAccountToSessionData(makeAccount()), + {...hooks, fetch: asFetch(makeMockFetch())}, + ) + const bundle = buildBundle(session) + registerBundleKillSwitch(bundle, hooks.kill) + const snapshot = jest.fn(() => 'never') + + await expect( + finishPreparation(bundle, Promise.reject(new Error('nope')), snapshot), + ).rejects.toThrow('nope') + + expect(snapshot).not.toHaveBeenCalled() + expect(bundle.agent.session).toBe(undefined) + }) +}) + +/* + * A hook must never throw: PasswordSession awaits its hooks inside the + * assignment to its internal session promise, so an escaping throw would leave + * that promise permanently rejected - every later request fails, while the + * session is never marked destroyed, so disposeBundle cannot even see that the + * bundle is dead. + */ +describe('a throwing onSessionChange does not brick the session', () => { + beforeEach(() => { + mockLoggerError.mockClear() + }) + + it('reports through logger.error and leaves the session usable', async () => { + const fetchMock = makeMockFetch() + const onSessionChange = jest.fn(() => { + throw new Error('reducer side effect exploded') + }) + let bundle!: SessionBundle + const hooks = makeSessionHooks({ + onSessionChange, + getBundle: () => bundle, + getDid: () => DID, + }) + const session = new PasswordSession( + sessionAccountToSessionData(makeAccount()), + {...hooks, fetch: asFetch(fetchMock)}, + ) + bundle = buildBundle(session) + hooks.arm() + + await session.refresh() + + expect(onSessionChange).toHaveBeenCalledTimes(1) + expect(mockLoggerError).toHaveBeenCalledTimes(1) + expect(mockLoggerError.mock.calls[0][1]).toEqual({ + message: "session: onSessionChange threw for a 'update' event", + }) + + /* the session still committed the rotation, and can still refresh again */ + expect(session.session.accessJwt).toBe('access-jwt-2') + await expect(session.refresh()).resolves.toBeDefined() + await expect( + session.fetchHandler('/xrpc/app.bsky.actor.getProfile', {}), + ).resolves.toBeDefined() + }) +}) diff --git a/src/state/session/__tests__/session-test.ts b/src/state/session/__tests__/session-test.ts index eebcfcf8d2..9cb2db6a59 100644 --- a/src/state/session/__tests__/session-test.ts +++ b/src/state/session/__tests__/session-test.ts @@ -1,8 +1,10 @@ -import {AtpAgent} from '@atproto/api' +import {type AtpAgent} from '@atproto/api' +import {type SessionData} from '@atproto/lex-password-session' import {describe, expect, it, jest} from '@jest/globals' -import {agentToSessionAccountOrThrow} from '../agent' import {type Action, getInitialState, reducer, type State} from '../reducer' +import {sessionDataToSessionAccount} from '../session-core' +import {type SessionAccount} from '../types' jest.mock('jwt-decode', () => ({ jwtDecode(_token: string) { @@ -20,6 +22,46 @@ jest.mock('#/lib/notifications/notifications', () => ({ return Promise.resolve() }, })) +/* + * The logout and account-removal reducer cases fire a push-token side effect + * whose first step, `createTemporaryAgentsAndResume`, builds real `AtpAgent`s + * and resumes them over the real network. Under jest that request outlives the + * suite: it rejects after teardown, and the resulting `logger.error` reaches + * for `nanoid` in an environment that no longer has it, failing whichever suite + * happens to be running at that moment. Stubbing the module keeps the side + * effect synchronous and offline. + */ +jest.mock('../util', () => ({ + createTemporaryAgentsAndResume: () => Promise.resolve([]), +})) + +// Reuse a bundle within each test: session events are scoped by bundle identity. +function makeBundle(service: string) { + return {service: new URL(service)} +} + +function makeAccount( + service: string, + session: { + active: boolean + did: string + handle: string + accessJwt: string + refreshJwt: string + email?: string + emailAuthFactor?: boolean + emailConfirmed?: boolean + }, +): SessionAccount { + const account = sessionDataToSessionAccount( + session as unknown as SessionData, + service, + ) + if (!account) { + throw new Error('Expected an account') + } + return account +} describe('session', () => { it('can log in and out', () => { @@ -27,8 +69,8 @@ describe('session', () => { expect(printState(state)).toMatchInlineSnapshot(` { "accounts": [], - "currentAgentState": { - "agent": { + "currentBundleState": { + "bundle": { "service": "https://public.api.bsky.app/", }, "did": undefined, @@ -37,22 +79,22 @@ describe('session', () => { } `) - const agent = new AtpAgent({service: 'https://alice.com'}) - agent.sessionManager.session = { + const aliceBundle = makeBundle('https://alice.com') + const aliceAccount = makeAccount('https://alice.com', { active: true, did: 'alice-did', handle: 'alice.test', accessJwt: 'alice-access-jwt-1', refreshJwt: 'alice-refresh-jwt-1', - } + }) state = run(state, [ { type: 'switched-to-account', - newAgent: agent, - newAccount: agentToSessionAccountOrThrow(agent), + newBundle: aliceBundle, + newAccount: aliceAccount, }, ]) - expect(state.currentAgentState.did).toBe('alice-did') + expect(state.currentBundleState.did).toBe('alice-did') expect(state.accounts.length).toBe(1) expect(state.accounts[0].did).toBe('alice-did') expect(state.accounts[0].accessJwt).toBe('alice-access-jwt-1') @@ -76,8 +118,8 @@ describe('session', () => { "status": undefined, }, ], - "currentAgentState": { - "agent": { + "currentBundleState": { + "bundle": { "service": "https://alice.com/", }, "did": "alice-did", @@ -92,7 +134,7 @@ describe('session', () => { }, ]) // Should keep the account but clear out the tokens. - expect(state.currentAgentState.did).toBe(undefined) + expect(state.currentBundleState.did).toBe(undefined) expect(state.accounts.length).toBe(1) expect(state.accounts[0].did).toBe('alice-did') expect(state.accounts[0].accessJwt).toBe(undefined) @@ -116,8 +158,8 @@ describe('session', () => { "status": undefined, }, ], - "currentAgentState": { - "agent": { + "currentBundleState": { + "bundle": { "service": "https://public.api.bsky.app/", }, "did": undefined, @@ -130,26 +172,26 @@ describe('session', () => { it('switches to the latest account, stores all of them', () => { let state = getInitialState([]) - const agent1 = new AtpAgent({service: 'https://alice.com'}) - agent1.sessionManager.session = { + const aliceBundle = makeBundle('https://alice.com') + const aliceAccount = makeAccount('https://alice.com', { active: true, did: 'alice-did', handle: 'alice.test', accessJwt: 'alice-access-jwt-1', refreshJwt: 'alice-refresh-jwt-1', - } + }) state = run(state, [ { // Switch to Alice. type: 'switched-to-account', - newAgent: agent1, - newAccount: agentToSessionAccountOrThrow(agent1), + newBundle: aliceBundle, + newAccount: aliceAccount, }, ]) expect(state.accounts.length).toBe(1) expect(state.accounts[0].did).toBe('alice-did') - expect(state.currentAgentState.did).toBe('alice-did') - expect(state.currentAgentState.agent).toBe(agent1) + expect(state.currentBundleState.did).toBe('alice-did') + expect(state.currentBundleState.bundle).toBe(aliceBundle) expect(printState(state)).toMatchInlineSnapshot(` { "accounts": [ @@ -169,8 +211,8 @@ describe('session', () => { "status": undefined, }, ], - "currentAgentState": { - "agent": { + "currentBundleState": { + "bundle": { "service": "https://alice.com/", }, "did": "alice-did", @@ -179,28 +221,28 @@ describe('session', () => { } `) - const agent2 = new AtpAgent({service: 'https://bob.com'}) - agent2.sessionManager.session = { + const bobBundle = makeBundle('https://bob.com') + const bobAccount = makeAccount('https://bob.com', { active: true, did: 'bob-did', handle: 'bob.test', accessJwt: 'bob-access-jwt-1', refreshJwt: 'bob-refresh-jwt-1', - } + }) state = run(state, [ { // Switch to Bob. type: 'switched-to-account', - newAgent: agent2, - newAccount: agentToSessionAccountOrThrow(agent2), + newBundle: bobBundle, + newAccount: bobAccount, }, ]) expect(state.accounts.length).toBe(2) // Bob should float upwards. expect(state.accounts[0].did).toBe('bob-did') expect(state.accounts[1].did).toBe('alice-did') - expect(state.currentAgentState.did).toBe('bob-did') - expect(state.currentAgentState.agent).toBe(agent2) + expect(state.currentBundleState.did).toBe('bob-did') + expect(state.currentBundleState.bundle).toBe(bobBundle) expect(printState(state)).toMatchInlineSnapshot(` { "accounts": [ @@ -235,8 +277,8 @@ describe('session', () => { "status": undefined, }, ], - "currentAgentState": { - "agent": { + "currentBundleState": { + "bundle": { "service": "https://bob.com/", }, "did": "bob-did", @@ -245,28 +287,28 @@ describe('session', () => { } `) - const agent3 = new AtpAgent({service: 'https://alice.com'}) - agent3.sessionManager.session = { + const aliceBundle2 = makeBundle('https://alice.com') + const aliceAccount2 = makeAccount('https://alice.com', { active: true, did: 'alice-did', handle: 'alice-updated.test', accessJwt: 'alice-access-jwt-2', refreshJwt: 'alice-refresh-jwt-2', - } + }) state = run(state, [ { // Switch back to Alice. type: 'switched-to-account', - newAgent: agent3, - newAccount: agentToSessionAccountOrThrow(agent3), + newBundle: aliceBundle2, + newAccount: aliceAccount2, }, ]) expect(state.accounts.length).toBe(2) // Alice should float upwards. expect(state.accounts[0].did).toBe('alice-did') expect(state.accounts[0].handle).toBe('alice-updated.test') - expect(state.currentAgentState.did).toBe('alice-did') - expect(state.currentAgentState.agent).toBe(agent3) + expect(state.currentBundleState.did).toBe('alice-did') + expect(state.currentBundleState.bundle).toBe(aliceBundle2) expect(printState(state)).toMatchInlineSnapshot(` { "accounts": [ @@ -301,8 +343,8 @@ describe('session', () => { "status": undefined, }, ], - "currentAgentState": { - "agent": { + "currentBundleState": { + "bundle": { "service": "https://alice.com/", }, "did": "alice-did", @@ -311,26 +353,26 @@ describe('session', () => { } `) - const agent4 = new AtpAgent({service: 'https://jay.com'}) - agent4.sessionManager.session = { + const jayBundle = makeBundle('https://jay.com') + const jayAccount = makeAccount('https://jay.com', { active: true, did: 'jay-did', handle: 'jay.test', accessJwt: 'jay-access-jwt-1', refreshJwt: 'jay-refresh-jwt-1', - } + }) state = run(state, [ { // Switch to Jay. type: 'switched-to-account', - newAgent: agent4, - newAccount: agentToSessionAccountOrThrow(agent4), + newBundle: jayBundle, + newAccount: jayAccount, }, ]) expect(state.accounts.length).toBe(3) expect(state.accounts[0].did).toBe('jay-did') - expect(state.currentAgentState.did).toBe('jay-did') - expect(state.currentAgentState.agent).toBe(agent4) + expect(state.currentBundleState.did).toBe('jay-did') + expect(state.currentBundleState.bundle).toBe(jayBundle) expect(printState(state)).toMatchInlineSnapshot(` { "accounts": [ @@ -380,8 +422,8 @@ describe('session', () => { "status": undefined, }, ], - "currentAgentState": { - "agent": { + "currentBundleState": { + "bundle": { "service": "https://jay.com/", }, "did": "jay-did", @@ -397,7 +439,7 @@ describe('session', () => { }, ]) expect(state.accounts.length).toBe(3) - expect(state.currentAgentState.did).toBe(undefined) + expect(state.currentBundleState.did).toBe(undefined) // All tokens should be gone. expect(state.accounts[0].accessJwt).toBe(undefined) expect(state.accounts[0].refreshJwt).toBe(undefined) @@ -454,8 +496,8 @@ describe('session', () => { "status": undefined, }, ], - "currentAgentState": { - "agent": { + "currentBundleState": { + "bundle": { "service": "https://public.api.bsky.app/", }, "did": undefined, @@ -468,25 +510,25 @@ describe('session', () => { it('can log back in after logging out', () => { let state = getInitialState([]) - const agent1 = new AtpAgent({service: 'https://alice.com'}) - agent1.sessionManager.session = { + const aliceBundle = makeBundle('https://alice.com') + const aliceAccount = makeAccount('https://alice.com', { active: true, did: 'alice-did', handle: 'alice.test', accessJwt: 'alice-access-jwt-1', refreshJwt: 'alice-refresh-jwt-1', - } + }) state = run(state, [ { type: 'switched-to-account', - newAgent: agent1, - newAccount: agentToSessionAccountOrThrow(agent1), + newBundle: aliceBundle, + newAccount: aliceAccount, }, ]) expect(state.accounts.length).toBe(1) expect(state.accounts[0].accessJwt).toBe('alice-access-jwt-1') expect(state.accounts[0].refreshJwt).toBe('alice-refresh-jwt-1') - expect(state.currentAgentState.did).toBe('alice-did') + expect(state.currentBundleState.did).toBe('alice-did') state = run(state, [ { @@ -496,7 +538,7 @@ describe('session', () => { expect(state.accounts.length).toBe(1) expect(state.accounts[0].accessJwt).toBe(undefined) expect(state.accounts[0].refreshJwt).toBe(undefined) - expect(state.currentAgentState.did).toBe(undefined) + expect(state.currentBundleState.did).toBe(undefined) expect(printState(state)).toMatchInlineSnapshot(` { "accounts": [ @@ -516,8 +558,8 @@ describe('session', () => { "status": undefined, }, ], - "currentAgentState": { - "agent": { + "currentBundleState": { + "bundle": { "service": "https://public.api.bsky.app/", }, "did": undefined, @@ -526,25 +568,25 @@ describe('session', () => { } `) - const agent2 = new AtpAgent({service: 'https://alice.com'}) - agent2.sessionManager.session = { + const aliceBundle2 = makeBundle('https://alice.com') + const aliceAccount2 = makeAccount('https://alice.com', { active: true, did: 'alice-did', handle: 'alice.test', accessJwt: 'alice-access-jwt-2', refreshJwt: 'alice-refresh-jwt-2', - } + }) state = run(state, [ { type: 'switched-to-account', - newAgent: agent2, - newAccount: agentToSessionAccountOrThrow(agent2), + newBundle: aliceBundle2, + newAccount: aliceAccount2, }, ]) expect(state.accounts.length).toBe(1) expect(state.accounts[0].accessJwt).toBe('alice-access-jwt-2') expect(state.accounts[0].refreshJwt).toBe('alice-refresh-jwt-2') - expect(state.currentAgentState.did).toBe('alice-did') + expect(state.currentBundleState.did).toBe('alice-did') expect(printState(state)).toMatchInlineSnapshot(` { "accounts": [ @@ -564,8 +606,8 @@ describe('session', () => { "status": undefined, }, ], - "currentAgentState": { - "agent": { + "currentBundleState": { + "bundle": { "service": "https://alice.com/", }, "did": "alice-did", @@ -578,25 +620,25 @@ describe('session', () => { it('can remove active account', () => { let state = getInitialState([]) - const agent1 = new AtpAgent({service: 'https://alice.com'}) - agent1.sessionManager.session = { + const aliceBundle = makeBundle('https://alice.com') + const aliceAccount = makeAccount('https://alice.com', { active: true, did: 'alice-did', handle: 'alice.test', accessJwt: 'alice-access-jwt-1', refreshJwt: 'alice-refresh-jwt-1', - } + }) state = run(state, [ { type: 'switched-to-account', - newAgent: agent1, - newAccount: agentToSessionAccountOrThrow(agent1), + newBundle: aliceBundle, + newAccount: aliceAccount, }, ]) expect(state.accounts.length).toBe(1) expect(state.accounts[0].accessJwt).toBe('alice-access-jwt-1') expect(state.accounts[0].refreshJwt).toBe('alice-refresh-jwt-1') - expect(state.currentAgentState.did).toBe('alice-did') + expect(state.currentBundleState.did).toBe('alice-did') state = run(state, [ { @@ -605,12 +647,12 @@ describe('session', () => { }, ]) expect(state.accounts.length).toBe(0) - expect(state.currentAgentState.did).toBe(undefined) + expect(state.currentBundleState.did).toBe(undefined) expect(printState(state)).toMatchInlineSnapshot(` { "accounts": [], - "currentAgentState": { - "agent": { + "currentBundleState": { + "bundle": { "service": "https://public.api.bsky.app/", }, "did": undefined, @@ -623,36 +665,36 @@ describe('session', () => { it('can remove inactive account', () => { let state = getInitialState([]) - const agent1 = new AtpAgent({service: 'https://alice.com'}) - agent1.sessionManager.session = { + const aliceBundle = makeBundle('https://alice.com') + const aliceAccount = makeAccount('https://alice.com', { active: true, did: 'alice-did', handle: 'alice.test', accessJwt: 'alice-access-jwt-1', refreshJwt: 'alice-refresh-jwt-1', - } - const agent2 = new AtpAgent({service: 'https://bob.com'}) - agent2.sessionManager.session = { + }) + const bobBundle = makeBundle('https://bob.com') + const bobAccount = makeAccount('https://bob.com', { active: true, did: 'bob-did', handle: 'bob.test', accessJwt: 'bob-access-jwt-1', refreshJwt: 'bob-refresh-jwt-1', - } + }) state = run(state, [ { type: 'switched-to-account', - newAgent: agent1, - newAccount: agentToSessionAccountOrThrow(agent1), + newBundle: aliceBundle, + newAccount: aliceAccount, }, { type: 'switched-to-account', - newAgent: agent2, - newAccount: agentToSessionAccountOrThrow(agent2), + newBundle: bobBundle, + newAccount: bobAccount, }, ]) expect(state.accounts.length).toBe(2) - expect(state.currentAgentState.did).toBe('bob-did') + expect(state.currentBundleState.did).toBe('bob-did') state = run(state, [ { @@ -661,7 +703,7 @@ describe('session', () => { }, ]) expect(state.accounts.length).toBe(1) - expect(state.currentAgentState.did).toBe('bob-did') + expect(state.currentBundleState.did).toBe('bob-did') expect(printState(state)).toMatchInlineSnapshot(` { "accounts": [ @@ -681,8 +723,8 @@ describe('session', () => { "status": undefined, }, ], - "currentAgentState": { - "agent": { + "currentBundleState": { + "bundle": { "service": "https://bob.com/", }, "did": "bob-did", @@ -698,51 +740,51 @@ describe('session', () => { }, ]) expect(state.accounts.length).toBe(0) - expect(state.currentAgentState.did).toBe(undefined) + expect(state.currentBundleState.did).toBe(undefined) }) it('can log out of the current account', () => { let state = getInitialState([]) - const agent1 = new AtpAgent({service: 'https://alice.com'}) - agent1.sessionManager.session = { + const aliceBundle = makeBundle('https://alice.com') + const aliceAccount = makeAccount('https://alice.com', { active: true, did: 'alice-did', handle: 'alice.test', accessJwt: 'alice-access-jwt-1', refreshJwt: 'alice-refresh-jwt-1', - } + }) state = run(state, [ { type: 'switched-to-account', - newAgent: agent1, - newAccount: agentToSessionAccountOrThrow(agent1), + newBundle: aliceBundle, + newAccount: aliceAccount, }, ]) expect(state.accounts.length).toBe(1) expect(state.accounts[0].accessJwt).toBe('alice-access-jwt-1') expect(state.accounts[0].refreshJwt).toBe('alice-refresh-jwt-1') - expect(state.currentAgentState.did).toBe('alice-did') + expect(state.currentBundleState.did).toBe('alice-did') - const agent2 = new AtpAgent({service: 'https://bob.com'}) - agent2.sessionManager.session = { + const bobBundle = makeBundle('https://bob.com') + const bobAccount = makeAccount('https://bob.com', { active: true, did: 'bob-did', handle: 'bob.test', accessJwt: 'bob-access-jwt-1', refreshJwt: 'bob-refresh-jwt-1', - } + }) state = run(state, [ { type: 'switched-to-account', - newAgent: agent2, - newAccount: agentToSessionAccountOrThrow(agent2), + newBundle: bobBundle, + newAccount: bobAccount, }, ]) expect(state.accounts.length).toBe(2) expect(state.accounts[0].accessJwt).toBe('bob-access-jwt-1') expect(state.accounts[0].refreshJwt).toBe('bob-refresh-jwt-1') - expect(state.currentAgentState.did).toBe('bob-did') + expect(state.currentBundleState.did).toBe('bob-did') state = run(state, [ { @@ -754,7 +796,7 @@ describe('session', () => { expect(state.accounts[0].refreshJwt).toBe(undefined) expect(state.accounts[1].accessJwt).toBe('alice-access-jwt-1') expect(state.accounts[1].refreshJwt).toBe('alice-refresh-jwt-1') - expect(state.currentAgentState.did).toBe(undefined) + expect(state.currentBundleState.did).toBe(undefined) expect(printState(state)).toMatchInlineSnapshot(` { "accounts": [ @@ -789,8 +831,8 @@ describe('session', () => { "status": undefined, }, ], - "currentAgentState": { - "agent": { + "currentBundleState": { + "bundle": { "service": "https://public.api.bsky.app/", }, "did": undefined, @@ -803,40 +845,38 @@ describe('session', () => { it('updates stored account with refreshed tokens', () => { let state = getInitialState([]) - const agent1 = new AtpAgent({service: 'https://alice.com'}) - agent1.sessionManager.session = { - active: true, - did: 'alice-did', - handle: 'alice.test', - accessJwt: 'alice-access-jwt-1', - refreshJwt: 'alice-refresh-jwt-1', - } + const aliceBundle = makeBundle('https://alice.com') state = run(state, [ { type: 'switched-to-account', - newAgent: agent1, - newAccount: agentToSessionAccountOrThrow(agent1), + newBundle: aliceBundle, + newAccount: makeAccount('https://alice.com', { + active: true, + did: 'alice-did', + handle: 'alice.test', + accessJwt: 'alice-access-jwt-1', + refreshJwt: 'alice-refresh-jwt-1', + }), }, ]) expect(state.accounts.length).toBe(1) - expect(state.currentAgentState.did).toBe('alice-did') + expect(state.currentBundleState.did).toBe('alice-did') - agent1.sessionManager.session = { - active: true, - did: 'alice-did', - handle: 'alice-updated.test', - accessJwt: 'alice-access-jwt-2', - refreshJwt: 'alice-refresh-jwt-2', - email: 'alice@foo.bar', - emailAuthFactor: false, - emailConfirmed: false, - } state = run(state, [ { - type: 'received-agent-event', + type: 'received-session-event', accountDid: 'alice-did', - agent: agent1, - refreshedAccount: agentToSessionAccountOrThrow(agent1), + bundle: aliceBundle, + refreshedAccount: makeAccount('https://alice.com', { + active: true, + did: 'alice-did', + handle: 'alice-updated.test', + accessJwt: 'alice-access-jwt-2', + refreshJwt: 'alice-refresh-jwt-2', + email: 'alice@foo.bar', + emailAuthFactor: false, + emailConfirmed: false, + }), sessionEvent: 'update', }, ]) @@ -845,7 +885,7 @@ describe('session', () => { expect(state.accounts[0].handle).toBe('alice-updated.test') expect(state.accounts[0].accessJwt).toBe('alice-access-jwt-2') expect(state.accounts[0].refreshJwt).toBe('alice-refresh-jwt-2') - expect(state.currentAgentState.did).toBe('alice-did') + expect(state.currentBundleState.did).toBe('alice-did') expect(printState(state)).toMatchInlineSnapshot(` { "accounts": [ @@ -865,8 +905,8 @@ describe('session', () => { "status": undefined, }, ], - "currentAgentState": { - "agent": { + "currentBundleState": { + "bundle": { "service": "https://alice.com/", }, "did": "alice-did", @@ -875,22 +915,21 @@ describe('session', () => { } `) - agent1.sessionManager.session = { - active: true, - did: 'alice-did', - handle: 'alice-updated.test', - accessJwt: 'alice-access-jwt-3', - refreshJwt: 'alice-refresh-jwt-3', - email: 'alice@foo.baz', - emailAuthFactor: true, - emailConfirmed: true, - } state = run(state, [ { - type: 'received-agent-event', + type: 'received-session-event', accountDid: 'alice-did', - agent: agent1, - refreshedAccount: agentToSessionAccountOrThrow(agent1), + bundle: aliceBundle, + refreshedAccount: makeAccount('https://alice.com', { + active: true, + did: 'alice-did', + handle: 'alice-updated.test', + accessJwt: 'alice-access-jwt-3', + refreshJwt: 'alice-refresh-jwt-3', + email: 'alice@foo.baz', + emailAuthFactor: true, + emailConfirmed: true, + }), sessionEvent: 'update', }, ]) @@ -899,7 +938,7 @@ describe('session', () => { expect(state.accounts[0].handle).toBe('alice-updated.test') expect(state.accounts[0].accessJwt).toBe('alice-access-jwt-3') expect(state.accounts[0].refreshJwt).toBe('alice-refresh-jwt-3') - expect(state.currentAgentState.did).toBe('alice-did') + expect(state.currentBundleState.did).toBe('alice-did') expect(printState(state)).toMatchInlineSnapshot(` { "accounts": [ @@ -919,8 +958,8 @@ describe('session', () => { "status": undefined, }, ], - "currentAgentState": { - "agent": { + "currentBundleState": { + "bundle": { "service": "https://alice.com/", }, "did": "alice-did", @@ -929,22 +968,21 @@ describe('session', () => { } `) - agent1.sessionManager.session = { - active: true, - did: 'alice-did', - handle: 'alice-updated.test', - accessJwt: 'alice-access-jwt-4', - refreshJwt: 'alice-refresh-jwt-4', - email: 'alice@foo.baz', - emailAuthFactor: false, - emailConfirmed: false, - } state = run(state, [ { - type: 'received-agent-event', + type: 'received-session-event', accountDid: 'alice-did', - agent: agent1, - refreshedAccount: agentToSessionAccountOrThrow(agent1), + bundle: aliceBundle, + refreshedAccount: makeAccount('https://alice.com', { + active: true, + did: 'alice-did', + handle: 'alice-updated.test', + accessJwt: 'alice-access-jwt-4', + refreshJwt: 'alice-refresh-jwt-4', + email: 'alice@foo.baz', + emailAuthFactor: false, + emailConfirmed: false, + }), sessionEvent: 'update', }, ]) @@ -953,7 +991,7 @@ describe('session', () => { expect(state.accounts[0].handle).toBe('alice-updated.test') expect(state.accounts[0].accessJwt).toBe('alice-access-jwt-4') expect(state.accounts[0].refreshJwt).toBe('alice-refresh-jwt-4') - expect(state.currentAgentState.did).toBe('alice-did') + expect(state.currentBundleState.did).toBe('alice-did') expect(printState(state)).toMatchInlineSnapshot(` { "accounts": [ @@ -973,8 +1011,8 @@ describe('session', () => { "status": undefined, }, ], - "currentAgentState": { - "agent": { + "currentBundleState": { + "bundle": { "service": "https://alice.com/", }, "did": "alice-did", @@ -987,37 +1025,35 @@ describe('session', () => { it('bails out of update on identical objects', () => { let state = getInitialState([]) - const agent1 = new AtpAgent({service: 'https://alice.com'}) - agent1.sessionManager.session = { - active: true, - did: 'alice-did', - handle: 'alice.test', - accessJwt: 'alice-access-jwt-1', - refreshJwt: 'alice-refresh-jwt-1', - } + const aliceBundle = makeBundle('https://alice.com') state = run(state, [ { type: 'switched-to-account', - newAgent: agent1, - newAccount: agentToSessionAccountOrThrow(agent1), + newBundle: aliceBundle, + newAccount: makeAccount('https://alice.com', { + active: true, + did: 'alice-did', + handle: 'alice.test', + accessJwt: 'alice-access-jwt-1', + refreshJwt: 'alice-refresh-jwt-1', + }), }, ]) expect(state.accounts.length).toBe(1) - expect(state.currentAgentState.did).toBe('alice-did') + expect(state.currentBundleState.did).toBe('alice-did') - agent1.sessionManager.session = { - active: true, - did: 'alice-did', - handle: 'alice-updated.test', - accessJwt: 'alice-access-jwt-2', - refreshJwt: 'alice-refresh-jwt-2', - } state = run(state, [ { - type: 'received-agent-event', + type: 'received-session-event', accountDid: 'alice-did', - agent: agent1, - refreshedAccount: agentToSessionAccountOrThrow(agent1), + bundle: aliceBundle, + refreshedAccount: makeAccount('https://alice.com', { + active: true, + did: 'alice-did', + handle: 'alice-updated.test', + accessJwt: 'alice-access-jwt-2', + refreshJwt: 'alice-refresh-jwt-2', + }), sessionEvent: 'update', }, ]) @@ -1027,28 +1063,33 @@ describe('session', () => { const lastState = state state = run(state, [ { - type: 'received-agent-event', + type: 'received-session-event', accountDid: 'alice-did', - agent: agent1, - refreshedAccount: agentToSessionAccountOrThrow(agent1), + bundle: aliceBundle, + refreshedAccount: makeAccount('https://alice.com', { + active: true, + did: 'alice-did', + handle: 'alice-updated.test', + accessJwt: 'alice-access-jwt-2', + refreshJwt: 'alice-refresh-jwt-2', + }), sessionEvent: 'update', }, ]) expect(lastState === state).toBe(true) - agent1.sessionManager.session = { - active: true, - did: 'alice-did', - handle: 'alice-updated.test', - accessJwt: 'alice-access-jwt-3', - refreshJwt: 'alice-refresh-jwt-3', - } state = run(state, [ { - type: 'received-agent-event', + type: 'received-session-event', accountDid: 'alice-did', - agent: agent1, - refreshedAccount: agentToSessionAccountOrThrow(agent1), + bundle: aliceBundle, + refreshedAccount: makeAccount('https://alice.com', { + active: true, + did: 'alice-did', + handle: 'alice-updated.test', + accessJwt: 'alice-access-jwt-3', + refreshJwt: 'alice-refresh-jwt-3', + }), sessionEvent: 'update', }, ]) @@ -1056,127 +1097,86 @@ describe('session', () => { expect(state.accounts[0].accessJwt).toBe('alice-access-jwt-3') }) - it('accepts updates from a stale agent', () => { + it('ignores updates from a stale bundle', () => { let state = getInitialState([]) - const agent1 = new AtpAgent({service: 'https://alice.com'}) - agent1.sessionManager.session = { - active: true, - did: 'alice-did', - handle: 'alice.test', - accessJwt: 'alice-access-jwt-1', - refreshJwt: 'alice-refresh-jwt-1', - } - - const agent2 = new AtpAgent({service: 'https://bob.com'}) - agent2.sessionManager.session = { - active: true, - did: 'bob-did', - handle: 'bob.test', - accessJwt: 'bob-access-jwt-1', - refreshJwt: 'bob-refresh-jwt-1', - } + const aliceBundle = makeBundle('https://alice.com') + const bobBundle = makeBundle('https://bob.com') state = run(state, [ { // Switch to Alice. type: 'switched-to-account', - newAgent: agent1, - newAccount: agentToSessionAccountOrThrow(agent1), + newBundle: aliceBundle, + newAccount: makeAccount('https://alice.com', { + active: true, + did: 'alice-did', + handle: 'alice.test', + accessJwt: 'alice-access-jwt-1', + refreshJwt: 'alice-refresh-jwt-1', + }), }, { // Switch to Bob. type: 'switched-to-account', - newAgent: agent2, - newAccount: agentToSessionAccountOrThrow(agent2), + newBundle: bobBundle, + newAccount: makeAccount('https://bob.com', { + active: true, + did: 'bob-did', + handle: 'bob.test', + accessJwt: 'bob-access-jwt-1', + refreshJwt: 'bob-refresh-jwt-1', + }), }, ]) expect(state.accounts.length).toBe(2) - expect(state.currentAgentState.did).toBe('bob-did') + expect(state.currentBundleState.did).toBe('bob-did') - agent1.sessionManager.session = { - active: true, - did: 'alice-did', - handle: 'alice-updated.test', - accessJwt: 'alice-access-jwt-2', - refreshJwt: 'alice-refresh-jwt-2', - email: 'alice@foo.bar', - emailAuthFactor: false, - emailConfirmed: false, - } + /* + * An 'update' from the stale (background) Alice bundle is dropped + * ENTIRELY - identical state object, no token write. A refresh completing + * after switching away must not resurrect fresh tokens into the + * switched-away account entry. + */ + const beforeStaleUpdate = state state = run(state, [ { - type: 'received-agent-event', + type: 'received-session-event', accountDid: 'alice-did', - agent: agent1, - refreshedAccount: agentToSessionAccountOrThrow(agent1), + bundle: aliceBundle, + refreshedAccount: makeAccount('https://alice.com', { + active: true, + did: 'alice-did', + handle: 'alice-updated.test', + accessJwt: 'alice-access-jwt-2', + refreshJwt: 'alice-refresh-jwt-2', + email: 'alice@foo.bar', + emailAuthFactor: false, + emailConfirmed: false, + }), sessionEvent: 'update', }, ]) - expect(state.accounts.length).toBe(2) + expect(beforeStaleUpdate === state).toBe(true) expect(state.accounts[1].did).toBe('alice-did') - // Should update Alice's tokens because otherwise they'll be stale. - expect(state.accounts[1].handle).toBe('alice-updated.test') - expect(state.accounts[1].accessJwt).toBe('alice-access-jwt-2') - expect(state.accounts[1].refreshJwt).toBe('alice-refresh-jwt-2') - expect(printState(state)).toMatchInlineSnapshot(` - { - "accounts": [ - { - "accessJwt": "bob-access-jwt-1", - "active": true, - "did": "bob-did", - "email": undefined, - "emailAuthFactor": false, - "emailConfirmed": false, - "handle": "bob.test", - "isSelfHosted": true, - "pdsUrl": undefined, - "refreshJwt": "bob-refresh-jwt-1", - "service": "https://bob.com/", - "signupQueued": false, - "status": undefined, - }, - { - "accessJwt": "alice-access-jwt-2", - "active": true, - "did": "alice-did", - "email": "alice@foo.bar", - "emailAuthFactor": false, - "emailConfirmed": false, - "handle": "alice-updated.test", - "isSelfHosted": true, - "pdsUrl": undefined, - "refreshJwt": "alice-refresh-jwt-2", - "service": "https://alice.com/", - "signupQueued": false, - "status": undefined, - }, - ], - "currentAgentState": { - "agent": { - "service": "https://bob.com/", - }, - "did": "bob-did", - }, - "needsPersist": true, - } - `) + // Alice's stored tokens are untouched (the stale update did not land). + expect(state.accounts[1].handle).toBe('alice.test') + expect(state.accounts[1].accessJwt).toBe('alice-access-jwt-1') + expect(state.accounts[1].refreshJwt).toBe('alice-refresh-jwt-1') - agent2.sessionManager.session = { - active: true, - did: 'bob-did', - handle: 'bob-updated.test', - accessJwt: 'bob-access-jwt-2', - refreshJwt: 'bob-refresh-jwt-2', - } state = run(state, [ { - // Update Bob. - type: 'received-agent-event', + // Update Bob (the current bundle) - this still applies. + type: 'received-session-event', accountDid: 'bob-did', - agent: agent2, - refreshedAccount: agentToSessionAccountOrThrow(agent2), + bundle: bobBundle, + refreshedAccount: makeAccount('https://bob.com', { + active: true, + did: 'bob-did', + handle: 'bob-updated.test', + accessJwt: 'bob-access-jwt-2', + refreshJwt: 'bob-refresh-jwt-2', + }), sessionEvent: 'update', }, ]) @@ -1205,23 +1205,23 @@ describe('session', () => { "status": undefined, }, { - "accessJwt": "alice-access-jwt-2", + "accessJwt": "alice-access-jwt-1", "active": true, "did": "alice-did", - "email": "alice@foo.bar", + "email": undefined, "emailAuthFactor": false, "emailConfirmed": false, - "handle": "alice-updated.test", + "handle": "alice.test", "isSelfHosted": true, "pdsUrl": undefined, - "refreshJwt": "alice-refresh-jwt-2", + "refreshJwt": "alice-refresh-jwt-1", "service": "https://alice.com/", "signupQueued": false, "status": undefined, }, ], - "currentAgentState": { - "agent": { + "currentBundleState": { + "bundle": { "service": "https://bob.com/", }, "did": "bob-did", @@ -1230,14 +1230,13 @@ describe('session', () => { } `) - // Ignore other events for inactive agent. + // Ignore other events for the inactive bundle too (network-error, expired). const lastState = state - agent1.sessionManager.session = undefined state = run(state, [ { - type: 'received-agent-event', + type: 'received-session-event', accountDid: 'alice-did', - agent: agent1, + bundle: aliceBundle, refreshedAccount: undefined, sessionEvent: 'network-error', }, @@ -1245,9 +1244,9 @@ describe('session', () => { expect(lastState === state).toBe(true) state = run(state, [ { - type: 'received-agent-event', + type: 'received-session-event', accountDid: 'alice-did', - agent: agent1, + bundle: aliceBundle, refreshedAccount: undefined, sessionEvent: 'expired', }, @@ -1255,37 +1254,128 @@ describe('session', () => { expect(lastState === state).toBe(true) }) - it('ignores updates from a removed agent', () => { + it('drops an update from a stale bundle even when its account entry still exists (no resurrection)', () => { let state = getInitialState([]) - const agent1 = new AtpAgent({service: 'https://alice.com'}) - agent1.sessionManager.session = { - active: true, - did: 'alice-did', - handle: 'alice.test', - accessJwt: 'alice-access-jwt-1', - refreshJwt: 'alice-refresh-jwt-1', - } + const aliceBundle = makeBundle('https://alice.com') + state = run(state, [ + { + type: 'switched-to-account', + newBundle: aliceBundle, + newAccount: makeAccount('https://alice.com', { + active: true, + did: 'alice-did', + handle: 'alice.test', + accessJwt: 'alice-access-jwt-1', + refreshJwt: 'alice-refresh-jwt-1', + }), + }, + ]) + expect(state.currentBundleState.did).toBe('alice-did') - const agent2 = new AtpAgent({service: 'https://bob.com'}) - agent2.sessionManager.session = { - active: true, - did: 'bob-did', - handle: 'bob.test', - accessJwt: 'bob-access-jwt-1', - refreshJwt: 'bob-refresh-jwt-1', - } + // Alice logs out: her account entry stays, but tokens are cleared and the + // current bundle becomes the public (logged-out) bundle. + state = run(state, [{type: 'logged-out-current-account'}]) + expect(state.currentBundleState.did).toBe(undefined) + expect(state.accounts[0].did).toBe('alice-did') + expect(state.accounts[0].accessJwt).toBe(undefined) + expect(state.accounts[0].refreshJwt).toBe(undefined) + + /* + * A refresh that was already in flight on the (now stale) Alice bundle + * completes and delivers fresh tokens. It must NOT resurrect them into the + * soft-logged-out account entry - the bundle no longer matches the current + * (public) bundle, so the event is dropped entirely. + */ + const afterLogout = state + state = run(state, [ + { + type: 'received-session-event', + accountDid: 'alice-did', + bundle: aliceBundle, + refreshedAccount: makeAccount('https://alice.com', { + active: true, + did: 'alice-did', + handle: 'alice.test', + accessJwt: 'alice-access-jwt-2', + refreshJwt: 'alice-refresh-jwt-2', + }), + sessionEvent: 'update', + }, + ]) + expect(afterLogout === state).toBe(true) + expect(state.accounts[0].accessJwt).toBe(undefined) + expect(state.accounts[0].refreshJwt).toBe(undefined) + expect(state.currentBundleState.did).toBe(undefined) + }) + + it('applies an update from the current bundle', () => { + let state = getInitialState([]) + + const aliceBundle = makeBundle('https://alice.com') + state = run(state, [ + { + type: 'switched-to-account', + newBundle: aliceBundle, + newAccount: makeAccount('https://alice.com', { + active: true, + did: 'alice-did', + handle: 'alice.test', + accessJwt: 'alice-access-jwt-1', + refreshJwt: 'alice-refresh-jwt-1', + }), + }, + ]) + + state = run(state, [ + { + type: 'received-session-event', + accountDid: 'alice-did', + bundle: aliceBundle, + refreshedAccount: makeAccount('https://alice.com', { + active: true, + did: 'alice-did', + handle: 'alice.test', + accessJwt: 'alice-access-jwt-2', + refreshJwt: 'alice-refresh-jwt-2', + }), + sessionEvent: 'update', + }, + ]) + // The current bundle's update lands and rotates the stored tokens. + expect(state.accounts[0].accessJwt).toBe('alice-access-jwt-2') + expect(state.accounts[0].refreshJwt).toBe('alice-refresh-jwt-2') + expect(state.currentBundleState.did).toBe('alice-did') + }) + + it('ignores updates from a removed bundle', () => { + let state = getInitialState([]) + + const aliceBundle = makeBundle('https://alice.com') + const bobBundle = makeBundle('https://bob.com') state = run(state, [ { type: 'switched-to-account', - newAgent: agent1, - newAccount: agentToSessionAccountOrThrow(agent1), + newBundle: aliceBundle, + newAccount: makeAccount('https://alice.com', { + active: true, + did: 'alice-did', + handle: 'alice.test', + accessJwt: 'alice-access-jwt-1', + refreshJwt: 'alice-refresh-jwt-1', + }), }, { type: 'switched-to-account', - newAgent: agent2, - newAccount: agentToSessionAccountOrThrow(agent2), + newBundle: bobBundle, + newAccount: makeAccount('https://bob.com', { + active: true, + did: 'bob-did', + handle: 'bob.test', + accessJwt: 'bob-access-jwt-1', + refreshJwt: 'bob-refresh-jwt-1', + }), }, { type: 'removed-account', @@ -1293,58 +1383,55 @@ describe('session', () => { }, ]) expect(state.accounts.length).toBe(1) - expect(state.currentAgentState.did).toBe('bob-did') + expect(state.currentBundleState.did).toBe('bob-did') - agent1.sessionManager.session = { - active: true, - did: 'alice-did', - handle: 'alice.test', - accessJwt: 'alice-access-jwt-2', - refreshJwt: 'alice-refresh-jwt-2', - } state = run(state, [ { - type: 'received-agent-event', + type: 'received-session-event', accountDid: 'alice-did', - agent: agent1, - refreshedAccount: agentToSessionAccountOrThrow(agent1), + bundle: aliceBundle, + refreshedAccount: makeAccount('https://alice.com', { + active: true, + did: 'alice-did', + handle: 'alice.test', + accessJwt: 'alice-access-jwt-2', + refreshJwt: 'alice-refresh-jwt-2', + }), sessionEvent: 'update', }, ]) expect(state.accounts.length).toBe(1) expect(state.accounts[0].did).toBe('bob-did') expect(state.accounts[0].accessJwt).toBe('bob-access-jwt-1') - expect(state.currentAgentState.did).toBe('bob-did') + expect(state.currentBundleState.did).toBe('bob-did') }) it('ignores network errors', () => { let state = getInitialState([]) - const agent1 = new AtpAgent({service: 'https://alice.com'}) - agent1.sessionManager.session = { - active: true, - did: 'alice-did', - handle: 'alice.test', - accessJwt: 'alice-access-jwt-1', - refreshJwt: 'alice-refresh-jwt-1', - } + const aliceBundle = makeBundle('https://alice.com') state = run(state, [ { // Switch to Alice. type: 'switched-to-account', - newAgent: agent1, - newAccount: agentToSessionAccountOrThrow(agent1), + newBundle: aliceBundle, + newAccount: makeAccount('https://alice.com', { + active: true, + did: 'alice-did', + handle: 'alice.test', + accessJwt: 'alice-access-jwt-1', + refreshJwt: 'alice-refresh-jwt-1', + }), }, ]) expect(state.accounts.length).toBe(1) - expect(state.currentAgentState.did).toBe('alice-did') + expect(state.currentBundleState.did).toBe('alice-did') - agent1.sessionManager.session = undefined state = run(state, [ { - type: 'received-agent-event', + type: 'received-session-event', accountDid: 'alice-did', - agent: agent1, + bundle: aliceBundle, refreshedAccount: undefined, sessionEvent: 'network-error', }, @@ -1352,7 +1439,7 @@ describe('session', () => { expect(state.accounts.length).toBe(1) expect(state.accounts[0].accessJwt).toBe('alice-access-jwt-1') expect(state.accounts[0].refreshJwt).toBe('alice-refresh-jwt-1') - expect(state.currentAgentState.did).toBe('alice-did') + expect(state.currentBundleState.did).toBe('alice-did') expect(printState(state)).toMatchInlineSnapshot(` { "accounts": [ @@ -1372,8 +1459,8 @@ describe('session', () => { "status": undefined, }, ], - "currentAgentState": { - "agent": { + "currentBundleState": { + "bundle": { "service": "https://alice.com/", }, "did": "alice-did", @@ -1386,31 +1473,29 @@ describe('session', () => { it('resets tokens on expired event', () => { let state = getInitialState([]) - const agent1 = new AtpAgent({service: 'https://alice.com'}) - agent1.sessionManager.session = { - active: true, - did: 'alice-did', - handle: 'alice.test', - accessJwt: 'alice-access-jwt-1', - refreshJwt: 'alice-refresh-jwt-1', - } + const aliceBundle = makeBundle('https://alice.com') state = run(state, [ { type: 'switched-to-account', - newAgent: agent1, - newAccount: agentToSessionAccountOrThrow(agent1), + newBundle: aliceBundle, + newAccount: makeAccount('https://alice.com', { + active: true, + did: 'alice-did', + handle: 'alice.test', + accessJwt: 'alice-access-jwt-1', + refreshJwt: 'alice-refresh-jwt-1', + }), }, ]) expect(state.accounts.length).toBe(1) expect(state.accounts[0].accessJwt).toBe('alice-access-jwt-1') - expect(state.currentAgentState.did).toBe('alice-did') + expect(state.currentBundleState.did).toBe('alice-did') - agent1.sessionManager.session = undefined state = run(state, [ { - type: 'received-agent-event', + type: 'received-session-event', accountDid: 'alice-did', - agent: agent1, + bundle: aliceBundle, refreshedAccount: undefined, sessionEvent: 'expired', }, @@ -1418,7 +1503,7 @@ describe('session', () => { expect(state.accounts.length).toBe(1) expect(state.accounts[0].accessJwt).toBe(undefined) expect(state.accounts[0].refreshJwt).toBe(undefined) - expect(state.currentAgentState.did).toBe(undefined) + expect(state.currentBundleState.did).toBe(undefined) expect(printState(state)).toMatchInlineSnapshot(` { "accounts": [ @@ -1438,74 +1523,8 @@ describe('session', () => { "status": undefined, }, ], - "currentAgentState": { - "agent": { - "service": "https://public.api.bsky.app/", - }, - "did": undefined, - }, - "needsPersist": true, - } - `) - }) - - it('resets tokens on created-failed event', () => { - let state = getInitialState([]) - - const agent1 = new AtpAgent({service: 'https://alice.com'}) - agent1.sessionManager.session = { - active: true, - did: 'alice-did', - handle: 'alice.test', - accessJwt: 'alice-access-jwt-1', - refreshJwt: 'alice-refresh-jwt-1', - } - state = run(state, [ - { - type: 'switched-to-account', - newAgent: agent1, - newAccount: agentToSessionAccountOrThrow(agent1), - }, - ]) - expect(state.accounts.length).toBe(1) - expect(state.accounts[0].accessJwt).toBe('alice-access-jwt-1') - expect(state.currentAgentState.did).toBe('alice-did') - - agent1.sessionManager.session = undefined - state = run(state, [ - { - type: 'received-agent-event', - accountDid: 'alice-did', - agent: agent1, - refreshedAccount: undefined, - sessionEvent: 'create-failed', - }, - ]) - expect(state.accounts.length).toBe(1) - expect(state.accounts[0].accessJwt).toBe(undefined) - expect(state.accounts[0].refreshJwt).toBe(undefined) - expect(state.currentAgentState.did).toBe(undefined) - expect(printState(state)).toMatchInlineSnapshot(` - { - "accounts": [ - { - "accessJwt": undefined, - "active": true, - "did": "alice-did", - "email": undefined, - "emailAuthFactor": false, - "emailConfirmed": false, - "handle": "alice.test", - "isSelfHosted": true, - "pdsUrl": undefined, - "refreshJwt": undefined, - "service": "https://alice.com/", - "signupQueued": false, - "status": undefined, - }, - ], - "currentAgentState": { - "agent": { + "currentBundleState": { + "bundle": { "service": "https://public.api.bsky.app/", }, "did": undefined, @@ -1518,59 +1537,53 @@ describe('session', () => { it('replaces local accounts with synced accounts', () => { let state = getInitialState([]) - const agent1 = new AtpAgent({service: 'https://alice.com'}) - agent1.sessionManager.session = { - active: true, - did: 'alice-did', - handle: 'alice.test', - accessJwt: 'alice-access-jwt-1', - refreshJwt: 'alice-refresh-jwt-1', - } - const agent2 = new AtpAgent({service: 'https://bob.com'}) - agent2.sessionManager.session = { - active: true, - did: 'bob-did', - handle: 'bob.test', - accessJwt: 'bob-access-jwt-1', - refreshJwt: 'bob-refresh-jwt-1', - } + const aliceBundle = makeBundle('https://alice.com') + const bobBundle = makeBundle('https://bob.com') state = run(state, [ { type: 'switched-to-account', - newAgent: agent1, - newAccount: agentToSessionAccountOrThrow(agent1), + newBundle: aliceBundle, + newAccount: makeAccount('https://alice.com', { + active: true, + did: 'alice-did', + handle: 'alice.test', + accessJwt: 'alice-access-jwt-1', + refreshJwt: 'alice-refresh-jwt-1', + }), }, { type: 'switched-to-account', - newAgent: agent2, - newAccount: agentToSessionAccountOrThrow(agent2), + newBundle: bobBundle, + newAccount: makeAccount('https://bob.com', { + active: true, + did: 'bob-did', + handle: 'bob.test', + accessJwt: 'bob-access-jwt-1', + refreshJwt: 'bob-refresh-jwt-1', + }), }, ]) expect(state.accounts.length).toBe(2) - expect(state.currentAgentState.did).toBe('bob-did') + expect(state.currentBundleState.did).toBe('bob-did') - const anotherTabAgent1 = new AtpAgent({service: 'https://jay.com'}) - anotherTabAgent1.sessionManager.session = { - active: true, - did: 'jay-did', - handle: 'jay.test', - accessJwt: 'jay-access-jwt-1', - refreshJwt: 'jay-refresh-jwt-1', - } - const anotherTabAgent2 = new AtpAgent({service: 'https://alice.com'}) - anotherTabAgent2.sessionManager.session = { - active: true, - did: 'bob-did', - handle: 'bob.test', - accessJwt: 'bob-access-jwt-2', - refreshJwt: 'bob-refresh-jwt-2', - } state = run(state, [ { type: 'synced-accounts', syncedAccounts: [ - agentToSessionAccountOrThrow(anotherTabAgent1), - agentToSessionAccountOrThrow(anotherTabAgent2), + makeAccount('https://jay.com', { + active: true, + did: 'jay-did', + handle: 'jay.test', + accessJwt: 'jay-access-jwt-1', + refreshJwt: 'jay-refresh-jwt-1', + }), + makeAccount('https://alice.com', { + active: true, + did: 'bob-did', + handle: 'bob.test', + accessJwt: 'bob-access-jwt-2', + refreshJwt: 'bob-refresh-jwt-2', + }), ], syncedCurrentDid: 'bob-did', }, @@ -1580,8 +1593,8 @@ describe('session', () => { expect(state.accounts[1].did).toBe('bob-did') expect(state.accounts[1].accessJwt).toBe('bob-access-jwt-2') // Keep Bob logged in. - // (We patch up agent.session outside the reducer for this to work.) - expect(state.currentAgentState.did).toBe('bob-did') + // (The bundle is rebuilt from the synced tokens outside the reducer.) + expect(state.currentBundleState.did).toBe('bob-did') expect(state.needsPersist).toBe(false) expect(printState(state)).toMatchInlineSnapshot(` { @@ -1617,8 +1630,8 @@ describe('session', () => { "status": undefined, }, ], - "currentAgentState": { - "agent": { + "currentBundleState": { + "bundle": { "service": "https://bob.com/", }, "did": "bob-did", @@ -1627,18 +1640,18 @@ describe('session', () => { } `) - const anotherTabAgent3 = new AtpAgent({service: 'https://clarence.com'}) - anotherTabAgent3.sessionManager.session = { - active: true, - did: 'clarence-did', - handle: 'clarence.test', - accessJwt: 'clarence-access-jwt-2', - refreshJwt: 'clarence-refresh-jwt-2', - } state = run(state, [ { type: 'synced-accounts', - syncedAccounts: [agentToSessionAccountOrThrow(anotherTabAgent3)], + syncedAccounts: [ + makeAccount('https://clarence.com', { + active: true, + did: 'clarence-did', + handle: 'clarence.test', + accessJwt: 'clarence-access-jwt-2', + refreshJwt: 'clarence-refresh-jwt-2', + }), + ], syncedCurrentDid: 'clarence-did', }, ]) @@ -1646,7 +1659,7 @@ describe('session', () => { expect(state.accounts[0].did).toBe('clarence-did') // Log out because we have no matching user. // (In practice, we'll resume this session outside the reducer.) - expect(state.currentAgentState.did).toBe(undefined) + expect(state.currentBundleState.did).toBe(undefined) expect(state.needsPersist).toBe(false) expect(printState(state)).toMatchInlineSnapshot(` { @@ -1667,8 +1680,8 @@ describe('session', () => { "status": undefined, }, ], - "currentAgentState": { - "agent": { + "currentBundleState": { + "bundle": { "service": "https://public.api.bsky.app/", }, "did": undefined, @@ -1677,6 +1690,94 @@ describe('session', () => { } `) }) + + it('replaces the current bundle on same-did cross-tab sync', () => { + let state = getInitialState([]) + + const aliceBundle = makeBundle('https://alice.com') + state = run(state, [ + { + type: 'switched-to-account', + newBundle: aliceBundle, + newAccount: makeAccount('https://alice.com', { + active: true, + did: 'alice-did', + handle: 'alice.test', + accessJwt: 'alice-access-jwt-1', + refreshJwt: 'alice-refresh-jwt-1', + }), + }, + ]) + expect(state.currentBundleState.bundle).toBe(aliceBundle) + expect(state.accounts[0].accessJwt).toBe('alice-access-jwt-1') + + // A fresh bundle rebuilt from synced tokens (no network). + const aliceBundle2 = makeBundle('https://alice.com') + const aliceAccount2 = makeAccount('https://alice.com', { + active: true, + did: 'alice-did', + handle: 'alice.test', + accessJwt: 'alice-access-jwt-2', + refreshJwt: 'alice-refresh-jwt-2', + }) + state = run(state, [ + { + type: 'replaced-current-bundle', + newBundle: aliceBundle2, + newAccount: aliceAccount2, + }, + ]) + // The bundle is swapped in place, the did is preserved. + expect(state.currentBundleState.bundle).toBe(aliceBundle2) + expect(state.currentBundleState.did).toBe('alice-did') + // The matching account entry is replaced with the synced one. + expect(state.accounts.length).toBe(1) + expect(state.accounts[0].did).toBe('alice-did') + expect(state.accounts[0].accessJwt).toBe('alice-access-jwt-2') + expect(state.accounts[0].refreshJwt).toBe('alice-refresh-jwt-2') + // Synced from another tab - don't persist. + expect(state.needsPersist).toBe(false) + }) + + it('does not touch the bundle on partial-refresh-session', () => { + let state = getInitialState([]) + + const aliceBundle = makeBundle('https://alice.com') + state = run(state, [ + { + type: 'switched-to-account', + newBundle: aliceBundle, + newAccount: makeAccount('https://alice.com', { + active: true, + did: 'alice-did', + handle: 'alice.test', + accessJwt: 'alice-access-jwt-1', + refreshJwt: 'alice-refresh-jwt-1', + email: 'alice@foo.bar', + emailAuthFactor: false, + emailConfirmed: false, + }), + }, + ]) + expect(state.currentBundleState.bundle).toBe(aliceBundle) + expect(state.accounts[0].emailConfirmed).toBe(false) + expect(state.accounts[0].emailAuthFactor).toBe(false) + + state = run(state, [ + { + type: 'partial-refresh-session', + accountDid: 'alice-did', + patch: {emailConfirmed: true, emailAuthFactor: true}, + }, + ]) + // The account email fields are patched. + expect(state.accounts[0].emailConfirmed).toBe(true) + expect(state.accounts[0].emailAuthFactor).toBe(true) + // The bundle is untouched - no session mutation, same reference. + expect(state.currentBundleState.bundle).toBe(aliceBundle) + expect(state.currentBundleState.did).toBe('alice-did') + expect(state.needsPersist).toBe(true) + }) }) function run(initialState: State, actions: Action[]): State { @@ -1690,9 +1791,9 @@ function run(initialState: State, actions: Action[]): State { function printState(state: State) { return { accounts: state.accounts, - currentAgentState: { - agent: {service: state.currentAgentState.agent.service}, - did: state.currentAgentState.did, + currentBundleState: { + bundle: {service: state.currentBundleState.bundle.service}, + did: state.currentBundleState.did, }, needsPersist: state.needsPersist, } diff --git a/src/state/session/agent-config.ts b/src/state/session/agent-config.ts deleted file mode 100644 index 3ee2718a39..0000000000 --- a/src/state/session/agent-config.ts +++ /dev/null @@ -1,12 +0,0 @@ -import AsyncStorage from '@react-native-async-storage/async-storage' - -const PREFIX = 'agent-labelers' - -export async function saveLabelers(did: string, value: string[]) { - await AsyncStorage.setItem(`${PREFIX}:${did}`, JSON.stringify(value)) -} - -export async function readLabelers(did: string): Promise { - const rawData = await AsyncStorage.getItem(`${PREFIX}:${did}`) - return rawData ? JSON.parse(rawData) : undefined -} diff --git a/src/state/session/agent.ts b/src/state/session/agent.ts index 78e9f79610..7246454ed2 100644 --- a/src/state/session/agent.ts +++ b/src/state/session/agent.ts @@ -1,338 +1,17 @@ import { Agent as BaseAgent, - type AppBskyActorProfile, - AtpAgent, type AtprotoServiceType, - type AtpSessionData, - type AtpSessionEvent, type Did, - type Un$Typed, } from '@atproto/api' -import {TID} from '@atproto/common-web' - -import {networkRetry} from '#/lib/async/retry' -import { - BLUESKY_PROXY_HEADER, - BSKY_SERVICE, - DISCOVER_SAVED_FEED, - IS_PROD_SERVICE, - PUBLIC_BSKY_SERVICE, - TIMELINE_SAVED_FEED, -} from '#/lib/constants' -import {logger} from '#/logger' -import {snoozeBirthdateUpdateAllowedForDid} from '#/state/birthdate' -import {restrictChatSettings} from '#/state/queries/messages/restrictChatSettings' -import {snoozeEmailConfirmationPrompt} from '#/state/shell/reminders' -import { - prefetchAgeAssuranceServerData, - setBirthdateForDid, - setCreatedAtForDid, -} from '#/ageAssurance/data' -import {unsafeGetAndComputeAgeAssurance} from '#/ageAssurance/state' -import {features} from '#/analytics' -import {emitNetworkConfirmed, emitNetworkLost} from '../events' -import {addSessionErrorLog} from './logging' -import { - configureModerationForAccount, - configureModerationForGuest, -} from './moderation' -import {type SessionAccount} from './types' -import {isSessionExpired, isSignupQueued} from './util' export type ProxyHeaderValue = `${Did}#${AtprotoServiceType}` -export function createPublicAgent() { - configureModerationForGuest() // Side effect but only relevant for tests - - const agent = new BskyAppAgent({service: PUBLIC_BSKY_SERVICE}) - agent.configureProxy(BLUESKY_PROXY_HEADER.get()) - return agent -} - -export async function createAgentAndResume( - storedAccount: SessionAccount, - onSessionChange: ( - agent: AtpAgent, - did: string, - event: AtpSessionEvent, - ) => void, -) { - const agent = new BskyAppAgent({service: storedAccount.service}) - if (storedAccount.pdsUrl) { - agent.sessionManager.pdsUrl = new URL(storedAccount.pdsUrl) - } - const gates = features.refresh({ - strategy: 'prefer-low-latency', - }) - const moderation = configureModerationForAccount(agent, storedAccount) - const prevSession: AtpSessionData = sessionAccountToSession(storedAccount) - if (isSessionExpired(storedAccount)) { - await networkRetry(1, () => agent.resumeSession(prevSession)) - } else { - agent.sessionManager.session = prevSession - } - - // after session is attached - const aa = prefetchAgeAssuranceServerData({agent}) - - agent.configureProxy(BLUESKY_PROXY_HEADER.get()) - - return agent.prepare({ - resolvers: [gates, moderation, aa], - onSessionChange, - }) -} - -export async function createAgentAndLogin( - { - service, - identifier, - password, - authFactorToken, - }: { - service: string - identifier: string - password: string - authFactorToken?: string - }, - onSessionChange: ( - agent: AtpAgent, - did: string, - event: AtpSessionEvent, - ) => void, -) { - const agent = new BskyAppAgent({service}) - await agent.login({ - identifier, - password, - authFactorToken, - allowTakendown: true, - }) - - const account = agentToSessionAccountOrThrow(agent) - const gates = features.refresh({strategy: 'prefer-fresh-gates'}) - const moderation = configureModerationForAccount(agent, account) - const aa = prefetchAgeAssuranceServerData({agent}) - - agent.configureProxy(BLUESKY_PROXY_HEADER.get()) - - return agent.prepare({ - resolvers: [gates, moderation, aa], - onSessionChange, - }) -} - -export async function createAgentAndCreateAccount( - { - service, - email, - password, - handle, - birthDate, - inviteCode, - verificationPhone, - verificationCode, - }: { - service: string - email: string - password: string - handle: string - birthDate: Date - inviteCode?: string - verificationPhone?: string - verificationCode?: string - }, - onSessionChange: ( - agent: AtpAgent, - did: string, - event: AtpSessionEvent, - ) => void, -) { - const agent = new BskyAppAgent({service}) - await agent.createAccount({ - email, - password, - handle, - inviteCode, - verificationPhone, - verificationCode, - }) - const account = agentToSessionAccountOrThrow(agent) - const gates = features.refresh({strategy: 'prefer-fresh-gates'}) - const moderation = configureModerationForAccount(agent, account) - - const createdAt = new Date().toISOString() - const birthdate = birthDate.toISOString() - - /* - * Since we have a race with account creation, profile creation, and AA - * state, set these values locally to ensure sync reads. Values are written - * to the server in the next step, so on subsequent reloads, the server will - * be the source of truth. - */ - setCreatedAtForDid({did: account.did, createdAt}) - setBirthdateForDid({did: account.did, birthdate}) - snoozeBirthdateUpdateAllowedForDid(account.did) - // do this last - const aa = prefetchAgeAssuranceServerData({agent}) - - // Not awaited so that we can still get into onboarding. - // This is OK because we won't let you toggle adult stuff until you set the date. - if (IS_PROD_SERVICE(service)) { - void Promise.allSettled([ - networkRetry(3, () => { - return agent.setPersonalDetails({ - birthDate: birthdate, - }) - }).catch(e => { - logger.info(`createAgentAndCreateAccount: failed to set birthDate`) - throw e - }), - networkRetry(3, () => { - return agent.upsertProfile(prev => { - const next: Un$Typed = prev || {} - next.displayName = handle - next.createdAt = createdAt - return next - }) - }).catch(e => { - logger.info( - `createAgentAndCreateAccount: failed to set initial profile`, - ) - throw e - }), - networkRetry(1, () => { - return agent.overwriteSavedFeeds([ - { - ...DISCOVER_SAVED_FEED, - id: TID.nextStr(), - }, - { - ...TIMELINE_SAVED_FEED, - id: TID.nextStr(), - }, - ]) - }).catch(e => { - logger.info(`createAgentAndCreateAccount: failed to set initial feeds`) - throw e - }), - // wait for AA data to load first, then check state - aa.then(() => { - const {flags} = unsafeGetAndComputeAgeAssurance({did: account.did}) - if (flags?.chatDisabled || flags?.groupChatDisabled) { - void restrictChatSettings({ - agent, - restrictIncoming: flags.chatDisabled, - restrictGroupInvites: flags.groupChatDisabled, - }) - } - }), - ]).then(promises => { - const rejected = promises.filter(p => p.status === 'rejected') - if (rejected.length > 0) { - logger.error( - `session: createAgentAndCreateAccount failed to save personal details and feeds`, - ) - } - }) - } else { - void Promise.allSettled([ - networkRetry(3, () => { - return agent.setPersonalDetails({ - birthDate: birthDate.toISOString(), - }) - }).catch(e => { - logger.info(`createAgentAndCreateAccount: failed to set birthDate`) - throw e - }), - networkRetry(3, () => { - return agent.upsertProfile(prev => { - const next: Un$Typed = prev || {} - next.createdAt = prev?.createdAt || new Date().toISOString() - return next - }) - }).catch(e => { - logger.info( - `createAgentAndCreateAccount: failed to set initial profile`, - ) - throw e - }), - ]).then(promises => { - const rejected = promises.filter(p => p.status === 'rejected') - if (rejected.length > 0) { - logger.error( - `session: createAgentAndCreateAccount failed to save personal details and feeds`, - ) - } - }) - } - - try { - // snooze first prompt after signup, defer to next prompt - snoozeEmailConfirmationPrompt() - } catch (e: any) { - logger.error(e, {message: `session: failed snoozeEmailConfirmationPrompt`}) - } - - agent.configureProxy(BLUESKY_PROXY_HEADER.get()) - - return agent.prepare({ - resolvers: [gates, moderation, aa], - onSessionChange, - }) -} - -export function agentToSessionAccountOrThrow(agent: AtpAgent): SessionAccount { - const account = agentToSessionAccount(agent) - if (!account) { - throw Error('Expected an active session') - } - return account -} - -export function agentToSessionAccount( - agent: AtpAgent, -): SessionAccount | undefined { - if (!agent.session) { - return undefined - } - return { - service: agent.serviceUrl.toString(), - did: agent.session.did, - handle: agent.session.handle, - email: agent.session.email, - emailConfirmed: agent.session.emailConfirmed || false, - emailAuthFactor: agent.session.emailAuthFactor || false, - refreshJwt: agent.session.refreshJwt, - accessJwt: agent.session.accessJwt, - signupQueued: isSignupQueued(agent.session.accessJwt), - active: agent.session.active, - status: agent.session.status, - pdsUrl: agent.pdsUrl?.toString(), - isSelfHosted: !agent.serviceUrl.toString().startsWith(BSKY_SERVICE), - } -} - -export function sessionAccountToSession( - account: SessionAccount, -): AtpSessionData { - return { - // Sorted in the same property order as when returned by BskyAgent (alphabetical). - accessJwt: account.accessJwt ?? '', - did: account.did, - email: account.email, - emailAuthFactor: account.emailAuthFactor, - emailConfirmed: account.emailConfirmed, - handle: account.handle, - refreshJwt: account.refreshJwt ?? '', - /** - * @see https://github.com/bluesky-social/atproto/blob/c5d36d5ba2a2c2a5c4f366a5621c06a5608e361e/packages/api/src/agent.ts#L188 - */ - active: account.active ?? true, - status: account.status, - } -} - +/** + * A bare `Agent` that applies a service-proxy header on construction. + * + * Used for the unauthenticated, service-specific calls that cannot go through + * the session agent (PDS detection, password reset, handle availability). + */ export class Agent extends BaseAgent { constructor( proxyHeader: ProxyHeaderValue | null, @@ -344,74 +23,3 @@ export class Agent extends BaseAgent { } } } - -// Not exported. Use factories above to create it. -// WARN: In the factories above, we _manually set a proxy header_ for the agent after we do whatever it is we are supposed to do. -// Ideally, we wouldn't be doing this. However, since there is so much logic that requires making calls to the PDS right now, it -// feels safer to just let those run as-is and set the header afterward. -let realFetch = globalThis.fetch -class BskyAppAgent extends AtpAgent { - persistSessionHandler: ((event: AtpSessionEvent) => void) | undefined = - undefined - - constructor({service}: {service: string}) { - super({ - service, - async fetch(...args) { - let success = false - try { - const result = await realFetch(...args) - success = true - return result - } catch (e) { - success = false - throw e - } finally { - if (success) { - emitNetworkConfirmed() - } else { - emitNetworkLost() - } - } - }, - persistSession: (event: AtpSessionEvent) => { - if (this.persistSessionHandler) { - this.persistSessionHandler(event) - } - }, - }) - } - - async prepare({ - resolvers, - onSessionChange, - }: { - // Not awaited in the calling code so we can delay blocking on them. - resolvers: Promise[] - onSessionChange: ( - agent: AtpAgent, - did: string, - event: AtpSessionEvent, - ) => void - }) { - // There's nothing else left to do, so block on them here. - await Promise.all(resolvers) - - // Now the agent is ready. - const account = agentToSessionAccountOrThrow(this) - this.persistSessionHandler = event => { - onSessionChange(this, account.did, event) - if (event !== 'create' && event !== 'update') { - addSessionErrorLog(account.did, event) - } - } - return {account, agent: this} - } - - dispose() { - this.sessionManager.session = undefined - this.persistSessionHandler = undefined - } -} - -export type {BskyAppAgent} diff --git a/src/state/session/bridge-agent.ts b/src/state/session/bridge-agent.ts new file mode 100644 index 0000000000..0c692a6c17 --- /dev/null +++ b/src/state/session/bridge-agent.ts @@ -0,0 +1,442 @@ +import { + AtpAgent, + type AtpAgentLoginOpts, + type AtpSessionData, + type ComAtprotoServerCreateAccount, + type ComAtprotoServerCreateSession, + type ComAtprotoServerRefreshSession, + CredentialSession, +} from '@atproto/api' +import { + type PasswordSession, + type SessionData, +} from '@atproto/lex-password-session' + +import {BLUESKY_PROXY_HEADER, PUBLIC_BSKY_SERVICE} from '#/lib/constants' +import {configureModerationForGuest} from './moderation' +import {networkAwareFetch} from './network' + +const UNSUPPORTED = + 'Not supported on PasswordSessionManager; use the session factories in session-core' + +/** + * Convert live `PasswordSession` session data into the `AtpSessionData` shape + * that `CredentialSession.session` consumers expect. + * + * The only real adaptation is `active`: `AtpSessionData` requires it, while the + * lexicon payload leaves it optional (absent means active, per the lexicon + * docs). + */ +function toAtpSessionData(d: SessionData): AtpSessionData { + return { + refreshJwt: d.refreshJwt, + accessJwt: d.accessJwt, + handle: d.handle, + did: d.did, + email: d.email, + emailConfirmed: d.emailConfirmed, + emailAuthFactor: d.emailAuthFactor, + active: d.active ?? true, + status: d.status, + } +} + +/** + * Parse a URL without throwing. + */ +function parseUrl(input: string): URL | undefined { + try { + return new URL(input) + } catch { + return undefined + } +} + +/** + * Read a property off an unknown value the way JS optional chaining would, + * without narrowing assumptions about the shape of a `LexMap`. + */ +function prop(value: unknown, key: string): unknown { + return typeof value === 'object' && value !== null + ? (value as Record)[key] + : undefined +} + +/** + * The PDS endpoint declared by a DID document, or `undefined`. + * + * This deliberately mirrors `extractPdsUrl` in `@atproto/lex-password-session`, + * which is the predicate `PasswordSession` uses to route its own requests: the + * first service entry whose `id` ends with `#atproto_pds`, taking its + * `serviceEndpoint` if it parses as a URL. It is looser than the + * `isValidDidDoc` + `getPdsEndpoint` pair from `@atproto/common-web` (no doc + * schema validation, no `type` check), and that is the point - a stricter + * predicate here would let `dispatchUrl` disagree with the host requests + * actually go to, which in turn mints service-auth tokens (video upload) for + * the wrong audience. + */ +function extractPdsUrl(didDoc: SessionData['didDoc']): URL | undefined { + const services = prop(didDoc, 'service') + if (!Array.isArray(services)) { + return undefined + } + /* + * `find`, not a scan: the inner session stops at the first `#atproto_pds` + * entry and gives up if its endpoint does not parse, rather than falling + * through to a later entry. + */ + const pds = services.find(service => { + const id = prop(service, 'id') + return typeof id === 'string' && id.endsWith('#atproto_pds') + }) + const endpoint = prop(pds, 'serviceEndpoint') + return typeof endpoint === 'string' ? parseUrl(endpoint) : undefined +} + +/** + * A `CredentialSession` whose auth core is a `PasswordSession`. + * + * This is the compat shim that lets a `PasswordSession` sit under `AtpAgent`: + * every call site that reads `agent.session`, `agent.pdsUrl`, + * `agent.dispatchUrl`, `agent.did` or calls `agent.resumeSession()` keeps + * working, while the actual tokens, refresh serialization and PDS routing live + * in the `PasswordSession` underneath. + * + * A `null` inner session means "logged out" - the public/guest agent. In that + * mode requests still go out (unauthenticated) through the inherited `fetch`. + */ +export class PasswordSessionManager extends CredentialSession { + #inner: PasswordSession | null + #storedPdsUrl: URL | undefined + #disposed = false + + /* + * Identity caches for the two pull-through accessors. Each holds the + * `SessionData` it was derived from so a repeated read returns the very same + * object (see the note on identity stability below). Keying on the whole + * `SessionData` rather than the individual field works because + * `PasswordSession` replaces the object wholesale on every rotation. + */ + #sessionSource: SessionData | undefined + #sessionValue: AtpSessionData | undefined + #pdsSource: SessionData | undefined + #pdsValue: URL | undefined + + constructor( + inner: PasswordSession | null, + {service, pdsUrl}: {service: string; pdsUrl?: string}, + ) { + /* + * `persistSession` is deliberately undefined: the inner `PasswordSession` + * owns persistence through its own hooks, and none of the inherited methods + * that would call this handler survive the overrides below. + */ + super(new URL(service), networkAwareFetch, undefined) + + this.#inner = inner + this.#storedPdsUrl = pdsUrl ? parseUrl(pdsUrl) : undefined + + /* + * `session` and `pdsUrl` are pull-through accessors over the inner session + * rather than mirrored values, installed here with `defineProperty` for two + * reasons. + * + * Why accessors at all: a mirror has to be written on every token rotation, + * and any missed write silently serves stale tokens. Pulling through cannot + * drift. + * + * Why `defineProperty` and not `get session()` in the class body: the + * parent declares `session` and `pdsUrl` as *properties*, and TypeScript + * rejects overriding a property with an accessor (TS2611). Installing them + * at runtime sidesteps that, and it is safe as long as + * `CredentialSession`'s emitted constructor does not assign either one + * (both are declaration-only), so there is nothing to clobber and no + * ordering hazard. + * + * For the same reason this class must NOT redeclare `session`/`pdsUrl` as + * fields: under `useDefineForClassFields` semantics (target esnext) a field + * declaration emits an own-property definition that would overwrite these + * accessors with `undefined`. + */ + Object.defineProperty(this, 'session', { + configurable: true, + get: () => this.#readSession(), + set: () => { + throw new Error('PasswordSessionManager.session is read-only') + }, + }) + Object.defineProperty(this, 'pdsUrl', { + configurable: true, + get: () => this.#readPdsUrl(), + set: () => { + throw new Error('PasswordSessionManager.pdsUrl is read-only') + }, + }) + } + + /** + * The inner session's live data, or `undefined` when there is nothing to read + * from. + * + * `PasswordSession`'s `session`/`did`/`handle` getters *throw* `Logged out` + * once the session has been destroyed. Every read in this class funnels + * through here so that failure mode can never escape into the app, which + * reads `agent.session` from render paths. + */ + #liveData(): SessionData | undefined { + if (this.#disposed || !this.#inner || this.#inner.destroyed) { + return undefined + } + return this.#inner.session + } + + /** + * The `session` accessor's implementation. + * + * Identity-cached on the source `SessionData`: consecutive reads with no + * intervening token rotation return the same object, and a rotation produces + * a new one. `CredentialSession` declares `session` as a plain field, so + * consumers are entitled to treat it as a value whose identity changes only + * when the session does; this class is read from render paths, and returning + * a freshly allocated object on every read would break that expectation for + * any memo, dependency array or reference comparison built on top of it. + */ + #readSession(): AtpSessionData | undefined { + const live = this.#liveData() + if (!live) { + this.#sessionSource = undefined + this.#sessionValue = undefined + return undefined + } + if (live !== this.#sessionSource) { + this.#sessionSource = live + this.#sessionValue = toAtpSessionData(live) + } + return this.#sessionValue + } + + /** + * The `pdsUrl` accessor's implementation. + * + * The DID document's PDS endpoint wins when there is one, derived with + * {@link extractPdsUrl} so this agrees exactly with the inner session's own + * routing. Before the first refresh delivers a didDoc (the non-expired resume + * fast path, which makes no network call) we fall back to the `pdsUrl` + * persisted on the account, so the very first requests still reach the right + * host - entryway accounts have `service: bsky.social` but live on a + * different PDS. + * + * Identity-cached on the didDoc for the same reason as `session`. + */ + #readPdsUrl(): URL | undefined { + const live = this.#liveData() + if (!live) { + this.#pdsSource = undefined + this.#pdsValue = undefined + return undefined + } + if (live !== this.#pdsSource) { + this.#pdsSource = live + this.#pdsValue = extractPdsUrl(live.didDoc) ?? this.#storedPdsUrl + } + return this.#pdsValue + } + + /* + * `did`, `hasSession` and `dispatchUrl` are deliberately NOT overridden: the + * inherited getters read `this.session` / `this.pdsUrl`, which resolve + * through the accessors above, so they are already live. + */ + + override async fetchHandler( + url: string, + init?: RequestInit, + ): Promise { + /* + * Absolutizing against `dispatchUrl` routes to the stored PDS on the resume + * fast path and to the didDoc PDS from the first refresh onwards, since + * `PasswordSession` resolves an already absolute URL against its own base + * as a no-op. + */ + const target = new URL(url, this.dispatchUrl) + const inner = this.#disposed ? null : this.#inner + + /* + * A caller that set its own `authorization` header bypasses the inner + * session entirely. This is mandatory: `PasswordSession.fetchHandler` + * throws `TypeError` on a pre-set authorization header rather than + * deferring to it. + * + * Bypassing also means these requests get no refresh-on-401 retry, since + * that lives in `PasswordSession.fetchHandler`. That is intentional: the + * caller supplied its own credential (a service-auth token, say), so + * rotating the session's tokens would not make the request any more likely + * to succeed on a retry. + */ + if ( + !inner || + inner.destroyed || + new Headers(init?.headers).has('authorization') + ) { + return (0, this.fetch)(target, init) + } + + /* + * `init ?? {}` because `PasswordSession.fetchHandler` reads `init.headers` + * unguarded, while the inherited signature makes `init` optional. + */ + return inner.fetchHandler(target.href, init ?? {}) + } + + /** + * Refresh the session, rejecting if nothing was refreshed. + * + * This restores the contract of the `CredentialSession.refreshSession` this + * class replaces, which rejected on any refresh failure. + * `PasswordSession.refresh()` does not: on a transient failure (a 500, a + * network error) it reports through `onUpdateFailure` and then *resolves* + * with the unchanged session data, reserving rejection for the cases where + * the session is definitively gone. Callers here read resolution as "tokens + * rotated" - `SignupQueued` refreshes and then re-checks the token scope, and + * the various verification dialogs refresh and then close - so a resolved + * no-op would silently loop or report success. + * + * The signal is the identity of the returned `SessionData`, not a field + * comparison: `PasswordSession` builds a brand new object on every successful + * rotation and returns the existing one untouched on a transient failure, so + * identity separates the two exactly. Comparing against the data captured + * immediately before the call also gets concurrent refreshes right - if + * another caller's refresh rotated the tokens while ours was queued behind it + * (`PasswordSession` serializes refreshes), the data we get back still + * differs from what we captured, which is a success for our caller. + */ + override async refreshSession(): Promise { + const inner = this.#disposed ? null : this.#inner + if (!inner || inner.destroyed) { + throw new Error('No session to refresh') + } + const before = this.#liveData() + const data = await inner.refresh() + if (data === before) { + throw new Error('Failed to refresh session') + } + /* + * Re-shape the lex payload into the `@atproto/api` XRPC response envelope. + * `headers` is empty because the inner session does not surface response + * headers, and no caller in this app reads them off a refresh. + */ + return { + success: true, + headers: {}, + data: { + accessJwt: data.accessJwt, + refreshJwt: data.refreshJwt, + handle: data.handle, + did: data.did, + didDoc: data.didDoc, + email: data.email, + emailConfirmed: data.emailConfirmed, + emailAuthFactor: data.emailAuthFactor, + active: data.active, + status: data.status, + }, + } + } + + /** + * Force a refresh, ignoring the passed-in session data. + * + * The inner session already owns its tokens, so there is nothing to install; + * every call site in the app uses `resumeSession` as "refresh my session + * now". The returned envelope is the refresh one, which is structurally a + * superset of `ComAtprotoServerGetSession.Response` (the shape `AtpAgent` + * advertises), so both layers stay type-correct. + * + * It inherits {@link PasswordSessionManager.refreshSession}'s contract, so it + * rejects rather than resolving when no tokens were rotated. + */ + override resumeSession( + _session: AtpSessionData, + ): Promise { + return this.refreshSession() + } + + override async logout(): Promise { + const inner = this.#inner + if (!inner || inner.destroyed) { + return + } + try { + await inner.logout() + } catch { + /* matches the parent, which swallows delete-session failures */ + } + } + + override login( + _opts: AtpAgentLoginOpts, + ): Promise { + return Promise.reject(new Error(UNSUPPORTED)) + } + + override createAccount( + _data: ComAtprotoServerCreateAccount.InputSchema, + _opts?: ComAtprotoServerCreateAccount.CallOptions, + ): Promise { + return Promise.reject(new Error(UNSUPPORTED)) + } + + /** + * Detach this manager from its inner session. + * + * All reads then behave as logged out and requests fall back to the + * unauthenticated `fetch` path. The inner session is left alone: it may still + * be shared, and logging out is a separate, explicit operation. + */ + dispose() { + this.#disposed = true + } +} + +/* + * Declaration merging to narrow the inherited `sessionManager` (typed as + * `CredentialSession` by `AtpAgent`) to the manager `BskyAppAgent` actually + * receives. A `declare` class field would be the direct way to say this, but + * babel's TypeScript transform rejects `declare` fields in this config, and a + * `get sessionManager()` override is forbidden because the parent declares it + * as a property (TS2611). The merge is sound: the constructor passes the + * manager straight to `super`, which assigns it. + */ +// eslint-disable-next-line typescript/no-unsafe-declaration-merging +export interface BskyAppAgent { + readonly sessionManager: PasswordSessionManager +} + +/** + * The app's `AtpAgent`, backed by a `PasswordSession`. + * + * Everything interesting lives in {@link PasswordSessionManager}; this exists + * so `useAgent()` consumers keep getting a real `AtpAgent` (proxy headers, + * labeler headers, the `app`/`com`/`chat` namespaces) and so the agent can be + * disposed alongside its session. + */ +export class BskyAppAgent extends AtpAgent { + constructor(manager: PasswordSessionManager) { + super(manager) + } + + dispose() { + this.sessionManager.dispose() + } +} + +/** Build the logged-out agent used for public/guest browsing. */ +export function createPublicAgent() { + configureModerationForGuest() // Side effect but only relevant for tests + + const agent = new BskyAppAgent( + new PasswordSessionManager(null, {service: PUBLIC_BSKY_SERVICE}), + ) + agent.configureProxy(BLUESKY_PROXY_HEADER.get()) + return agent +} diff --git a/src/state/session/create-account.ts b/src/state/session/create-account.ts new file mode 100644 index 0000000000..fd67681482 --- /dev/null +++ b/src/state/session/create-account.ts @@ -0,0 +1,243 @@ +import {type AppBskyActorProfile, type Un$Typed} from '@atproto/api' +import {TID} from '@atproto/common-web' +import {PasswordSession} from '@atproto/lex-password-session' + +import {networkRetry} from '#/lib/async/retry' +import { + BLUESKY_PROXY_HEADER, + DISCOVER_SAVED_FEED, + IS_PROD_SERVICE, + TIMELINE_SAVED_FEED, +} from '#/lib/constants' +import {logger} from '#/logger' +import {snoozeBirthdateUpdateAllowedForDid} from '#/state/birthdate' +import {restrictChatSettings} from '#/state/queries/messages/restrictChatSettings' +import {snoozeEmailConfirmationPrompt} from '#/state/shell/reminders' +import { + prefetchAgeAssuranceServerData, + setBirthdateForDid, + setCreatedAtForDid, +} from '#/ageAssurance/data' +import {unsafeGetAndComputeAgeAssurance} from '#/ageAssurance/state' +import {features} from '#/analytics' +import {type BskyAppAgent} from './bridge-agent' +import {configureModerationForAccount} from './moderation' +import { + buildBundle, + finishPreparation, + makeSessionHooks, + type OnSessionChange, + registerBundleKillSwitch, + type SessionBundle, +} from './session-core' +import {sessionDataToSessionAccount} from './session-data' +import {type SessionAccount} from './types' + +/** Create an account, prepare its session, and start post-signup writes. */ +export async function createSessionBundleAndCreateAccount( + { + service, + email, + password, + handle, + birthDate, + inviteCode, + verificationPhone, + verificationCode, + }: { + service: string + email: string + password: string + handle: string + birthDate: Date + inviteCode?: string + verificationPhone?: string + verificationCode?: string + }, + onSessionChange: OnSessionChange, +): Promise<{account: SessionAccount; bundle: SessionBundle}> { + let bundle!: SessionBundle + let accountDid = '' + const hooks = makeSessionHooks({ + onSessionChange, + getBundle: () => bundle, + getDid: () => accountDid, + }) + + const session = await PasswordSession.createAccount( + { + email, + password, + /* the lexicon types handle as `${string}.${string}`; user input is a plain string */ + handle: handle as `${string}.${string}`, + inviteCode, + verificationPhone, + verificationCode, + }, + {...hooks, service}, + ) + + bundle = buildBundle(session) + registerBundleKillSwitch(bundle, hooks.kill) + // Seed the hook and the deferred writes with refresh-stable account fields. + const earlyAccount = snapshotNewAccount(session, email) + accountDid = earlyAccount.did + + const gates = features.refresh({strategy: 'prefer-fresh-gates'}) + configureModerationForAccount(bundle.agent, earlyAccount) + + const createdAt = new Date().toISOString() + const birthdate = birthDate.toISOString() + + /* + * Since we have a race with account creation, profile creation, and AA + * state, set these values locally to ensure sync reads. Values are written + * to the server in the next step, so on subsequent reloads, the server will + * be the source of truth. + */ + setCreatedAtForDid({did: earlyAccount.did, createdAt}) + setBirthdateForDid({did: earlyAccount.did, birthdate}) + snoozeBirthdateUpdateAllowedForDid(earlyAccount.did) + // Start the prefetch after seeding its synchronous birthdate inputs. + const aa = prefetchAgeAssuranceServerData({agent: bundle.agent}) + + const isProd = Boolean(IS_PROD_SERVICE(service)) + const postSignupTasks: Promise[] = [ + savePersonalDetails(bundle.agent, birthdate), + initializeProfile(bundle.agent, {handle, createdAt, isProd}), + ] + if (isProd) { + postSignupTasks.push( + initializeSavedFeeds(bundle.agent), + restrictChatAfterAgeAssurance(aa, bundle.agent, earlyAccount.did), + ) + } + // Post-signup writes are not required to enter onboarding. + void reportPostSignupFailures(postSignupTasks) + + try { + // snooze first prompt after signup, defer to next prompt + snoozeEmailConfirmationPrompt() + } catch (e) { + logger.error(e instanceof Error ? e : String(e), { + message: `session: failed snoozeEmailConfirmationPrompt`, + }) + } + + bundle.agent.configureProxy(BLUESKY_PROXY_HEADER.get()) + + // Preparation may auto-refresh the session while hooks are still disarmed. + const account = await finishPreparation( + bundle, + Promise.all([gates, aa]), + () => snapshotNewAccount(session, email), + ) + hooks.arm() + return {account, bundle} +} + +/** + * Snapshot a just-created session as a `SessionAccount`. + * + * `com.atproto.server.createAccount` returns only tokens, handle, did and + * didDoc, so the session carries no email state or `active` flag until the + * first refresh. Synthesize those fields from the creation input so the + * persisted account does not briefly claim the email is unknown. + */ +function snapshotNewAccount( + session: PasswordSession, + email: string, +): SessionAccount { + const account = sessionDataToSessionAccount( + session.session, + session.session.service, + ) + if (!account) { + throw Error('Expected an active session') + } + return { + ...account, + email: account.email ?? email, + emailConfirmed: account.emailConfirmed ?? false, + emailAuthFactor: account.emailAuthFactor ?? false, + active: account.active ?? true, + } +} + +function savePersonalDetails(agent: BskyAppAgent, birthDate: string) { + return retryPostSignupTask('set birthDate', 3, () => + agent.setPersonalDetails({birthDate}), + ) +} + +function initializeProfile( + agent: BskyAppAgent, + { + handle, + createdAt, + isProd, + }: { + handle: string + createdAt: string + isProd: boolean + }, +) { + return retryPostSignupTask('set initial profile', 3, () => + agent.upsertProfile(prev => { + const next: Un$Typed = prev || {} + if (isProd) { + next.displayName = handle + next.createdAt = createdAt + } else { + next.createdAt = prev?.createdAt || new Date().toISOString() + } + return next + }), + ) +} + +function initializeSavedFeeds(agent: BskyAppAgent) { + return retryPostSignupTask('set initial feeds', 1, () => + agent.overwriteSavedFeeds([ + {...DISCOVER_SAVED_FEED, id: TID.nextStr()}, + {...TIMELINE_SAVED_FEED, id: TID.nextStr()}, + ]), + ) +} + +function restrictChatAfterAgeAssurance( + ageAssurance: Promise, + agent: BskyAppAgent, + did: string, +) { + return ageAssurance.then(() => { + const {flags} = unsafeGetAndComputeAgeAssurance({did}) + if (flags?.chatDisabled || flags?.groupChatDisabled) { + void restrictChatSettings({ + agent, + restrictIncoming: flags.chatDisabled, + restrictGroupInvites: flags.groupChatDisabled, + }) + } + }) +} + +function retryPostSignupTask( + description: string, + retries: number, + task: () => Promise, +) { + return networkRetry(retries, task).catch(e => { + logger.info(`createSessionBundleAndCreateAccount: failed to ${description}`) + throw e + }) +} + +async function reportPostSignupFailures(tasks: Promise[]) { + const results = await Promise.allSettled(tasks) + if (results.some(result => result.status === 'rejected')) { + logger.error( + `session: createSessionBundleAndCreateAccount failed to save post-signup settings`, + ) + } +} diff --git a/src/state/session/expiry-rescue.ts b/src/state/session/expiry-rescue.ts new file mode 100644 index 0000000000..f750a1ba5c --- /dev/null +++ b/src/state/session/expiry-rescue.ts @@ -0,0 +1,30 @@ +import {type SessionAccount} from './types' + +/** Maximum failed token generations considered during one expiry rescue. */ +export const MAX_EXPIRY_RESCUE_GENERATIONS = 5 + +/** Pick the first unfailed token generation newer than the one that expired. */ +export function pickExpiryRescueCandidate({ + dyingRefreshJwt, + candidates, + failedRefreshJwts, +}: { + dyingRefreshJwt: string + candidates: (SessionAccount | undefined)[] + failedRefreshJwts: ReadonlySet +}): SessionAccount | undefined { + if (failedRefreshJwts.size >= MAX_EXPIRY_RESCUE_GENERATIONS) { + return undefined + } + for (const candidate of candidates) { + const refreshJwt = candidate?.refreshJwt + if ( + refreshJwt && + refreshJwt !== dyingRefreshJwt && + !failedRefreshJwts.has(refreshJwt) + ) { + return candidate + } + } + return undefined +} diff --git a/src/state/session/index.tsx b/src/state/session/index.tsx index fd31261a9d..1a22a0e215 100644 --- a/src/state/session/index.tsx +++ b/src/state/session/index.tsx @@ -3,12 +3,14 @@ import { useCallback, useContext, useEffect, + useInsertionEffect, useMemo, useRef, useState, useSyncExternalStore, } from 'react' -import {type AtpAgent, type AtpSessionEvent} from '@atproto/api' +import {type AtpAgent} from '@atproto/api' +import {type SessionData} from '@atproto/lex-password-session' import * as persisted from '#/state/persisted' import {useCloseAllActiveElements} from '#/state/util' @@ -16,17 +18,28 @@ import {useGlobalDialogsControlContext} from '#/components/dialogs/Context' import {AnalyticsContext, useAnalyticsBase, utils} from '#/analytics' import {IS_WEB} from '#/env' import {emitSessionDropped} from '../events' -import { - agentToSessionAccount, - type BskyAppAgent, - createAgentAndCreateAccount, - createAgentAndLogin, - createAgentAndResume, - sessionAccountToSession, -} from './agent' +import {createSessionBundleAndCreateAccount} from './create-account' +import {pickExpiryRescueCandidate} from './expiry-rescue' import {type Action, getInitialState, reducer, type State} from './reducer' -export {isSignupQueued} from './util' -import {addSessionDebugLog} from './logging' +import { + type AtpSessionEvent, + createSessionBundleAndLogin, + createSessionBundleAndResume, + createSessionBundleFromStoredAccount, + disposeBundle, + type PublicSessionBundle, + type SessionBundle, + sessionDataToSessionAccount, +} from './session-core' +export {isSignupQueued} from './session-data' +import { + addSessionDebugLog, + getBundleId, + redactAccount, + redactPersistedSession, + redactSessionData, + redactState, +} from './logging' export type {SessionAccount} from '#/state/session/types' import {clearPersistedQueryStorage} from '#/lib/persisted-query-storage' @@ -47,8 +60,11 @@ const StateContext = createContext({ }) StateContext.displayName = 'SessionStateContext' -const AgentContext = createContext(null) -AgentContext.displayName = 'SessionAgentContext' +/** Active account bundle, or the public bundle when logged out. */ +const BundleContext = createContext( + null, +) +BundleContext.displayName = 'SessionBundleContext' const ApiContext = createContext({ createAccount: async () => {}, @@ -68,7 +84,7 @@ class SessionStore { constructor() { // Careful: By the time this runs, `persisted` needs to already be filled. const initialState = getInitialState(persisted.get('session').accounts) - addSessionDebugLog({type: 'reducer:init', state: initialState}) + addSessionDebugLog({type: 'reducer:init', state: redactState(initialState)}) this.state = initialState } @@ -92,10 +108,13 @@ class SessionStore { const persistedData = { accounts: nextState.accounts, currentAccount: nextState.accounts.find( - a => a.did === nextState.currentAgentState.did, + a => a.did === nextState.currentBundleState.did, ), } - addSessionDebugLog({type: 'persisted:broadcast', data: persistedData}) + addSessionDebugLog({ + type: 'persisted:broadcast', + data: redactPersistedSession(persistedData), + }) void persisted.write('session', persistedData) } this.listeners.forEach(listener => listener()) @@ -110,15 +129,118 @@ export function Provider({children}: React.PropsWithChildren<{}>) { const state = useSyncExternalStore(store.subscribe, store.getState) const onboardingDispatch = useOnboardingDispatch() - const onAgentSessionChange = useCallback( - (agent: AtpAgent, accountDid: string, sessionEvent: AtpSessionEvent) => { - const refreshedAccount = agentToSessionAccount(agent) // Mutable, so snapshot it right away. - if (sessionEvent === 'expired' || sessionEvent === 'create-failed') { + // Refresh-token generations that have already failed during expiry rescue. + const failedExpiryTokensRef = useRef>>(new Map()) + /* + * Rescued bundles need this callback for their own events. A ref avoids a + * self-reference in the callback's dependency list. It is filled by the + * insertion effect below, which commits well before any session hook can + * fire: hooks are armed only after an asynchronous session factory resolves. + */ + const onSessionChangeRef = useRef< + | (( + bundle: SessionBundle, + accountDid: string, + sessionEvent: AtpSessionEvent, + sessionData?: SessionData, + ) => void) + | null + >(null) + + const onSessionChange = useCallback( + ( + bundle: SessionBundle, + accountDid: string, + sessionEvent: AtpSessionEvent, + sessionData?: SessionData, + ) => { + if (sessionEvent === 'update' && sessionData) { + failedExpiryTokensRef.current.get(accountDid)?.clear() + } + + /* + * PasswordSession invokes its hooks before updating its live getter. Use + * the delivered payload so a refresh persists the newly rotated tokens. + * + * A refresh payload carries no didDoc unless the server sends one, so the + * stored account's `pdsUrl` is threaded in as the fallback. Without it the + * refresh would persist `pdsUrl: undefined` and the next cold start would + * route pre-refresh requests to the entryway instead of the PDS. + */ + const refreshedAccount = + sessionEvent === 'update' && sessionData + ? sessionDataToSessionAccount( + sessionData, + sessionData.service, + store.getState().accounts.find(a => a.did === accountDid)?.pdsUrl, + ) + : undefined + + /* + * A stale tab may expire a token after another tab has already rotated it. + * Prefer a newer persisted or reducer generation over logging every tab + * out. Failed generations are recorded and bounded to guarantee that a + * repeatedly expiring session eventually falls through to logout. + */ + if (sessionEvent === 'expired') { + const current = store.getState() + const currentBundle = current.currentBundleState.bundle as unknown as + | SessionBundle + | PublicSessionBundle + const dyingRefreshJwt = sessionData?.refreshJwt + // Stale bundle events are handled by the reducer's identity guard. + if ( + currentBundle === bundle && + current.currentBundleState.did === accountDid && + dyingRefreshJwt + ) { + let failedSet = failedExpiryTokensRef.current.get(accountDid) + if (!failedSet) { + failedSet = new Set() + failedExpiryTokensRef.current.set(accountDid, failedSet) + } + failedSet.add(dyingRefreshJwt) + + const persistedCandidate = persisted + .readLatest('session') + .accounts.find(a => a.did === accountDid) + const reducerCandidate = current.accounts.find( + a => a.did === accountDid, + ) + const candidate = pickExpiryRescueCandidate({ + dyingRefreshJwt, + candidates: [persistedCandidate, reducerCandidate], + failedRefreshJwts: failedSet, + }) + + if (candidate) { + const rebuilt = createSessionBundleFromStoredAccount( + candidate, + onSessionChangeRef.current!, + ) + if (rebuilt) { + store.dispatch({ + type: 'replaced-current-bundle', + newBundle: rebuilt.bundle, + newAccount: rebuilt.account, + }) + return + } + } + } + } + + // Only the current bundle may report that its session was dropped. + if ( + sessionEvent === 'expired' && + store.getState().currentBundleState.bundle === bundle + ) { emitSessionDropped() } + // Bundle identity prevents stale sessions from changing the active account. store.dispatch({ - type: 'received-agent-event', - agent, + type: 'received-session-event', + bundle, refreshedAccount, accountDid, sessionEvent, @@ -126,48 +248,65 @@ export function Provider({children}: React.PropsWithChildren<{}>) { }, [store], ) + /* + * Writing the ref during render is forbidden under React Compiler. An + * insertion effect is the earliest commit-time slot, and the only reader + * (`onSessionChange`'s expiry-rescue path) runs from armed session hooks, + * which cannot fire before the first commit. + */ + useInsertionEffect(() => { + onSessionChangeRef.current = onSessionChange + }, [onSessionChange]) const createAccount = useCallback( async (params, metrics) => { addSessionDebugLog({type: 'method:start', method: 'createAccount'}) const signal = cancelPendingTask() ax.metric('account:create:begin', {}) - const {agent, account} = await createAgentAndCreateAccount( + const {bundle, account} = await createSessionBundleAndCreateAccount( params, - onAgentSessionChange, + onSessionChange, ) if (signal.aborted) { + // The factory returns an armed bundle, so a superseded signup must dispose it. + disposeBundle(bundle) return } store.dispatch({ type: 'switched-to-account', - newAgent: agent, + newBundle: bundle, newAccount: account, }) ax.metric('account:create:success', metrics, { session: utils.accountToSessionMetadata(account), }) - addSessionDebugLog({type: 'method:end', method: 'createAccount', account}) + addSessionDebugLog({ + type: 'method:end', + method: 'createAccount', + account: redactAccount(account), + }) }, - [ax, store, onAgentSessionChange, cancelPendingTask], + [ax, store, onSessionChange, cancelPendingTask], ) const login = useCallback( async (params, logContext) => { addSessionDebugLog({type: 'method:start', method: 'login'}) const signal = cancelPendingTask() - const {agent, account} = await createAgentAndLogin( + const {bundle, account} = await createSessionBundleAndLogin( params, - onAgentSessionChange, + onSessionChange, ) if (signal.aborted) { + // The factory returns an armed bundle, so a superseded login must dispose it. + disposeBundle(bundle) return } store.dispatch({ type: 'switched-to-account', - newAgent: agent, + newBundle: bundle, newAccount: account, }) ax.metric( @@ -175,9 +314,13 @@ export function Provider({children}: React.PropsWithChildren<{}>) { {logContext, withPassword: true}, {session: utils.accountToSessionMetadata(account)}, ) - addSessionDebugLog({type: 'method:end', method: 'login', account}) + addSessionDebugLog({ + type: 'method:end', + method: 'login', + account: redactAccount(account), + }) }, - [ax, store, onAgentSessionChange, cancelPendingTask], + [ax, store, onSessionChange, cancelPendingTask], ) const logoutCurrentAccount = useCallback< @@ -196,17 +339,17 @@ export function Provider({children}: React.PropsWithChildren<{}>) { { session: utils.accountToSessionMetadata( prevState.accounts.find( - a => a.did === prevState.currentAgentState.did, + a => a.did === prevState.currentBundleState.did, ), ), }, ) addSessionDebugLog({type: 'method:end', method: 'logout'}) - if (prevState.currentAgentState.did) { + if (prevState.currentBundleState.did) { clearAgeAssuranceServerDataForDid({ - did: prevState.currentAgentState.did, + did: prevState.currentBundleState.did, }) - void clearPersistedQueryStorage(prevState.currentAgentState.did) + void clearPersistedQueryStorage(prevState.currentBundleState.did) } // reset onboarding flow on logout onboardingDispatch({type: 'skip'}) @@ -230,7 +373,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) { { session: utils.accountToSessionMetadata( prevState.accounts.find( - a => a.did === prevState.currentAgentState.did, + a => a.did === prevState.currentBundleState.did, ), ), }, @@ -251,61 +394,94 @@ export function Provider({children}: React.PropsWithChildren<{}>) { addSessionDebugLog({ type: 'method:start', method: 'resumeSession', - account: storedAccount, + account: redactAccount(storedAccount), }) const signal = cancelPendingTask() - const {agent, account} = await createAgentAndResume( + const {bundle, account} = await createSessionBundleAndResume( storedAccount, - onAgentSessionChange, + onSessionChange, ) if (signal.aborted) { + // The factory returns an armed bundle, so a superseded resume must dispose it. + disposeBundle(bundle) + return + } + /* + * A cross-tab logout may clear or remove the account while resume is in + * flight. Check the account entry rather than the current did so ordinary + * account switching remains valid. + */ + const latest = store.getState() + const latestEntry = latest.accounts.find(a => a.did === account.did) + if (!latestEntry || !latestEntry.refreshJwt) { + disposeBundle(bundle) return } store.dispatch({ type: 'switched-to-account', - newAgent: agent, + newBundle: bundle, newAccount: account, }) - addSessionDebugLog({type: 'method:end', method: 'resumeSession', account}) + addSessionDebugLog({ + type: 'method:end', + method: 'resumeSession', + account: redactAccount(account), + }) if (isSwitchingAccounts) { // reset onboarding flow on switch account onboardingDispatch({type: 'skip'}) } }, - [store, onAgentSessionChange, cancelPendingTask, onboardingDispatch], + [store, onSessionChange, cancelPendingTask, onboardingDispatch], ) const partialRefreshSession = useCallback< SessionApiContext['partialRefreshSession'] >(async () => { - const agent = state.currentAgentState.agent as BskyAppAgent + /* + * Read the live bundle rather than the one captured by this render: a + * dispatch that lands before the next render would otherwise leave this + * holding a disposed bundle, whose agent dispatches unauthenticated. + */ + const bundle = store.getState().currentBundleState + .bundle as unknown as SessionBundle const signal = cancelPendingTask() - const {data} = await agent.com.atproto.server.getSession() + /* getSession targets the PDS; only the persisted account fields are patched. */ + const {data} = await bundle.agent.com.atproto.server.getSession() if (signal.aborted) return store.dispatch({ type: 'partial-refresh-session', - accountDid: agent.session!.did, + /* + * Read the did off the response rather than the session: the bundle may + * have been disposed while the request was in flight, and the live + * getters throw in that state. + */ + accountDid: data.did, patch: { emailConfirmed: data.emailConfirmed, emailAuthFactor: data.emailAuthFactor, }, }) - }, [store, state, cancelPendingTask]) + }, [store, cancelPendingTask]) const removeAccount = useCallback( account => { addSessionDebugLog({ type: 'method:start', method: 'removeAccount', - account, + account: redactAccount(account), }) cancelPendingTask() store.dispatch({ type: 'removed-account', accountDid: account.did, }) - addSessionDebugLog({type: 'method:end', method: 'removeAccount', account}) + addSessionDebugLog({ + type: 'method:end', + method: 'removeAccount', + account: redactAccount(account), + }) clearAgeAssuranceServerDataForDid({did: account.did}) }, [store, cancelPendingTask], @@ -313,7 +489,10 @@ export function Provider({children}: React.PropsWithChildren<{}>) { useEffect(() => { return persisted.onUpdate('session', nextSession => { const synced = nextSession - addSessionDebugLog({type: 'persisted:receive', data: synced}) + addSessionDebugLog({ + type: 'persisted:receive', + data: redactPersistedSession(synced), + }) store.dispatch({ type: 'synced-accounts', syncedAccounts: synced.accounts, @@ -322,38 +501,91 @@ export function Provider({children}: React.PropsWithChildren<{}>) { const syncedAccount = synced.accounts.find( a => a.did === synced.currentAccount?.did, ) + /* + * Cancel pending work when another tab logs out the account this tab + * considers current. Do not cancel unrelated work between logged-out tabs. + */ + const syncedDid = syncedAccount?.refreshJwt + ? syncedAccount.did + : undefined + if ( + syncedDid === undefined && + state.currentBundleState.did !== undefined + ) { + cancelPendingTask() + } if (syncedAccount && syncedAccount.refreshJwt) { - if (syncedAccount.did !== state.currentAgentState.did) { - /* - * Web handling: if leader tab has switched to a diff account that is - * stale, it will refresh the session before triggering the update to - * follower tabs. Follower tabs will therefore receive the fresh - * session. See APP-1960, or ask Eric. - */ + if (syncedAccount.did !== state.currentBundleState.did) { + // The leader refreshes before broadcasting, so followers receive fresh tokens. void resumeSession(syncedAccount) } else { - const agent = state.currentAgentState.agent as AtpAgent - const prevSession = agent.session - // eslint-disable-next-line react-compiler/react-compiler - agent.sessionManager.session = sessionAccountToSession(syncedAccount) - addSessionDebugLog({ - type: 'agent:patch', - agent, - prevSession, - nextSession: agent.session, + /* + * PasswordSession cannot be patched in place. Rebuild from the tokens + * the leader already refreshed, then dispose the previous bundle. + */ + const prevBundle = state.currentBundleState.bundle as unknown as + | SessionBundle + | PublicSessionBundle + // Avoid replacing the live bundle for an unrelated account update. + const live = + prevBundle.session && !prevBundle.session.destroyed + ? prevBundle.session.session + : undefined + if ( + live && + live.accessJwt === syncedAccount.accessJwt && + live.refreshJwt === syncedAccount.refreshJwt + ) { + return + } + const rebuilt = createSessionBundleFromStoredAccount( + syncedAccount, + onSessionChange, + newBundle => { + const current = store.getState() + const latestAccount = current.accounts.find( + account => account.did === syncedAccount.did, + ) + const isCurrent = + current.currentBundleState.bundle === prevBundle && + latestAccount?.accessJwt === syncedAccount.accessJwt && + latestAccount?.refreshJwt === syncedAccount.refreshJwt + if (isCurrent) { + addSessionDebugLog({ + type: 'bundle:patch', + bundleId: getBundleId(newBundle), + prevSession: redactSessionData( + prevBundle.session && !prevBundle.session.destroyed + ? prevBundle.session.session + : undefined, + ), + nextSession: redactSessionData(newBundle.session.session), + }) + } + return isCurrent + }, + ) + if (!rebuilt) { + return + } + const {bundle: newBundle, account: newAccount} = rebuilt + store.dispatch({ + type: 'replaced-current-bundle', + newBundle, + newAccount, }) } } }) - }, [store, state, resumeSession]) + }, [store, state, resumeSession, onSessionChange, cancelPendingTask]) const stateContext = useMemo( () => ({ accounts: state.accounts, currentAccount: state.accounts.find( - a => a.did === state.currentAgentState.did, + a => a.did === state.currentBundleState.did, ), - hasSession: !!state.currentAgentState.did, + hasSession: !!state.currentBundleState.did, }), [state], ) @@ -379,26 +611,38 @@ export function Provider({children}: React.PropsWithChildren<{}>) { ], ) + const bundle = state.currentBundleState.bundle as unknown as + | SessionBundle + | PublicSessionBundle + // @ts-expect-error window type is not declared, debug only // eslint-disable-next-line react-hooks/immutability - if (__DEV__ && IS_WEB) window.agent = state.currentAgentState.agent + if (__DEV__ && IS_WEB) window.agent = bundle.agent - const agent = state.currentAgentState.agent as BskyAppAgent - const currentAgentRef = useRef(agent) + const currentBundleRef = useRef(bundle) + /* + * Disposal is deferred to this post-commit effect deliberately: components may + * still render against the outgoing bundle during the commit that swaps it, so + * tearing its agent down inline would pull the agent out from under them. The + * reducer's bundle-identity guard drops any events the not-yet-disposed session + * emits in that window. + */ useEffect(() => { - if (currentAgentRef.current !== agent) { - // Read the previous value and immediately advance the pointer. - const prevAgent = currentAgentRef.current - currentAgentRef.current = agent - addSessionDebugLog({type: 'agent:switch', prevAgent, nextAgent: agent}) - // We never reuse agents so let's fully neutralize the previous one. - // This ensures it won't try to consume any refresh tokens. - prevAgent.dispose() + if (currentBundleRef.current !== bundle) { + const prevBundle = currentBundleRef.current + currentBundleRef.current = bundle + addSessionDebugLog({ + type: 'bundle:switch', + prevBundleId: getBundleId(prevBundle), + nextBundleId: getBundleId(bundle), + }) + // Replaced bundles must never consume another refresh token. + disposeBundle(prevBundle) } - }, [agent]) + }, [bundle]) return ( - + ) { - + ) } @@ -453,10 +697,13 @@ export function useRequireAuth() { ) } +/** + * The active session's agent, or the public agent when logged out. + */ export function useAgent(): AtpAgent { - const agent = useContext(AgentContext) - if (!agent) { + const bundle = useContext(BundleContext) + if (!bundle) { throw Error('useAgent() must be below .') } - return agent + return bundle.agent } diff --git a/src/state/session/logging.ts b/src/state/session/logging.ts index da017d823f..adc495d54d 100644 --- a/src/state/session/logging.ts +++ b/src/state/session/logging.ts @@ -1,21 +1,140 @@ -import {type AtpSessionData, type AtpSessionEvent} from '@atproto/api' +import {type SessionData} from '@atproto/lex-password-session' import {type Schema} from '../persisted' import {type Action, type State} from './reducer' -import {type SessionAccount} from './types' +import {type AtpSessionEvent, type SessionAccount} from './types' type Reducer = (state: State, action: Action) => State +/** + * An account reduced to the fields that are safe to ship off-device. + * + * Credentials become presence booleans and PII (email, service, pdsUrl) is + * dropped entirely. The log only ever needs to answer "which account was live, + * and did it still hold credentials?". The stubs at the bottom of this file are + * expected to be revived against Sentry or Bitdrift, so no payload type may be + * capable of carrying a live JWT in the first place. + */ +export type RedactedAccount = { + did: string + handle: string + active: boolean | undefined + status: string | undefined + signupQueued: boolean | undefined + hasAccessJwt: boolean + hasRefreshJwt: boolean +} + +/** The account list plus the current did, in redacted form. */ +export type RedactedSessionSnapshot = { + accounts: RedactedAccount[] + currentDid: string | undefined +} + +/** Live session data reduced to identity plus credential presence. */ +export type RedactedSessionData = { + did: string + handle: string + hasAccessJwt: boolean + hasRefreshJwt: boolean +} + +function redact(account: SessionAccount): RedactedAccount { + return { + did: account.did, + handle: account.handle, + active: account.active, + status: account.status, + signupQueued: account.signupQueued, + hasAccessJwt: !!account.accessJwt, + hasRefreshJwt: !!account.refreshJwt, + } +} + +export function redactAccount( + account: SessionAccount | undefined, +): RedactedAccount | undefined { + return account ? redact(account) : undefined +} + +export function redactState(state: State): RedactedSessionSnapshot { + return { + accounts: state.accounts.map(redact), + currentDid: state.currentBundleState.did, + } +} + +export function redactPersistedSession( + data: Schema['session'], +): RedactedSessionSnapshot { + return { + accounts: data.accounts.map(redact), + currentDid: data.currentAccount?.did, + } +} + +export function redactSessionData( + data: SessionData | undefined, +): RedactedSessionData | undefined { + if (!data) return undefined + return { + did: data.did, + handle: data.handle, + hasAccessJwt: !!data.accessJwt, + hasRefreshJwt: !!data.refreshJwt, + } +} + +/* + * Bundles are logged for identity only - which one was live, and which one + * replaced it. Logging the object itself would reach its session, and through + * it the tokens, so each is mapped to an opaque per-run id instead. + */ +const bundleIds = new WeakMap() +const runId = Math.random().toString(36).slice(2) +let nextBundleId = 1 + +export function getBundleId(bundle: object): string { + let id = bundleIds.get(bundle) + if (id === undefined) { + id = runId + '::' + nextBundleId++ + bundleIds.set(bundle, id) + } + return id +} + +/** An action reduced to its discriminant plus the did it targets, if any. */ +type RedactedAction = { + type: Action['type'] + accountDid?: string +} + +function redactAction(action: Action): RedactedAction { + switch (action.type) { + case 'received-session-event': + case 'removed-account': + case 'partial-refresh-session': + return {type: action.type, accountDid: action.accountDid} + case 'switched-to-account': + case 'replaced-current-bundle': + return {type: action.type, accountDid: action.newAccount.did} + case 'synced-accounts': + return {type: action.type, accountDid: action.syncedCurrentDid} + default: + return {type: action.type} + } +} + type Log = | { type: 'reducer:init' - state: State + state: RedactedSessionSnapshot } | { type: 'reducer:call' - action: Action - prevState: State - nextState: State + action: RedactedAction + prevState: RedactedSessionSnapshot + nextState: RedactedSessionSnapshot } | { type: 'method:start' @@ -25,7 +144,7 @@ type Log = | 'logout' | 'resumeSession' | 'removeAccount' - account?: SessionAccount + account?: RedactedAccount } | { type: 'method:end' @@ -35,32 +154,41 @@ type Log = | 'logout' | 'resumeSession' | 'removeAccount' - account?: SessionAccount + account?: RedactedAccount } | { type: 'persisted:broadcast' - data: Schema['session'] + data: RedactedSessionSnapshot } | { type: 'persisted:receive' - data: Schema['session'] + data: RedactedSessionSnapshot } | { - type: 'agent:switch' - prevAgent: object - nextAgent: object + type: 'bundle:switch' + prevBundleId: string + nextBundleId: string } | { - type: 'agent:patch' - agent: object - prevSession: AtpSessionData | undefined - nextSession: AtpSessionData | undefined + /* + * Dev-only bundle-swap log. Bundles are identified by their opaque ids; + * the session snapshots record only identity and credential presence. + */ + type: 'bundle:patch' + bundleId: string + prevSession: RedactedSessionData | undefined + nextSession: RedactedSessionData | undefined } export function wrapSessionReducerForLogging(reducer: Reducer): Reducer { return function loggingWrapper(prevState: State, action: Action): State { const nextState = reducer(prevState, action) - addSessionDebugLog({type: 'reducer:call', prevState, action, nextState}) + addSessionDebugLog({ + type: 'reducer:call', + prevState: redactState(prevState), + action: redactAction(action), + nextState: redactState(nextState), + }) return nextState } } diff --git a/src/state/session/moderation.ts b/src/state/session/moderation.ts index 8fc234d732..2354fab4fa 100644 --- a/src/state/session/moderation.ts +++ b/src/state/session/moderation.ts @@ -1,10 +1,31 @@ import {AtpAgent, BSKY_LABELER_DID} from '@atproto/api' import {IS_TEST_USER} from '#/lib/constants' +import {account as accountStorage} from '#/storage' import {configureAdditionalModerationAuthorities} from './additional-moderation-authorities' -import {readLabelers} from './agent-config' import {type SessionAccount} from './types' +/** + * Cache an account's subscribed labeler DIDs. Called on every preferences + * fetch, so the cache is eventually consistent with the server. + */ +export function saveLabelers(did: string, value: string[]) { + accountStorage.set([did, 'labelers'], value) +} + +/** + * Read the cached labeler DIDs for an account, or `undefined` if none have + * been cached yet (first session on this device) or the entry is unreadable. + */ +export function readLabelers(did: string): string[] | undefined { + try { + return accountStorage.get([did, 'labelers']) + } catch { + /* a corrupt entry fails JSON.parse inside Storage.get; treat as no cache */ + return undefined + } +} + export function configureModerationForGuest() { // This global mutation is *only* OK because this code is only relevant for testing. // Don't add any other global behavior here! @@ -12,7 +33,12 @@ export function configureModerationForGuest() { configureAdditionalModerationAuthorities() } -export async function configureModerationForAccount( +/** + * Configure global app labelers and the account's cached labeler + * subscriptions. Fully synchronous so session setup can apply labeler headers + * in the same tick, before any request goes out. + */ +export function configureModerationForAccount( agent: AtpAgent, account: SessionAccount, ) { @@ -20,11 +46,12 @@ export async function configureModerationForAccount( // Don't add any other global behavior here! switchToBskyAppLabeler() if (IS_TEST_USER(account.handle)) { - await trySwitchToTestAppLabeler(agent) + // Test accounts may briefly use the production authority while this resolves. + void trySwitchToTestAppLabeler(agent) } // The code below is actually relevant to production (and isn't global). - const labelerDids = await readLabelers(account.did).catch(_ => {}) + const labelerDids = readLabelers(account.did) if (labelerDids) { agent.configureLabelersHeader( labelerDids.filter(did => did !== BSKY_LABELER_DID), @@ -41,6 +68,7 @@ function switchToBskyAppLabeler() { AtpAgent.configure({appLabelers: [BSKY_LABELER_DID]}) } +/** Resolve and install the test environment's moderation authority. */ async function trySwitchToTestAppLabeler(agent: AtpAgent) { const did = ( await agent diff --git a/src/state/session/network.ts b/src/state/session/network.ts new file mode 100644 index 0000000000..84e92d564b --- /dev/null +++ b/src/state/session/network.ts @@ -0,0 +1,23 @@ +import {emitNetworkConfirmed, emitNetworkLost} from '#/state/events' + +/* + * Captured once at module load so the wrapper below is immune to later + * monkey-patching of globalThis.fetch. + */ +const realFetch = globalThis.fetch + +/** + * Fetch wrapper that reports network reachability to the app-wide event bus. + * Any resolved response (including HTTP errors) confirms the network is up; a + * thrown error (DNS failure, timeout, offline) reports it as lost. + */ +export const networkAwareFetch: typeof fetch = async (...args) => { + try { + const res = await realFetch(...args) + emitNetworkConfirmed() + return res + } catch (e) { + emitNetworkLost() + throw e + } +} diff --git a/src/state/session/reducer.ts b/src/state/session/reducer.ts index d22dd4a021..8571539f87 100644 --- a/src/state/session/reducer.ts +++ b/src/state/session/reducer.ts @@ -1,43 +1,43 @@ -import {type AtpAgent, type AtpSessionEvent} from '@atproto/api' - import {unregisterPushToken} from '#/lib/notifications/notifications' import {logger} from '#/lib/notifications/util' -import {createPublicAgent} from './agent' import {wrapSessionReducerForLogging} from './logging' -import {type SessionAccount} from './types' +import {createPublicSessionBundle} from './session-core' +import {type AtpSessionEvent, type SessionAccount} from './types' import {createTemporaryAgentsAndResume} from './util' -// A hack so that the reducer can't read anything from the agent. -// From the reducer's point of view, it should be a completely opaque object. -type OpaqueBskyAgent = { +// Keep session internals outside the reducer's static view of a bundle. +type OpaqueSessionBundle = { readonly service: URL - readonly api: unknown - readonly app: unknown - readonly com: unknown } -type AgentState = { - readonly agent: OpaqueBskyAgent +type BundleState = { + readonly bundle: OpaqueSessionBundle readonly did: string | undefined } export type State = { readonly accounts: SessionAccount[] - readonly currentAgentState: AgentState - needsPersist: boolean // Mutated in an effect. + readonly currentBundleState: BundleState + needsPersist: boolean // Cleared after persistence is scheduled. } export type Action = | { - type: 'received-agent-event' - agent: OpaqueBskyAgent + type: 'received-session-event' + bundle: OpaqueSessionBundle accountDid: string refreshedAccount: SessionAccount | undefined sessionEvent: AtpSessionEvent } | { type: 'switched-to-account' - newAgent: OpaqueBskyAgent + newBundle: OpaqueSessionBundle + newAccount: SessionAccount + } + | { + // Replace an immutable session from synced or rescued tokens without rebroadcasting. + type: 'replaced-current-bundle' + newBundle: OpaqueSessionBundle newAccount: SessionAccount } | { @@ -61,9 +61,9 @@ export type Action = patch: Pick } -function createPublicAgentState(): AgentState { +function createPublicBundleState(): BundleState { return { - agent: createPublicAgent(), + bundle: createPublicSessionBundle(), did: undefined, } } @@ -71,22 +71,20 @@ function createPublicAgentState(): AgentState { export function getInitialState(persistedAccounts: SessionAccount[]): State { return { accounts: persistedAccounts, - currentAgentState: createPublicAgentState(), + currentBundleState: createPublicBundleState(), needsPersist: false, } } let reducer = (state: State, action: Action): State => { switch (action.type) { - case 'received-agent-event': { - const {agent, accountDid, refreshedAccount, sessionEvent} = action - if ( - refreshedAccount === undefined && - agent !== state.currentAgentState.agent - ) { - // If the session got cleared out (e.g. due to expiry or network error) but - // this account isn't the active one, don't clear it out at this time. - // This way, if the problem is transient, it'll work on next resume. + case 'received-session-event': { + const {bundle, accountDid, refreshedAccount, sessionEvent} = action + if (bundle !== state.currentBundleState.bundle) { + /* + * Stale bundles must neither log out the current account nor restore + * tokens after logout or an account switch. + */ return state } if (sessionEvent === 'network-error') { @@ -98,7 +96,6 @@ let reducer = (state: State, action: Action): State => { !existingAccount || JSON.stringify(existingAccount) === JSON.stringify(refreshedAccount) ) { - // Fast path without a state update. return state } return { @@ -118,26 +115,40 @@ let reducer = (state: State, action: Action): State => { return a } }), - currentAgentState: refreshedAccount - ? state.currentAgentState - : createPublicAgentState(), // Log out if expired. + currentBundleState: refreshedAccount + ? state.currentBundleState + : createPublicBundleState(), // Log out if expired. needsPersist: true, } } case 'switched-to-account': { - const {newAccount, newAgent} = action + const {newAccount, newBundle} = action return { accounts: [ newAccount, ...state.accounts.filter(a => a.did !== newAccount.did), ], - currentAgentState: { + currentBundleState: { did: newAccount.did, - agent: newAgent, + bundle: newBundle, }, needsPersist: true, } } + case 'replaced-current-bundle': { + const {newBundle, newAccount} = action + return { + ...state, + currentBundleState: { + did: state.currentBundleState.did, + bundle: newBundle, + }, + accounts: state.accounts.map(a => + a.did === newAccount.did ? newAccount : a, + ), + needsPersist: false, // Synced from another tab. Don't persist to avoid cycles. + } + } case 'removed-account': { const {accountDid} = action @@ -159,16 +170,16 @@ let reducer = (state: State, action: Action): State => { return { accounts: state.accounts.filter(a => a.did !== accountDid), - currentAgentState: - state.currentAgentState.did === accountDid - ? createPublicAgentState() // Log out if removing the current one. - : state.currentAgentState, + currentBundleState: + state.currentBundleState.did === accountDid + ? createPublicBundleState() // Log out if removing the current one. + : state.currentBundleState, needsPersist: true, } } case 'logged-out-current-account': { - const {currentAgentState} = state - const accountDid = currentAgentState.did + const {currentBundleState} = state + const accountDid = currentBundleState.did // side effect const account = state.accounts.find(a => a.did === accountDid) if (account && accountDid) { @@ -195,7 +206,7 @@ let reducer = (state: State, action: Action): State => { } : a, ), - currentAgentState: createPublicAgentState(), + currentBundleState: createPublicBundleState(), needsPersist: true, } } @@ -216,7 +227,7 @@ let reducer = (state: State, action: Action): State => { refreshJwt: undefined, accessJwt: undefined, })), - currentAgentState: createPublicAgentState(), + currentBundleState: createPublicBundleState(), needsPersist: true, } } @@ -224,33 +235,19 @@ let reducer = (state: State, action: Action): State => { const {syncedAccounts, syncedCurrentDid} = action return { accounts: syncedAccounts, - currentAgentState: - syncedCurrentDid === state.currentAgentState.did - ? state.currentAgentState - : createPublicAgentState(), // Log out if different user. + currentBundleState: + syncedCurrentDid === state.currentBundleState.did + ? state.currentBundleState + : createPublicBundleState(), // Log out if different user. needsPersist: false, // Synced from another tab. Don't persist to avoid cycles. } } case 'partial-refresh-session': { const {accountDid, patch} = action - const agent = state.currentAgentState.agent as AtpAgent - - /* - * Only mutating values that are safe. Be very careful with this. - */ - if (agent.session) { - agent.session.emailConfirmed = - patch.emailConfirmed ?? agent.session.emailConfirmed - agent.session.emailAuthFactor = - patch.emailAuthFactor ?? agent.session.emailAuthFactor - } + // PasswordSession has no setter; consumers read these fields from the account. return { ...state, - currentAgentState: { - ...state.currentAgentState, - agent, - }, accounts: state.accounts.map(a => { if (a.did === accountDid) { return { diff --git a/src/state/session/session-core.ts b/src/state/session/session-core.ts new file mode 100644 index 0000000000..5e3a9e8a8f --- /dev/null +++ b/src/state/session/session-core.ts @@ -0,0 +1,438 @@ +import { + PasswordSession, + type PasswordSessionOptions, + type SessionData, +} from '@atproto/lex-password-session' + +import {BLUESKY_PROXY_HEADER, PUBLIC_BSKY_SERVICE} from '#/lib/constants' +import {logger} from '#/logger' +import {prefetchAgeAssuranceServerData} from '#/ageAssurance/data' +import {features} from '#/analytics' +import { + BskyAppAgent, + createPublicAgent, + PasswordSessionManager, +} from './bridge-agent' +import {addSessionErrorLog} from './logging' +import {configureModerationForAccount} from './moderation' +import {networkAwareFetch} from './network' +import { + isSessionExpired, + sessionAccountToSessionData, + sessionDataToSessionAccount, +} from './session-data' +import {type AtpSessionEvent, type SessionAccount} from './types' + +export {networkAwareFetch} from './network' +export { + isSignupQueued, + sessionAccountToSessionData, + sessionDataToSessionAccount, +} from './session-data' +export type {AtpSessionEvent} from './types' + +/** + * The service the bundle authenticated against. + * + * `PasswordSession`'s getters throw once the session is destroyed, so the read + * is guarded and falls back to the public service. + */ +function deriveServiceUrl(session: PasswordSession | null): URL { + return new URL( + session && !session.destroyed + ? session.session.service + : PUBLIC_BSKY_SERVICE, + ) +} + +/** An `AtpAgent` bridged over one `PasswordSession`, the bundle's sole auth core. */ +export type SessionBundle = { + session: PasswordSession + agent: BskyAppAgent + readonly service: URL +} + +/** + * `PasswordSession` exposes no local (logout-free) destroy, so disposal is + * implemented by disabling its injected fetch and hooks. Keep that lifecycle + * state private and tied to bundle identity. + */ +const bundleKillSwitches = new WeakMap void>() + +/** + * Register the lifecycle closure used by {@link disposeBundle}. + * + * Disposing also detaches the bridge agent from its session, so a stale + * bundle's `agent.session` / `agent.pdsUrl` read as `undefined` rather than + * serving tokens the app has stopped tracking. + */ +export function registerBundleKillSwitch( + bundle: SessionBundle, + kill: () => void, +) { + bundleKillSwitches.set(bundle, () => { + kill() + bundle.agent.dispose() + }) +} + +/** + * Wrap a session in the bridge agent. + * + * `storedPdsUrl` seeds {@link PasswordSessionManager}'s PDS routing so requests + * made before the first refresh delivers a didDoc still reach the right host. + * Once a didDoc arrives the manager prefers its endpoint. + */ +export function buildBundle( + session: PasswordSession, + storedPdsUrl?: string, +): SessionBundle { + const manager = new PasswordSessionManager(session, { + service: deriveServiceUrl(session).toString(), + pdsUrl: storedPdsUrl, + }) + return { + session, + agent: new BskyAppAgent(manager), + get service() { + return deriveServiceUrl(session) + }, + } +} + +/** + * PasswordSession delivers `sessionData` before updating its live getter. The + * provider uses that payload for rotated tokens and expiry rescue. + */ +export type OnSessionChange = ( + bundle: SessionBundle, + did: string, + event: AtpSessionEvent, + sessionData?: SessionData, +) => void + +/** + * Hooks stay inert during initial session preparation. `kill()` disarms them + * and disables the injected fetch so a disposed session cannot refresh or + * dispatch. + */ +export function makeSessionHooks({ + onSessionChange, + getBundle, + getDid, +}: { + onSessionChange: OnSessionChange + /** Deferred: hooks are created before the bundle exists. */ + getBundle: () => SessionBundle + /** Deferred: hooks are created before the bundle exists. */ + getDid: () => string +}) { + let armed = false + let killed = false + const dispatch = (event: AtpSessionEvent, sessionData?: SessionData) => { + if (!armed) { + return + } + /* + * A hook must never throw. PasswordSession awaits its hooks inside the + * assignment to its internal session promise, so a synchronous throw here + * leaves that promise permanently rejected: every later request fails, and + * because the session is never marked destroyed, disposeBundle cannot even + * see that the bundle is dead. The dispatch path reaches reducer side + * effects and event emitters, so treat it as capable of throwing. + */ + try { + const did = getDid() + onSessionChange(getBundle(), did, event, sessionData) + if (event !== 'update') { + addSessionErrorLog(did, event) + } + } catch (e) { + logger.error(e instanceof Error ? e : String(e), { + message: `session: onSessionChange threw for a '${event}' event`, + }) + } + } + const hooks: PasswordSessionOptions = { + fetch: (input, init) => { + if (killed) { + throw new Error('session disposed') + } + return networkAwareFetch(input, init) + }, + onUpdated(data) { + dispatch('update', data) + }, + onDeleted(data) { + dispatch('expired', data) + }, + onUpdateFailure() { + dispatch('network-error') + }, + } + return Object.assign(hooks, { + arm() { + armed = true + }, + kill() { + killed = true + armed = false + }, + }) +} + +/** The agent exposed while logged out. */ +export type PublicSessionBundle = { + session: null + agent: BskyAppAgent + readonly service: URL +} + +/** + * Build the logged-out bundle. `createPublicAgent` installs the guest + * moderation authorities as part of building the agent. + */ +export function createPublicSessionBundle(): PublicSessionBundle { + return { + session: null, + agent: createPublicAgent(), + service: new URL(PUBLIC_BSKY_SERVICE), + } +} + +/** + * Run the prepare tail shared by the asynchronous factories. + * + * Preparation does real network work, so it can both reject and - when a + * request gets a 401 and the session's own refresh then fails definitively - + * destroy the session underneath us. + * + * A session destroyed during preparation is fatal rather than recoverable. The + * hooks are still disarmed at that point, so the session's `expired` event was + * swallowed and nothing will ever tell the reducer to log the account out; + * returning the bundle anyway would leave the app looking signed in over a + * session that can only make unauthenticated requests. Failing instead matches + * what `CredentialSession.resumeSession` did on a revoked token, and every + * caller already handles a rejected factory. Checking `destroyed` first also + * keeps `PasswordSession`'s `Logged out` getter throw from escaping as the + * opaque rejection a caller would surface, so `snapshot` only ever runs against + * a live session. + * + * Both failure modes dispose: the bundle is fully built by this point, and a + * still-live session left behind would keep its refresh and dispatch paths + * alive with nothing tracking it. (Disposal is a no-op for the destroyed case, + * where the session already refuses to refresh and the bridge agent already + * reads as logged out - but the two paths are indistinguishable to the caller, + * so both go through it.) + */ +export async function finishPreparation( + bundle: SessionBundle, + preparation: Promise, + snapshot: () => T, +): Promise { + try { + await preparation + if (bundle.session.destroyed) { + throw new Error('Session was revoked while it was being prepared') + } + return snapshot() + } catch (e) { + disposeBundle(bundle) + throw e + } +} + +/** + * Resume a stored account into a {@link SessionBundle}. Expired sessions take a + * network resume; still-valid stored tokens take a synchronous no-network fast + * path. Hooks are armed only after the prepare tail resolves. + */ +export async function createSessionBundleAndResume( + storedAccount: SessionAccount, + onSessionChange: OnSessionChange, +): Promise<{account: SessionAccount; bundle: SessionBundle}> { + const gates = features.refresh({strategy: 'prefer-low-latency'}) + let bundle!: SessionBundle + const hooks = makeSessionHooks({ + onSessionChange, + getBundle: () => bundle, + getDid: () => storedAccount.did, + }) + + let session: PasswordSession + const sessionData = sessionAccountToSessionData(storedAccount) + if (isSessionExpired(storedAccount)) { + /* + * The arm latch swallows resume's initial onUpdated event. + * + * There is deliberately no network retry here: `resume` rejects only when + * the session is definitively invalid, and it swallows everything else - + * a failed refresh reports through `onUpdateFailure` and resolves with the + * stale tokens. So an offline cold start now stays signed in with dead + * tokens (requests fail until connectivity returns) rather than throwing + * the way the old `CredentialSession.resumeSession` did, and retrying a + * definitive rejection would only repeat a request that cannot succeed. + */ + session = await PasswordSession.resume(sessionData, hooks) + } else { + // Sync fast path: trust the stored tokens, no network. + session = new PasswordSession(sessionData, hooks) + } + + bundle = buildBundle(session, storedAccount.pdsUrl) + registerBundleKillSwitch(bundle, hooks.kill) + // The returned account is captured again after asynchronous preparation. + const earlyAccount = + sessionDataToSessionAccount( + session.session, + session.session.service, + storedAccount.pdsUrl, + ) ?? storedAccount + + configureModerationForAccount(bundle.agent, earlyAccount) + const aa = prefetchAgeAssuranceServerData({agent: bundle.agent}) + + /* + * The proxy header is applied after the PDS-targeting setup above, so those + * calls run without it. + */ + bundle.agent.configureProxy(BLUESKY_PROXY_HEADER.get()) + + // Preparation may auto-refresh the session while hooks are still disarmed. + const account = await finishPreparation( + bundle, + Promise.all([gates, aa]), + () => + sessionDataToSessionAccount( + session.session, + session.session.service, + storedAccount.pdsUrl, + ) ?? storedAccount, + ) + hooks.arm() + return {account, bundle} +} + +/** + * Log in with credentials and build a {@link SessionBundle}. + */ +export async function createSessionBundleAndLogin( + { + service, + identifier, + password, + authFactorToken, + }: { + service: string + identifier: string + password: string + authFactorToken?: string + }, + onSessionChange: OnSessionChange, +): Promise<{account: SessionAccount; bundle: SessionBundle}> { + let bundle!: SessionBundle + let accountDid = '' + const hooks = makeSessionHooks({ + onSessionChange, + getBundle: () => bundle, + getDid: () => accountDid, + }) + + const session = await PasswordSession.login({ + ...hooks, + service, + identifier, + password, + authFactorToken, + allowTakendown: true, + }) + + bundle = buildBundle(session) + registerBundleKillSwitch(bundle, hooks.kill) + // Seed the hook's did before it is armed. + const earlyAccount = sessionDataToSessionAccountOrThrow(session) + accountDid = earlyAccount.did + + const gates = features.refresh({strategy: 'prefer-fresh-gates'}) + configureModerationForAccount(bundle.agent, earlyAccount) + const aa = prefetchAgeAssuranceServerData({agent: bundle.agent}) + + bundle.agent.configureProxy(BLUESKY_PROXY_HEADER.get()) + + // Preparation may auto-refresh the session while hooks are still disarmed. + const account = await finishPreparation( + bundle, + Promise.all([gates, aa]), + () => sessionDataToSessionAccountOrThrow(session), + ) + hooks.arm() + return {account, bundle} +} + +/** + * Rebuild a bundle synchronously from stored tokens. The optional guard runs + * after construction but before hooks are armed; rejected bundles are disposed. + */ +export function createSessionBundleFromStoredAccount( + storedAccount: SessionAccount, + onSessionChange: OnSessionChange, + shouldActivate: ( + bundle: SessionBundle, + account: SessionAccount, + ) => boolean = () => true, +): {account: SessionAccount; bundle: SessionBundle} | undefined { + let bundle!: SessionBundle + const hooks = makeSessionHooks({ + onSessionChange, + getBundle: () => bundle, + getDid: () => storedAccount.did, + }) + const session = new PasswordSession( + sessionAccountToSessionData(storedAccount), + hooks, + ) + bundle = buildBundle(session, storedAccount.pdsUrl) + registerBundleKillSwitch(bundle, hooks.kill) + configureModerationForAccount(bundle.agent, storedAccount) + bundle.agent.configureProxy(BLUESKY_PROXY_HEADER.get()) + + const account = session.destroyed + ? storedAccount + : (sessionDataToSessionAccount( + session.session, + session.session.service, + storedAccount.pdsUrl, + ) ?? storedAccount) + if (!shouldActivate(bundle, account)) { + disposeBundle(bundle) + return undefined + } + hooks.arm() + return {account, bundle} +} + +export function sessionDataToSessionAccountOrThrow( + session: PasswordSession, +): SessionAccount { + const account = sessionDataToSessionAccount( + session.session, + session.session.service, + ) + if (!account) { + throw Error('Expected an active session') + } + return account +} + +/** + * Disable a replaced bundle without revoking its server session. PasswordSession + * has no local destroy operation, so the registered lifecycle closure disables + * its fetch and hooks instead. + */ +export function disposeBundle(bundle: SessionBundle | PublicSessionBundle) { + const session = bundle.session + if (!session || session.destroyed) { + return + } + bundleKillSwitches.get(bundle)?.() +} diff --git a/src/state/session/session-data.ts b/src/state/session/session-data.ts new file mode 100644 index 0000000000..2fab1cbd99 --- /dev/null +++ b/src/state/session/session-data.ts @@ -0,0 +1,107 @@ +import {type AtpSessionData} from '@atproto/api' +import {getPdsEndpoint, isValidDidDoc} from '@atproto/common-web' +import {type SessionData} from '@atproto/lex-password-session' +import {jwtDecode} from 'jwt-decode' + +import {BSKY_SERVICE} from '#/lib/constants' +import {isJwtExpired} from '#/lib/jwt' +import {hasProp} from '#/lib/type-guards' +import {type SessionAccount} from './types' + +/** Whether an access token was issued for a queued (waitlisted) signup. */ +export function isSignupQueued(accessJwt: string | undefined) { + if (accessJwt) { + const sessData = jwtDecode(accessJwt) + return ( + hasProp(sessData, 'scope') && + sessData.scope === 'com.atproto.signupQueued' + ) + } + return false +} + +/** + * Convert live `PasswordSession` session data into the persisted + * `SessionAccount` snapshot. + * + * The object literal's field order is load-bearing: the reducer's + * `JSON.stringify` fast path and the session test snapshots depend on + * byte-stable serialization. `service` and `pdsUrl` are normalized through + * `new URL().toString()` for a stable trailing slash. + * + * `pdsUrl` comes from the DID document or a pre-refresh stored value. It does + * not fall back to the login service. + */ +export function sessionDataToSessionAccount( + session: SessionData | null | undefined, + service: string, + storedPdsUrl?: string, +): SessionAccount | undefined { + if (!session) { + return undefined + } + const normalizedService = new URL(service).toString() + const didDocPdsUrl = + session.didDoc && isValidDidDoc(session.didDoc) + ? getPdsEndpoint(session.didDoc) + : undefined + const pdsUrl = didDocPdsUrl ?? storedPdsUrl + return { + service: normalizedService, + did: session.did, + handle: session.handle, + email: session.email, + emailConfirmed: session.emailConfirmed || false, + emailAuthFactor: session.emailAuthFactor || false, + refreshJwt: session.refreshJwt, + accessJwt: session.accessJwt, + signupQueued: isSignupQueued(session.accessJwt), + active: session.active, + status: session.status, + pdsUrl: pdsUrl ? new URL(pdsUrl).toString() : undefined, + isSelfHosted: !normalizedService.startsWith(BSKY_SERVICE), + } +} + +/** Convert a persisted account into data suitable for `PasswordSession`. */ +export function sessionAccountToSessionData( + account: SessionAccount, +): SessionData { + return { + accessJwt: account.accessJwt ?? '', + active: account.active ?? true, + did: account.did as SessionData['did'], + email: account.email, + emailAuthFactor: account.emailAuthFactor, + emailConfirmed: account.emailConfirmed, + handle: account.handle as SessionData['handle'], + refreshJwt: account.refreshJwt ?? '', + status: account.status, + service: account.service, + } +} + +/** Convert a persisted account into data suitable for `AtpAgent`. */ +export function sessionAccountToSession( + account: SessionAccount, +): AtpSessionData { + return { + // Sorted in the same property order as when returned by BskyAgent (alphabetical). + accessJwt: account.accessJwt ?? '', + did: account.did, + email: account.email, + emailAuthFactor: account.emailAuthFactor, + emailConfirmed: account.emailConfirmed, + handle: account.handle, + refreshJwt: account.refreshJwt ?? '', + /** + * @see https://github.com/bluesky-social/atproto/blob/c5d36d5ba2a2c2a5c4f366a5621c06a5608e361e/packages/api/src/agent.ts#L188 + */ + active: account.active ?? true, + status: account.status, + } +} + +export function isSessionExpired(account: SessionAccount) { + return account.accessJwt ? isJwtExpired(account.accessJwt) : true +} diff --git a/src/state/session/types.ts b/src/state/session/types.ts index 8a9afba42c..ca1bca62f1 100644 --- a/src/state/session/types.ts +++ b/src/state/session/types.ts @@ -3,6 +3,9 @@ import {type Metrics} from '#/analytics/metrics' export type SessionAccount = PersistedAccount +/** Session-change events understood by the reducer and logging hooks. */ +export type AtpSessionEvent = 'update' | 'expired' | 'network-error' + export type SessionStateContext = { accounts: SessionAccount[] currentAccount: SessionAccount | undefined @@ -44,11 +47,9 @@ export type SessionApiContext = { ) => Promise removeAccount: (account: SessionAccount) => void /** - * Calls `getSession` and updates select fields on the current account and - * `BskyAgent`. This is an alternative to `resumeSession`, which updates - * current account/agent using the `persistSessionHandler`, but is more load - * bearing. This patches in updates without causing any side effects via - * `persistSessionHandler`. + * Calls `getSession` and patches the email fields of the current account. + * Unlike `resumeSession`, this does not rotate tokens or rebuild the session, + * so it produces no session-change side effects. */ partialRefreshSession: () => Promise } diff --git a/src/state/session/util.ts b/src/state/session/util.ts index ea6d817f36..af1c2846c7 100644 --- a/src/state/session/util.ts +++ b/src/state/session/util.ts @@ -1,36 +1,16 @@ import AtpAgent from '@atproto/api' -import {jwtDecode} from 'jwt-decode' -import {isJwtExpired} from '#/lib/jwt' -import {hasProp} from '#/lib/type-guards' import * as persisted from '#/state/persisted' -import {sessionAccountToSession} from './agent' +import {sessionAccountToSession} from './session-data' import {type SessionAccount} from './types' +export {isSessionExpired, isSignupQueued} from './session-data' + export function readLastActiveAccount() { const {currentAccount, accounts} = persisted.get('session') return accounts.find(a => a.did === currentAccount?.did) } -export function isSignupQueued(accessJwt: string | undefined) { - if (accessJwt) { - const sessData = jwtDecode(accessJwt) - return ( - hasProp(sessData, 'scope') && - sessData.scope === 'com.atproto.signupQueued' - ) - } - return false -} - -export function isSessionExpired(account: SessionAccount) { - if (account.accessJwt) { - return isJwtExpired(account.accessJwt) - } else { - return true - } -} - /** * Creates and attempted to resumeSession for every stored session. * Intended to be used to send push token revokations just before logout. diff --git a/src/storage/schema.ts b/src/storage/schema.ts index 2f4424792d..53b4ffb022 100644 --- a/src/storage/schema.ts +++ b/src/storage/schema.ts @@ -108,4 +108,15 @@ export type Account = { * account after a switch, until that account's preferences loaded. */ isBetaUser?: boolean + + /** + * The account's subscribed labeler DIDs, cached from preferences so the + * `atproto-accept-labelers` header can be configured synchronously at + * session start, before preferences load. Eventually consistent: rewritten + * on every preferences fetch (see `saveLabelers` in + * `#/state/session/moderation`). Until the first fetch lands there is + * simply no cache entry and initial requests go out without per-account + * labeler headers. + */ + labelers?: string[] } diff --git a/src/view/com/composer/drafts/state/api.ts b/src/view/com/composer/drafts/state/api.ts index 12ed4acf5a..b8fddc618f 100644 --- a/src/view/com/composer/drafts/state/api.ts +++ b/src/view/com/composer/drafts/state/api.ts @@ -11,7 +11,7 @@ import {mimeToExt} from '#/lib/media/video/util' import {shortenLinks} from '#/lib/strings/rich-text-manip' import {type ComposerImage} from '#/state/gallery' import {threadgateAllowUISettingToAllowRecordValue} from '#/state/queries/threadgate/util' -import {createPublicAgent} from '#/state/session/agent' +import {createPublicAgent} from '#/state/session/bridge-agent' import { type ComposerState, type EmbedDraft,