diff --git a/.changeset/brave-clouds-swim.md b/.changeset/brave-clouds-swim.md
new file mode 100644
index 00000000000..cc611bc48c1
--- /dev/null
+++ b/.changeset/brave-clouds-swim.md
@@ -0,0 +1,5 @@
+---
+'@clerk/expo': minor
+---
+
+Add native Google Sign-In support for iOS and Android using built-in native modules.
diff --git a/packages/expo/android/build.gradle b/packages/expo/android/build.gradle
new file mode 100644
index 00000000000..ee1fab8fa00
--- /dev/null
+++ b/packages/expo/android/build.gradle
@@ -0,0 +1,64 @@
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+
+group = 'com.clerk.expo'
+version = '1.0.0'
+
+// Dependency versions - centralized for easier updates
+// See: https://docs.gradle.org/current/userguide/version_catalogs.html for app-level version catalogs
+ext {
+ credentialsVersion = "1.3.0"
+ googleIdVersion = "1.1.1"
+ kotlinxCoroutinesVersion = "1.7.3"
+}
+
+def safeExtGet(prop, fallback) {
+ rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
+}
+
+android {
+ namespace "expo.modules.clerk.googlesignin"
+
+ compileSdk safeExtGet("compileSdkVersion", 36)
+
+ defaultConfig {
+ minSdk safeExtGet("minSdkVersion", 24)
+ targetSdk safeExtGet("targetSdkVersion", 36)
+ versionCode 1
+ versionName "1.0.0"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ }
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_17
+ targetCompatibility JavaVersion.VERSION_17
+ }
+
+ kotlinOptions {
+ jvmTarget = "17"
+ }
+
+ sourceSets {
+ main {
+ java.srcDirs = ['src/main/java']
+ }
+ }
+}
+
+dependencies {
+ // Expo modules core
+ implementation project(':expo-modules-core')
+
+ // Credential Manager for Google Sign-In with nonce support
+ implementation "androidx.credentials:credentials:$credentialsVersion"
+ implementation "androidx.credentials:credentials-play-services-auth:$credentialsVersion"
+ implementation "com.google.android.libraries.identity.googleid:googleid:$googleIdVersion"
+
+ // Coroutines for async operations
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlinxCoroutinesVersion"
+}
diff --git a/packages/expo/android/src/main/AndroidManifest.xml b/packages/expo/android/src/main/AndroidManifest.xml
new file mode 100644
index 00000000000..a2f47b6057d
--- /dev/null
+++ b/packages/expo/android/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/packages/expo/android/src/main/java/expo/modules/clerk/googlesignin/ClerkGoogleSignInModule.kt b/packages/expo/android/src/main/java/expo/modules/clerk/googlesignin/ClerkGoogleSignInModule.kt
new file mode 100644
index 00000000000..3234fea2214
--- /dev/null
+++ b/packages/expo/android/src/main/java/expo/modules/clerk/googlesignin/ClerkGoogleSignInModule.kt
@@ -0,0 +1,264 @@
+package expo.modules.clerk.googlesignin
+
+import android.content.Context
+import androidx.credentials.ClearCredentialStateRequest
+import androidx.credentials.CredentialManager
+import androidx.credentials.CustomCredential
+import androidx.credentials.GetCredentialRequest
+import androidx.credentials.GetCredentialResponse
+import androidx.credentials.exceptions.GetCredentialCancellationException
+import androidx.credentials.exceptions.GetCredentialException
+import androidx.credentials.exceptions.NoCredentialException
+import com.google.android.libraries.identity.googleid.GetGoogleIdOption
+import com.google.android.libraries.identity.googleid.GetSignInWithGoogleOption
+import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential
+import com.google.android.libraries.identity.googleid.GoogleIdTokenParsingException
+import expo.modules.kotlin.Promise
+import expo.modules.kotlin.exception.CodedException
+import expo.modules.kotlin.modules.Module
+import expo.modules.kotlin.modules.ModuleDefinition
+import expo.modules.kotlin.records.Field
+import expo.modules.kotlin.records.Record
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+
+// Configuration parameters
+class ConfigureParams : Record {
+ @Field
+ val webClientId: String = ""
+
+ @Field
+ val hostedDomain: String? = null
+
+ @Field
+ val autoSelectEnabled: Boolean? = null
+}
+
+// Sign-in parameters
+class SignInParams : Record {
+ @Field
+ val nonce: String? = null
+
+ @Field
+ val filterByAuthorizedAccounts: Boolean? = null
+}
+
+// Create account parameters
+class CreateAccountParams : Record {
+ @Field
+ val nonce: String? = null
+}
+
+// Explicit sign-in parameters
+class ExplicitSignInParams : Record {
+ @Field
+ val nonce: String? = null
+}
+
+// Custom exceptions
+class GoogleSignInCancelledException : CodedException("SIGN_IN_CANCELLED", "User cancelled the sign-in flow", null)
+class GoogleSignInNoCredentialException : CodedException("NO_SAVED_CREDENTIAL_FOUND", "No saved credential found", null)
+class GoogleSignInException(message: String) : CodedException("GOOGLE_SIGN_IN_ERROR", message, null)
+class GoogleSignInNotConfiguredException : CodedException("NOT_CONFIGURED", "Google Sign-In is not configured. Call configure() first.", null)
+class GoogleSignInActivityUnavailableException : CodedException("E_ACTIVITY_UNAVAILABLE", "Activity is not available", null)
+
+class ClerkGoogleSignInModule : Module() {
+ private var webClientId: String? = null
+ private var hostedDomain: String? = null
+ private var autoSelectEnabled: Boolean = false
+ private val mainScope = CoroutineScope(Dispatchers.Main)
+
+ private val context: Context
+ get() = requireNotNull(appContext.reactContext)
+
+ private val credentialManager: CredentialManager
+ get() = CredentialManager.create(context)
+
+ override fun definition() = ModuleDefinition {
+ Name("ClerkGoogleSignIn")
+
+ // Configure the module
+ Function("configure") { params: ConfigureParams ->
+ webClientId = params.webClientId
+ hostedDomain = params.hostedDomain
+ autoSelectEnabled = params.autoSelectEnabled ?: false
+ }
+
+ // Sign in - attempts automatic sign-in with saved credentials
+ AsyncFunction("signIn") { params: SignInParams?, promise: Promise ->
+ val clientId = webClientId ?: run {
+ promise.reject(GoogleSignInNotConfiguredException())
+ return@AsyncFunction
+ }
+
+ val activity = appContext.currentActivity ?: run {
+ promise.reject(GoogleSignInActivityUnavailableException())
+ return@AsyncFunction
+ }
+
+ mainScope.launch {
+ try {
+ val googleIdOption = GetGoogleIdOption.Builder()
+ .setFilterByAuthorizedAccounts(params?.filterByAuthorizedAccounts ?: true)
+ .setServerClientId(clientId)
+ .setAutoSelectEnabled(autoSelectEnabled)
+ .apply {
+ params?.nonce?.let { setNonce(it) }
+ }
+ .build()
+
+ val request = GetCredentialRequest.Builder()
+ .addCredentialOption(googleIdOption)
+ .build()
+
+ val result = credentialManager.getCredential(
+ request = request,
+ context = activity
+ )
+
+ handleSignInResult(result, promise)
+ } catch (e: GetCredentialCancellationException) {
+ promise.reject(GoogleSignInCancelledException())
+ } catch (e: NoCredentialException) {
+ promise.reject(GoogleSignInNoCredentialException())
+ } catch (e: GetCredentialException) {
+ promise.reject(GoogleSignInException(e.message ?: "Unknown error"))
+ } catch (e: Exception) {
+ promise.reject(GoogleSignInException(e.message ?: "Unknown error"))
+ }
+ }
+ }
+
+ // Create account - shows account creation UI
+ AsyncFunction("createAccount") { params: CreateAccountParams?, promise: Promise ->
+ val clientId = webClientId ?: run {
+ promise.reject(GoogleSignInNotConfiguredException())
+ return@AsyncFunction
+ }
+
+ val activity = appContext.currentActivity ?: run {
+ promise.reject(GoogleSignInActivityUnavailableException())
+ return@AsyncFunction
+ }
+
+ mainScope.launch {
+ try {
+ val googleIdOption = GetGoogleIdOption.Builder()
+ .setFilterByAuthorizedAccounts(false) // Show all accounts for creation
+ .setServerClientId(clientId)
+ .apply {
+ params?.nonce?.let { setNonce(it) }
+ }
+ .build()
+
+ val request = GetCredentialRequest.Builder()
+ .addCredentialOption(googleIdOption)
+ .build()
+
+ val result = credentialManager.getCredential(
+ request = request,
+ context = activity
+ )
+
+ handleSignInResult(result, promise)
+ } catch (e: GetCredentialCancellationException) {
+ promise.reject(GoogleSignInCancelledException())
+ } catch (e: NoCredentialException) {
+ promise.reject(GoogleSignInNoCredentialException())
+ } catch (e: GetCredentialException) {
+ promise.reject(GoogleSignInException(e.message ?: "Unknown error"))
+ } catch (e: Exception) {
+ promise.reject(GoogleSignInException(e.message ?: "Unknown error"))
+ }
+ }
+ }
+
+ // Explicit sign-in - uses Sign In With Google button flow
+ AsyncFunction("presentExplicitSignIn") { params: ExplicitSignInParams?, promise: Promise ->
+ val clientId = webClientId ?: run {
+ promise.reject(GoogleSignInNotConfiguredException())
+ return@AsyncFunction
+ }
+
+ val activity = appContext.currentActivity ?: run {
+ promise.reject(GoogleSignInActivityUnavailableException())
+ return@AsyncFunction
+ }
+
+ mainScope.launch {
+ try {
+ val signInWithGoogleOption = GetSignInWithGoogleOption.Builder(clientId)
+ .apply {
+ params?.nonce?.let { setNonce(it) }
+ hostedDomain?.let { setHostedDomainFilter(it) }
+ }
+ .build()
+
+ val request = GetCredentialRequest.Builder()
+ .addCredentialOption(signInWithGoogleOption)
+ .build()
+
+ val result = credentialManager.getCredential(
+ request = request,
+ context = activity
+ )
+
+ handleSignInResult(result, promise)
+ } catch (e: GetCredentialCancellationException) {
+ promise.reject(GoogleSignInCancelledException())
+ } catch (e: GetCredentialException) {
+ promise.reject(GoogleSignInException(e.message ?: "Unknown error"))
+ } catch (e: Exception) {
+ promise.reject(GoogleSignInException(e.message ?: "Unknown error"))
+ }
+ }
+ }
+
+ // Sign out - clears credential state
+ AsyncFunction("signOut") { promise: Promise ->
+ mainScope.launch {
+ try {
+ credentialManager.clearCredentialState(ClearCredentialStateRequest())
+ promise.resolve(null)
+ } catch (e: Exception) {
+ promise.reject(GoogleSignInException(e.message ?: "Failed to sign out"))
+ }
+ }
+ }
+ }
+
+ private fun handleSignInResult(result: GetCredentialResponse, promise: Promise) {
+ when (val credential = result.credential) {
+ is CustomCredential -> {
+ if (credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL) {
+ try {
+ val googleIdTokenCredential = GoogleIdTokenCredential.createFrom(credential.data)
+
+ promise.resolve(mapOf(
+ "type" to "success",
+ "data" to mapOf(
+ "idToken" to googleIdTokenCredential.idToken,
+ "user" to mapOf(
+ "id" to googleIdTokenCredential.id,
+ "email" to googleIdTokenCredential.id,
+ "name" to googleIdTokenCredential.displayName,
+ "givenName" to googleIdTokenCredential.givenName,
+ "familyName" to googleIdTokenCredential.familyName,
+ "photo" to googleIdTokenCredential.profilePictureUri?.toString()
+ )
+ )
+ ))
+ } catch (e: GoogleIdTokenParsingException) {
+ promise.reject(GoogleSignInException("Failed to parse Google ID token: ${e.message}"))
+ }
+ } else {
+ promise.reject(GoogleSignInException("Unexpected credential type: ${credential.type}"))
+ }
+ }
+ else -> {
+ promise.reject(GoogleSignInException("Unexpected credential type"))
+ }
+ }
+ }
+}
diff --git a/packages/expo/app.plugin.js b/packages/expo/app.plugin.js
new file mode 100644
index 00000000000..974c376b2c1
--- /dev/null
+++ b/packages/expo/app.plugin.js
@@ -0,0 +1,44 @@
+const { createRunOncePlugin, withInfoPlist } = require('@expo/config-plugins');
+
+/**
+ * Expo config plugin for @clerk/clerk-expo.
+ *
+ * This plugin configures the iOS URL scheme required for Google Sign-In.
+ * The native Android module is automatically linked via expo-module.config.json.
+ */
+function withClerkGoogleSignIn(config) {
+ // Get the iOS URL scheme from environment or config.extra
+ // We capture it here before entering the mod callback
+ const iosUrlScheme =
+ process.env.EXPO_PUBLIC_CLERK_GOOGLE_IOS_URL_SCHEME || config.extra?.EXPO_PUBLIC_CLERK_GOOGLE_IOS_URL_SCHEME;
+
+ if (!iosUrlScheme) {
+ // No URL scheme configured, skip iOS configuration
+ return config;
+ }
+
+ // Add iOS URL scheme for Google Sign-In
+ config = withInfoPlist(config, modConfig => {
+ if (!Array.isArray(modConfig.modResults.CFBundleURLTypes)) {
+ modConfig.modResults.CFBundleURLTypes = [];
+ }
+
+ // Check if the scheme is already added to avoid duplicates
+ const schemeExists = modConfig.modResults.CFBundleURLTypes.some(urlType =>
+ urlType.CFBundleURLSchemes?.includes(iosUrlScheme),
+ );
+
+ if (!schemeExists) {
+ // Add Google Sign-In URL scheme
+ modConfig.modResults.CFBundleURLTypes.push({
+ CFBundleURLSchemes: [iosUrlScheme],
+ });
+ }
+
+ return modConfig;
+ });
+
+ return config;
+}
+
+module.exports = createRunOncePlugin(withClerkGoogleSignIn, '@clerk/clerk-expo', '1.0.0');
diff --git a/packages/expo/expo-module.config.json b/packages/expo/expo-module.config.json
new file mode 100644
index 00000000000..e59f14eef13
--- /dev/null
+++ b/packages/expo/expo-module.config.json
@@ -0,0 +1,9 @@
+{
+ "platforms": ["android", "ios"],
+ "android": {
+ "modules": ["expo.modules.clerk.googlesignin.ClerkGoogleSignInModule"]
+ },
+ "ios": {
+ "modules": ["ClerkGoogleSignInModule"]
+ }
+}
diff --git a/packages/expo/ios/ClerkGoogleSignIn.podspec b/packages/expo/ios/ClerkGoogleSignIn.podspec
new file mode 100644
index 00000000000..be0f3551b2b
--- /dev/null
+++ b/packages/expo/ios/ClerkGoogleSignIn.podspec
@@ -0,0 +1,22 @@
+require 'json'
+
+package = JSON.parse(File.read(File.join(__dir__, '..', 'package.json')))
+
+Pod::Spec.new do |s|
+ s.name = 'ClerkGoogleSignIn'
+ s.version = package['version']
+ s.summary = 'Native Google Sign-In module for Clerk Expo'
+ s.description = 'Native Google Sign-In functionality using Google Sign-In SDK with nonce support for Clerk authentication'
+ s.license = package['license']
+ s.author = package['author']
+ s.homepage = package['homepage']
+ s.platforms = { :ios => '13.4' }
+ s.swift_version = '5.4'
+ s.source = { :git => 'https://github.com/clerk/javascript.git' }
+ s.static_framework = true
+
+ s.dependency 'ExpoModulesCore'
+ s.dependency 'GoogleSignIn', '~> 9.0'
+
+ s.source_files = '*.swift'
+end
diff --git a/packages/expo/ios/ClerkGoogleSignInModule.swift b/packages/expo/ios/ClerkGoogleSignInModule.swift
new file mode 100644
index 00000000000..c06f85b8031
--- /dev/null
+++ b/packages/expo/ios/ClerkGoogleSignInModule.swift
@@ -0,0 +1,229 @@
+import ExpoModulesCore
+import GoogleSignIn
+
+public class ClerkGoogleSignInModule: Module {
+ private var clientId: String?
+ private var hostedDomain: String?
+
+ public func definition() -> ModuleDefinition {
+ Name("ClerkGoogleSignIn")
+
+ // Configure the module
+ Function("configure") { (params: ConfigureParams) in
+ self.clientId = params.iosClientId ?? params.webClientId
+ self.hostedDomain = params.hostedDomain
+
+ // Set the configuration globally
+ // clientID: iOS client ID for OAuth flow
+ // serverClientID: Web client ID for token audience (what Clerk backend verifies)
+ if let clientId = self.clientId {
+ let config = GIDConfiguration(
+ clientID: clientId,
+ serverClientID: params.webClientId
+ )
+ GIDSignIn.sharedInstance.configuration = config
+ }
+ }
+
+ // Sign in - attempts sign-in with hint if available
+ AsyncFunction("signIn") { (params: SignInParams?, promise: Promise) in
+ guard self.clientId != nil else {
+ promise.reject(NotConfiguredException())
+ return
+ }
+
+ DispatchQueue.main.async {
+ guard let presentingVC = self.getPresentingViewController() else {
+ promise.reject(GoogleSignInException(message: "No presenting view controller available"))
+ return
+ }
+
+ // Build sign-in hint if filtering by authorized accounts
+ let hint: String? = params?.filterByAuthorizedAccounts == true
+ ? GIDSignIn.sharedInstance.currentUser?.profile?.email
+ : nil
+
+ GIDSignIn.sharedInstance.signIn(
+ withPresenting: presentingVC,
+ hint: hint,
+ additionalScopes: nil,
+ nonce: params?.nonce
+ ) { result, error in
+ self.handleSignInResult(result: result, error: error, promise: promise)
+ }
+ }
+ }
+
+ // Create account - shows account creation UI (same as sign in on iOS)
+ AsyncFunction("createAccount") { (params: CreateAccountParams?, promise: Promise) in
+ guard self.clientId != nil else {
+ promise.reject(NotConfiguredException())
+ return
+ }
+
+ DispatchQueue.main.async {
+ guard let presentingVC = self.getPresentingViewController() else {
+ promise.reject(GoogleSignInException(message: "No presenting view controller available"))
+ return
+ }
+
+ GIDSignIn.sharedInstance.signIn(
+ withPresenting: presentingVC,
+ hint: nil,
+ additionalScopes: nil,
+ nonce: params?.nonce
+ ) { result, error in
+ self.handleSignInResult(result: result, error: error, promise: promise)
+ }
+ }
+ }
+
+ // Explicit sign-in - uses standard Google Sign-In flow
+ AsyncFunction("presentExplicitSignIn") { (params: ExplicitSignInParams?, promise: Promise) in
+ guard self.clientId != nil else {
+ promise.reject(NotConfiguredException())
+ return
+ }
+
+ DispatchQueue.main.async {
+ guard let presentingVC = self.getPresentingViewController() else {
+ promise.reject(GoogleSignInException(message: "No presenting view controller available"))
+ return
+ }
+
+ GIDSignIn.sharedInstance.signIn(
+ withPresenting: presentingVC,
+ hint: nil,
+ additionalScopes: nil,
+ nonce: params?.nonce
+ ) { result, error in
+ self.handleSignInResult(result: result, error: error, promise: promise)
+ }
+ }
+ }
+
+ // Sign out - clears credential state
+ AsyncFunction("signOut") { (promise: Promise) in
+ GIDSignIn.sharedInstance.signOut()
+ promise.resolve(nil)
+ }
+ }
+
+ private func getPresentingViewController() -> UIViewController? {
+ guard let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
+ let window = scene.windows.first,
+ let rootVC = window.rootViewController else {
+ return nil
+ }
+
+ var topVC = rootVC
+ while let presentedVC = topVC.presentedViewController {
+ topVC = presentedVC
+ }
+ return topVC
+ }
+
+ private func handleSignInResult(result: GIDSignInResult?, error: Error?, promise: Promise) {
+ if let error = error {
+ let nsError = error as NSError
+
+ // Check for user cancellation
+ if nsError.domain == kGIDSignInErrorDomain && nsError.code == GIDSignInError.canceled.rawValue {
+ promise.reject(SignInCancelledException())
+ return
+ }
+
+ promise.reject(GoogleSignInException(message: error.localizedDescription))
+ return
+ }
+
+ guard let result = result,
+ let idToken = result.user.idToken?.tokenString else {
+ promise.reject(GoogleSignInException(message: "No ID token received"))
+ return
+ }
+
+ let user = result.user
+ let profile = user.profile
+
+ let response: [String: Any] = [
+ "type": "success",
+ "data": [
+ "idToken": idToken,
+ "user": [
+ "id": user.userID ?? "",
+ "email": profile?.email ?? "",
+ "name": profile?.name ?? "",
+ "givenName": profile?.givenName ?? "",
+ "familyName": profile?.familyName ?? "",
+ "photo": profile?.imageURL(withDimension: 200)?.absoluteString ?? NSNull()
+ ] as [String: Any]
+ ] as [String: Any]
+ ]
+
+ promise.resolve(response)
+ }
+}
+
+// MARK: - Records
+
+struct ConfigureParams: Record {
+ @Field
+ var webClientId: String = ""
+
+ @Field
+ var iosClientId: String?
+
+ @Field
+ var hostedDomain: String?
+
+ @Field
+ var autoSelectEnabled: Bool?
+}
+
+struct SignInParams: Record {
+ @Field
+ var nonce: String?
+
+ @Field
+ var filterByAuthorizedAccounts: Bool?
+}
+
+struct CreateAccountParams: Record {
+ @Field
+ var nonce: String?
+}
+
+struct ExplicitSignInParams: Record {
+ @Field
+ var nonce: String?
+}
+
+// MARK: - Exceptions
+
+class SignInCancelledException: Exception {
+ override var code: String { "SIGN_IN_CANCELLED" }
+ override var reason: String { "User cancelled the sign-in flow" }
+}
+
+class NoSavedCredentialException: Exception {
+ override var code: String { "NO_SAVED_CREDENTIAL_FOUND" }
+ override var reason: String { "No saved credential found" }
+}
+
+class NotConfiguredException: Exception {
+ override var code: String { "NOT_CONFIGURED" }
+ override var reason: String { "Google Sign-In is not configured. Call configure() first." }
+}
+
+class GoogleSignInException: Exception {
+ private let errorMessage: String
+
+ init(message: String) {
+ self.errorMessage = message
+ super.init()
+ }
+
+ override var code: String { "GOOGLE_SIGN_IN_ERROR" }
+ override var reason: String { errorMessage }
+}
diff --git a/packages/expo/package.json b/packages/expo/package.json
index d380e29ce6a..8234975313e 100644
--- a/packages/expo/package.json
+++ b/packages/expo/package.json
@@ -56,16 +56,28 @@
"types": "./dist/experimental.d.ts",
"default": "./dist/experimental.js"
},
- "./legacy": {
- "types": "./dist/legacy.d.ts",
- "default": "./dist/legacy.js"
- }
+ "./google-one-tap": {
+ "types": "./dist/google-one-tap/index.d.ts",
+ "default": "./dist/google-one-tap/index.js"
+ },
+ "./app.plugin.js": "./app.plugin.js",
+ "./package.json": "./package.json"
},
"main": "./dist/index.js",
"source": "./src/index.ts",
"types": "./dist/index.d.ts",
"files": [
- "dist"
+ "dist",
+ "android",
+ "ios",
+ "expo-module.config.json",
+ "app.plugin.js",
+ "web",
+ "local-credentials",
+ "passkeys",
+ "secure-store",
+ "resource-cache",
+ "token-cache"
],
"scripts": {
"build": "tsup",
@@ -93,19 +105,22 @@
"@types/base-64": "^1.0.2",
"expo-apple-authentication": "^7.2.4",
"expo-auth-session": "^5.4.0",
+ "expo-constants": "^18.0.0",
"expo-crypto": "^15.0.7",
"expo-local-authentication": "^13.8.0",
+ "expo-modules-core": "^3.0.0",
"expo-secure-store": "^12.8.1",
"expo-web-browser": "^12.8.2",
"react-native": "^0.81.4"
},
"peerDependencies": {
"@clerk/expo-passkeys": ">=0.0.6",
- "expo": ">=53 <55",
"expo-apple-authentication": ">=7.0.0",
"expo-auth-session": ">=5",
+ "expo-constants": ">=12",
"expo-crypto": ">=12",
"expo-local-authentication": ">=13.5.0",
+ "expo-modules-core": ">=3.0.0",
"expo-secure-store": ">=12.4.0",
"expo-web-browser": ">=12.5.0",
"react": "catalog:peer-react",
@@ -119,6 +134,9 @@
"expo-apple-authentication": {
"optional": true
},
+ "expo-constants": {
+ "optional": true
+ },
"expo-crypto": {
"optional": true
},
diff --git a/packages/expo/src/google-one-tap/ClerkGoogleOneTapSignIn.ts b/packages/expo/src/google-one-tap/ClerkGoogleOneTapSignIn.ts
new file mode 100644
index 00000000000..00004316b05
--- /dev/null
+++ b/packages/expo/src/google-one-tap/ClerkGoogleOneTapSignIn.ts
@@ -0,0 +1,205 @@
+import { requireNativeModule } from 'expo-modules-core';
+
+import type {
+ CancelledResponse,
+ ConfigureParams,
+ CreateAccountParams,
+ ExplicitSignInParams,
+ NoSavedCredentialFound,
+ OneTapResponse,
+ OneTapSuccessResponse,
+ SignInParams,
+} from './types';
+
+// Type for the native module methods
+interface ClerkGoogleSignInNativeModule {
+ configure(params: ConfigureParams): void;
+ signIn(params: SignInParams): Promise;
+ createAccount(params: CreateAccountParams): Promise;
+ presentExplicitSignIn(params: ExplicitSignInParams): Promise;
+ signOut(): Promise;
+}
+
+// Lazy-load the native module to avoid crashes when not available
+let _nativeModule: ClerkGoogleSignInNativeModule | null = null;
+
+function getNativeModule(): ClerkGoogleSignInNativeModule {
+ if (!_nativeModule) {
+ _nativeModule = requireNativeModule('ClerkGoogleSignIn');
+ }
+ return _nativeModule;
+}
+
+/**
+ * Check if a response indicates the user cancelled the sign-in flow.
+ */
+export function isCancelledResponse(response: OneTapResponse): response is CancelledResponse {
+ return response.type === 'cancelled';
+}
+
+/**
+ * Check if a response indicates no saved credential was found.
+ */
+export function isNoSavedCredentialFoundResponse(response: OneTapResponse): response is NoSavedCredentialFound {
+ return response.type === 'noSavedCredentialFound';
+}
+
+/**
+ * Check if a response is a successful sign-in.
+ */
+export function isSuccessResponse(response: OneTapResponse): response is OneTapSuccessResponse {
+ return response.type === 'success';
+}
+
+/**
+ * Check if an error has a code property (Google Sign-In error).
+ */
+export function isErrorWithCode(error: unknown): error is { code: string; message: string } {
+ return (
+ error !== null &&
+ typeof error === 'object' &&
+ 'code' in error &&
+ typeof (error as { code: unknown }).code === 'string'
+ );
+}
+
+/**
+ * Clerk's Google One Tap Sign-In module for Android.
+ *
+ * This module provides native Google Sign-In functionality using Google's
+ * Credential Manager API with full nonce support for replay attack protection.
+ *
+ * @example
+ * ```typescript
+ * import { ClerkGoogleOneTapSignIn } from '@clerk/clerk-expo';
+ *
+ * // Configure once at app startup
+ * ClerkGoogleOneTapSignIn.configure({
+ * webClientId: 'YOUR_WEB_CLIENT_ID',
+ * });
+ *
+ * // Sign in with nonce
+ * const nonce = generateNonce();
+ * const response = await ClerkGoogleOneTapSignIn.signIn({ nonce });
+ *
+ * if (response.type === 'success') {
+ * const { idToken } = response.data;
+ * // Use idToken with Clerk
+ * }
+ * ```
+ *
+ * @platform Android
+ */
+export const ClerkGoogleOneTapSignIn = {
+ /**
+ * Configure Google Sign-In. Must be called before any sign-in methods.
+ *
+ * @param params - Configuration parameters
+ * @param params.webClientId - The web client ID from Google Cloud Console (required)
+ * @param params.hostedDomain - Optional domain restriction
+ * @param params.autoSelectEnabled - Auto-select for single credential (default: false)
+ */
+ configure(params: ConfigureParams): void {
+ getNativeModule().configure(params);
+ },
+
+ /**
+ * Attempt to sign in with saved credentials (One Tap).
+ *
+ * This method will show the One Tap UI if there are saved credentials,
+ * or return a "noSavedCredentialFound" response if there are none.
+ *
+ * @param params - Sign-in parameters
+ * @param params.nonce - Cryptographic nonce for replay protection
+ * @param params.filterByAuthorizedAccounts - Only show previously authorized accounts (default: true)
+ *
+ * @returns Promise resolving to OneTapResponse
+ */
+ async signIn(params?: SignInParams): Promise {
+ try {
+ return await getNativeModule().signIn(params ?? {});
+ } catch (error) {
+ if (isErrorWithCode(error)) {
+ if (error.code === 'SIGN_IN_CANCELLED') {
+ return { type: 'cancelled', data: null };
+ }
+ if (error.code === 'NO_SAVED_CREDENTIAL_FOUND') {
+ return { type: 'noSavedCredentialFound', data: null };
+ }
+ }
+ throw error;
+ }
+ },
+
+ /**
+ * Create a new account (shows all Google accounts).
+ *
+ * This method shows the account picker with all available Google accounts,
+ * not just previously authorized ones.
+ *
+ * @param params - Create account parameters
+ * @param params.nonce - Cryptographic nonce for replay protection
+ *
+ * @returns Promise resolving to OneTapResponse
+ */
+ async createAccount(params?: CreateAccountParams): Promise {
+ try {
+ return await getNativeModule().createAccount(params ?? {});
+ } catch (error) {
+ if (isErrorWithCode(error)) {
+ if (error.code === 'SIGN_IN_CANCELLED') {
+ return { type: 'cancelled', data: null };
+ }
+ if (error.code === 'NO_SAVED_CREDENTIAL_FOUND') {
+ return { type: 'noSavedCredentialFound', data: null };
+ }
+ }
+ throw error;
+ }
+ },
+
+ /**
+ * Present explicit sign-in UI (Google Sign-In button flow).
+ *
+ * This shows the full Google Sign-In UI, similar to clicking a
+ * "Sign in with Google" button.
+ *
+ * @param params - Explicit sign-in parameters
+ * @param params.nonce - Cryptographic nonce for replay protection
+ *
+ * @returns Promise resolving to OneTapResponse
+ */
+ async presentExplicitSignIn(params?: ExplicitSignInParams): Promise {
+ try {
+ return await getNativeModule().presentExplicitSignIn(params ?? {});
+ } catch (error) {
+ if (isErrorWithCode(error)) {
+ if (error.code === 'SIGN_IN_CANCELLED') {
+ return { type: 'cancelled', data: null };
+ }
+ }
+ throw error;
+ }
+ },
+
+ /**
+ * Sign out and clear credential state.
+ *
+ * This disables automatic sign-in until the user signs in again.
+ */
+ async signOut(): Promise {
+ await getNativeModule().signOut();
+ },
+};
+
+export type {
+ ConfigureParams,
+ SignInParams,
+ CreateAccountParams,
+ ExplicitSignInParams,
+ OneTapResponse,
+ OneTapSuccessResponse,
+ CancelledResponse,
+ NoSavedCredentialFound,
+ GoogleUser,
+} from './types';
diff --git a/packages/expo/src/google-one-tap/index.ts b/packages/expo/src/google-one-tap/index.ts
new file mode 100644
index 00000000000..1877117ef61
--- /dev/null
+++ b/packages/expo/src/google-one-tap/index.ts
@@ -0,0 +1,21 @@
+export {
+ ClerkGoogleOneTapSignIn,
+ isCancelledResponse,
+ isNoSavedCredentialFoundResponse,
+ isSuccessResponse,
+ isErrorWithCode,
+} from './ClerkGoogleOneTapSignIn';
+
+export type {
+ ConfigureParams,
+ SignInParams,
+ CreateAccountParams,
+ ExplicitSignInParams,
+ OneTapResponse,
+ OneTapSuccessResponse,
+ CancelledResponse,
+ NoSavedCredentialFound,
+ GoogleUser,
+ GoogleSignInError,
+ GoogleSignInErrorCode,
+} from './types';
diff --git a/packages/expo/src/google-one-tap/types.ts b/packages/expo/src/google-one-tap/types.ts
new file mode 100644
index 00000000000..98cd34c4d1a
--- /dev/null
+++ b/packages/expo/src/google-one-tap/types.ts
@@ -0,0 +1,162 @@
+/**
+ * Configuration parameters for Google One Tap Sign-In.
+ */
+export type ConfigureParams = {
+ /**
+ * The web client ID from Google Cloud Console.
+ * This is required for Google Sign-In to work.
+ * On iOS, this is used as the serverClientID for token audience.
+ */
+ webClientId: string;
+
+ /**
+ * The iOS client ID from Google Cloud Console.
+ * This is only used on iOS for the OAuth flow.
+ * If not provided, webClientId will be used.
+ * @platform iOS
+ */
+ iosClientId?: string;
+
+ /**
+ * Optional hosted domain to restrict sign-in to a specific domain.
+ */
+ hostedDomain?: string;
+
+ /**
+ * Whether to enable auto-select for returning users.
+ * When true, if only one credential is available, it will be automatically selected.
+ * @default false
+ */
+ autoSelectEnabled?: boolean;
+};
+
+/**
+ * Parameters for the signIn method.
+ */
+export type SignInParams = {
+ /**
+ * A cryptographically random string used to mitigate replay attacks.
+ * The nonce will be included in the ID token.
+ */
+ nonce?: string;
+
+ /**
+ * Whether to filter credentials to only show accounts that have been
+ * previously authorized for this app.
+ * @default true
+ */
+ filterByAuthorizedAccounts?: boolean;
+};
+
+/**
+ * Parameters for the createAccount method.
+ */
+export type CreateAccountParams = {
+ /**
+ * A cryptographically random string used to mitigate replay attacks.
+ * The nonce will be included in the ID token.
+ */
+ nonce?: string;
+};
+
+/**
+ * Parameters for the presentExplicitSignIn method.
+ */
+export type ExplicitSignInParams = {
+ /**
+ * A cryptographically random string used to mitigate replay attacks.
+ * The nonce will be included in the ID token.
+ */
+ nonce?: string;
+};
+
+/**
+ * User information returned from Google Sign-In.
+ */
+export type GoogleUser = {
+ /**
+ * The user's unique Google identifier (OIDC "sub" claim).
+ * This is distinct from the user's email address.
+ */
+ id: string;
+
+ /**
+ * The user's email address.
+ */
+ email: string;
+
+ /**
+ * The user's full display name.
+ */
+ name: string | null;
+
+ /**
+ * The user's given (first) name.
+ */
+ givenName: string | null;
+
+ /**
+ * The user's family (last) name.
+ */
+ familyName: string | null;
+
+ /**
+ * URL to the user's profile picture.
+ */
+ photo: string | null;
+};
+
+/**
+ * Successful sign-in response.
+ */
+export type OneTapSuccessResponse = {
+ type: 'success';
+ data: {
+ /**
+ * The Google ID token containing user information and nonce.
+ */
+ idToken: string;
+
+ /**
+ * The user's information.
+ */
+ user: GoogleUser;
+ };
+};
+
+/**
+ * Response when the user cancels the sign-in flow.
+ */
+export type CancelledResponse = {
+ type: 'cancelled';
+ data: null;
+};
+
+/**
+ * Response when no saved credential is found.
+ */
+export type NoSavedCredentialFound = {
+ type: 'noSavedCredentialFound';
+ data: null;
+};
+
+/**
+ * Union type for all possible One Tap responses.
+ */
+export type OneTapResponse = OneTapSuccessResponse | CancelledResponse | NoSavedCredentialFound;
+
+/**
+ * Error codes that can be thrown by the Google Sign-In module.
+ */
+export type GoogleSignInErrorCode =
+ | 'SIGN_IN_CANCELLED'
+ | 'NO_SAVED_CREDENTIAL_FOUND'
+ | 'NOT_CONFIGURED'
+ | 'GOOGLE_SIGN_IN_ERROR';
+
+/**
+ * Error thrown by the Google Sign-In module.
+ */
+export interface GoogleSignInError extends Error {
+ code: GoogleSignInErrorCode;
+}
diff --git a/packages/expo/src/hooks/__tests__/useSignInWithGoogle.test.ts b/packages/expo/src/hooks/__tests__/useSignInWithGoogle.test.ts
new file mode 100644
index 00000000000..8b46bdf2165
--- /dev/null
+++ b/packages/expo/src/hooks/__tests__/useSignInWithGoogle.test.ts
@@ -0,0 +1,270 @@
+import { renderHook } from '@testing-library/react';
+import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
+
+import { useSignInWithGoogle } from '../useSignInWithGoogle.android';
+
+const mocks = vi.hoisted(() => {
+ return {
+ useSignIn: vi.fn(),
+ useSignUp: vi.fn(),
+ ClerkGoogleOneTapSignIn: {
+ configure: vi.fn(),
+ presentExplicitSignIn: vi.fn(),
+ },
+ isSuccessResponse: vi.fn(),
+ };
+});
+
+vi.mock('@clerk/react/legacy', () => {
+ return {
+ useSignIn: mocks.useSignIn,
+ useSignUp: mocks.useSignUp,
+ };
+});
+
+vi.mock('../../google-one-tap', () => {
+ return {
+ ClerkGoogleOneTapSignIn: mocks.ClerkGoogleOneTapSignIn,
+ isSuccessResponse: mocks.isSuccessResponse,
+ };
+});
+
+vi.mock('react-native', () => {
+ return {
+ Platform: {
+ OS: 'android',
+ },
+ };
+});
+
+vi.mock('expo-modules-core', () => {
+ return {
+ EventEmitter: vi.fn(),
+ requireNativeModule: vi.fn(),
+ };
+});
+
+vi.mock('expo-constants', () => {
+ return {
+ default: {
+ expoConfig: {
+ extra: {
+ EXPO_PUBLIC_CLERK_GOOGLE_WEB_CLIENT_ID: 'mock-web-client-id.apps.googleusercontent.com',
+ },
+ },
+ },
+ };
+});
+
+vi.mock('expo-crypto', () => {
+ return {
+ randomUUID: vi.fn(() => 'mock-uuid-nonce'),
+ digestStringAsync: vi.fn(() => Promise.resolve('mock-hashed-nonce')),
+ CryptoDigestAlgorithm: {
+ SHA256: 'SHA256',
+ },
+ };
+});
+
+describe('useSignInWithGoogle', () => {
+ const mockSignIn = {
+ create: vi.fn(),
+ createdSessionId: 'test-session-id',
+ firstFactorVerification: {
+ status: 'verified',
+ },
+ };
+
+ const mockSignUp = {
+ create: vi.fn(),
+ createdSessionId: null,
+ };
+
+ const mockSetActive = vi.fn();
+
+ beforeEach(() => {
+ vi.clearAllMocks();
+
+ mocks.useSignIn.mockReturnValue({
+ signIn: mockSignIn,
+ setActive: mockSetActive,
+ isLoaded: true,
+ });
+
+ mocks.useSignUp.mockReturnValue({
+ signUp: mockSignUp,
+ isLoaded: true,
+ });
+ });
+
+ afterEach(() => {
+ vi.restoreAllMocks();
+ });
+
+ describe('startGoogleAuthenticationFlow', () => {
+ test('should return the hook with startGoogleAuthenticationFlow function', () => {
+ const { result } = renderHook(() => useSignInWithGoogle());
+
+ expect(result.current).toHaveProperty('startGoogleAuthenticationFlow');
+ expect(typeof result.current.startGoogleAuthenticationFlow).toBe('function');
+ });
+
+ test('should successfully sign in existing user', async () => {
+ const mockIdToken = 'mock-id-token';
+ mocks.ClerkGoogleOneTapSignIn.presentExplicitSignIn.mockResolvedValue({
+ type: 'success',
+ data: { idToken: mockIdToken },
+ });
+ mocks.isSuccessResponse.mockReturnValue(true);
+
+ mockSignIn.create.mockResolvedValue(undefined);
+ mockSignIn.firstFactorVerification.status = 'verified';
+ mockSignIn.createdSessionId = 'test-session-id';
+
+ const { result } = renderHook(() => useSignInWithGoogle());
+
+ const response = await result.current.startGoogleAuthenticationFlow();
+
+ expect(mocks.ClerkGoogleOneTapSignIn.configure).toHaveBeenCalledWith({
+ webClientId: 'mock-web-client-id.apps.googleusercontent.com',
+ });
+ expect(mocks.ClerkGoogleOneTapSignIn.presentExplicitSignIn).toHaveBeenCalledWith({
+ nonce: 'mock-uuid-nonce',
+ });
+ expect(mockSignIn.create).toHaveBeenCalledWith({
+ strategy: 'google_one_tap',
+ token: mockIdToken,
+ });
+ expect(response.createdSessionId).toBe('test-session-id');
+ expect(response.setActive).toBe(mockSetActive);
+ });
+
+ test('should handle transfer flow for new user', async () => {
+ const mockIdToken = 'mock-id-token';
+ mocks.ClerkGoogleOneTapSignIn.presentExplicitSignIn.mockResolvedValue({
+ type: 'success',
+ data: { idToken: mockIdToken },
+ });
+ mocks.isSuccessResponse.mockReturnValue(true);
+
+ mockSignIn.create.mockResolvedValue(undefined);
+ mockSignIn.firstFactorVerification.status = 'transferable';
+
+ const mockSignUpWithSession = { ...mockSignUp, createdSessionId: 'new-user-session-id' };
+ mocks.useSignUp.mockReturnValue({
+ signUp: mockSignUpWithSession,
+ isLoaded: true,
+ });
+
+ const { result } = renderHook(() => useSignInWithGoogle());
+
+ const response = await result.current.startGoogleAuthenticationFlow({
+ unsafeMetadata: { source: 'test' },
+ });
+
+ expect(mockSignIn.create).toHaveBeenCalledWith({
+ strategy: 'google_one_tap',
+ token: mockIdToken,
+ });
+ expect(mockSignUp.create).toHaveBeenCalledWith({
+ transfer: true,
+ unsafeMetadata: { source: 'test' },
+ });
+ expect(response.createdSessionId).toBe('new-user-session-id');
+ });
+
+ test('should handle user cancellation gracefully', async () => {
+ mocks.ClerkGoogleOneTapSignIn.presentExplicitSignIn.mockResolvedValue({
+ type: 'cancelled',
+ data: null,
+ });
+ mocks.isSuccessResponse.mockReturnValue(false);
+
+ const { result } = renderHook(() => useSignInWithGoogle());
+
+ const response = await result.current.startGoogleAuthenticationFlow();
+
+ expect(response.createdSessionId).toBe(null);
+ expect(response.setActive).toBe(mockSetActive);
+ });
+
+ test('should handle SIGN_IN_CANCELLED error code', async () => {
+ const cancelError = Object.assign(new Error('User canceled'), { code: 'SIGN_IN_CANCELLED' });
+ mocks.ClerkGoogleOneTapSignIn.presentExplicitSignIn.mockRejectedValue(cancelError);
+
+ const { result } = renderHook(() => useSignInWithGoogle());
+
+ const response = await result.current.startGoogleAuthenticationFlow();
+
+ expect(response.createdSessionId).toBe(null);
+ expect(response.setActive).toBe(mockSetActive);
+ });
+
+ test('should return early when clerk is not loaded', async () => {
+ mocks.useSignIn.mockReturnValue({
+ signIn: mockSignIn,
+ setActive: mockSetActive,
+ isLoaded: false,
+ });
+
+ const { result } = renderHook(() => useSignInWithGoogle());
+
+ const response = await result.current.startGoogleAuthenticationFlow();
+
+ expect(mocks.ClerkGoogleOneTapSignIn.configure).not.toHaveBeenCalled();
+ expect(mocks.ClerkGoogleOneTapSignIn.presentExplicitSignIn).not.toHaveBeenCalled();
+ expect(response.createdSessionId).toBe(null);
+ });
+
+ test('should fall back to signUp when external_account_not_found error occurs', async () => {
+ const mockIdToken = 'mock-id-token';
+ mocks.ClerkGoogleOneTapSignIn.presentExplicitSignIn.mockResolvedValue({
+ type: 'success',
+ data: { idToken: mockIdToken },
+ });
+ mocks.isSuccessResponse.mockReturnValue(true);
+
+ // Mock signIn.create to throw external_account_not_found Clerk error
+ const externalAccountError = {
+ clerkError: true,
+ errors: [{ code: 'external_account_not_found' }],
+ message: 'External account not found',
+ };
+ mockSignIn.create.mockRejectedValue(externalAccountError);
+
+ // Mock signUp.create to succeed with a new session
+ const mockSignUpWithSession = {
+ ...mockSignUp,
+ create: vi.fn().mockResolvedValue(undefined),
+ createdSessionId: 'new-signup-session-id',
+ };
+ mocks.useSignUp.mockReturnValue({
+ signUp: mockSignUpWithSession,
+ isLoaded: true,
+ });
+
+ const { result } = renderHook(() => useSignInWithGoogle());
+
+ const response = await result.current.startGoogleAuthenticationFlow({
+ unsafeMetadata: { referral: 'google' },
+ });
+
+ // Verify signIn.create was called first
+ expect(mockSignIn.create).toHaveBeenCalledWith({
+ strategy: 'google_one_tap',
+ token: mockIdToken,
+ });
+
+ // Verify signUp.create was called as fallback with the token
+ expect(mockSignUpWithSession.create).toHaveBeenCalledWith({
+ strategy: 'google_one_tap',
+ token: mockIdToken,
+ unsafeMetadata: { referral: 'google' },
+ });
+
+ // Verify the session was created
+ expect(response.createdSessionId).toBe('new-signup-session-id');
+ expect(response.setActive).toBe(mockSetActive);
+ });
+ });
+});
diff --git a/packages/expo/src/hooks/index.ts b/packages/expo/src/hooks/index.ts
index f5fdd57daf2..05747be3351 100644
--- a/packages/expo/src/hooks/index.ts
+++ b/packages/expo/src/hooks/index.ts
@@ -12,6 +12,7 @@ export {
} from '@clerk/react';
export * from './useSignInWithApple';
+export * from './useSignInWithGoogle';
export * from './useSSO';
export * from './useOAuth';
export * from './useAuth';
diff --git a/packages/expo/src/hooks/useSignInWithGoogle.android.ts b/packages/expo/src/hooks/useSignInWithGoogle.android.ts
new file mode 100644
index 00000000000..71922944fd6
--- /dev/null
+++ b/packages/expo/src/hooks/useSignInWithGoogle.android.ts
@@ -0,0 +1,209 @@
+import { useSignIn, useSignUp } from '@clerk/react/legacy';
+import type { SetActive, SignInResource, SignUpResource } from '@clerk/shared/types';
+import Constants from 'expo-constants';
+import * as Crypto from 'expo-crypto';
+
+import { ClerkGoogleOneTapSignIn, isSuccessResponse } from '../google-one-tap';
+import { errorThrower } from '../utils/errors';
+
+type SignUpUnsafeMetadata = Record;
+
+export type StartGoogleAuthenticationFlowParams = {
+ unsafeMetadata?: SignUpUnsafeMetadata;
+};
+
+export type StartGoogleAuthenticationFlowReturnType = {
+ createdSessionId: string | null;
+ setActive?: SetActive;
+ signIn?: SignInResource;
+ signUp?: SignUpResource;
+};
+
+/**
+ * Hook for native Google Authentication on Android using Clerk's built-in Google One Tap module.
+ *
+ * This hook provides a simplified way to authenticate users with their Google account
+ * using the native Android Google Sign-In UI with Credential Manager. The authentication
+ * flow automatically handles the ID token exchange with Clerk's backend and manages
+ * the transfer flow between sign-in and sign-up.
+ *
+ * Features:
+ * - Native Google One Tap UI
+ * - Built-in nonce support for replay attack protection
+ * - No additional dependencies required
+ *
+ * @example
+ * ```tsx
+ * import { useSignInWithGoogle } from '@clerk/clerk-expo';
+ * import { Button } from 'react-native';
+ *
+ * function GoogleSignInButton() {
+ * const { startGoogleAuthenticationFlow } = useSignInWithGoogle();
+ *
+ * const onPress = async () => {
+ * try {
+ * const { createdSessionId, setActive } = await startGoogleAuthenticationFlow();
+ *
+ * if (createdSessionId && setActive) {
+ * await setActive({ session: createdSessionId });
+ * }
+ * } catch (err) {
+ * console.error('Google Authentication error:', err);
+ * }
+ * };
+ *
+ * return ;
+ * }
+ * ```
+ *
+ * @platform Android - This is the Android-specific implementation using Credential Manager
+ *
+ * @returns An object containing the `startGoogleAuthenticationFlow` function
+ */
+export function useSignInWithGoogle() {
+ const { signIn, setActive, isLoaded: isSignInLoaded } = useSignIn();
+ const { signUp, isLoaded: isSignUpLoaded } = useSignUp();
+
+ async function startGoogleAuthenticationFlow(
+ startGoogleAuthenticationFlowParams?: StartGoogleAuthenticationFlowParams,
+ ): Promise {
+ if (!isSignInLoaded || !isSignUpLoaded) {
+ return {
+ createdSessionId: null,
+ signIn,
+ signUp,
+ setActive,
+ };
+ }
+
+ // Get environment variables from expo-constants
+ const webClientId =
+ Constants.expoConfig?.extra?.EXPO_PUBLIC_CLERK_GOOGLE_WEB_CLIENT_ID ||
+ process.env.EXPO_PUBLIC_CLERK_GOOGLE_WEB_CLIENT_ID;
+
+ if (!webClientId) {
+ return errorThrower.throw(
+ 'Google Sign-In credentials not found. Please set EXPO_PUBLIC_CLERK_GOOGLE_WEB_CLIENT_ID in your .env file.',
+ );
+ }
+
+ // Configure Google Sign-In
+ ClerkGoogleOneTapSignIn.configure({
+ webClientId,
+ });
+
+ // Generate a cryptographic nonce for replay attack protection
+ const nonce = Crypto.randomUUID();
+
+ try {
+ // Try to sign in with Google One Tap (shows saved accounts)
+ // Using presentExplicitSignIn for consistent "Sign in with Google" button behavior
+ const response = await ClerkGoogleOneTapSignIn.presentExplicitSignIn({
+ nonce,
+ });
+
+ // User cancelled
+ if (!isSuccessResponse(response)) {
+ return {
+ createdSessionId: null,
+ setActive,
+ signIn,
+ signUp,
+ };
+ }
+
+ const { idToken } = response.data;
+
+ try {
+ // Try to sign in with the Google One Tap strategy
+ await signIn.create({
+ strategy: 'google_one_tap',
+ token: idToken,
+ });
+
+ // Check if we need to transfer to SignUp (user doesn't exist yet)
+ const userNeedsToBeCreated = signIn.firstFactorVerification.status === 'transferable';
+
+ if (userNeedsToBeCreated) {
+ // User doesn't exist - create a new SignUp with transfer
+ await signUp.create({
+ transfer: true,
+ unsafeMetadata: startGoogleAuthenticationFlowParams?.unsafeMetadata,
+ });
+
+ return {
+ createdSessionId: signUp.createdSessionId,
+ setActive,
+ signIn,
+ signUp,
+ };
+ }
+
+ // User exists - return the SignIn session
+ return {
+ createdSessionId: signIn.createdSessionId,
+ setActive,
+ signIn,
+ signUp,
+ };
+ } catch (signInError: unknown) {
+ // Handle the case where the user doesn't exist (external_account_not_found)
+ const isClerkError =
+ signInError &&
+ typeof signInError === 'object' &&
+ 'clerkError' in signInError &&
+ (signInError as { clerkError: boolean }).clerkError === true;
+
+ const hasExternalAccountNotFoundError =
+ signInError &&
+ typeof signInError === 'object' &&
+ 'errors' in signInError &&
+ Array.isArray((signInError as { errors: unknown[] }).errors) &&
+ (signInError as { errors: Array<{ code: string }> }).errors.some(
+ err => err.code === 'external_account_not_found',
+ );
+
+ if (isClerkError && hasExternalAccountNotFoundError) {
+ // User doesn't exist - create a new SignUp with the token
+ await signUp.create({
+ strategy: 'google_one_tap',
+ token: idToken,
+ unsafeMetadata: startGoogleAuthenticationFlowParams?.unsafeMetadata,
+ });
+
+ return {
+ createdSessionId: signUp.createdSessionId,
+ setActive,
+ signIn,
+ signUp,
+ };
+ }
+
+ // Re-throw if it's a different error
+ throw signInError;
+ }
+ } catch (error: unknown) {
+ // Handle Google Sign-In errors
+ if (error && typeof error === 'object' && 'code' in error) {
+ const errorCode = (error as { code: string }).code;
+
+ // User canceled the sign-in flow
+ if (errorCode === 'SIGN_IN_CANCELLED') {
+ return {
+ createdSessionId: null,
+ setActive,
+ signIn,
+ signUp,
+ };
+ }
+ }
+
+ // Re-throw other errors
+ throw error;
+ }
+ }
+
+ return {
+ startGoogleAuthenticationFlow,
+ };
+}
diff --git a/packages/expo/src/hooks/useSignInWithGoogle.ios.ts b/packages/expo/src/hooks/useSignInWithGoogle.ios.ts
new file mode 100644
index 00000000000..1cafd40ca0c
--- /dev/null
+++ b/packages/expo/src/hooks/useSignInWithGoogle.ios.ts
@@ -0,0 +1,214 @@
+import { useSignIn, useSignUp } from '@clerk/react/legacy';
+import type { SetActive, SignInResource, SignUpResource } from '@clerk/shared/types';
+import Constants from 'expo-constants';
+import * as Crypto from 'expo-crypto';
+
+import { ClerkGoogleOneTapSignIn, isSuccessResponse } from '../google-one-tap';
+import { errorThrower } from '../utils/errors';
+
+type SignUpUnsafeMetadata = Record;
+
+export type StartGoogleAuthenticationFlowParams = {
+ unsafeMetadata?: SignUpUnsafeMetadata;
+};
+
+export type StartGoogleAuthenticationFlowReturnType = {
+ createdSessionId: string | null;
+ setActive?: SetActive;
+ signIn?: SignInResource;
+ signUp?: SignUpResource;
+};
+
+/**
+ * Hook for native Google Authentication on iOS using Clerk's built-in Google Sign-In module.
+ *
+ * This hook provides a simplified way to authenticate users with their Google account
+ * using the native iOS Google Sign-In UI. The authentication flow automatically
+ * handles the ID token exchange with Clerk's backend and manages the transfer flow
+ * between sign-in and sign-up.
+ *
+ * Features:
+ * - Native Google Sign-In UI
+ * - Built-in nonce support for replay attack protection
+ * - No additional dependencies required
+ *
+ * @example
+ * ```tsx
+ * import { useSignInWithGoogle } from '@clerk/clerk-expo';
+ * import { Button } from 'react-native';
+ *
+ * function GoogleSigninButton() {
+ * const { startGoogleAuthenticationFlow } = useSignInWithGoogle();
+ *
+ * const onPress = async () => {
+ * try {
+ * const { createdSessionId, setActive } = await startGoogleAuthenticationFlow();
+ *
+ * if (createdSessionId && setActive) {
+ * await setActive({ session: createdSessionId });
+ * }
+ * } catch (err) {
+ * console.error('Google Authentication error:', err);
+ * }
+ * };
+ *
+ * return ;
+ * }
+ * ```
+ *
+ * @platform iOS - This is the iOS-specific implementation using Google Sign-In SDK
+ *
+ * @returns An object containing the `startGoogleAuthenticationFlow` function
+ */
+export function useSignInWithGoogle() {
+ const { signIn, setActive, isLoaded: isSignInLoaded } = useSignIn();
+ const { signUp, isLoaded: isSignUpLoaded } = useSignUp();
+
+ async function startGoogleAuthenticationFlow(
+ startGoogleAuthenticationFlowParams?: StartGoogleAuthenticationFlowParams,
+ ): Promise {
+ if (!isSignInLoaded || !isSignUpLoaded) {
+ return {
+ createdSessionId: null,
+ signIn,
+ signUp,
+ setActive,
+ };
+ }
+
+ // Get environment variables from expo-constants
+ const webClientId =
+ Constants.expoConfig?.extra?.EXPO_PUBLIC_CLERK_GOOGLE_WEB_CLIENT_ID ||
+ process.env.EXPO_PUBLIC_CLERK_GOOGLE_WEB_CLIENT_ID;
+ const iosClientId =
+ Constants.expoConfig?.extra?.EXPO_PUBLIC_CLERK_GOOGLE_IOS_CLIENT_ID ||
+ process.env.EXPO_PUBLIC_CLERK_GOOGLE_IOS_CLIENT_ID;
+
+ if (!webClientId || !iosClientId) {
+ return errorThrower.throw(
+ 'Google Sign-In credentials not found. Please set EXPO_PUBLIC_CLERK_GOOGLE_WEB_CLIENT_ID and EXPO_PUBLIC_CLERK_GOOGLE_IOS_CLIENT_ID in your .env file.',
+ );
+ }
+
+ // Configure Google Sign-In with both client IDs
+ // - iosClientId: Used for the OAuth flow on iOS
+ // - webClientId: Used as serverClientID for token audience (what Clerk backend verifies)
+ ClerkGoogleOneTapSignIn.configure({
+ webClientId,
+ iosClientId,
+ });
+
+ // Generate a cryptographic nonce for replay attack protection
+ const nonce = Crypto.randomUUID();
+
+ try {
+ // Present Google Sign-In UI with nonce
+ const response = await ClerkGoogleOneTapSignIn.presentExplicitSignIn({
+ nonce,
+ });
+
+ // User cancelled
+ if (!isSuccessResponse(response)) {
+ return {
+ createdSessionId: null,
+ setActive,
+ signIn,
+ signUp,
+ };
+ }
+
+ const { idToken } = response.data;
+
+ try {
+ // Try to sign in with the Google One Tap strategy
+ await signIn.create({
+ strategy: 'google_one_tap',
+ token: idToken,
+ });
+
+ // Check if we need to transfer to SignUp (user doesn't exist yet)
+ const userNeedsToBeCreated = signIn.firstFactorVerification.status === 'transferable';
+
+ if (userNeedsToBeCreated) {
+ // User doesn't exist - create a new SignUp with transfer
+ await signUp.create({
+ transfer: true,
+ unsafeMetadata: startGoogleAuthenticationFlowParams?.unsafeMetadata,
+ });
+
+ return {
+ createdSessionId: signUp.createdSessionId,
+ setActive,
+ signIn,
+ signUp,
+ };
+ }
+
+ // User exists - return the SignIn session
+ return {
+ createdSessionId: signIn.createdSessionId,
+ setActive,
+ signIn,
+ signUp,
+ };
+ } catch (signInError: unknown) {
+ // Handle the case where the user doesn't exist (external_account_not_found)
+ const isClerkError =
+ signInError &&
+ typeof signInError === 'object' &&
+ 'clerkError' in signInError &&
+ (signInError as { clerkError: boolean }).clerkError === true;
+
+ const hasExternalAccountNotFoundError =
+ signInError &&
+ typeof signInError === 'object' &&
+ 'errors' in signInError &&
+ Array.isArray((signInError as { errors: unknown[] }).errors) &&
+ (signInError as { errors: Array<{ code: string }> }).errors.some(
+ err => err.code === 'external_account_not_found',
+ );
+
+ if (isClerkError && hasExternalAccountNotFoundError) {
+ // User doesn't exist - create a new SignUp with the token
+ await signUp.create({
+ strategy: 'google_one_tap',
+ token: idToken,
+ unsafeMetadata: startGoogleAuthenticationFlowParams?.unsafeMetadata,
+ });
+
+ return {
+ createdSessionId: signUp.createdSessionId,
+ setActive,
+ signIn,
+ signUp,
+ };
+ }
+
+ // Re-throw if it's a different error
+ throw signInError;
+ }
+ } catch (error: unknown) {
+ // Handle Google Sign-In errors
+ if (error && typeof error === 'object' && 'code' in error) {
+ const errorCode = (error as { code: string }).code;
+
+ // User canceled the sign-in flow
+ if (errorCode === 'SIGN_IN_CANCELLED') {
+ return {
+ createdSessionId: null,
+ setActive,
+ signIn,
+ signUp,
+ };
+ }
+ }
+
+ // Re-throw other errors
+ throw error;
+ }
+ }
+
+ return {
+ startGoogleAuthenticationFlow,
+ };
+}
diff --git a/packages/expo/src/hooks/useSignInWithGoogle.ts b/packages/expo/src/hooks/useSignInWithGoogle.ts
new file mode 100644
index 00000000000..0872a3e0e19
--- /dev/null
+++ b/packages/expo/src/hooks/useSignInWithGoogle.ts
@@ -0,0 +1,71 @@
+import type { SetActive, SignInResource, SignUpResource } from '@clerk/shared/types';
+
+import { errorThrower } from '../utils/errors';
+
+type SignUpUnsafeMetadata = Record;
+
+export type StartGoogleAuthenticationFlowParams = {
+ unsafeMetadata?: SignUpUnsafeMetadata;
+};
+
+export type StartGoogleAuthenticationFlowReturnType = {
+ createdSessionId: string | null;
+ setActive?: SetActive;
+ signIn?: SignInResource;
+ signUp?: SignUpResource;
+};
+
+/**
+ * Stub for Google Authentication hook on unsupported platforms.
+ *
+ * Native Google Authentication is only available on iOS and Android.
+ * For web platforms, use the OAuth-based Google Sign-In flow instead via useSSO.
+ *
+ * @example
+ * ```tsx
+ * import { useSSO } from '@clerk/clerk-expo';
+ * import { Button } from 'react-native';
+ *
+ * function GoogleSignInButton() {
+ * const { startSSOFlow } = useSSO();
+ *
+ * const onPress = async () => {
+ * try {
+ * const { createdSessionId, setActive } = await startSSOFlow({
+ * strategy: 'oauth_google'
+ * });
+ *
+ * if (createdSessionId && setActive) {
+ * await setActive({ session: createdSessionId });
+ * }
+ * } catch (err) {
+ * console.error('Google Authentication error:', err);
+ * }
+ * };
+ *
+ * return ;
+ * }
+ * ```
+ *
+ * @platform iOS, Android - This hook only works on iOS and Android. On other platforms, it will throw an error.
+ *
+ * @returns An object containing the `startGoogleAuthenticationFlow` function that throws an error
+ */
+export function useSignInWithGoogle(): {
+ startGoogleAuthenticationFlow: (
+ startGoogleAuthenticationFlowParams?: StartGoogleAuthenticationFlowParams,
+ ) => Promise;
+} {
+ function startGoogleAuthenticationFlow(
+ _startGoogleAuthenticationFlowParams?: StartGoogleAuthenticationFlowParams,
+ ): Promise {
+ return errorThrower.throw(
+ 'Native Google Authentication is only available on iOS and Android. ' +
+ 'For web and other platforms, please use the OAuth-based flow with useSSO and strategy: "oauth_google".',
+ );
+ }
+
+ return {
+ startGoogleAuthenticationFlow,
+ };
+}
diff --git a/packages/expo/vitest.setup.mts b/packages/expo/vitest.setup.mts
index 69aa8d2876c..970d1051ede 100644
--- a/packages/expo/vitest.setup.mts
+++ b/packages/expo/vitest.setup.mts
@@ -1,6 +1,20 @@
-import { beforeAll } from 'vitest';
+import { beforeAll, vi } from 'vitest';
globalThis.PACKAGE_NAME = '@clerk/expo';
globalThis.PACKAGE_VERSION = '0.0.0-test';
+// Mock globalThis.expo for expo-modules-core
+if (!globalThis.expo) {
+ // @ts-expect-error - Mocking expo for tests
+ globalThis.expo = {
+ EventEmitter: vi.fn(),
+ };
+}
+
+// Define __DEV__ for expo-modules-core
+if (typeof globalThis.__DEV__ === 'undefined') {
+ // @ts-expect-error - Mocking __DEV__ for tests
+ globalThis.__DEV__ = false;
+}
+
beforeAll(() => {});
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 232cb752a0f..85aea5a3aba 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -566,6 +566,9 @@ importers:
'@clerk/expo-passkeys':
specifier: workspace:*
version: link:../expo-passkeys
+ '@react-native-google-signin/google-signin':
+ specifier: ^16.0.0
+ version: 16.0.0(expo@54.0.13(@babel/core@7.28.4)(graphql@16.11.0)(react-native@0.81.4(@babel/core@7.28.4)(@react-native-community/cli@12.3.7)(@types/react@18.3.26)(react@18.3.1))(react@18.3.1))(react-native@0.81.4(@babel/core@7.28.4)(@react-native-community/cli@12.3.7)(@types/react@18.3.26)(react@18.3.1))(react@18.3.1)
'@types/base-64':
specifier: ^1.0.2
version: 1.0.2
@@ -574,13 +577,19 @@ importers:
version: 7.2.4(expo@54.0.23(@babel/core@7.28.5)(graphql@16.12.0)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.26)(react@18.3.1))(react@18.3.1))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.26)(react@18.3.1))
expo-auth-session:
specifier: ^5.4.0
- version: 5.5.2(expo@54.0.23(@babel/core@7.28.5)(graphql@16.12.0)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.26)(react@18.3.1))(react@18.3.1))
+ version: 5.4.0(expo@54.0.13(@babel/core@7.28.4)(graphql@16.11.0)(react-native@0.81.4(@babel/core@7.28.4)(@react-native-community/cli@12.3.7)(@types/react@18.3.26)(react@18.3.1))(react@18.3.1))
+ expo-constants:
+ specifier: ^18.0.0
+ version: 18.0.9(expo@54.0.13(@babel/core@7.28.4)(graphql@16.11.0)(react-native@0.81.4(@babel/core@7.28.4)(@react-native-community/cli@12.3.7)(@types/react@18.3.26)(react@18.3.1))(react@18.3.1))(react-native@0.81.4(@babel/core@7.28.4)(@react-native-community/cli@12.3.7)(@types/react@18.3.26)(react@18.3.1))
expo-crypto:
specifier: ^15.0.7
version: 15.0.7(expo@54.0.23(@babel/core@7.28.5)(graphql@16.12.0)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.26)(react@18.3.1))(react@18.3.1))
expo-local-authentication:
specifier: ^13.8.0
- version: 13.8.0(expo@54.0.23(@babel/core@7.28.5)(graphql@16.12.0)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.26)(react@18.3.1))(react@18.3.1))
+ version: 13.8.0(expo@54.0.13(@babel/core@7.28.4)(graphql@16.11.0)(react-native@0.81.4(@babel/core@7.28.4)(@react-native-community/cli@12.3.7)(@types/react@18.3.26)(react@18.3.1))(react@18.3.1))
+ expo-modules-core:
+ specifier: ^3.0.0
+ version: 3.0.21(react-native@0.81.4(@babel/core@7.28.4)(@react-native-community/cli@12.3.7)(@types/react@18.3.26)(react@18.3.1))(react@18.3.1)
expo-secure-store:
specifier: ^12.8.1
version: 12.8.1(expo@54.0.23(@babel/core@7.28.5)(graphql@16.12.0)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@18.3.26)(react@18.3.1))(react@18.3.1))
@@ -3653,9 +3662,21 @@ packages:
'@oxc-project/types@0.96.0':
resolution: {integrity: sha512-r/xkmoXA0xEpU6UGtn18CNVjXH6erU3KCpCDbpLmbVxBFor1U9MqN5Z2uMmCHJuXjJzlnDR+hWY+yPoLo8oHDw==}
- '@oxc-transform/binding-android-arm64@0.96.0':
- resolution: {integrity: sha512-wOm+ZsqFvyZ7B9RefUMsj0zcXw77Z2pXA51nbSQyPXqr+g0/pDGxriZWP8Sdpz/e4AEaKPA9DvrwyOZxu7GRDQ==}
- engines: {node: ^20.19.0 || >=22.12.0}
+ '@oxc-project/types@0.71.0':
+ resolution: {integrity: sha512-5CwQ4MI+P4MQbjLWXgNurA+igGwu/opNetIE13LBs9+V93R64MLvDKOOLZIXSzEfovU3Zef3q3GjPnMTgJTn2w==}
+
+ '@oxc-project/types@0.87.0':
+ resolution: {integrity: sha512-ipZFWVGE9fADBVXXWJWY/cxpysc41Gt5upKDeb32F6WMgFyO7XETUMVq8UuREKCih+Km5E6p2VhEvf6Fuhey6g==}
+
+ '@oxc-project/types@0.94.0':
+ resolution: {integrity: sha512-+UgQT/4o59cZfH6Cp7G0hwmqEQ0wE+AdIwhikdwnhWI9Dp8CgSY081+Q3O67/wq3VJu8mgUEB93J9EHHn70fOw==}
+
+ '@oxc-project/types@0.98.0':
+ resolution: {integrity: sha512-Vzmd6FsqVuz5HQVcRC/hrx7Ujo3WEVeQP7C2UNP5uy1hUY4SQvMB+93jxkI1KRHz9a/6cni3glPOtvteN+zpsw==}
+
+ '@oxc-transform/binding-android-arm64@0.87.0':
+ resolution: {integrity: sha512-B7W6J8T9cS054LUGLfYkYz8bz5+t+4yPftZ67Bn6MJ03okMLnbbEfm1bID1tqcP5tJwMurTILVy/dQfDYDcMgQ==}
+ engines: {node: '>=14.0.0'}
cpu: [arm64]
os: [android]
@@ -3880,8 +3901,143 @@ packages:
'@quansync/fs@0.1.5':
resolution: {integrity: sha512-lNS9hL2aS2NZgNW7BBj+6EBl4rOf8l+tQ0eRY6JWCI8jI2kc53gSoqbjojU0OnAWhzoXiOjFyGsHcDGePB3lhA==}
- '@react-native/assets-registry@0.81.5':
- resolution: {integrity: sha512-705B6x/5Kxm1RKRvSv0ADYWm5JOnoiQ1ufW7h8uu2E6G9Of/eE6hP/Ivw3U5jI16ERqZxiKQwk34VJbB0niX9w==}
+ '@radix-ui/primitive@1.1.3':
+ resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==}
+
+ '@radix-ui/react-compose-refs@1.1.2':
+ resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==}
+ peerDependencies:
+ '@types/react': '*'
+ react: 18.3.1
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-context@1.1.2':
+ resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==}
+ peerDependencies:
+ '@types/react': '*'
+ react: 18.3.1
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-form@0.1.8':
+ resolution: {integrity: sha512-QM70k4Zwjttifr5a4sZFts9fn8FzHYvQ5PiB19O2HsYibaHSVt9fH9rzB0XZo/YcM+b7t/p7lYCT/F5eOeF5yQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: 18.3.1
+ react-dom: 18.3.1
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-id@1.1.1':
+ resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==}
+ peerDependencies:
+ '@types/react': '*'
+ react: 18.3.1
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-label@2.1.7':
+ resolution: {integrity: sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: 18.3.1
+ react-dom: 18.3.1
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-primitive@2.1.3':
+ resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: 18.3.1
+ react-dom: 18.3.1
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@radix-ui/react-slot@1.2.3':
+ resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==}
+ peerDependencies:
+ '@types/react': '*'
+ react: 18.3.1
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@radix-ui/react-use-layout-effect@1.1.1':
+ resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==}
+ peerDependencies:
+ '@types/react': '*'
+ react: 18.3.1
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
+ '@react-native-community/cli-clean@12.3.7':
+ resolution: {integrity: sha512-BCYW77QqyxfhiMEBOoHyciJRNV6Rhz1RvclReIKnCA9wAwmoJBeu4Mu+AwiECA2bUITX16fvPt3NwDsSd1jwfQ==}
+
+ '@react-native-community/cli-config@12.3.7':
+ resolution: {integrity: sha512-IU2UhO9yj1rEBNhHWGzIXpPDzha4hizLP/PUOrhR4BUf6RVPUWEp+e1PXNGR0qjIf6esu7OC7t6mLOhH0NUJEw==}
+
+ '@react-native-community/cli-debugger-ui@12.3.7':
+ resolution: {integrity: sha512-UHUFrRdcjWSCdWG9KIp2QjuRIahBQnb9epnQI7JCq6NFbFHYfEI4rI7msjMn+gG8/tSwKTV2PTPuPmZ5wWlE7Q==}
+
+ '@react-native-community/cli-doctor@12.3.7':
+ resolution: {integrity: sha512-gCamZztRoAyhciuQPqdz4Xe4t3gOdNsaADNd+rva+Rx8W2PoPeNv60i7/et06wlsn6B6Sh0/hMiAftJbiHDFkg==}
+
+ '@react-native-community/cli-hermes@12.3.7':
+ resolution: {integrity: sha512-ezzeiSKjRXK2+i1AAe7NhhN9CEHrgtRmTn2MAdBpE++N8fH5EQZgxFcGgGdwGvns2fm9ivyyeVnI5eAYwvM+jg==}
+
+ '@react-native-community/cli-platform-android@12.3.7':
+ resolution: {integrity: sha512-mOltF3cpjNdJb3WSFwEHc1GH4ibCcnOvQ34OdWyblKy9ijuvG5SjNTlYR/UW/CURaDi3OUKAhxQMTY5d27bzGQ==}
+
+ '@react-native-community/cli-platform-ios@12.3.7':
+ resolution: {integrity: sha512-2WnVsMH4ORZIhBm/5nCms1NeeKG4KarNC7PMLmrXWXB/bibDcaNsjrJiqnmCUcpTEvTQTokRfoO7Aj6NM0Cqow==}
+
+ '@react-native-community/cli-plugin-metro@12.3.7':
+ resolution: {integrity: sha512-ahEw0Vfnv2Nv/jdZ2QDuGjQ9l2SczO4lXjb3ubu5vEYNLyTw3jYsLMK6iES7YQ/ApQmKdG476HU1O9uZdpaYPg==}
+
+ '@react-native-community/cli-server-api@12.3.7':
+ resolution: {integrity: sha512-LYETs3CCjrLn1ZU0kYv44TywiIl5IPFHZGeXhAh2TtgOk4mo3kvXxECDil9CdO3bmDra6qyiG61KHvzr8IrHdg==}
+
+ '@react-native-community/cli-tools@12.3.7':
+ resolution: {integrity: sha512-7NL/1/i+wzd4fBr/FSr3ypR05tiU/Kv9l/M1sL1c6jfcDtWXAL90R161gQkQFK7shIQ8Idp0dQX1rq49tSyfQw==}
+
+ '@react-native-community/cli-types@12.3.7':
+ resolution: {integrity: sha512-NFtUMyIrNfi3A5C1cjVKDVvYHvvOF7MnOMwdD8jm2NQKewQJrehKBh1eMuykKdqhWyZmuemD4KKhL8f4FxgG0w==}
+
+ '@react-native-community/cli@12.3.7':
+ resolution: {integrity: sha512-7+mOhk+3+X3BjSJZZvYrDJynA00gPYTlvT28ZjiLlbuVGfqfNiBKaxuF7rty+gjjpch4iKGvLhIhSN5cuOsdHQ==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ '@react-native-google-signin/google-signin@16.0.0':
+ resolution: {integrity: sha512-jVuzPo8odREekFc0b4RK3YsqCvedtLIM2P6NSszFr9cYyhKrUNikffPapL6LmkL9qkb8K6pDeb5CXg4qALOc0g==}
+ peerDependencies:
+ expo: '>=52.0.40'
+ react: 18.3.1
+ react-native: '*'
+ peerDependenciesMeta:
+ expo:
+ optional: true
+
+ '@react-native/assets-registry@0.81.4':
+ resolution: {integrity: sha512-AMcDadefBIjD10BRqkWw+W/VdvXEomR6aEZ0fhQRAv7igrBzb4PTn4vHKYg+sUK0e3wa74kcMy2DLc/HtnGcMA==}
engines: {node: '>= 20.19.4'}
'@react-native/babel-plugin-codegen@0.76.9':
@@ -3975,95 +4131,252 @@ packages:
cpu: [arm64]
os: [android]
- '@rolldown/binding-darwin-arm64@1.0.0-beta.47':
- resolution: {integrity: sha512-Lc3nrkxeaDVCVl8qR3qoxh6ltDZfkQ98j5vwIr5ALPkgjZtDK4BGCrrBoLpGVMg+csWcaqUbwbKwH5yvVa0oOw==}
+ '@rolldown/binding-android-arm64@1.0.0-beta.51':
+ resolution: {integrity: sha512-Ctn8FUXKWWQI9pWC61P1yumS9WjQtelNS9riHwV7oCkknPGaAry4o7eFx2KgoLMnI2BgFJYpW7Im8/zX3BuONg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [android]
+
+ '@rolldown/binding-darwin-arm64@1.0.0-beta.43':
+ resolution: {integrity: sha512-kuVWnZsE4vEjMF/10SbSUyzucIW2zmdsqFghYMqy+fsjXnRHg0luTU6qWF8IqJf4Cbpm9NEZRnjIEPpAbdiSNQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [darwin]
- '@rolldown/binding-darwin-x64@1.0.0-beta.47':
- resolution: {integrity: sha512-eBYxQDwP0O33plqNVqOtUHqRiSYVneAknviM5XMawke3mwMuVlAsohtOqEjbCEl/Loi/FWdVeks5WkqAkzkYWQ==}
+ '@rolldown/binding-darwin-arm64@1.0.0-beta.51':
+ resolution: {integrity: sha512-EL1aRW2Oq15ShUEkBPsDtLMO8GTqfb/ktM/dFaVzXKQiEE96Ss6nexMgfgQrg8dGnNpndFyffVDb5IdSibsu1g==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@rolldown/binding-darwin-arm64@1.0.0-beta.9-commit.d91dfb5':
+ resolution: {integrity: sha512-Mp0/gqiPdepHjjVm7e0yL1acWvI0rJVVFQEADSezvAjon9sjQ7CEg9JnXICD4B1YrPmN9qV/e7cQZCp87tTV4w==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@rolldown/binding-darwin-x64@1.0.0-beta.43':
+ resolution: {integrity: sha512-u9Ps4sh6lcmJ3vgLtyEg/x4jlhI64U0mM93Ew+tlfFdLDe7yKyA+Fe80cpr2n1mNCeZXrvTSbZluKpXQ0GxLjw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [darwin]
- '@rolldown/binding-freebsd-x64@1.0.0-beta.47':
- resolution: {integrity: sha512-Ns+kgp2+1Iq/44bY/Z30DETUSiHY7ZuqaOgD5bHVW++8vme9rdiWsN4yG4rRPXkdgzjvQ9TDHmZZKfY4/G11AA==}
+ '@rolldown/binding-darwin-x64@1.0.0-beta.51':
+ resolution: {integrity: sha512-uGtYKlFen9pMIPvkHPWZVDtmYhMQi5g5Ddsndg1gf3atScKYKYgs5aDP4DhHeTwGXQglhfBG7lEaOIZ4UAIWww==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [darwin]
+
+ '@rolldown/binding-darwin-x64@1.0.0-beta.9-commit.d91dfb5':
+ resolution: {integrity: sha512-40re4rMNrsi57oavRzIOpRGmg3QRlW6Ea8Q3znaqgOuJuKVrrm2bIQInTfkZJG7a4/5YMX7T951d0+toGLTdCA==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@rolldown/binding-freebsd-x64@1.0.0-beta.43':
+ resolution: {integrity: sha512-h9lUtVtXgfbk/tnicMpbFfZ3DJvk5Zn2IvmlC1/e0+nUfwoc/TFqpfrRRqcNBXk/e+xiWMSKv6b0MF8N+Rtvlg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@rolldown/binding-freebsd-x64@1.0.0-beta.51':
+ resolution: {integrity: sha512-JRoVTQtHYbZj1P07JLiuTuXjiBtIa7ag7/qgKA6CIIXnAcdl4LrOf7nfDuHPJcuRKaP5dzecMgY99itvWfmUFQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [freebsd]
- '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.47':
- resolution: {integrity: sha512-4PecgWCJhTA2EFOlptYJiNyVP2MrVP4cWdndpOu3WmXqWqZUmSubhb4YUAIxAxnXATlGjC1WjxNPhV7ZllNgdA==}
+ '@rolldown/binding-freebsd-x64@1.0.0-beta.9-commit.d91dfb5':
+ resolution: {integrity: sha512-8BDM939bbMariZupiHp3OmP5N+LXPT4mULA0hZjDaq970PCxv4krZOSMG+HkWUUwmuQROtV+/00xw39EO0P+8g==}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.43':
+ resolution: {integrity: sha512-IX2C6bA6wM2rX/RvD75ko+ix9yxPKjKGGq7pOhB8wGI4Z4fqX5B1nDHga/qMDmAdCAR1m9ymzxkmqhm/AFYf7A==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm]
os: [linux]
- '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.47':
- resolution: {integrity: sha512-CyIunZ6D9U9Xg94roQI1INt/bLkOpPsZjZZkiaAZ0r6uccQdICmC99M9RUPlMLw/qg4yEWLlQhG73W/mG437NA==}
+ '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.51':
+ resolution: {integrity: sha512-BKATVnpPZ0TYBW9XfDwyd4kPGgvf964HiotIwUgpMrFOFYWqpZ+9ONNzMV4UFAYC7Hb5C2qgYQk/qj2OnAd4RQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [linux]
+
+ '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.9-commit.d91dfb5':
+ resolution: {integrity: sha512-sntsPaPgrECpBB/+2xrQzVUt0r493TMPI+4kWRMhvMsmrxOqH1Ep5lM0Wua/ZdbfZNwm1aVa5pcESQfNfM4Fhw==}
+ cpu: [arm]
+ os: [linux]
+
+ '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.43':
+ resolution: {integrity: sha512-mcjd57vEj+CEQbZAzUiaxNzNgwwgOpFtZBWcINm8DNscvkXl5b/s622Z1dqGNWSdrZmdjdC6LWMvu8iHM6v9sQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
libc: [glibc]
- '@rolldown/binding-linux-arm64-musl@1.0.0-beta.47':
- resolution: {integrity: sha512-doozc/Goe7qRCSnzfJbFINTHsMktqmZQmweull6hsZZ9sjNWQ6BWQnbvOlfZJe4xE5NxM1NhPnY5Giqnl3ZrYQ==}
+ '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.51':
+ resolution: {integrity: sha512-xLd7da5jkfbVsBCm1buIRdWtuXY8+hU3+6ESXY/Tk5X5DPHaifrUblhYDgmA34dQt6WyNC2kfXGgrduPEvDI6Q==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.9-commit.d91dfb5':
+ resolution: {integrity: sha512-5clBW/I+er9F2uM1OFjJFWX86y7Lcy0M+NqsN4s3o07W+8467Zk8oQa4B45vdaXoNUF/yqIAgKkA/OEdQDxZqA==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rolldown/binding-linux-arm64-musl@1.0.0-beta.43':
+ resolution: {integrity: sha512-Pa8QMwlkrztTo/1mVjZmPIQ44tCSci10TBqxzVBvXVA5CFh5EpiEi99fPSll2dHG2uT4dCOMeC6fIhyDdb0zXA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
libc: [musl]
- '@rolldown/binding-linux-x64-gnu@1.0.0-beta.47':
- resolution: {integrity: sha512-fodvSMf6Aqwa0wEUSTPewmmZOD44rc5Tpr5p9NkwQ6W1SSpUKzD3SwpJIgANDOhwiYhDuiIaYPGB7Ujkx1q0UQ==}
+ '@rolldown/binding-linux-arm64-musl@1.0.0-beta.51':
+ resolution: {integrity: sha512-EQFXTgHxxTzv3t5EmjUP/DfxzFYx9sMndfLsYaAY4DWF6KsK1fXGYsiupif6qPTViPC9eVmRm78q0pZU/kuIPg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@rolldown/binding-linux-arm64-musl@1.0.0-beta.9-commit.d91dfb5':
+ resolution: {integrity: sha512-wv+rnAfQDk9p/CheX8/Kmqk2o1WaFa4xhWI9gOyDMk/ljvOX0u0ubeM8nI1Qfox7Tnh71eV5AjzSePXUhFOyOg==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@rolldown/binding-linux-x64-gnu@1.0.0-beta.43':
+ resolution: {integrity: sha512-BgynXKMjeaX4AfWLARhOKDetBOOghnSiVRjAHVvhiAaDXgdQN8e65mSmXRiVoVtD3cHXx/cfU8Gw0p0K+qYKVQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
libc: [glibc]
- '@rolldown/binding-linux-x64-musl@1.0.0-beta.47':
- resolution: {integrity: sha512-Rxm5hYc0mGjwLh5sjlGmMygxAaV2gnsx7CNm2lsb47oyt5UQyPDZf3GP/ct8BEcwuikdqzsrrlIp8+kCSvMFNQ==}
+ '@rolldown/binding-linux-x64-gnu@1.0.0-beta.51':
+ resolution: {integrity: sha512-p5P6Xpa68w3yFaAdSzIZJbj+AfuDnMDqNSeglBXM7UlJT14Q4zwK+rV+8Mhp9MiUb4XFISZtbI/seBprhkQbiQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rolldown/binding-linux-x64-gnu@1.0.0-beta.9-commit.d91dfb5':
+ resolution: {integrity: sha512-gxD0/xhU4Py47IH3bKZbWtvB99tMkUPGPJFRfSc5UB9Osoje0l0j1PPbxpUtXIELurYCqwLBKXIMTQGifox1BQ==}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rolldown/binding-linux-x64-musl@1.0.0-beta.43':
+ resolution: {integrity: sha512-VIsoPlOB/tDSAw9CySckBYysoIBqLeps1/umNSYUD8pMtalJyzMTneAVI1HrUdf4ceFmQ5vARoLIXSsPwVFxNg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
libc: [musl]
- '@rolldown/binding-openharmony-arm64@1.0.0-beta.47':
- resolution: {integrity: sha512-YakuVe+Gc87jjxazBL34hbr8RJpRuFBhun7NEqoChVDlH5FLhLXjAPHqZd990TVGVNkemourf817Z8u2fONS8w==}
+ '@rolldown/binding-linux-x64-musl@1.0.0-beta.51':
+ resolution: {integrity: sha512-sNVVyLa8HB8wkFipdfz1s6i0YWinwpbMWk5hO5S+XAYH2UH67YzUT13gs6wZTKg2x/3gtgXzYnHyF5wMIqoDAw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@rolldown/binding-linux-x64-musl@1.0.0-beta.9-commit.d91dfb5':
+ resolution: {integrity: sha512-HotuVe3XUjDwqqEMbm3o3IRkP9gdm8raY/btd/6KE3JGLF/cv4+3ff1l6nOhAZI8wulWDPEXPtE7v+HQEaTXnA==}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@rolldown/binding-openharmony-arm64@1.0.0-beta.43':
+ resolution: {integrity: sha512-YDXTxVJG67PqTQMKyjVJSddoPbSWJ4yRz/E3xzTLHqNrTDGY0UuhG8EMr8zsYnfH/0cPFJ3wjQd/hJWHuR6nkA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@rolldown/binding-openharmony-arm64@1.0.0-beta.51':
+ resolution: {integrity: sha512-e/JMTz9Q8+T3g/deEi8DK44sFWZWGKr9AOCW5e8C8SCVWzAXqYXAG7FXBWBNzWEZK0Rcwo9TQHTQ9Q0gXgdCaA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [openharmony]
- '@rolldown/binding-wasm32-wasi@1.0.0-beta.47':
- resolution: {integrity: sha512-ak2GvTFQz3UAOw8cuQq8pWE+TNygQB6O47rMhvevvTzETh7VkHRFtRUwJynX5hwzFvQMP6G0az5JrBGuwaMwYQ==}
+ '@rolldown/binding-wasm32-wasi@1.0.0-beta.43':
+ resolution: {integrity: sha512-3M+2DmorXvDuAIGYQ9Z93Oy1G9ETkejLwdXXb1uRTgKN9pMcu7N+KG2zDrJwqyxeeLIFE22AZGtSJm3PJbNu9Q==}
engines: {node: '>=14.0.0'}
cpu: [wasm32]
- '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.47':
- resolution: {integrity: sha512-o5BpmBnXU+Cj+9+ndMcdKjhZlPb79dVPBZnWwMnI4RlNSSq5yOvFZqvfPYbyacvnW03Na4n5XXQAPhu3RydZ0w==}
+ '@rolldown/binding-wasm32-wasi@1.0.0-beta.51':
+ resolution: {integrity: sha512-We3LWqSu6J9s5Y0MK+N7fUiiu37aBGPG3Pc347EoaROuAwkCS2u9xJ5dpIyLW4B49CIbS3KaPmn4kTgPb3EyPw==}
+ engines: {node: '>=14.0.0'}
+ cpu: [wasm32]
+
+ '@rolldown/binding-wasm32-wasi@1.0.0-beta.9-commit.d91dfb5':
+ resolution: {integrity: sha512-8Cx+ucbd8n2dIr21FqBh6rUvTVL0uTgEtKR7l+MUZ5BgY4dFh1e4mPVX8oqmoYwOxBiXrsD2JIOCz4AyKLKxWA==}
+ engines: {node: '>=14.21.3'}
+ cpu: [wasm32]
+
+ '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.43':
+ resolution: {integrity: sha512-/B1j1pJs33y9ywtslOMxryUPHq8zIGu/OGEc2gyed0slimJ8fX2uR/SaJVhB4+NEgCFIeYDR4CX6jynAkeRuCA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [win32]
- '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.47':
- resolution: {integrity: sha512-FVOmfyYehNE92IfC9Kgs913UerDog2M1m+FADJypKz0gmRg3UyTt4o1cZMCAl7MiR89JpM9jegNO1nXuP1w1vw==}
+ '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.51':
+ resolution: {integrity: sha512-fj56buHRuMM+r/cb6ZYfNjNvO/0xeFybI6cTkTROJatdP4fvmQ1NS8D/Lm10FCSDEOkqIz8hK3TGpbAThbPHsA==}
engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [win32]
+
+ '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.9-commit.d91dfb5':
+ resolution: {integrity: sha512-Vhq5vikrVDxAa75fxsyqj0c0Y/uti/TwshXI71Xb8IeUQJOBnmLUsn5dgYf5ljpYYkNa0z9BPAvUDIDMmyDi+w==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.43':
+ resolution: {integrity: sha512-29oG1swCz7hNP+CQYrsM4EtylsKwuYzM8ljqbqC5TsQwmKat7P8ouDpImsqg/GZxFSXcPP9ezQm0Q0wQwGM3JA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [ia32]
+ os: [win32]
+
+ '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.51':
+ resolution: {integrity: sha512-fkqEqaeEx8AySXiDm54b/RdINb3C0VovzJA3osMhZsbn6FoD73H0AOIiaVAtGr6x63hefruVKTX8irAm4Jkt2w==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [ia32]
+ os: [win32]
+
+ '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.9-commit.d91dfb5':
+ resolution: {integrity: sha512-lN7RIg9Iugn08zP2aZN9y/MIdG8iOOCE93M1UrFlrxMTqPf8X+fDzmR/OKhTSd1A2pYNipZHjyTcb5H8kyQSow==}
cpu: [ia32]
os: [win32]
- '@rolldown/binding-win32-x64-msvc@1.0.0-beta.47':
- resolution: {integrity: sha512-by/70F13IUE101Bat0oeH8miwWX5mhMFPk1yjCdxoTNHTyTdLgb0THNaebRM6AP7Kz+O3O2qx87sruYuF5UxHg==}
+ '@rolldown/binding-win32-x64-msvc@1.0.0-beta.43':
+ resolution: {integrity: sha512-eWBV1Ef3gfGNehxVGCyXs7wLayRIgCmyItuCZwYYXW5bsk4EvR4n2GP5m3ohjnx7wdiY3nLmwQfH2Knb5gbNZw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [win32]
- '@rolldown/pluginutils@1.0.0-beta.27':
- resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==}
+ '@rolldown/binding-win32-x64-msvc@1.0.0-beta.51':
+ resolution: {integrity: sha512-CWuLG/HMtrVcjKGa0C4GnuxONrku89g0+CsH8nT0SNhOtREXuzwgjIXNJImpE/A/DMf9JF+1Xkrq/YRr+F/rCg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [win32]
+
+ '@rolldown/binding-win32-x64-msvc@1.0.0-beta.9-commit.d91dfb5':
+ resolution: {integrity: sha512-7/7cLIn48Y+EpQ4CePvf8reFl63F15yPUlg4ZAhl+RXJIfydkdak1WD8Ir3AwAO+bJBXzrfNL+XQbxm0mcQZmw==}
+ cpu: [x64]
+ os: [win32]
+
+ '@rolldown/pluginutils@1.0.0-beta.11':
+ resolution: {integrity: sha512-L/gAA/hyCSuzTF1ftlzUSI/IKr2POHsv1Dd78GfqkR83KMNuswWD61JxGV2L7nRwBBBSDr6R1gCkdTmoN7W4ag==}
'@rolldown/pluginutils@1.0.0-beta.29':
resolution: {integrity: sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==}
- '@rolldown/pluginutils@1.0.0-beta.47':
- resolution: {integrity: sha512-8QagwMH3kNCuzD8EWL8R2YPW5e4OrHNSAHRFDdmFqEwEaD/KcNKjVoumo+gP2vW5eKB2UPbM6vTYiGZX0ixLnw==}
+ '@rolldown/pluginutils@1.0.0-beta.43':
+ resolution: {integrity: sha512-5Uxg7fQUCmfhax7FJke2+8B6cqgeUJUD9o2uXIKXhD+mG0mL6NObmVoi9wXEU1tY89mZKgAYA6fTbftx3q2ZPQ==}
+
+ '@rolldown/pluginutils@1.0.0-beta.51':
+ resolution: {integrity: sha512-51/8cNXMrqWqX3o8DZidhwz1uYq0BhHDDSfVygAND1Skx5s1TDw3APSSxCMcFFedwgqGcx34gRouwY+m404BBQ==}
+
+ '@rolldown/pluginutils@1.0.0-beta.9-commit.d91dfb5':
+ resolution: {integrity: sha512-8sExkWRK+zVybw3+2/kBkYBFeLnEUWz1fT7BLHplpzmtqkOfTbAQ9gkt4pzwGIIZmg4Qn5US5ACjUBenrhezwQ==}
'@rollup/plugin-alias@5.1.1':
resolution: {integrity: sha512-PR9zDb+rOzkRb2VD+EuKB7UC41vU5DIwZ5qqCpk0KJudcWAyi8rvYOhS7+L5aZCspw1stTViLgN5v6FF1p5cgQ==}
@@ -12309,8 +12622,17 @@ packages:
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
- rollup-plugin-visualizer@6.0.5:
- resolution: {integrity: sha512-9+HlNgKCVbJDs8tVtjQ43US12eqaiHyyiLMdBwQ7vSZPiHMysGNo2E88TAp1si5wx8NAoYriI2A5kuKfIakmJg==}
+ rolldown@1.0.0-beta.51:
+ resolution: {integrity: sha512-ZRLgPlS91l4JztLYEZnmMcd3Umcla1hkXJgiEiR4HloRJBBoeaX8qogTu5Jfu36rRMVLndzqYv0h+M5gJAkUfg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+
+ rolldown@1.0.0-beta.9-commit.d91dfb5:
+ resolution: {integrity: sha512-FHkj6gGEiEgmAXQchglofvUUdwj2Oiw603Rs+zgFAnn9Cb7T7z3fiaEc0DbN3ja4wYkW6sF2rzMEtC1V4BGx/g==}
+ hasBin: true
+
+ rollup-plugin-visualizer@6.0.3:
+ resolution: {integrity: sha512-ZU41GwrkDcCpVoffviuM9Clwjy5fcUxlz0oMoTXTYsK+tcIFzbdacnrr2n8TXcHxbGKKXtOdjxM2HUS4HjkwIw==}
engines: {node: '>=18'}
hasBin: true
peerDependencies:
@@ -18270,9 +18592,19 @@ snapshots:
'@oxc-parser/binding-win32-x64-msvc@0.96.0':
optional: true
- '@oxc-project/types@0.96.0': {}
+ '@oxc-project/runtime@0.71.0':
+ optional: true
+
+ '@oxc-project/types@0.71.0':
+ optional: true
+
+ '@oxc-project/types@0.87.0': {}
+
+ '@oxc-project/types@0.94.0': {}
+
+ '@oxc-project/types@0.98.0': {}
- '@oxc-transform/binding-android-arm64@0.96.0':
+ '@oxc-transform/binding-android-arm64@0.87.0':
optional: true
'@oxc-transform/binding-darwin-arm64@0.96.0':
@@ -18423,7 +18755,233 @@ snapshots:
'@react-native/babel-plugin-codegen@0.76.9(@babel/preset-env@7.28.5(@babel/core@7.28.5))':
dependencies:
- '@react-native/codegen': 0.76.9(@babel/preset-env@7.28.5(@babel/core@7.28.5))
+ react: 18.3.1
+ optionalDependencies:
+ '@types/react': 18.3.26
+
+ '@radix-ui/react-context@1.1.2(@types/react@18.3.26)(react@18.3.1)':
+ dependencies:
+ react: 18.3.1
+ optionalDependencies:
+ '@types/react': 18.3.26
+
+ '@radix-ui/react-form@0.1.8(@types/react-dom@18.3.7(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@radix-ui/primitive': 1.1.3
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.26)(react@18.3.1)
+ '@radix-ui/react-context': 1.1.2(@types/react@18.3.26)(react@18.3.1)
+ '@radix-ui/react-id': 1.1.1(@types/react@18.3.26)(react@18.3.1)
+ '@radix-ui/react-label': 2.1.7(@types/react-dom@18.3.7(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ optionalDependencies:
+ '@types/react': 18.3.26
+ '@types/react-dom': 18.3.7(@types/react@18.3.26)
+
+ '@radix-ui/react-id@1.1.1(@types/react@18.3.26)(react@18.3.1)':
+ dependencies:
+ '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.26)(react@18.3.1)
+ react: 18.3.1
+ optionalDependencies:
+ '@types/react': 18.3.26
+
+ '@radix-ui/react-label@2.1.7(@types/react-dom@18.3.7(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ optionalDependencies:
+ '@types/react': 18.3.26
+ '@types/react-dom': 18.3.7(@types/react@18.3.26)
+
+ '@radix-ui/react-primitive@2.1.3(@types/react-dom@18.3.7(@types/react@18.3.26))(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@radix-ui/react-slot': 1.2.3(@types/react@18.3.26)(react@18.3.1)
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ optionalDependencies:
+ '@types/react': 18.3.26
+ '@types/react-dom': 18.3.7(@types/react@18.3.26)
+
+ '@radix-ui/react-slot@1.2.3(@types/react@18.3.26)(react@18.3.1)':
+ dependencies:
+ '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.26)(react@18.3.1)
+ react: 18.3.1
+ optionalDependencies:
+ '@types/react': 18.3.26
+
+ '@radix-ui/react-use-layout-effect@1.1.1(@types/react@18.3.26)(react@18.3.1)':
+ dependencies:
+ react: 18.3.1
+ optionalDependencies:
+ '@types/react': 18.3.26
+
+ '@react-native-community/cli-clean@12.3.7':
+ dependencies:
+ '@react-native-community/cli-tools': 12.3.7
+ chalk: 4.1.2
+ execa: 5.1.1
+ transitivePeerDependencies:
+ - encoding
+ optional: true
+
+ '@react-native-community/cli-config@12.3.7':
+ dependencies:
+ '@react-native-community/cli-tools': 12.3.7
+ chalk: 4.1.2
+ cosmiconfig: 5.2.1
+ deepmerge: 4.3.1
+ glob: 7.2.3
+ joi: 17.13.3
+ transitivePeerDependencies:
+ - encoding
+ optional: true
+
+ '@react-native-community/cli-debugger-ui@12.3.7':
+ dependencies:
+ serve-static: 1.16.2
+ transitivePeerDependencies:
+ - supports-color
+ optional: true
+
+ '@react-native-community/cli-doctor@12.3.7':
+ dependencies:
+ '@react-native-community/cli-config': 12.3.7
+ '@react-native-community/cli-platform-android': 12.3.7
+ '@react-native-community/cli-platform-ios': 12.3.7
+ '@react-native-community/cli-tools': 12.3.7
+ chalk: 4.1.2
+ command-exists: 1.2.9
+ deepmerge: 4.3.1
+ envinfo: 7.17.0
+ execa: 5.1.1
+ hermes-profile-transformer: 0.0.6
+ node-stream-zip: 1.15.0
+ ora: 5.4.1
+ semver: 7.7.3
+ strip-ansi: 5.2.0
+ wcwidth: 1.0.1
+ yaml: 2.8.1
+ transitivePeerDependencies:
+ - encoding
+ optional: true
+
+ '@react-native-community/cli-hermes@12.3.7':
+ dependencies:
+ '@react-native-community/cli-platform-android': 12.3.7
+ '@react-native-community/cli-tools': 12.3.7
+ chalk: 4.1.2
+ hermes-profile-transformer: 0.0.6
+ transitivePeerDependencies:
+ - encoding
+ optional: true
+
+ '@react-native-community/cli-platform-android@12.3.7':
+ dependencies:
+ '@react-native-community/cli-tools': 12.3.7
+ chalk: 4.1.2
+ execa: 5.1.1
+ fast-xml-parser: 4.5.3
+ glob: 7.2.3
+ logkitty: 0.7.1
+ transitivePeerDependencies:
+ - encoding
+ optional: true
+
+ '@react-native-community/cli-platform-ios@12.3.7':
+ dependencies:
+ '@react-native-community/cli-tools': 12.3.7
+ chalk: 4.1.2
+ execa: 5.1.1
+ fast-xml-parser: 4.5.3
+ glob: 7.2.3
+ ora: 5.4.1
+ transitivePeerDependencies:
+ - encoding
+ optional: true
+
+ '@react-native-community/cli-plugin-metro@12.3.7':
+ optional: true
+
+ '@react-native-community/cli-server-api@12.3.7':
+ dependencies:
+ '@react-native-community/cli-debugger-ui': 12.3.7
+ '@react-native-community/cli-tools': 12.3.7
+ compression: 1.8.1
+ connect: 3.7.0
+ errorhandler: 1.5.1
+ nocache: 3.0.4
+ pretty-format: 26.6.2
+ serve-static: 1.16.2
+ ws: 7.5.10
+ transitivePeerDependencies:
+ - bufferutil
+ - encoding
+ - supports-color
+ - utf-8-validate
+ optional: true
+
+ '@react-native-community/cli-tools@12.3.7':
+ dependencies:
+ appdirsjs: 1.2.7
+ chalk: 4.1.2
+ find-up: 5.0.0
+ mime: 2.6.0
+ node-fetch: 2.7.0
+ open: 6.4.0
+ ora: 5.4.1
+ semver: 7.7.3
+ shell-quote: 1.8.3
+ sudo-prompt: 9.2.1
+ transitivePeerDependencies:
+ - encoding
+ optional: true
+
+ '@react-native-community/cli-types@12.3.7':
+ dependencies:
+ joi: 17.13.3
+ optional: true
+
+ '@react-native-community/cli@12.3.7':
+ dependencies:
+ '@react-native-community/cli-clean': 12.3.7
+ '@react-native-community/cli-config': 12.3.7
+ '@react-native-community/cli-debugger-ui': 12.3.7
+ '@react-native-community/cli-doctor': 12.3.7
+ '@react-native-community/cli-hermes': 12.3.7
+ '@react-native-community/cli-plugin-metro': 12.3.7
+ '@react-native-community/cli-server-api': 12.3.7
+ '@react-native-community/cli-tools': 12.3.7
+ '@react-native-community/cli-types': 12.3.7
+ chalk: 4.1.2
+ commander: 9.5.0
+ deepmerge: 4.3.1
+ execa: 5.1.1
+ find-up: 4.1.0
+ fs-extra: 8.1.0
+ graceful-fs: 4.2.11
+ prompts: 2.4.2
+ semver: 7.7.3
+ transitivePeerDependencies:
+ - bufferutil
+ - encoding
+ - supports-color
+ - utf-8-validate
+ optional: true
+
+ '@react-native-google-signin/google-signin@16.0.0(expo@54.0.13(@babel/core@7.28.4)(graphql@16.11.0)(react-native@0.81.4(@babel/core@7.28.4)(@react-native-community/cli@12.3.7)(@types/react@18.3.26)(react@18.3.1))(react@18.3.1))(react-native@0.81.4(@babel/core@7.28.4)(@react-native-community/cli@12.3.7)(@types/react@18.3.26)(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ react: 18.3.1
+ react-native: 0.81.4(@babel/core@7.28.4)(@react-native-community/cli@12.3.7)(@types/react@18.3.26)(react@18.3.1)
+ optionalDependencies:
+ expo: 54.0.13(@babel/core@7.28.4)(graphql@16.11.0)(react-native@0.81.4(@babel/core@7.28.4)(@react-native-community/cli@12.3.7)(@types/react@18.3.26)(react@18.3.1))(react@18.3.1)
+
+ '@react-native/assets-registry@0.81.4': {}
+
+ '@react-native/babel-plugin-codegen@0.76.9(@babel/preset-env@7.26.0(@babel/core@7.28.4))':
+ dependencies:
+ '@react-native/codegen': 0.76.9(@babel/preset-env@7.26.0(@babel/core@7.28.4))
transitivePeerDependencies:
- '@babel/preset-env'
- supports-color
@@ -18636,54 +19194,141 @@ snapshots:
'@rolldown/binding-android-arm64@1.0.0-beta.47':
optional: true
- '@rolldown/binding-darwin-arm64@1.0.0-beta.47':
+ '@rolldown/binding-android-arm64@1.0.0-beta.51':
optional: true
- '@rolldown/binding-darwin-x64@1.0.0-beta.47':
+ '@rolldown/binding-darwin-arm64@1.0.0-beta.43':
+ optional: true
+
+ '@rolldown/binding-darwin-arm64@1.0.0-beta.51':
+ optional: true
+
+ '@rolldown/binding-darwin-arm64@1.0.0-beta.9-commit.d91dfb5':
optional: true
'@rolldown/binding-freebsd-x64@1.0.0-beta.47':
optional: true
- '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.47':
+ '@rolldown/binding-darwin-x64@1.0.0-beta.51':
+ optional: true
+
+ '@rolldown/binding-darwin-x64@1.0.0-beta.9-commit.d91dfb5':
optional: true
'@rolldown/binding-linux-arm64-gnu@1.0.0-beta.47':
optional: true
- '@rolldown/binding-linux-arm64-musl@1.0.0-beta.47':
+ '@rolldown/binding-freebsd-x64@1.0.0-beta.51':
+ optional: true
+
+ '@rolldown/binding-freebsd-x64@1.0.0-beta.9-commit.d91dfb5':
optional: true
'@rolldown/binding-linux-x64-gnu@1.0.0-beta.47':
optional: true
- '@rolldown/binding-linux-x64-musl@1.0.0-beta.47':
+ '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.51':
+ optional: true
+
+ '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.9-commit.d91dfb5':
optional: true
'@rolldown/binding-openharmony-arm64@1.0.0-beta.47':
optional: true
- '@rolldown/binding-wasm32-wasi@1.0.0-beta.47':
+ '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.51':
+ optional: true
+
+ '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.9-commit.d91dfb5':
+ optional: true
+
+ '@rolldown/binding-linux-arm64-musl@1.0.0-beta.43':
+ optional: true
+
+ '@rolldown/binding-linux-arm64-musl@1.0.0-beta.51':
+ optional: true
+
+ '@rolldown/binding-linux-arm64-musl@1.0.0-beta.9-commit.d91dfb5':
+ optional: true
+
+ '@rolldown/binding-linux-x64-gnu@1.0.0-beta.43':
+ optional: true
+
+ '@rolldown/binding-linux-x64-gnu@1.0.0-beta.51':
+ optional: true
+
+ '@rolldown/binding-linux-x64-gnu@1.0.0-beta.9-commit.d91dfb5':
+ optional: true
+
+ '@rolldown/binding-linux-x64-musl@1.0.0-beta.43':
+ optional: true
+
+ '@rolldown/binding-linux-x64-musl@1.0.0-beta.51':
+ optional: true
+
+ '@rolldown/binding-linux-x64-musl@1.0.0-beta.9-commit.d91dfb5':
+ optional: true
+
+ '@rolldown/binding-openharmony-arm64@1.0.0-beta.43':
+ optional: true
+
+ '@rolldown/binding-openharmony-arm64@1.0.0-beta.51':
+ optional: true
+
+ '@rolldown/binding-wasm32-wasi@1.0.0-beta.43':
dependencies:
'@napi-rs/wasm-runtime': 1.0.7
optional: true
- '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.47':
+ '@rolldown/binding-wasm32-wasi@1.0.0-beta.51':
+ dependencies:
+ '@napi-rs/wasm-runtime': 1.0.7
+ optional: true
+
+ '@rolldown/binding-wasm32-wasi@1.0.0-beta.9-commit.d91dfb5':
+ dependencies:
+ '@napi-rs/wasm-runtime': 0.2.12
optional: true
'@rolldown/binding-win32-ia32-msvc@1.0.0-beta.47':
optional: true
- '@rolldown/binding-win32-x64-msvc@1.0.0-beta.47':
+ '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.51':
+ optional: true
+
+ '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.9-commit.d91dfb5':
+ optional: true
+
+ '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.43':
+ optional: true
+
+ '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.51':
+ optional: true
+
+ '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.9-commit.d91dfb5':
+ optional: true
+
+ '@rolldown/binding-win32-x64-msvc@1.0.0-beta.43':
+ optional: true
+
+ '@rolldown/binding-win32-x64-msvc@1.0.0-beta.51':
optional: true
- '@rolldown/pluginutils@1.0.0-beta.27': {}
+ '@rolldown/binding-win32-x64-msvc@1.0.0-beta.9-commit.d91dfb5':
+ optional: true
+
+ '@rolldown/pluginutils@1.0.0-beta.11': {}
'@rolldown/pluginutils@1.0.0-beta.29': {}
'@rolldown/pluginutils@1.0.0-beta.47': {}
- '@rollup/plugin-alias@5.1.1(rollup@4.53.1)':
+ '@rolldown/pluginutils@1.0.0-beta.51': {}
+
+ '@rolldown/pluginutils@1.0.0-beta.9-commit.d91dfb5':
+ optional: true
+
+ '@rollup/plugin-alias@5.1.1(rollup@4.52.4)':
optionalDependencies:
rollup: 4.53.1
@@ -20302,13 +20947,13 @@ snapshots:
'@vitejs/plugin-vue-jsx@5.1.1(vite@7.2.2(@types/node@22.19.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.24(typescript@5.8.3))':
dependencies:
- '@babel/core': 7.28.5
- '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.5)
- '@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.28.5)
- '@rolldown/pluginutils': 1.0.0-beta.47
- '@vue/babel-plugin-jsx': 1.5.0(@babel/core@7.28.5)
- vite: 7.2.2(@types/node@22.19.0)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1)
- vue: 3.5.24(typescript@5.8.3)
+ '@babel/core': 7.28.4
+ '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-typescript': 7.28.0(@babel/core@7.28.4)
+ '@rolldown/pluginutils': 1.0.0-beta.51
+ '@vue/babel-plugin-jsx': 1.5.0(@babel/core@7.28.4)
+ vite: 7.1.5(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.0)(tsx@4.19.2)(yaml@2.8.1)
+ vue: 3.5.21(typescript@5.8.3)
transitivePeerDependencies:
- supports-color
@@ -28936,7 +29581,7 @@ snapshots:
glob: 11.0.3
package-json-from-dist: 1.0.1
- rolldown-plugin-dts@0.16.12(rolldown@1.0.0-beta.47)(typescript@5.8.3)(vue-tsc@3.1.4(typescript@5.8.3)):
+ rolldown-plugin-dts@0.16.12(rolldown@1.0.0-beta.51)(typescript@5.8.3):
dependencies:
'@babel/generator': 7.28.5
'@babel/parser': 7.28.5
@@ -28946,8 +29591,8 @@ snapshots:
debug: 4.4.3(supports-color@8.1.1)
dts-resolver: 2.1.2
get-tsconfig: 4.13.0
- magic-string: 0.30.21
- rolldown: 1.0.0-beta.47
+ magic-string: 0.30.19
+ rolldown: 1.0.0-beta.51
optionalDependencies:
typescript: 5.8.3
vue-tsc: 3.1.4(typescript@5.8.3)
@@ -28975,7 +29620,48 @@ snapshots:
'@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.47
'@rolldown/binding-win32-x64-msvc': 1.0.0-beta.47
- rollup-plugin-visualizer@6.0.5(rolldown@1.0.0-beta.47)(rollup@4.53.1):
+ rolldown@1.0.0-beta.51:
+ dependencies:
+ '@oxc-project/types': 0.98.0
+ '@rolldown/pluginutils': 1.0.0-beta.51
+ optionalDependencies:
+ '@rolldown/binding-android-arm64': 1.0.0-beta.51
+ '@rolldown/binding-darwin-arm64': 1.0.0-beta.51
+ '@rolldown/binding-darwin-x64': 1.0.0-beta.51
+ '@rolldown/binding-freebsd-x64': 1.0.0-beta.51
+ '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.51
+ '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.51
+ '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.51
+ '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.51
+ '@rolldown/binding-linux-x64-musl': 1.0.0-beta.51
+ '@rolldown/binding-openharmony-arm64': 1.0.0-beta.51
+ '@rolldown/binding-wasm32-wasi': 1.0.0-beta.51
+ '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.51
+ '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.51
+ '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.51
+
+ rolldown@1.0.0-beta.9-commit.d91dfb5:
+ dependencies:
+ '@oxc-project/runtime': 0.71.0
+ '@oxc-project/types': 0.71.0
+ '@rolldown/pluginutils': 1.0.0-beta.9-commit.d91dfb5
+ ansis: 4.2.0
+ optionalDependencies:
+ '@rolldown/binding-darwin-arm64': 1.0.0-beta.9-commit.d91dfb5
+ '@rolldown/binding-darwin-x64': 1.0.0-beta.9-commit.d91dfb5
+ '@rolldown/binding-freebsd-x64': 1.0.0-beta.9-commit.d91dfb5
+ '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.9-commit.d91dfb5
+ '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.9-commit.d91dfb5
+ '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.9-commit.d91dfb5
+ '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.9-commit.d91dfb5
+ '@rolldown/binding-linux-x64-musl': 1.0.0-beta.9-commit.d91dfb5
+ '@rolldown/binding-wasm32-wasi': 1.0.0-beta.9-commit.d91dfb5
+ '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.9-commit.d91dfb5
+ '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.9-commit.d91dfb5
+ '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.9-commit.d91dfb5
+ optional: true
+
+ rollup-plugin-visualizer@6.0.3(rolldown@1.0.0-beta.9-commit.d91dfb5)(rollup@4.52.4):
dependencies:
open: 8.4.2
picomatch: 4.0.3
@@ -30219,8 +30905,8 @@ snapshots:
diff: 8.0.2
empathic: 2.0.0
hookable: 5.5.3
- rolldown: 1.0.0-beta.47
- rolldown-plugin-dts: 0.16.12(rolldown@1.0.0-beta.47)(typescript@5.8.3)(vue-tsc@3.1.4(typescript@5.8.3))
+ rolldown: 1.0.0-beta.51
+ rolldown-plugin-dts: 0.16.12(rolldown@1.0.0-beta.51)(typescript@5.8.3)
semver: 7.7.3
tinyexec: 1.0.2
tinyglobby: 0.2.15