From 24858c079f816222a8a6b8da077ea018945512d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Ska=C5=82ka?= Date: Thu, 12 Feb 2026 15:26:13 +0100 Subject: [PATCH 001/118] Add base PaymentCredentialsGenerator --- .../java/com/expensify/wallet/WalletModule.kt | 66 +++++++++++++++---- 1 file changed, 53 insertions(+), 13 deletions(-) diff --git a/android/src/main/java/com/expensify/wallet/WalletModule.kt b/android/src/main/java/com/expensify/wallet/WalletModule.kt index 368f31f2..9d114a86 100644 --- a/android/src/main/java/com/expensify/wallet/WalletModule.kt +++ b/android/src/main/java/com/expensify/wallet/WalletModule.kt @@ -5,6 +5,14 @@ import android.app.Activity.RESULT_CANCELED import android.app.Activity.RESULT_OK import android.content.ActivityNotFoundException import android.content.Intent +import com.expensify.wallet.Utils.getAsyncResult +import com.expensify.wallet.Utils.toCardData +import com.expensify.wallet.error.InvalidNetworkError +import com.expensify.wallet.event.OnCardActivatedEvent +import com.expensify.wallet.model.CardData +import com.expensify.wallet.model.CardStatus +import com.expensify.wallet.model.TokenizationStatus +import com.expensify.wallet.model.WalletData import com.facebook.react.bridge.ActivityEventListener import com.facebook.react.bridge.Arguments import com.facebook.react.bridge.Promise @@ -15,23 +23,22 @@ import com.facebook.react.bridge.ReactMethod import com.facebook.react.bridge.ReadableMap import com.facebook.react.bridge.WritableMap import com.facebook.react.modules.core.DeviceEventManagerModule +import com.google.android.gms.common.api.ApiException import com.google.android.gms.tapandpay.TapAndPay import com.google.android.gms.tapandpay.TapAndPayClient +import com.google.android.gms.tapandpay.issuer.GeneratePaymentCredentialsRequest +import com.google.android.gms.tapandpay.issuer.GeneratePaymentCredentialsResponse +import com.google.android.gms.tapandpay.issuer.PaymentCredentialsGenerator import com.google.android.gms.tapandpay.issuer.PushTokenizeRequest import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import com.expensify.wallet.Utils.getAsyncResult -import com.expensify.wallet.Utils.toCardData -import com.expensify.wallet.error.InvalidNetworkError -import com.expensify.wallet.event.OnCardActivatedEvent -import com.expensify.wallet.model.CardStatus -import com.expensify.wallet.model.TokenizationStatus -import com.expensify.wallet.model.WalletData -import com.google.android.gms.common.api.ApiException -import kotlinx.coroutines.Deferred import java.nio.charset.Charset import java.util.Locale +import java.util.concurrent.Callable +import java.util.concurrent.Future +import java.util.concurrent.FutureTask class WalletModule internal constructor(context: ReactApplicationContext) : @@ -198,6 +205,7 @@ class WalletModule internal constructor(context: ReactApplicationContext) : .setDisplayName(displayName) .setLastDigits(cardData.lastDigits) .setUserAddress(cardData.userAddress) + .setPaymentCredentialsGenerator(createPaymentCredentialsGenerator(cardData)) .build() tapAndPayClient.pushTokenize( @@ -208,6 +216,38 @@ class WalletModule internal constructor(context: ReactApplicationContext) : } } + private fun createPaymentCredentialsGenerator( + cardData: CardData + ): PaymentCredentialsGenerator { + return object : PaymentCredentialsGenerator { + override fun generate(request: GeneratePaymentCredentialsRequest): Future { + val callable = Callable { + val tspOpcBytes = cardData.opaquePaymentCard.toByteArray(Charsets.UTF_8) + var googleOpcBytes: ByteArray? = null + + if (request.googleOpaquePaymentCardRequested) { + googleOpcBytes = cardData.googleOpaquePaymentCard?.toByteArray(Charsets.UTF_8) + } + + GeneratePaymentCredentialsResponse.Builder() + .setOpaquePaymentCard(tspOpcBytes) + .setGoogleOpaquePaymentCard(googleOpcBytes) + .build() + } + + val futureTask = FutureTask(callable) + // Run it immediately (since we have the data already) + futureTask.run() + return futureTask + } + + // This switch enables the UAPP flow + override fun getGoogleOpaquePaymentCardSupported(): Boolean { + return true + } + } + } + @ReactMethod override fun resumeAddCardToGoogleWallet(data: ReadableMap, promise: Promise) { try { @@ -243,7 +283,7 @@ class WalletModule internal constructor(context: ReactApplicationContext) : promise.resolve(Arguments.createArray()) return@addOnCompleteListener } - + val tokensArray = Arguments.createArray() task.result.forEach { tokenInfo -> val tokenData = Arguments.createMap().apply { @@ -253,7 +293,7 @@ class WalletModule internal constructor(context: ReactApplicationContext) : } tokensArray.pushMap(tokenData) } - + promise.resolve(tokensArray) } .addOnFailureListener { e -> @@ -330,13 +370,13 @@ class WalletModule internal constructor(context: ReactApplicationContext) : data.getString("cardHolderName")?.let { name -> if (name.isNotEmpty()) return name } - + data.getString("lastDigits")?.let { digits -> if (digits.isNotEmpty()) { return "${network.uppercase(Locale.getDefault())} Card *$digits" } } - + return "${network.uppercase(Locale.getDefault())} Card" } From c82af877d33c20e36ba783d5dec95982d75c9400 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Ska=C5=82ka?= Date: Thu, 12 Feb 2026 16:33:19 +0100 Subject: [PATCH 002/118] Update Push Tokenization results handling --- .../java/com/expensify/wallet/WalletModule.kt | 62 ++++++++++++++----- 1 file changed, 48 insertions(+), 14 deletions(-) diff --git a/android/src/main/java/com/expensify/wallet/WalletModule.kt b/android/src/main/java/com/expensify/wallet/WalletModule.kt index 9d114a86..f84cb179 100644 --- a/android/src/main/java/com/expensify/wallet/WalletModule.kt +++ b/android/src/main/java/com/expensify/wallet/WalletModule.kt @@ -5,6 +5,9 @@ import android.app.Activity.RESULT_CANCELED import android.app.Activity.RESULT_OK import android.content.ActivityNotFoundException import android.content.Intent +import android.content.IntentSender +import android.util.Log +import androidx.core.content.IntentCompat import com.expensify.wallet.Utils.getAsyncResult import com.expensify.wallet.Utils.toCardData import com.expensify.wallet.error.InvalidNetworkError @@ -26,10 +29,13 @@ import com.facebook.react.modules.core.DeviceEventManagerModule import com.google.android.gms.common.api.ApiException import com.google.android.gms.tapandpay.TapAndPay import com.google.android.gms.tapandpay.TapAndPayClient +import com.google.android.gms.tapandpay.TapAndPayStatusCodes import com.google.android.gms.tapandpay.issuer.GeneratePaymentCredentialsRequest import com.google.android.gms.tapandpay.issuer.GeneratePaymentCredentialsResponse import com.google.android.gms.tapandpay.issuer.PaymentCredentialsGenerator import com.google.android.gms.tapandpay.issuer.PushTokenizeRequest +import com.google.android.gms.tapandpay.issuer.PushTokenizeResult +import com.google.android.gms.tapandpay.issuer.TokenizationOutcome import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers @@ -76,23 +82,39 @@ class WalletModule internal constructor(context: ReactApplicationContext) : pendingCreateWalletPromise?.resolve(resultCode == RESULT_OK) pendingCreateWalletPromise = null } else if (requestCode == REQUEST_CODE_PUSH_TOKENIZE) { - if (resultCode == RESULT_OK) { - data?.let { - val tokenId = it.getStringExtra(TapAndPay.EXTRA_ISSUER_TOKEN_ID).toString() - sendEvent( - context, - OnCardActivatedEvent.NAME, - OnCardActivatedEvent("active", tokenId).toMap() - ) - pendingPushTokenizePromise?.resolve(TokenizationStatus.SUCCESS.code) - } - } else if (resultCode == RESULT_CANCELED) { + if (resultCode == RESULT_CANCELED) { sendEvent( context, OnCardActivatedEvent.NAME, OnCardActivatedEvent("canceled", null).toMap() ) pendingPushTokenizePromise?.resolve(TokenizationStatus.CANCELED.code) + return + } + + if (data != null) { + val result = + IntentCompat.getParcelableExtra( + data, TapAndPay.EXTRA_PUSH_TOKENIZE_RESULT, PushTokenizeResult::class.java + ) + + if (result != null) { + val isSavedToCloud = result.cardResult + val tokenOutcomes = result.tokenizationOutcomes + + if (isSavedToCloud || tokenOutcomes.isNotEmpty()) { + val tokenId = tokenOutcomes.firstOrNull()?.issuerTokenId ?: "card_on_file_only" + + sendEvent(context, OnCardActivatedEvent.NAME, OnCardActivatedEvent("active", tokenId).toMap()) + pendingPushTokenizePromise?.resolve(TokenizationStatus.SUCCESS.code) + } else { + val errorMsg = "Card not saved. Status: ${result.cardStatus}" + pendingPushTokenizePromise?.reject(E_OPERATION_FAILED, errorMsg) + } + } else { + // Data intent is null (rare but possible failure case). Report to Google if you observe this. + pendingPushTokenizePromise?.reject(E_OPERATION_FAILED, "Unexpected error.") + } } } } @@ -208,9 +230,21 @@ class WalletModule internal constructor(context: ReactApplicationContext) : .setPaymentCredentialsGenerator(createPaymentCredentialsGenerator(cardData)) .build() - tapAndPayClient.pushTokenize( - activity, pushTokenizeRequest, REQUEST_CODE_PUSH_TOKENIZE - ) + tapAndPayClient.pushTokenize(pushTokenizeRequest) + .addOnSuccessListener { pendingIntent -> + try { + activity.startIntentSenderForResult( + pendingIntent.intentSender, + REQUEST_CODE_PUSH_TOKENIZE, + null, 0, 0, 0 + ) + } catch (e: IntentSender.SendIntentException) { + promise.reject(E_OPERATION_FAILED, "Failed to launch Google Pay: ${e.message}") + } + } + .addOnFailureListener { e -> + promise.reject(E_OPERATION_FAILED, "Google Pay API Error: ${e.message}") + } } catch (e: java.lang.Exception) { promise.reject(e) } From e1af64efc278d6a8023e8c9db1183cb89792da40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Ska=C5=82ka?= Date: Fri, 13 Feb 2026 12:16:48 +0100 Subject: [PATCH 003/118] Add UAPP new props --- android/src/main/java/com/expensify/wallet/Utils.kt | 8 +++++++- .../main/java/com/expensify/wallet/WalletModule.kt | 11 ++++++++--- .../main/java/com/expensify/wallet/model/CardData.kt | 5 ++++- apps/common-app/src/CONST.ts | 2 ++ src/NativeWallet.ts | 2 ++ 5 files changed, 23 insertions(+), 5 deletions(-) diff --git a/android/src/main/java/com/expensify/wallet/Utils.kt b/android/src/main/java/com/expensify/wallet/Utils.kt index 6bb09ef4..363ab7e8 100644 --- a/android/src/main/java/com/expensify/wallet/Utils.kt +++ b/android/src/main/java/com/expensify/wallet/Utils.kt @@ -29,12 +29,18 @@ object Utils { .setPhoneNumber(addressMap.getString("phoneNumber") ?: "") .build() + val isVirtual = if (hasKey("isVirtualCard")) this.getBoolean("isVirtualCard") else false + val isBounce = if (hasKey("isBounceProvisioned")) this.getBoolean("isBounceProvisioned") else false + return CardData( network = this.getString("network") ?: "", opaquePaymentCard = this.getString("opaquePaymentCard") ?: "", + googleOpaquePaymentCard = this.getString("googleOpaquePaymentCard") ?: "", cardHolderName = this.getString("cardHolderName") ?: "", lastDigits = this.getString("lastDigits") ?: "", - userAddress = userAddress + userAddress = userAddress, + isVirtualCard = isVirtual, + isBounceProvisioned = isBounce, ) } diff --git a/android/src/main/java/com/expensify/wallet/WalletModule.kt b/android/src/main/java/com/expensify/wallet/WalletModule.kt index f84cb179..04d2c22d 100644 --- a/android/src/main/java/com/expensify/wallet/WalletModule.kt +++ b/android/src/main/java/com/expensify/wallet/WalletModule.kt @@ -29,13 +29,12 @@ import com.facebook.react.modules.core.DeviceEventManagerModule import com.google.android.gms.common.api.ApiException import com.google.android.gms.tapandpay.TapAndPay import com.google.android.gms.tapandpay.TapAndPayClient -import com.google.android.gms.tapandpay.TapAndPayStatusCodes import com.google.android.gms.tapandpay.issuer.GeneratePaymentCredentialsRequest import com.google.android.gms.tapandpay.issuer.GeneratePaymentCredentialsResponse import com.google.android.gms.tapandpay.issuer.PaymentCredentialsGenerator +import com.google.android.gms.tapandpay.issuer.PushTokenizeExtraOptions import com.google.android.gms.tapandpay.issuer.PushTokenizeRequest import com.google.android.gms.tapandpay.issuer.PushTokenizeResult -import com.google.android.gms.tapandpay.issuer.TokenizationOutcome import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers @@ -220,6 +219,11 @@ class WalletModule internal constructor(context: ReactApplicationContext) : val displayName = getDisplayName(data, cardData.network) pendingPushTokenizePromise = promise + val pushTokenizeExtraOptions = PushTokenizeExtraOptions.newBuilder() + .setIsBounceProvisioned(cardData.isBounceProvisioned) + .setEnrollForVirtualCards(cardData.isVirtualCard) + .build() + val pushTokenizeRequest = PushTokenizeRequest.Builder() .setOpaquePaymentCard(cardData.opaquePaymentCard.toByteArray(Charset.forName("UTF-8"))) .setNetwork(cardNetwork) @@ -228,6 +232,7 @@ class WalletModule internal constructor(context: ReactApplicationContext) : .setLastDigits(cardData.lastDigits) .setUserAddress(cardData.userAddress) .setPaymentCredentialsGenerator(createPaymentCredentialsGenerator(cardData)) + .setPushTokenizeExtraOptions(pushTokenizeExtraOptions) .build() tapAndPayClient.pushTokenize(pushTokenizeRequest) @@ -260,7 +265,7 @@ class WalletModule internal constructor(context: ReactApplicationContext) : var googleOpcBytes: ByteArray? = null if (request.googleOpaquePaymentCardRequested) { - googleOpcBytes = cardData.googleOpaquePaymentCard?.toByteArray(Charsets.UTF_8) + googleOpcBytes = cardData.googleOpaquePaymentCard?.toByteArray(Charsets.UTF_8) } GeneratePaymentCredentialsResponse.Builder() diff --git a/android/src/main/java/com/expensify/wallet/model/CardData.kt b/android/src/main/java/com/expensify/wallet/model/CardData.kt index d9191e0e..419b2b2a 100644 --- a/android/src/main/java/com/expensify/wallet/model/CardData.kt +++ b/android/src/main/java/com/expensify/wallet/model/CardData.kt @@ -5,8 +5,11 @@ import com.google.android.gms.tapandpay.issuer.UserAddress data class CardData( val platform: String = "android", val network: String, - val opaquePaymentCard: String, + val opaquePaymentCard: String, // TSP OPC + val googleOpaquePaymentCard: String? = null, val cardHolderName: String, val lastDigits: String, val userAddress: UserAddress, + val isVirtualCard: Boolean = false, + val isBounceProvisioned: Boolean = false, ) diff --git a/apps/common-app/src/CONST.ts b/apps/common-app/src/CONST.ts index 8752ef19..6f20632e 100644 --- a/apps/common-app/src/CONST.ts +++ b/apps/common-app/src/CONST.ts @@ -17,6 +17,8 @@ const AndroidDummyCardData: AndroidCardData = { cardHolderName: 'John Doe', lastDigits: '4321', userAddress: dummyAddress, + isVirtualCard: false, + isBounceProvisioned: false, }; const AndroidDummyResumeCardData: AndroidResumeCardData = { diff --git a/src/NativeWallet.ts b/src/NativeWallet.ts index cf8a47d7..8d01bb9a 100644 --- a/src/NativeWallet.ts +++ b/src/NativeWallet.ts @@ -27,6 +27,8 @@ type AndroidCardData = { cardHolderName: string; lastDigits: string; userAddress: UserAddress; + isVirtualCard?: boolean; + isBounceProvisioned?: boolean; }; type AndroidResumeCardData = { From 54cd3fe8aae8e3667b43a56f1a2c444aa3ca2b07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Ska=C5=82ka?= Date: Fri, 13 Feb 2026 15:58:31 +0100 Subject: [PATCH 004/118] Add ability to fetch GOPC in RN during push provisioning --- .../java/com/expensify/wallet/WalletModule.kt | 69 ++++++++++++++----- apps/common-app/src/CONST.ts | 7 +- apps/common-app/src/walletUtils.ts | 10 ++- src/NativeWallet.ts | 15 ++++ src/index.tsx | 41 ++++++++++- 5 files changed, 120 insertions(+), 22 deletions(-) diff --git a/android/src/main/java/com/expensify/wallet/WalletModule.kt b/android/src/main/java/com/expensify/wallet/WalletModule.kt index 04d2c22d..e9a82dca 100644 --- a/android/src/main/java/com/expensify/wallet/WalletModule.kt +++ b/android/src/main/java/com/expensify/wallet/WalletModule.kt @@ -6,7 +6,6 @@ import android.app.Activity.RESULT_OK import android.content.ActivityNotFoundException import android.content.Intent import android.content.IntentSender -import android.util.Log import androidx.core.content.IntentCompat import com.expensify.wallet.Utils.getAsyncResult import com.expensify.wallet.Utils.toCardData @@ -41,9 +40,10 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import java.nio.charset.Charset import java.util.Locale -import java.util.concurrent.Callable +import java.util.UUID +import java.util.concurrent.CompletableFuture +import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.Future -import java.util.concurrent.FutureTask class WalletModule internal constructor(context: ReactApplicationContext) : @@ -63,6 +63,8 @@ class WalletModule internal constructor(context: ReactApplicationContext) : private var pendingCreateWalletPromise: Promise? = null private var pendingPushTokenizePromise: Promise? = null + private val pendingProvisioningFutures = ConcurrentHashMap>() + override fun initialize() { super.initialize() reactApplicationContext.addActivityEventListener(cardListener) @@ -260,24 +262,28 @@ class WalletModule internal constructor(context: ReactApplicationContext) : ): PaymentCredentialsGenerator { return object : PaymentCredentialsGenerator { override fun generate(request: GeneratePaymentCredentialsRequest): Future { - val callable = Callable { - val tspOpcBytes = cardData.opaquePaymentCard.toByteArray(Charsets.UTF_8) - var googleOpcBytes: ByteArray? = null + if (!request.googleOpaquePaymentCardRequested) { + val response = GeneratePaymentCredentialsResponse.Builder() + .setOpaquePaymentCard(cardData.opaquePaymentCard.toByteArray(Charsets.UTF_8)) + .build() - if (request.googleOpaquePaymentCardRequested) { - googleOpcBytes = cardData.googleOpaquePaymentCard?.toByteArray(Charsets.UTF_8) - } + return CompletableFuture.completedFuture(response) + } - GeneratePaymentCredentialsResponse.Builder() - .setOpaquePaymentCard(tspOpcBytes) - .setGoogleOpaquePaymentCard(googleOpcBytes) - .build() + val future = CompletableFuture() + val requestId = UUID.randomUUID().toString() + pendingProvisioningFutures[requestId] = future + + val params = Arguments.createMap().apply { + putString("requestId", requestId) + putString("serverSessionId", request.serverSessionId) + putString("walletId", request.walletId) + putString("opaquePaymentCard", cardData.opaquePaymentCard) } - val futureTask = FutureTask(callable) - // Run it immediately (since we have the data already) - futureTask.run() - return futureTask + // send event to the RN so the Google OPC can be generated on the backend + sendEvent(reactApplicationContext, "onPaymentCredentialsRequest", params) + return future } // This switch enables the UAPP flow @@ -287,6 +293,35 @@ class WalletModule internal constructor(context: ReactApplicationContext) : } } + @ReactMethod + override fun AndroidProvidePaymentCredentials(requestId: String, responseData: ReadableMap, promise: Promise) { + val future = pendingProvisioningFutures.remove(requestId) + if (future == null) { + promise.reject(E_OPERATION_FAILED, "Request ID not found or timed out") + return + } + + try { + val tspOpc = responseData.getString("opaquePaymentCard") + val googleOpc = responseData.getString("googleOpaquePaymentCard") + + if (tspOpc == null) { + throw Exception("opaquePaymentCard is required") + } + val response = GeneratePaymentCredentialsResponse.Builder() + .setGoogleOpaquePaymentCard(googleOpc?.toByteArray(Charsets.UTF_8)) + .setOpaquePaymentCard(tspOpc.toByteArray(Charsets.UTF_8)) + .build() + + future.complete(response) + promise.resolve(true) + + } catch (e: Exception) { + future.completeExceptionally(e) + promise.reject(E_OPERATION_FAILED, e.message) + } + } + @ReactMethod override fun resumeAddCardToGoogleWallet(data: ReadableMap, promise: Promise) { try { diff --git a/apps/common-app/src/CONST.ts b/apps/common-app/src/CONST.ts index 6f20632e..66fbb15e 100644 --- a/apps/common-app/src/CONST.ts +++ b/apps/common-app/src/CONST.ts @@ -42,4 +42,9 @@ const IOSDummyEncryptPayload: IOSEncryptPayload = { ephemeralPublicKey: 'ZXBoZW1lcmFsUHVibGljS2V5MTIz', }; -export {AndroidDummyCardData, AndroidDummyResumeCardData, IOSDummyCardData, IOSDummyEncryptPayload}; +const IOSDummyPaymentCredentialsResponse = { + opaquePaymentCard: 'encryptedCardInformation123456', + googleOpaquePaymentCard: 'GOPC', +}; + +export {AndroidDummyCardData, AndroidDummyResumeCardData, IOSDummyCardData, IOSDummyEncryptPayload, IOSDummyPaymentCredentialsResponse}; diff --git a/apps/common-app/src/walletUtils.ts b/apps/common-app/src/walletUtils.ts index 2d5769bf..6ef9f961 100644 --- a/apps/common-app/src/walletUtils.ts +++ b/apps/common-app/src/walletUtils.ts @@ -1,5 +1,5 @@ import {addCardToAppleWallet, addCardToGoogleWallet, listTokens, resumeAddCardToGoogleWallet} from '@expensify/react-native-wallet'; -import type {CardStatus} from '@expensify/react-native-wallet'; +import type {AndroidPaymentCredentialsResponse, CardStatus, onPaymentCredentialsRequestPayload} from '@expensify/react-native-wallet'; import {Platform} from 'react-native'; import * as CONST from './CONST'; @@ -9,6 +9,12 @@ function issuerEncryptPayloadCallback(_nonce: string, _nonceSignature: string, _ return Promise.resolve(CONST.IOSDummyEncryptPayload); } +function handlePaymentCredentialsGeneration(_data: onPaymentCredentialsRequestPayload): Promise { + // Here send data to your server or you TSP to generate the payment credentials + // for example: fetch('https://tsp.com/generatePaymentCredentials', {method: 'POST', body: data}) + return Promise.resolve(CONST.IOSDummyPaymentCredentialsResponse); +} + async function addCardToWallet(cardStatus?: CardStatus) { if (Platform.OS === 'android') { if (cardStatus === 'requireActivation') { @@ -24,7 +30,7 @@ async function addCardToWallet(cardStatus?: CardStatus) { tokenReferenceID: existingToken.identifier, }); } - return addCardToGoogleWallet(CONST.AndroidDummyCardData); + return addCardToGoogleWallet(CONST.AndroidDummyCardData, handlePaymentCredentialsGeneration); } else { return addCardToAppleWallet(CONST.IOSDummyCardData, issuerEncryptPayloadCallback); } diff --git a/src/NativeWallet.ts b/src/NativeWallet.ts index 8d01bb9a..c9350bde 100644 --- a/src/NativeWallet.ts +++ b/src/NativeWallet.ts @@ -50,6 +50,13 @@ type onCardActivatedPayload = { actionStatus: 'active' | 'canceled'; }; +type onPaymentCredentialsRequestPayload = { + requestId: string; + serverSessionId: string; + walletId: string; + opaquePaymentCard: string; +}; + type IOSAddPaymentPassData = { status: number; nonce: string; @@ -71,6 +78,11 @@ type TokenInfo = { tokenState: number; }; +type AndroidPaymentCredentialsResponse = { + opaquePaymentCard: string; + googleOpaquePaymentCard: string; +}; + export interface Spec extends TurboModule { checkWalletAvailability(): Promise; ensureGoogleWalletInitialized(): Promise; @@ -78,6 +90,7 @@ export interface Spec extends TurboModule { getCardStatusBySuffix(last4Digits: string): Promise; getCardStatusByIdentifier(identifier: string, tsp: string): Promise; addCardToGoogleWallet(cardData: AndroidCardData): Promise; + AndroidProvidePaymentCredentials(requestId: string, responseData: AndroidPaymentCredentialsResponse): Promise; resumeAddCardToGoogleWallet(cardData: AndroidResumeCardData): Promise; listTokens(): Promise; IOSPresentAddPaymentPassView(cardData: IOSCardData): Promise; @@ -109,9 +122,11 @@ export type { CardStatus, UserAddress, onCardActivatedPayload, + onPaymentCredentialsRequestPayload, Platform, IOSAddPaymentPassData, IOSEncryptPayload, TokenizationStatus, TokenInfo, + AndroidPaymentCredentialsResponse, }; diff --git a/src/index.tsx b/src/index.tsx index 1f947564..47aa179c 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,3 +1,4 @@ +/* eslint-disable no-console */ /* eslint-disable @lwc/lwc/no-async-await */ import {NativeEventEmitter, Platform} from 'react-native'; import type {EmitterSubscription} from 'react-native'; @@ -11,8 +12,10 @@ import type { IOSEncryptPayload, AndroidWalletData, onCardActivatedPayload, + onPaymentCredentialsRequestPayload, IOSAddPaymentPassData, TokenInfo, + AndroidPaymentCredentialsResponse, } from './NativeWallet'; import {getCardState, getTokenizationStatus} from './utils'; import AddToWalletButton from './AddToWalletButton'; @@ -22,6 +25,22 @@ function getModuleLinkingRejection() { } const eventEmitter = new NativeEventEmitter(Wallet); +let paymentCredentialsHandler: ((data: onPaymentCredentialsRequestPayload) => Promise) | null = null; + +if (Platform.OS === 'android' && Wallet) { + eventEmitter.addListener('onPaymentCredentialsRequest', async (data: onPaymentCredentialsRequestPayload) => { + if (paymentCredentialsHandler) { + try { + const responseData = await paymentCredentialsHandler(data); + await Wallet?.AndroidProvidePaymentCredentials(data.requestId, responseData); + } catch (error) { + console.error('[react-native-wallet] Error handling payment credentials request:', error); + } + } else { + console.warn('[react-native-wallet] Received payment credentials request but no handler is set. Call setPaymentCredentialsHandler() to handle this event.'); + } + }); +} function addListener(event: string, callback: (data: onCardActivatedPayload) => void): EmitterSubscription { return eventEmitter.addListener(event, callback); @@ -78,7 +97,10 @@ async function getCardStatusByIdentifier(identifier: string, tsp: string): Promi return getCardState(tokenState); } -async function addCardToGoogleWallet(cardData: AndroidCardData): Promise { +async function addCardToGoogleWallet( + cardData: AndroidCardData, + handlePaymentCredentialsGeneration: (data: onPaymentCredentialsRequestPayload) => Promise, +): Promise { if (Platform.OS === 'ios') { throw new Error('addCardToGoogleWallet is not available on iOS'); } @@ -90,7 +112,10 @@ async function addCardToGoogleWallet(cardData: AndroidCardData): Promise Date: Fri, 13 Feb 2026 15:59:04 +0100 Subject: [PATCH 005/118] Add new function to paper spec --- .../src/paper/java/com/expensify/wallet/NativeWalletSpec.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/android/src/paper/java/com/expensify/wallet/NativeWalletSpec.java b/android/src/paper/java/com/expensify/wallet/NativeWalletSpec.java index 999f0cb8..5cd1bfa4 100644 --- a/android/src/paper/java/com/expensify/wallet/NativeWalletSpec.java +++ b/android/src/paper/java/com/expensify/wallet/NativeWalletSpec.java @@ -57,6 +57,10 @@ public NativeWalletSpec(ReactApplicationContext reactContext) { @DoNotStrip public abstract void addCardToGoogleWallet(ReadableMap cardData, Promise promise); + @ReactMethod + @DoNotStrip + public abstract void AndroidProvidePaymentCredentials(String requestId, ReadableMap responseData, Promise promise); + @ReactMethod @DoNotStrip public abstract void resumeAddCardToGoogleWallet(ReadableMap cardData, Promise promise); From 3412195fe208d15c40111bb02e77413cbe0a7215 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Ska=C5=82ka?= Date: Mon, 16 Feb 2026 11:18:38 +0100 Subject: [PATCH 006/118] Update docs --- README.md | 61 ++++++++++++++++-- docs/UAPP.md | 174 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 230 insertions(+), 5 deletions(-) create mode 100644 docs/UAPP.md diff --git a/README.md b/README.md index 8adbce80..c8247077 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ - ♻️ Universal solution (Apple Wallet and Google Wallet) - πŸ’³ Easy and secure addition of payment cards from your app directly - πŸ’° Supports Visa, MasterCard, Amex and Discover payment cards +- πŸ” UAPP (User Account Payment Provisioning) support for enhanced security - πŸŽ‰ Expo support (expo-plugin) ## Getting started @@ -157,6 +158,7 @@ Here you can find data elements used in the library, essential to work with Goog ### Android - **Opaque Payment Card** (OPC) - a binary blob of information Google Pay receives from the issuer app that could be presented to TSP to receive a token. +- **Google Opaque Payment Card** (Google OPC) - an encrypted version of the OPC required for UAPP (User Account Payment Provisioning) flow. This is generated by your backend using Google's encryption keys during the provisioning process. [Learn more about UAPP β†’](./docs/UAPP.md) - **Token Service Provider** (TSP) - a service that enhances payment security by replacing a credit card number during transactions with a unique digital identifier - token. The TSP specifies the tokenization service used to create a given token e.g. Visa, MasterCard, American Express. - **Token Reference ID** - a unique identifying number to refer to a DPAN (Dynamic Personal Account Number). Token Providers will assign each DPAN an issuer token ID at the time of tokenization. @@ -168,7 +170,7 @@ Here you can find data elements used in the library, essential to work with Goog - **Ephemeral Public Key** - a key used by elliptic curve cryptography (ECC) (Base 64 encoded). # API Reference -The library offers seven functions for seamless integration and use of the Apple Wallet and Google Wallet APIs. Additionally, it includes one listener that informs when the added card has been activated. Below, these functions are described along with the data types involved. +The library offers functions for seamless integration and use of the Apple Wallet and Google Wallet APIs. Additionally, it includes listeners and handlers for managing provisioning events. Below, these functions are described along with the data types involved. ## Functions @@ -178,19 +180,20 @@ The library offers seven functions for seamless integration and use of the Apple | **checkWalletAvailability** | Checks if the wallet is ready and initializes it if possible. | None | `boolean` | βœ… | βœ… | | **getCardStatusBySuffix** | Retrieves the current status of a card in the wallet. | `lastDigits: string`
(The last few digits of the card number) | `CardStatus` | βœ… | βœ… | | **getCardStatusByIdentifier** | Returns the state of a card based on a platform-specific identifier. On Android, it's `Token Reference ID` and on iOS, it's `Primary Account Identifier`. | `identifier: string`,
`tsp: string` | `CardStatus` | βœ… | βœ… | -| **addCardToGoogleWallet** | Initiates native Push Provisioning flow for adding a card to the Google Wallet. | `data`: `AndroidCardData` | `TokenizationStatus` | ❌ | βœ… | +| **addCardToGoogleWallet** | Initiates native Push Provisioning flow for adding a card to the Google Wallet. For UAPP flow, pass a handler as the second parameter. [Learn more β†’](./docs/UAPP.md) | `data`: `AndroidCardData`,
`handlePaymentCredentials?`: `(data: onPaymentCredentialsRequestPayload) => Promise` | `TokenizationStatus` | ❌ | βœ… | | **resumeAddCardToGoogleWallet** | Resumes the Push Provisioning flow for adding a card to the Google Wallet using existing token reference ID. | `data`: `AndroidResumeCardData` | `TokenizationStatus` | ❌ | βœ… | | **listTokens** | Lists all tokens currently stored in the Google Wallet. | None | `TokenInfo[]` | ❌ | βœ… | | **addCardToAppleWallet** | Initiates native Push Provisioning flow for adding a card to the Apple Wallet. | `data`: `IOSCardData`,
`issuerEncrypt-`
`PayloadCallback: IOSIssuerCallback` | `void` | βœ… | ❌ | - ## Data Types | Type | Description | Fields | |------|-------------|--------| | **AndroidWalletData** | Specific information for Android devices required for wallet transactions. | `deviceID: string`,
`walletAccountID: string` | -| **AndroidCardData** | Data related to a card that is to be added on Android platform wallets. | `network: string`,
`opaquePaymentCard: string`,
`cardHolderName: string`,
`lastDigits: string`,
`userAddress: UserAddress` | +| **AndroidCardData** | Data related to a card that is to be added on Android platform wallets. | `network: string`,
`opaquePaymentCard: string`,
`cardHolderName: string`,
`lastDigits: string`,
`userAddress: UserAddress`,
`isVirtualCard?: boolean`,
`isBounceProvisioned?: boolean` | | **AndroidResumeCardData** | Simplified data structure for resuming card addition to Google Wallet using existing token reference ID. | `network: string`,
`tokenReferenceID: string`,
`cardHolderName?: string`,
`lastDigits?: string` | +| **AndroidPaymentCredentialsResponse** | Response object containing encrypted payment credentials for UAPP flow. [Learn more β†’](./docs/UAPP.md) | `opaquePaymentCard: string`,
`googleOpaquePaymentCard: string` | +| **onPaymentCredentialsRequestPayload** | Payload received when Google Wallet requests payment credentials during UAPP provisioning. [Learn more β†’](./docs/UAPP.md) | `requestId: string`,
`serverSessionId: string`,
`walletId: string`,
`opaquePaymentCard: string` | | **UserAddress** | Structured address used for cardholder verification. | `name: string`,
`addressOne: string`,
`addressTwo: string`,
`city: string`,
`administrativeArea: string`,
`countryCode: string`,
`postalCode: string`,
`phoneNumber: string` | | **IOSCardData** | Data related to a card that is to be added on iOS platform. | `network: string`,
`activationData: string`,
`encryptedPassData: string`,
`ephemeralPublicKey: string`,
`cardHolderTitle: string`,
`cardHolderName: string`,
`lastDigits: string`,
`cardDescription: string`,
`cardDescriptionComment: string` | | **onCardActivatedPayload** | Data used by listener to notice when a card’s status changes. | `tokenId: string`,
`actionStatus: 'activated' \| 'canceled'`
| @@ -198,6 +201,27 @@ The library offers seven functions for seamless integration and use of the Apple | **IOSEncryptPayload** | An object containing the necessary elements to complete the addition of a card to Apple Wallet. | `encryptedPassData: string`,
`activationData: string`,
`ephemeralPublicKey: string` | | **TokenInfo** | Information about a token stored in Google Wallet. | `identifier: string`,
`lastDigits: string`,
`tokenState: number` | +### AndroidCardData Optional Fields + +The `AndroidCardData` type includes two optional boolean fields that provide additional information about the card being provisioned: + +#### `isVirtualCard?: boolean` + +Indicates whether the card being provisioned is a virtual card. Virtual cards are digitally-generated card numbers that are linked to a physical card account. They provide enhanced security by: +- Allowing users to make online purchases without exposing their physical card number +- Enabling easy cancellation and regeneration without affecting the physical card +- Providing transaction-specific or merchant-specific card numbers + +When set to `true`, this flag informs Google Wallet that the card being provisioned is a virtual card, which may affect how the card is displayed and managed in the wallet interface. + +#### `isBounceProvisioned?: boolean` + +Indicates whether the card has been provisioned through "bounce provisioning." Bounce provisioning is a two-step provisioning flow where: +1. The user initially provisions a card directly through Google Wallet (outside your app) +2. Your app later "bounces" or redirects the user back to complete additional verification or setup steps + +This flag helps Google Wallet track cards that were initially added through the Google Wallet app but require additional app-specific configuration or verification. When set to `true`, it signals that this card was previously provisioned through the Google Wallet interface and is now being completed or updated through your application. + ## Card Status | Type | Possible Values | @@ -283,17 +307,44 @@ const base64ToHex = (base64: string): string => Buffer.from(base64, "base64").to This will depend on the implementation of your backend/issuer so make sure to check with them otherwise IOS might fail to provision the card. +# UAPP (User Account Payment Provisioning) + +For Android applications that require enhanced security for card provisioning, this library supports UAPP (User Account Payment Provisioning) flow with Google Wallet. + +πŸ“– **[Read the complete UAPP documentation β†’](./docs/UAPP.md)** + +The UAPP guide covers: +- What UAPP is and how it works +- Step-by-step setup instructions +- Backend implementation requirements +- Error handling and troubleshooting +- Complete code examples + # Publishing your app To successfully publish your app, you will need to navigate through a series of mandatory test cases on both platforms. ### Android +> [!WARNING] +> **Testing is mandatory before production release.** Your access to the Google Wallet production environment may be revoked if you do not provide the required test evidence for launch approval. Complete all testing requirements before deploying your app. + Before deploying your app to the Google Play Store, make sure you have taken care of the security when implementing Push Provisioning and properly tested your code. To accomplish it, please familiarize yourself with [the Google's App Push Provisioning best practices](https://developers.google.com/pay/issuers/apis/push-provisioning/android/best-practices). The latest information about deploying apps with Google TapAndPay SDK can be found in the [pre-launch process](https://developers.google.com/pay/issuers/apis/push-provisioning/android/launch-process#step_3_issuer_app_product_review) and [beta tests](https://developers.google.com/pay/issuers/apis/push-provisioning/android/beta-testing) sections in Google documentation. Make sure to complete all of the steps specified by Google connected to the __Google's branding__, __API safety__, and __app stability__. -The app will need to be reviewed by Google. During this process, it will need to pass 4 mandatory test cases that are specified [here](https://developers.google.com/pay/issuers/apis/push-provisioning/android/test-cases). They verify how your app handles card state tracking in different scenarios. +#### Testing Requirements + +The app will need to be reviewed by Google. During this process, it will need to pass 4 mandatory test cases that are specified in the [official Google testing documentation](https://developers.google.com/pay/issuers/apis/push-provisioning/android/test-cases). They verify how your app handles card state tracking in different scenarios. + +**Key testing criteria include:** +- Proper handling of card states (active, pending, suspended, etc.) +- Correct UI behavior when cards are already provisioned +- Secure handling of sensitive payment data +- Compliance with Google's branding guidelines +- Proper error handling and user feedback + +Please refer to the [complete testing criteria documentation](https://developers.google.com/pay/issuers/apis/push-provisioning/android/test-cases) before submitting your app for review. > [!NOTE] >Please make sure to hide the `Add to Google Wallet` buttons when cards are already added to the wallet. diff --git a/docs/UAPP.md b/docs/UAPP.md new file mode 100644 index 00000000..acb3b857 --- /dev/null +++ b/docs/UAPP.md @@ -0,0 +1,174 @@ +# UAPP (User Account Payment Provisioning) for Android + +## What is UAPP? + +UAPP (User Account Payment Provisioning) is an enhanced security flow for Google Wallet that requires encrypted payment credentials during card provisioning. Instead of providing only the TSP Opaque Payment Card (OPC), your backend must also generate a Google Opaque Payment Card (Google OPC) using Google's encryption keys. It allows users to provision cards to Google Wallet and save their cards to their Google Account. + +This library handles the UAPP flow automatically. You just need to set up a handler that communicates with your backend to retrieve the encrypted credentials. + +> [!IMPORTANT] +> Please familiarize yourself with [official Google docs ](https://developers.google.com/pay/issuers/apis/push-provisioning/android)before starting the implementation! + +## How UAPP Works + +1. User initiates card provisioning by calling `addCardToGoogleWallet()` +2. During provisioning, Google Wallet requests encrypted credentials +3. The library automatically triggers your registered handler with provisioning details +4. Your handler calls your backend with the server session ID and OPC +5. Your backend generates the Google OPC using Google's encryption keys +6. Your handler returns both the TSP OPC and Google OPC +7. The library automatically provides these credentials to Google Wallet +8. Provisioning continues seamlessly + +## Setup + +To enable UAPP support, pass a payment credentials handler directly to the `addCardToGoogleWallet()` function: + +```typescript +import { addCardToGoogleWallet } from '@expensify/react-native-wallet'; + +// Define your handler function +const handlePaymentCredentials = async (data) => { + const { requestId, serverSessionId, walletId, opaquePaymentCard } = data; + + // Call your backend to get encrypted credentials + const response = await fetch('https://your-backend.com/api/wallet/credentials', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + serverSessionId, + opaquePaymentCard, + walletId, + }), + }); + + const credentials = await response.json(); + + // Return the credentials - library will automatically send them to Google Wallet + return { + opaquePaymentCard: credentials.tspOpc, + googleOpaquePaymentCard: credentials.googleOpc, + }; +}; + +// Pass the handler when adding a card +const status = await addCardToGoogleWallet(cardData, handlePaymentCredentials); +``` + +## Backend Implementation + +> [!NOTE] +> Please refer to the [official Google documentation](https://developers.google.com/pay/issuers/apis/push-provisioning/android/upgrade_to_upp#generate_the_google_opc_from_the_issuers_backend) while implementing the backend for UAPP Push Provisioning. + +Your backend needs to: + +1. Receive the `serverSessionId`, `opaquePaymentCard`, and `walletId` +2. Call Google's UAPP API with these parameters +3. Use Google's encryption keys to generate the Google OPC +4. Return both the TSP OPC and Google OPC to your app + +Example backend response: +```json +{ + "tspOpc": "original-opaque-payment-card-from-tsp", + "googleOpc": "encrypted-google-opaque-payment-card" +} +``` + +## Related Data Types + +### `onPaymentCredentialsRequestPayload` + +Payload received when Google Wallet requests payment credentials during UAPP provisioning: + +```typescript +{ + requestId: string; // Unique identifier for this request + serverSessionId: string; // Session ID from Google (crucial for backend) + walletId: string; // Google Wallet ID + opaquePaymentCard: string; // Original OPC from TSP +} +``` + +### `AndroidPaymentCredentialsResponse` + +Response object containing encrypted payment credentials for UAPP flow: + +```typescript +{ + opaquePaymentCard: string; // TSP Opaque Payment Card + googleOpaquePaymentCard: string; // Encrypted Google OPC +} +``` + +## Required Data Elements + +For UAPP provisioning, you need: + +- **Opaque Payment Card** (OPC) - a binary blob of information Google Pay receives from the issuer app that could be presented to TSP to receive a token. +- **Google Opaque Payment Card** (Google OPC) - an encrypted version of the OPC required for UAPP (User Account Payment Provisioning) flow. This is generated by your backend using Google's encryption keys during the provisioning process. + +## API Reference + +### `addCardToGoogleWallet()` + +Initiates the Google Wallet card provisioning flow with UAPP support. + +**Parameters:** +- `cardData: AndroidCardData` - Card information required for provisioning +- `handlePaymentCredentialsGeneration: (data: onPaymentCredentialsRequestPayload) => Promise` - Handler function that receives the payment credentials request and returns encrypted credentials from your backend + +**Returns:** `Promise` - Status of the tokenization process ('success', 'canceled', or 'error') + +**Platform:** Android only + +**Example:** +```typescript +const status = await addCardToGoogleWallet( + cardData, + async (data) => { + const credentials = await fetchCredentialsFromBackend(data); + return credentials; + } +); +``` + +### AndroidCardData Optional Fields + +When using UAPP with `addCardToGoogleWallet()`, the `AndroidCardData` object supports two optional boolean fields: + +- **`isVirtualCard?: boolean`** - Set to `true` if provisioning a virtual card (digitally-generated card number linked to a physical card account). Virtual cards provide enhanced security for online transactions. + +- **`isBounceProvisioned?: boolean`** - Set to `true` if the card was initially provisioned through Google Wallet and is now being completed through your app. This is used in bounce provisioning flows where users start in Google Wallet and are redirected to your app for additional verification. + +For more details, see the [AndroidCardData Optional Fields](../README.md#androidcarddata-optional-fields) section in the main documentation. + +## Testing + +> [!WARNING] +> **UAPP implementation must be tested before production use.** Failure to provide required test evidence may result in revoking access to your production environment. Please make sure your implementation is thoroughly tested and approved by Google. + +When implementing UAPP, ensure your app meets all Google testing criteria: + +1. **UAPP Flow Testing** - Verify that payment credentials are correctly generated and transmitted during provisioning +2. **Backend Integration** - Ensure your backend correctly generates Google OPC using Google's encryption keys +3. **Error Handling** - Test timeout scenarios and credential validation failures +4. **Security Compliance** - Verify secure handling of encrypted payment credentials + +For comprehensive testing requirements, refer to: +- [Google Push Provisioning Testing Criteria](https://developers.google.com/pay/issuers/apis/push-provisioning/android/test-cases) +- [UAPP-specific Testing Guidelines](https://developers.google.com/pay/issuers/apis/push-provisioning/android/upgrade_to_upp#functional_test_cases) +- Main documentation [Publishing your app](../README.md#publishing-your-app) section + +## Troubleshooting + +### Provisioning fails with timeout + +If the backend request takes too long, Google Wallet may timeout. Ensure your backend responds within a reasonable timeframe (typically < 10 seconds). + +### Invalid credentials error + +Verify that: +1. Your backend is correctly generating the Google OPC using Google's encryption keys +2. Both `opaquePaymentCard` and `googleOpaquePaymentCard` are returned in the response +3. The credentials match the card being provisioned From fcc4cdd041b4cb8503fdd1c880cb88d43e3b6e53 Mon Sep 17 00:00:00 2001 From: Bruno Nascimento Date: Mon, 21 Sep 2026 17:54:28 -0300 Subject: [PATCH 007/118] feat: pass optional primaryAccountIdentifier to Apple Wallet PassKit uses this id to recognize a reissued card after the PAN suffix changes. --- ios/RNWallet.mm | 8 ++++++-- ios/WalletManager.swift | 3 +++ ios/stucts/CardInfo.swift | 6 ++++++ src/NativeWallet.ts | 2 ++ 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/ios/RNWallet.mm b/ios/RNWallet.mm index bf424e5f..fa7e5323 100644 --- a/ios/RNWallet.mm +++ b/ios/RNWallet.mm @@ -39,12 +39,16 @@ - (instancetype)init { reject:(RCTPromiseRejectBlock)reject) { @try { - NSDictionary *cardDataDict = @{ + NSMutableDictionary *cardDataDict = [@{ @"network": [self safeString:cardData.network()], @"cardHolderName": [self safeString:cardData.cardHolderName()], @"lastDigits": [self safeString:cardData.lastDigits()], @"cardDescription": [self safeString:cardData.cardDescription()], - }; + } mutableCopy]; + NSString *primaryAccountIdentifier = [self safeString:cardData.primaryAccountIdentifier()]; + if (primaryAccountIdentifier.length > 0) { + cardDataDict[@"primaryAccountIdentifier"] = primaryAccountIdentifier; + } dispatch_async(dispatch_get_main_queue(), ^{ [self->walletManager IOSPresentAddPaymentPassViewWithCardData:cardDataDict completion:^(OperationResult result, NSDictionary* data) { [self handleWalletResponse:result data:data completedBlock:resolve errorPrefix:@"present_payment_pass_view_failed" defaultErrorMessage:@"Failed to present the payment pass view" rejecter:reject]; diff --git a/ios/WalletManager.swift b/ios/WalletManager.swift index 13c40c6a..8bbda583 100644 --- a/ios/WalletManager.swift +++ b/ios/WalletManager.swift @@ -111,6 +111,9 @@ open class WalletManager: UIViewController { configuration.cardholderName = card.cardHolderName configuration.primaryAccountSuffix = card.lastDigits configuration.localizedDescription = String(card.cardDescription) + if let primaryAccountIdentifier = card.primaryAccountIdentifier { + configuration.primaryAccountIdentifier = primaryAccountIdentifier + } guard let enrollViewController = PKAddPaymentPassViewController(requestConfiguration: configuration, delegate: self) else { completion(.error, [ diff --git a/ios/stucts/CardInfo.swift b/ios/stucts/CardInfo.swift index c02a3c7c..13499153 100644 --- a/ios/stucts/CardInfo.swift +++ b/ios/stucts/CardInfo.swift @@ -5,6 +5,7 @@ struct CardInfo { let cardHolderName: String let lastDigits: String let cardDescription: String + let primaryAccountIdentifier: String? init(cardData: NSDictionary) throws { guard let networkString = cardData["network"] as? String, !networkString.isEmpty, @@ -19,6 +20,11 @@ struct CardInfo { self.cardHolderName = cardHolderName self.lastDigits = lastDigits self.cardDescription = cardDescription + if let identifier = cardData["primaryAccountIdentifier"] as? String, !identifier.isEmpty { + self.primaryAccountIdentifier = identifier + } else { + self.primaryAccountIdentifier = nil + } } private static func getNetwork(from identifier: String) -> PKPaymentNetwork? { diff --git a/src/NativeWallet.ts b/src/NativeWallet.ts index f15df6e9..c0149b8a 100644 --- a/src/NativeWallet.ts +++ b/src/NativeWallet.ts @@ -41,6 +41,8 @@ type IOSCardData = { cardHolderName: string; lastDigits: string; cardDescription: string; + /** Opaque issuer id. PassKit uses it to recognize a reissued card. */ + primaryAccountIdentifier?: string; }; type onCardActivatedPayload = { From 31c1613213fc153f154b3ac9ef5b7acab415e14b Mon Sep 17 00:00:00 2001 From: Jakub Korytko Date: Thu, 8 Oct 2026 14:56:28 +0200 Subject: [PATCH 008/118] fix: clear pending tokenize promise on async pushTokenize failures --- android/src/main/java/com/expensify/wallet/WalletModule.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/android/src/main/java/com/expensify/wallet/WalletModule.kt b/android/src/main/java/com/expensify/wallet/WalletModule.kt index 86a4c6f6..0b4d7e7d 100644 --- a/android/src/main/java/com/expensify/wallet/WalletModule.kt +++ b/android/src/main/java/com/expensify/wallet/WalletModule.kt @@ -5,7 +5,6 @@ import android.app.Activity.RESULT_CANCELED import android.app.Activity.RESULT_OK import android.content.ActivityNotFoundException import android.content.Intent -import android.content.IntentSender import androidx.core.content.IntentCompat import com.expensify.wallet.Utils.getAsyncResult import com.expensify.wallet.Utils.toCardData @@ -251,11 +250,13 @@ class WalletModule internal constructor(context: ReactApplicationContext) : REQUEST_CODE_PUSH_TOKENIZE, null, 0, 0, 0 ) - } catch (e: IntentSender.SendIntentException) { + } catch (e: Exception) { + pendingPushTokenizePromise = null promise.reject(E_OPERATION_FAILED, "Failed to launch Google Pay: ${e.message}") } } .addOnFailureListener { e -> + pendingPushTokenizePromise = null promise.reject(E_OPERATION_FAILED, "Google Pay API Error: ${e.message}") } } catch (e: java.lang.Exception) { From 8283655bb1dbe94b705554967e8b7e4c0e66092d Mon Sep 17 00:00:00 2001 From: Jakub Korytko Date: Thu, 8 Oct 2026 15:02:08 +0200 Subject: [PATCH 009/118] fix: settle pending payment credentials requests on error, timeout and invalidate --- .../java/com/expensify/wallet/WalletModule.kt | 24 +++++++++++++++ .../expensify/wallet/NativeWalletSpec.java | 4 +++ ios/RNWallet.mm | 21 ++++++++++++++ src/NativeWallet.ts | 1 + src/index.tsx | 29 +++++++++++-------- 5 files changed, 67 insertions(+), 12 deletions(-) diff --git a/android/src/main/java/com/expensify/wallet/WalletModule.kt b/android/src/main/java/com/expensify/wallet/WalletModule.kt index 0b4d7e7d..2eec63cb 100644 --- a/android/src/main/java/com/expensify/wallet/WalletModule.kt +++ b/android/src/main/java/com/expensify/wallet/WalletModule.kt @@ -5,6 +5,8 @@ import android.app.Activity.RESULT_CANCELED import android.app.Activity.RESULT_OK import android.content.ActivityNotFoundException import android.content.Intent +import android.os.Handler +import android.os.Looper import androidx.core.content.IntentCompat import com.expensify.wallet.Utils.getAsyncResult import com.expensify.wallet.Utils.toCardData @@ -45,6 +47,7 @@ import java.util.UUID import java.util.concurrent.CompletableFuture import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.Future +import java.util.concurrent.TimeoutException class WalletModule internal constructor(context: ReactApplicationContext) : @@ -57,6 +60,8 @@ class WalletModule internal constructor(context: ReactApplicationContext) : const val E_SDK_API = "SDK API Error" const val E_OPERATION_FAILED = "E_OPERATION_FAILED" const val E_INVALID_DATA = "E_INVALID_DATA" + + const val PAYMENT_CREDENTIALS_TIMEOUT_MS = 60_000L } private val activity: Activity @@ -69,6 +74,7 @@ class WalletModule internal constructor(context: ReactApplicationContext) : private val moduleScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) private val pendingProvisioningFutures = ConcurrentHashMap>() + private val mainHandler = Handler(Looper.getMainLooper()) override fun initialize() { super.initialize() @@ -79,6 +85,9 @@ class WalletModule internal constructor(context: ReactApplicationContext) : super.invalidate() moduleScope.cancel() reactApplicationContext.removeActivityEventListener(cardListener) + mainHandler.removeCallbacksAndMessages(null) + pendingProvisioningFutures.values.forEach { it.completeExceptionally(IllegalStateException("Wallet module invalidated")) } + pendingProvisioningFutures.clear() } private val cardListener = object : ActivityEventListener { @@ -281,6 +290,10 @@ class WalletModule internal constructor(context: ReactApplicationContext) : val future = CompletableFuture() val requestId = UUID.randomUUID().toString() pendingProvisioningFutures[requestId] = future + mainHandler.postDelayed({ + pendingProvisioningFutures.remove(requestId) + ?.completeExceptionally(TimeoutException("Payment credentials were not provided in time")) + }, PAYMENT_CREDENTIALS_TIMEOUT_MS) val params = Arguments.createMap().apply { putString("requestId", requestId) @@ -330,6 +343,17 @@ class WalletModule internal constructor(context: ReactApplicationContext) : } } + @ReactMethod + override fun AndroidRejectPaymentCredentials(requestId: String, errorMessage: String, promise: Promise) { + val future = pendingProvisioningFutures.remove(requestId) + if (future == null) { + promise.reject(E_OPERATION_FAILED, "Request ID not found or timed out") + return + } + future.completeExceptionally(Exception(errorMessage)) + promise.resolve(true) + } + @ReactMethod override fun resumeAddCardToGoogleWallet(data: ReadableMap, promise: Promise) { if (pendingPushTokenizePromise != null) { diff --git a/android/src/paper/java/com/expensify/wallet/NativeWalletSpec.java b/android/src/paper/java/com/expensify/wallet/NativeWalletSpec.java index 5cd1bfa4..052fb38b 100644 --- a/android/src/paper/java/com/expensify/wallet/NativeWalletSpec.java +++ b/android/src/paper/java/com/expensify/wallet/NativeWalletSpec.java @@ -61,6 +61,10 @@ public NativeWalletSpec(ReactApplicationContext reactContext) { @DoNotStrip public abstract void AndroidProvidePaymentCredentials(String requestId, ReadableMap responseData, Promise promise); + @ReactMethod + @DoNotStrip + public abstract void AndroidRejectPaymentCredentials(String requestId, String errorMessage, Promise promise); + @ReactMethod @DoNotStrip public abstract void resumeAddCardToGoogleWallet(ReadableMap cardData, Promise promise); diff --git a/ios/RNWallet.mm b/ios/RNWallet.mm index 4981b78a..a17297c7 100644 --- a/ios/RNWallet.mm +++ b/ios/RNWallet.mm @@ -135,6 +135,27 @@ - (void)addCardToGoogleWallet: // no-op } +- (void)AndroidProvidePaymentCredentials:(NSString *)requestId + responseData: +#ifdef RCT_NEW_ARCH_ENABLED + (JS::NativeWallet::AndroidPaymentCredentialsResponse &)responseData +#else + (NSDictionary *)responseData +#endif + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject +{ + // no-op +} + +- (void)AndroidRejectPaymentCredentials:(NSString *)requestId + errorMessage:(NSString *)errorMessage + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject +{ + // no-op +} + - (void)getSecureWalletInfo:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { // no-op } diff --git a/src/NativeWallet.ts b/src/NativeWallet.ts index 9052a226..f06dd397 100644 --- a/src/NativeWallet.ts +++ b/src/NativeWallet.ts @@ -91,6 +91,7 @@ export interface Spec extends TurboModule { getCardStatusByIdentifier(identifier: string, tsp: string): Promise; addCardToGoogleWallet(cardData: AndroidCardData): Promise; AndroidProvidePaymentCredentials(requestId: string, responseData: AndroidPaymentCredentialsResponse): Promise; + AndroidRejectPaymentCredentials(requestId: string, errorMessage: string): Promise; resumeAddCardToGoogleWallet(cardData: AndroidResumeCardData): Promise; listTokens(): Promise; IOSPresentAddPaymentPassView(cardData: IOSCardData): Promise; diff --git a/src/index.tsx b/src/index.tsx index 5cdf95e1..3e36ea17 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -30,15 +30,17 @@ let paymentCredentialsHandler: ((data: onPaymentCredentialsRequestPayload) => Pr if (Platform.OS === 'android' && Wallet) { eventEmitter ??= new NativeEventEmitter(Wallet); eventEmitter.addListener('onPaymentCredentialsRequest', async (data: onPaymentCredentialsRequestPayload) => { - if (paymentCredentialsHandler) { - try { - const responseData = await paymentCredentialsHandler(data); - await Wallet?.AndroidProvidePaymentCredentials(data.requestId, responseData); - } catch (error) { - console.error('[react-native-wallet] Error handling payment credentials request:', error); - } - } else { - console.warn('[react-native-wallet] Received payment credentials request but no handler is set. Call setPaymentCredentialsHandler() to handle this event.'); + if (!paymentCredentialsHandler) { + console.warn(`[${PACKAGE_NAME}] Received payment credentials request but no handler is set`); + await Wallet?.AndroidRejectPaymentCredentials(data.requestId, 'No payment credentials handler is set').catch(() => undefined); + return; + } + try { + const responseData = await paymentCredentialsHandler(data); + await Wallet?.AndroidProvidePaymentCredentials(data.requestId, responseData); + } catch (error) { + console.error(`[${PACKAGE_NAME}] Error handling payment credentials request:`, error); + await Wallet?.AndroidRejectPaymentCredentials(data.requestId, error instanceof Error ? error.message : String(error)).catch(() => undefined); } }); } @@ -121,9 +123,12 @@ async function addCardToGoogleWallet( } paymentCredentialsHandler = handlePaymentCredentialsGeneration; - const tokenizationStatus = await Wallet.addCardToGoogleWallet(cardData); - paymentCredentialsHandler = null; - return getTokenizationStatus(tokenizationStatus); + try { + const tokenizationStatus = await Wallet.addCardToGoogleWallet(cardData); + return getTokenizationStatus(tokenizationStatus); + } finally { + paymentCredentialsHandler = null; + } } async function resumeAddCardToGoogleWallet(cardData: AndroidResumeCardData): Promise { From 22c5f7eadb7aa8357f953eb1b5b80607af20876b Mon Sep 17 00:00:00 2001 From: Jakub Korytko Date: Thu, 8 Oct 2026 15:02:11 +0200 Subject: [PATCH 010/118] fix: report activation only for successful tokenization outcomes --- android/src/main/java/com/expensify/wallet/WalletModule.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/android/src/main/java/com/expensify/wallet/WalletModule.kt b/android/src/main/java/com/expensify/wallet/WalletModule.kt index 2eec63cb..1a3c7d53 100644 --- a/android/src/main/java/com/expensify/wallet/WalletModule.kt +++ b/android/src/main/java/com/expensify/wallet/WalletModule.kt @@ -118,10 +118,10 @@ class WalletModule internal constructor(context: ReactApplicationContext) : if (result != null) { val isSavedToCloud = result.cardResult - val tokenOutcomes = result.tokenizationOutcomes + val successfulOutcome = result.tokenizationOutcomes.firstOrNull { it.tokenResult } - if (isSavedToCloud || tokenOutcomes.isNotEmpty()) { - val tokenId = tokenOutcomes.firstOrNull()?.issuerTokenId ?: "card_on_file_only" + if (isSavedToCloud || successfulOutcome != null) { + val tokenId = successfulOutcome?.issuerTokenId sendEvent(context, OnCardActivatedEvent.NAME, OnCardActivatedEvent("activated", tokenId).toMap()) localPromise?.resolve(TokenizationStatus.SUCCESS.code) From 138c622c432ad54cb7af4957c2f79bd5bc46b67a Mon Sep 17 00:00:00 2001 From: Jakub Korytko Date: Thu, 8 Oct 2026 15:02:55 +0200 Subject: [PATCH 011/118] docs: require payment credentials handler and note tested TapAndPay SDK --- README.md | 6 +++--- docs/UAPP.md | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 67bd2cc9..113fa513 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ To be able to interact with the Google Wallet on the Android please make sure to 1. Visit [the Google Pay Android Push Provisioning API documentation](https://developers.google.com/pay/issuers/apis/push-provisioning/android/) and request access to it. 2. Once getting an approval from the Google team - 1. Download the [TapAndPay SDK](https://developers.google.com/pay/issuers/apis/push-provisioning/android/releases). + 1. Download the [TapAndPay SDK](https://developers.google.com/pay/issuers/apis/push-provisioning/android/releases). The library uses the UAPP APIs (`PaymentCredentialsGenerator`, `PushTokenizeResult`), so older SDK versions without them will not compile. Tested with 18.10.1. 2. Unzip it and extract the SDK into the `/android/libs` folder in your React Native project (if there is no `libs` folder, create one). 3. Add `/android/libs` to `.gitignore`. @@ -135,7 +135,7 @@ In your `app.json` file, add the following configuration: "@expensify/react-native-wallet", { enableApplePayProvisioning: true, - googleTapAndPaySdkPath: "./libs/tapandpay-v18.7.0.zip", // path to the Google Tap & Pay SDK zip file + googleTapAndPaySdkPath: "./libs/tapandpay-v18.10.1.zip", // path to the Google Tap & Pay SDK zip file }, ], ] @@ -180,7 +180,7 @@ The library offers functions for seamless integration and use of the Apple Walle | **checkWalletAvailability** | Checks if the wallet is ready and initializes it if possible. | None | `boolean` | βœ… | βœ… | | **getCardStatusBySuffix** | Retrieves the current status of a card in the wallet. | `lastDigits: string`
(The last few digits of the card number) | `CardStatus` | βœ… | βœ… | | **getCardStatusByIdentifier** | Returns the state of a card based on a platform-specific identifier. On Android, it's `Token Reference ID` and on iOS, it's `Primary Account Identifier`. | `identifier: string`,
`tsp: string` | `CardStatus` | βœ… | βœ… | -| **addCardToGoogleWallet** | Initiates native Push Provisioning flow for adding a card to the Google Wallet. For UAPP flow, pass a handler as the second parameter. [Learn more β†’](./docs/UAPP.md) | `data`: `AndroidCardData`,
`handlePaymentCredentials?`: `(data: onPaymentCredentialsRequestPayload) => Promise` | `TokenizationStatus` | ❌ | βœ… | +| **addCardToGoogleWallet** | Initiates native Push Provisioning flow for adding a card to the Google Wallet. Requires a handler that returns the payment credentials for the UAPP flow. [Learn more β†’](./docs/UAPP.md) | `data`: `AndroidCardData`,
`handlePaymentCredentials`: `(data: onPaymentCredentialsRequestPayload) => Promise` | `TokenizationStatus` | ❌ | βœ… | | **resumeAddCardToGoogleWallet** | Resumes the Push Provisioning flow for adding a card to the Google Wallet using existing token reference ID. | `data`: `AndroidResumeCardData` | `TokenizationStatus` | ❌ | βœ… | | **listTokens** | Lists all tokens currently stored in the Google Wallet. | None | `TokenInfo[]` | ❌ | βœ… | | **addCardToAppleWallet** | Initiates native Push Provisioning flow for adding a card to the Apple Wallet. | `data`: `IOSCardData`,
`issuerEncrypt-`
`PayloadCallback: IOSIssuerCallback` | `void` | βœ… | ❌ | diff --git a/docs/UAPP.md b/docs/UAPP.md index acb3b857..1c771827 100644 --- a/docs/UAPP.md +++ b/docs/UAPP.md @@ -22,7 +22,7 @@ This library handles the UAPP flow automatically. You just need to set up a hand ## Setup -To enable UAPP support, pass a payment credentials handler directly to the `addCardToGoogleWallet()` function: +UAPP is always enabled on Android, so `addCardToGoogleWallet()` requires a payment credentials handler as its second argument: ```typescript import { addCardToGoogleWallet } from '@expensify/react-native-wallet'; @@ -120,6 +120,8 @@ Initiates the Google Wallet card provisioning flow with UAPP support. **Returns:** `Promise` - Status of the tokenization process ('success', 'canceled', or 'error') +The handler is only called when Google Wallet requests a Google OPC. If it throws, the request is reported to Google Wallet as failed. If it does not resolve within 60 seconds, the request fails with a timeout. + **Platform:** Android only **Example:** From 68447d6ae280e17da8e3f2e32eba19a9ada6dbff Mon Sep 17 00:00:00 2001 From: Jakub Korytko Date: Thu, 8 Oct 2026 15:02:57 +0200 Subject: [PATCH 012/118] chore: rename Android dummy credentials and drop unused card field --- android/src/main/java/com/expensify/wallet/Utils.kt | 1 - android/src/main/java/com/expensify/wallet/model/CardData.kt | 1 - apps/common-app/src/CONST.ts | 4 ++-- apps/common-app/src/walletUtils.ts | 2 +- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/android/src/main/java/com/expensify/wallet/Utils.kt b/android/src/main/java/com/expensify/wallet/Utils.kt index 9bb76c6d..7bce2507 100644 --- a/android/src/main/java/com/expensify/wallet/Utils.kt +++ b/android/src/main/java/com/expensify/wallet/Utils.kt @@ -35,7 +35,6 @@ object Utils { return CardData( network = this.getString("network") ?: "", opaquePaymentCard = this.getString("opaquePaymentCard") ?: "", - googleOpaquePaymentCard = this.getString("googleOpaquePaymentCard") ?: "", cardHolderName = this.getString("cardHolderName") ?: "", lastDigits = this.getString("lastDigits") ?: "", userAddress = userAddress, diff --git a/android/src/main/java/com/expensify/wallet/model/CardData.kt b/android/src/main/java/com/expensify/wallet/model/CardData.kt index 419b2b2a..a348ee77 100644 --- a/android/src/main/java/com/expensify/wallet/model/CardData.kt +++ b/android/src/main/java/com/expensify/wallet/model/CardData.kt @@ -6,7 +6,6 @@ data class CardData( val platform: String = "android", val network: String, val opaquePaymentCard: String, // TSP OPC - val googleOpaquePaymentCard: String? = null, val cardHolderName: String, val lastDigits: String, val userAddress: UserAddress, diff --git a/apps/common-app/src/CONST.ts b/apps/common-app/src/CONST.ts index 66fbb15e..e3130925 100644 --- a/apps/common-app/src/CONST.ts +++ b/apps/common-app/src/CONST.ts @@ -42,9 +42,9 @@ const IOSDummyEncryptPayload: IOSEncryptPayload = { ephemeralPublicKey: 'ZXBoZW1lcmFsUHVibGljS2V5MTIz', }; -const IOSDummyPaymentCredentialsResponse = { +const AndroidDummyPaymentCredentialsResponse = { opaquePaymentCard: 'encryptedCardInformation123456', googleOpaquePaymentCard: 'GOPC', }; -export {AndroidDummyCardData, AndroidDummyResumeCardData, IOSDummyCardData, IOSDummyEncryptPayload, IOSDummyPaymentCredentialsResponse}; +export {AndroidDummyCardData, AndroidDummyResumeCardData, IOSDummyCardData, IOSDummyEncryptPayload, AndroidDummyPaymentCredentialsResponse}; diff --git a/apps/common-app/src/walletUtils.ts b/apps/common-app/src/walletUtils.ts index 6ef9f961..8498306e 100644 --- a/apps/common-app/src/walletUtils.ts +++ b/apps/common-app/src/walletUtils.ts @@ -12,7 +12,7 @@ function issuerEncryptPayloadCallback(_nonce: string, _nonceSignature: string, _ function handlePaymentCredentialsGeneration(_data: onPaymentCredentialsRequestPayload): Promise { // Here send data to your server or you TSP to generate the payment credentials // for example: fetch('https://tsp.com/generatePaymentCredentials', {method: 'POST', body: data}) - return Promise.resolve(CONST.IOSDummyPaymentCredentialsResponse); + return Promise.resolve(CONST.AndroidDummyPaymentCredentialsResponse); } async function addCardToWallet(cardStatus?: CardStatus) { From 81f9973b5946137e1af47b08a637ba361befd515 Mon Sep 17 00:00:00 2001 From: Jakub Korytko Date: Thu, 8 Oct 2026 15:12:50 +0200 Subject: [PATCH 013/118] fix: keep legacy result handling for resume tokenize flow --- .../java/com/expensify/wallet/WalletModule.kt | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/com/expensify/wallet/WalletModule.kt b/android/src/main/java/com/expensify/wallet/WalletModule.kt index 1a3c7d53..1baa333f 100644 --- a/android/src/main/java/com/expensify/wallet/WalletModule.kt +++ b/android/src/main/java/com/expensify/wallet/WalletModule.kt @@ -55,6 +55,7 @@ class WalletModule internal constructor(context: ReactApplicationContext) : companion object { const val NAME = "RNWallet" const val REQUEST_CODE_PUSH_TOKENIZE: Int = 0xA001 + const val REQUEST_CODE_RESUME_TOKENIZE: Int = 0xA003 const val REQUEST_CREATE_WALLET: Int = 0xA002 const val E_SDK_API = "SDK API Error" @@ -97,6 +98,27 @@ class WalletModule internal constructor(context: ReactApplicationContext) : if (requestCode == REQUEST_CREATE_WALLET) { pendingCreateWalletPromise?.resolve(resultCode == RESULT_OK) pendingCreateWalletPromise = null + } else if (requestCode == REQUEST_CODE_RESUME_TOKENIZE) { + val localPromise = pendingPushTokenizePromise + pendingPushTokenizePromise = null + if (resultCode == RESULT_OK) { + val tokenId = data?.getStringExtra(TapAndPay.EXTRA_ISSUER_TOKEN_ID) + sendEvent( + context, + OnCardActivatedEvent.NAME, + OnCardActivatedEvent("activated", tokenId).toMap() + ) + localPromise?.resolve(TokenizationStatus.SUCCESS.code) + } else if (resultCode == RESULT_CANCELED) { + sendEvent( + context, + OnCardActivatedEvent.NAME, + OnCardActivatedEvent("canceled", null).toMap() + ) + localPromise?.resolve(TokenizationStatus.CANCELED.code) + } else { + localPromise?.resolve(TokenizationStatus.ERROR.code) + } } else if (requestCode == REQUEST_CODE_PUSH_TOKENIZE) { val localPromise = pendingPushTokenizePromise pendingPushTokenizePromise = null @@ -377,7 +399,7 @@ class WalletModule internal constructor(context: ReactApplicationContext) : tokenServiceProvider, displayName, cardNetwork, - REQUEST_CODE_PUSH_TOKENIZE + REQUEST_CODE_RESUME_TOKENIZE ) } catch (e: java.lang.Exception) { pendingPushTokenizePromise = null From 6c40f8836723e917298e320152dc8008df2c50a1 Mon Sep 17 00:00:00 2001 From: Jakub Korytko Date: Thu, 8 Oct 2026 15:12:52 +0200 Subject: [PATCH 014/118] fix: type onCardActivated tokenId as nullable --- README.md | 2 +- src/NativeWallet.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 113fa513..7b879530 100644 --- a/README.md +++ b/README.md @@ -196,7 +196,7 @@ The library offers functions for seamless integration and use of the Apple Walle | **onPaymentCredentialsRequestPayload** | Payload received when Google Wallet requests payment credentials during UAPP provisioning. [Learn more β†’](./docs/UAPP.md) | `requestId: string`,
`serverSessionId: string`,
`walletId: string`,
`opaquePaymentCard: string` | | **UserAddress** | Structured address used for cardholder verification. | `name: string`,
`addressOne: string`,
`addressTwo: string`,
`city: string`,
`administrativeArea: string`,
`countryCode: string`,
`postalCode: string`,
`phoneNumber: string` | | **IOSCardData** | Data related to a card that is to be added on iOS platform. | `network: string`,
`activationData: string`,
`encryptedPassData: string`,
`ephemeralPublicKey: string`,
`cardHolderTitle: string`,
`cardHolderName: string`,
`lastDigits: string`,
`cardDescription: string`,
`cardDescriptionComment: string` | -| **onCardActivatedPayload** | Data used by listener to notice when a card's status changes. | `tokenId: string`,
`status: 'activated' \| 'canceled'`
| +| **onCardActivatedPayload** | Data used by listener to notice when a card's status changes. | `tokenId: string \| null`,
`status: 'activated' \| 'canceled'`
| | **IOSIssuerCallback** | This callback is invoked with a nonce, its signature, and a certificate array obtained from Apple. It is expected that you will forward these details to your server or the card issuer's API to securely encrypt the payload required for adding cards to the Apple Wallet. | `(nonce: string, nonceSignature: string, certificate: string[]) => IOSEncryptPayload` | | **IOSEncryptPayload** | An object containing the necessary elements to complete the addition of a card to Apple Wallet. | `encryptedPassData: string`,
`activationData: string`,
`ephemeralPublicKey: string` | | **TokenInfo** | Information about a token stored in Google Wallet. | `identifier: string`,
`lastDigits: string`,
`tokenState: number` | diff --git a/src/NativeWallet.ts b/src/NativeWallet.ts index f06dd397..5c1e507d 100644 --- a/src/NativeWallet.ts +++ b/src/NativeWallet.ts @@ -46,7 +46,7 @@ type IOSCardData = { }; type onCardActivatedPayload = { - tokenId: string; + tokenId: string | null; status: 'activated' | 'canceled'; }; From 156aa2efea50f546b291c3c1ae20f02104a7664a Mon Sep 17 00:00:00 2001 From: Jakub Korytko Date: Fri, 9 Oct 2026 13:53:55 +0200 Subject: [PATCH 015/118] fix: settle UAPP tokenize result without PushTokenizeResult like the legacy flow --- .../java/com/expensify/wallet/WalletModule.kt | 44 ++++++++++--------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/android/src/main/java/com/expensify/wallet/WalletModule.kt b/android/src/main/java/com/expensify/wallet/WalletModule.kt index 1baa333f..4eb6a8cb 100644 --- a/android/src/main/java/com/expensify/wallet/WalletModule.kt +++ b/android/src/main/java/com/expensify/wallet/WalletModule.kt @@ -132,29 +132,33 @@ class WalletModule internal constructor(context: ReactApplicationContext) : return } - if (data != null) { - val result = - IntentCompat.getParcelableExtra( - data, TapAndPay.EXTRA_PUSH_TOKENIZE_RESULT, PushTokenizeResult::class.java - ) - - if (result != null) { - val isSavedToCloud = result.cardResult - val successfulOutcome = result.tokenizationOutcomes.firstOrNull { it.tokenResult } - - if (isSavedToCloud || successfulOutcome != null) { - val tokenId = successfulOutcome?.issuerTokenId + val result = data?.let { + IntentCompat.getParcelableExtra( + it, TapAndPay.EXTRA_PUSH_TOKENIZE_RESULT, PushTokenizeResult::class.java + ) + } - sendEvent(context, OnCardActivatedEvent.NAME, OnCardActivatedEvent("activated", tokenId).toMap()) - localPromise?.resolve(TokenizationStatus.SUCCESS.code) - } else { - val errorMsg = "Card not saved. Status: ${result.cardStatus}" - localPromise?.reject(E_OPERATION_FAILED, errorMsg) - } + if (result == null) { + if (resultCode == RESULT_OK) { + sendEvent(context, OnCardActivatedEvent.NAME, OnCardActivatedEvent("activated", null).toMap()) + localPromise?.resolve(TokenizationStatus.SUCCESS.code) } else { - // Data intent is null (rare but possible failure case). Report to Google if you observe this. - localPromise?.reject(E_OPERATION_FAILED, "Unexpected error.") + localPromise?.resolve(TokenizationStatus.ERROR.code) } + return + } + + val isSavedToCloud = result.cardResult + val successfulOutcome = result.tokenizationOutcomes.firstOrNull { it.tokenResult } + + if (isSavedToCloud || successfulOutcome != null) { + val tokenId = successfulOutcome?.issuerTokenId + + sendEvent(context, OnCardActivatedEvent.NAME, OnCardActivatedEvent("activated", tokenId).toMap()) + localPromise?.resolve(TokenizationStatus.SUCCESS.code) + } else { + val errorMsg = "Card not saved. Status: ${result.cardStatus}" + localPromise?.reject(E_OPERATION_FAILED, errorMsg) } } } From 472ee270de460c2303bbb661959485d2a5bcaee3 Mon Sep 17 00:00:00 2001 From: Jakub Korytko Date: Fri, 9 Oct 2026 14:14:32 +0200 Subject: [PATCH 016/118] docs: fix UAPP acronym expansion --- README.md | 8 ++++---- docs/UAPP.md | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 7b879530..8a6c9e22 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ - ♻️ Universal solution (Apple Wallet and Google Wallet) - πŸ’³ Easy and secure addition of payment cards from your app directly - πŸ’° Supports Visa, MasterCard, Amex and Discover payment cards -- πŸ” UAPP (User Account Payment Provisioning) support for enhanced security +- πŸ” UAPP (Unified Android Push Provisioning) support for enhanced security - πŸŽ‰ Expo support (expo-plugin) ## Getting started @@ -158,7 +158,7 @@ Here you can find data elements used in the library, essential to work with Goog ### Android - **Opaque Payment Card** (OPC) - a binary blob of information Google Pay receives from the issuer app that could be presented to TSP to receive a token. -- **Google Opaque Payment Card** (Google OPC) - an encrypted version of the OPC required for UAPP (User Account Payment Provisioning) flow. This is generated by your backend using Google's encryption keys during the provisioning process. [Learn more about UAPP β†’](./docs/UAPP.md) +- **Google Opaque Payment Card** (Google OPC) - an encrypted version of the OPC required for UAPP (Unified Android Push Provisioning) flow. This is generated by your backend using Google's encryption keys during the provisioning process. [Learn more about UAPP β†’](./docs/UAPP.md) - **Token Service Provider** (TSP) - a service that enhances payment security by replacing a credit card number during transactions with a unique digital identifier - token. The TSP specifies the tokenization service used to create a given token e.g. Visa, MasterCard, American Express. - **Token Reference ID** - a unique identifying number to refer to a DPAN (Dynamic Personal Account Number). Token Providers will assign each DPAN an issuer token ID at the time of tokenization. @@ -307,9 +307,9 @@ const base64ToHex = (base64: string): string => Buffer.from(base64, "base64").to This will depend on the implementation of your backend/issuer so make sure to check with them otherwise IOS might fail to provision the card. -# UAPP (User Account Payment Provisioning) +# UAPP (Unified Android Push Provisioning) -For Android applications that require enhanced security for card provisioning, this library supports UAPP (User Account Payment Provisioning) flow with Google Wallet. +For Android applications that require enhanced security for card provisioning, this library supports UAPP (Unified Android Push Provisioning) flow with Google Wallet. πŸ“– **[Read the complete UAPP documentation β†’](./docs/UAPP.md)** diff --git a/docs/UAPP.md b/docs/UAPP.md index 1c771827..15b63709 100644 --- a/docs/UAPP.md +++ b/docs/UAPP.md @@ -1,8 +1,8 @@ -# UAPP (User Account Payment Provisioning) for Android +# UAPP (Unified Android Push Provisioning) for Android ## What is UAPP? -UAPP (User Account Payment Provisioning) is an enhanced security flow for Google Wallet that requires encrypted payment credentials during card provisioning. Instead of providing only the TSP Opaque Payment Card (OPC), your backend must also generate a Google Opaque Payment Card (Google OPC) using Google's encryption keys. It allows users to provision cards to Google Wallet and save their cards to their Google Account. +UAPP (Unified Android Push Provisioning) is an enhanced security flow for Google Wallet that requires encrypted payment credentials during card provisioning. Instead of providing only the TSP Opaque Payment Card (OPC), your backend must also generate a Google Opaque Payment Card (Google OPC) using Google's encryption keys. It allows users to provision cards to Google Wallet and save their cards to their Google Account. This library handles the UAPP flow automatically. You just need to set up a handler that communicates with your backend to retrieve the encrypted credentials. @@ -106,7 +106,7 @@ Response object containing encrypted payment credentials for UAPP flow: For UAPP provisioning, you need: - **Opaque Payment Card** (OPC) - a binary blob of information Google Pay receives from the issuer app that could be presented to TSP to receive a token. -- **Google Opaque Payment Card** (Google OPC) - an encrypted version of the OPC required for UAPP (User Account Payment Provisioning) flow. This is generated by your backend using Google's encryption keys during the provisioning process. +- **Google Opaque Payment Card** (Google OPC) - an encrypted version of the OPC required for UAPP (Unified Android Push Provisioning) flow. This is generated by your backend using Google's encryption keys during the provisioning process. ## API Reference From f8e160cea75c207f13bc508b83a39d0b0b94e258 Mon Sep 17 00:00:00 2001 From: Jakub Korytko Date: Fri, 9 Oct 2026 14:29:38 +0200 Subject: [PATCH 017/118] chore: bump version to 0.2.0-0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 812ab2b3..e3c8efbe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@expensify/react-native-wallet", - "version": "0.1.41", + "version": "0.2.0-0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@expensify/react-native-wallet", - "version": "0.1.41", + "version": "0.2.0-0", "license": "MIT", "workspaces": [ "apps/common-app", diff --git a/package.json b/package.json index 455db5aa..9450b664 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@expensify/react-native-wallet", - "version": "0.1.41", + "version": "0.2.0-0", "description": "A React Native module designed for seamless integration of Card Push Provisioning into Apple Wallet and Google Wallet, enabling easy and secure addition of payment cards directly from your application.", "source": "./src/index.tsx", "main": "lib/commonjs/index", From efa481074b30ca586af491710fc91981d2a17afd Mon Sep 17 00:00:00 2001 From: Jakub Korytko Date: Fri, 9 Oct 2026 14:46:08 +0200 Subject: [PATCH 018/118] refactor: split onActivityResult into per-request handlers --- .../java/com/expensify/wallet/WalletModule.kt | 115 +++++++++--------- 1 file changed, 55 insertions(+), 60 deletions(-) diff --git a/android/src/main/java/com/expensify/wallet/WalletModule.kt b/android/src/main/java/com/expensify/wallet/WalletModule.kt index 4eb6a8cb..fbf015cc 100644 --- a/android/src/main/java/com/expensify/wallet/WalletModule.kt +++ b/android/src/main/java/com/expensify/wallet/WalletModule.kt @@ -95,75 +95,70 @@ class WalletModule internal constructor(context: ReactApplicationContext) : override fun onActivityResult( activity: Activity, requestCode: Int, resultCode: Int, data: Intent? ) { - if (requestCode == REQUEST_CREATE_WALLET) { - pendingCreateWalletPromise?.resolve(resultCode == RESULT_OK) - pendingCreateWalletPromise = null - } else if (requestCode == REQUEST_CODE_RESUME_TOKENIZE) { - val localPromise = pendingPushTokenizePromise - pendingPushTokenizePromise = null - if (resultCode == RESULT_OK) { - val tokenId = data?.getStringExtra(TapAndPay.EXTRA_ISSUER_TOKEN_ID) - sendEvent( - context, - OnCardActivatedEvent.NAME, - OnCardActivatedEvent("activated", tokenId).toMap() - ) - localPromise?.resolve(TokenizationStatus.SUCCESS.code) - } else if (resultCode == RESULT_CANCELED) { - sendEvent( - context, - OnCardActivatedEvent.NAME, - OnCardActivatedEvent("canceled", null).toMap() - ) - localPromise?.resolve(TokenizationStatus.CANCELED.code) - } else { - localPromise?.resolve(TokenizationStatus.ERROR.code) - } - } else if (requestCode == REQUEST_CODE_PUSH_TOKENIZE) { - val localPromise = pendingPushTokenizePromise - pendingPushTokenizePromise = null - if (resultCode == RESULT_CANCELED) { - sendEvent( - context, - OnCardActivatedEvent.NAME, - OnCardActivatedEvent("canceled", null).toMap() - ) - localPromise?.resolve(TokenizationStatus.CANCELED.code) - return + when (requestCode) { + REQUEST_CREATE_WALLET -> { + pendingCreateWalletPromise?.resolve(resultCode == RESULT_OK) + pendingCreateWalletPromise = null } + REQUEST_CODE_RESUME_TOKENIZE -> handleResumeTokenizeResult(resultCode, data) + REQUEST_CODE_PUSH_TOKENIZE -> handlePushTokenizeResult(resultCode, data) + } + } - val result = data?.let { - IntentCompat.getParcelableExtra( - it, TapAndPay.EXTRA_PUSH_TOKENIZE_RESULT, PushTokenizeResult::class.java - ) - } + override fun onNewIntent(intent: Intent) {} + } - if (result == null) { - if (resultCode == RESULT_OK) { - sendEvent(context, OnCardActivatedEvent.NAME, OnCardActivatedEvent("activated", null).toMap()) - localPromise?.resolve(TokenizationStatus.SUCCESS.code) - } else { - localPromise?.resolve(TokenizationStatus.ERROR.code) - } - return - } + private fun takePendingTokenizePromise(): Promise? = + pendingPushTokenizePromise.also { pendingPushTokenizePromise = null } + + private fun sendCardActivatedEvent(status: String, tokenId: String?) { + sendEvent(reactApplicationContext, OnCardActivatedEvent.NAME, OnCardActivatedEvent(status, tokenId).toMap()) + } - val isSavedToCloud = result.cardResult - val successfulOutcome = result.tokenizationOutcomes.firstOrNull { it.tokenResult } + private fun handleResumeTokenizeResult(resultCode: Int, data: Intent?) { + val promise = takePendingTokenizePromise() + when (resultCode) { + RESULT_OK -> { + sendCardActivatedEvent("activated", data?.getStringExtra(TapAndPay.EXTRA_ISSUER_TOKEN_ID)) + promise?.resolve(TokenizationStatus.SUCCESS.code) + } + RESULT_CANCELED -> { + sendCardActivatedEvent("canceled", null) + promise?.resolve(TokenizationStatus.CANCELED.code) + } + else -> promise?.resolve(TokenizationStatus.ERROR.code) + } + } - if (isSavedToCloud || successfulOutcome != null) { - val tokenId = successfulOutcome?.issuerTokenId + private fun handlePushTokenizeResult(resultCode: Int, data: Intent?) { + val promise = takePendingTokenizePromise() + if (resultCode == RESULT_CANCELED) { + sendCardActivatedEvent("canceled", null) + promise?.resolve(TokenizationStatus.CANCELED.code) + return + } - sendEvent(context, OnCardActivatedEvent.NAME, OnCardActivatedEvent("activated", tokenId).toMap()) - localPromise?.resolve(TokenizationStatus.SUCCESS.code) - } else { - val errorMsg = "Card not saved. Status: ${result.cardStatus}" - localPromise?.reject(E_OPERATION_FAILED, errorMsg) - } + val result = data?.let { + IntentCompat.getParcelableExtra(it, TapAndPay.EXTRA_PUSH_TOKENIZE_RESULT, PushTokenizeResult::class.java) + } + + if (result == null) { + if (resultCode == RESULT_OK) { + sendCardActivatedEvent("activated", null) + promise?.resolve(TokenizationStatus.SUCCESS.code) + } else { + promise?.resolve(TokenizationStatus.ERROR.code) } + return } - override fun onNewIntent(intent: Intent) {} + val successfulOutcome = result.tokenizationOutcomes.firstOrNull { it.tokenResult } + if (result.cardResult || successfulOutcome != null) { + sendCardActivatedEvent("activated", successfulOutcome?.issuerTokenId) + promise?.resolve(TokenizationStatus.SUCCESS.code) + } else { + promise?.reject(E_OPERATION_FAILED, "Card not saved. Status: ${result.cardStatus}") + } } @ReactMethod From 467d386306195b903693aae830e59710161c019e Mon Sep 17 00:00:00 2001 From: Jakub Korytko Date: Fri, 9 Oct 2026 15:58:53 +0200 Subject: [PATCH 019/118] fix: mark onCardActivated tokenId as optional --- README.md | 2 +- src/NativeWallet.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8a6c9e22..57add64c 100644 --- a/README.md +++ b/README.md @@ -196,7 +196,7 @@ The library offers functions for seamless integration and use of the Apple Walle | **onPaymentCredentialsRequestPayload** | Payload received when Google Wallet requests payment credentials during UAPP provisioning. [Learn more β†’](./docs/UAPP.md) | `requestId: string`,
`serverSessionId: string`,
`walletId: string`,
`opaquePaymentCard: string` | | **UserAddress** | Structured address used for cardholder verification. | `name: string`,
`addressOne: string`,
`addressTwo: string`,
`city: string`,
`administrativeArea: string`,
`countryCode: string`,
`postalCode: string`,
`phoneNumber: string` | | **IOSCardData** | Data related to a card that is to be added on iOS platform. | `network: string`,
`activationData: string`,
`encryptedPassData: string`,
`ephemeralPublicKey: string`,
`cardHolderTitle: string`,
`cardHolderName: string`,
`lastDigits: string`,
`cardDescription: string`,
`cardDescriptionComment: string` | -| **onCardActivatedPayload** | Data used by listener to notice when a card's status changes. | `tokenId: string \| null`,
`status: 'activated' \| 'canceled'`
| +| **onCardActivatedPayload** | Data used by listener to notice when a card's status changes. | `tokenId?: string \| null`,
`status: 'activated' \| 'canceled'`
| | **IOSIssuerCallback** | This callback is invoked with a nonce, its signature, and a certificate array obtained from Apple. It is expected that you will forward these details to your server or the card issuer's API to securely encrypt the payload required for adding cards to the Apple Wallet. | `(nonce: string, nonceSignature: string, certificate: string[]) => IOSEncryptPayload` | | **IOSEncryptPayload** | An object containing the necessary elements to complete the addition of a card to Apple Wallet. | `encryptedPassData: string`,
`activationData: string`,
`ephemeralPublicKey: string` | | **TokenInfo** | Information about a token stored in Google Wallet. | `identifier: string`,
`lastDigits: string`,
`tokenState: number` | diff --git a/src/NativeWallet.ts b/src/NativeWallet.ts index 5c1e507d..12f8e099 100644 --- a/src/NativeWallet.ts +++ b/src/NativeWallet.ts @@ -46,7 +46,7 @@ type IOSCardData = { }; type onCardActivatedPayload = { - tokenId: string | null; + tokenId?: string | null; status: 'activated' | 'canceled'; }; From ac9c58488ca2ddc19e6cdd50764fd5e934bcf57f Mon Sep 17 00:00:00 2001 From: Jakub Korytko Date: Mon, 12 Oct 2026 13:44:39 +0200 Subject: [PATCH 020/118] fix: report payment credentials failures with GeneratePaymentCredentialsException --- .../java/com/expensify/wallet/WalletModule.kt | 19 +++++++++++++------ docs/UAPP.md | 2 +- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/android/src/main/java/com/expensify/wallet/WalletModule.kt b/android/src/main/java/com/expensify/wallet/WalletModule.kt index fbf015cc..3cb54f74 100644 --- a/android/src/main/java/com/expensify/wallet/WalletModule.kt +++ b/android/src/main/java/com/expensify/wallet/WalletModule.kt @@ -29,6 +29,7 @@ import com.facebook.react.modules.core.DeviceEventManagerModule import com.google.android.gms.common.api.ApiException import com.google.android.gms.tapandpay.TapAndPay import com.google.android.gms.tapandpay.TapAndPayClient +import com.google.android.gms.tapandpay.issuer.GeneratePaymentCredentialsException import com.google.android.gms.tapandpay.issuer.GeneratePaymentCredentialsRequest import com.google.android.gms.tapandpay.issuer.GeneratePaymentCredentialsResponse import com.google.android.gms.tapandpay.issuer.PaymentCredentialsGenerator @@ -47,7 +48,6 @@ import java.util.UUID import java.util.concurrent.CompletableFuture import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.Future -import java.util.concurrent.TimeoutException class WalletModule internal constructor(context: ReactApplicationContext) : @@ -87,7 +87,7 @@ class WalletModule internal constructor(context: ReactApplicationContext) : moduleScope.cancel() reactApplicationContext.removeActivityEventListener(cardListener) mainHandler.removeCallbacksAndMessages(null) - pendingProvisioningFutures.values.forEach { it.completeExceptionally(IllegalStateException("Wallet module invalidated")) } + pendingProvisioningFutures.values.forEach { it.completeExceptionally(paymentCredentialsError()) } pendingProvisioningFutures.clear() } @@ -295,6 +295,10 @@ class WalletModule internal constructor(context: ReactApplicationContext) : } } + // Google Wallet only understands GeneratePaymentCredentialsException; UNKNOWN_ERROR covers backend failures and timeouts + private fun paymentCredentialsError() = + GeneratePaymentCredentialsException(GeneratePaymentCredentialsException.UNKNOWN_ERROR) + private fun createPaymentCredentialsGenerator( cardData: CardData ): PaymentCredentialsGenerator { @@ -313,7 +317,7 @@ class WalletModule internal constructor(context: ReactApplicationContext) : pendingProvisioningFutures[requestId] = future mainHandler.postDelayed({ pendingProvisioningFutures.remove(requestId) - ?.completeExceptionally(TimeoutException("Payment credentials were not provided in time")) + ?.completeExceptionally(paymentCredentialsError()) }, PAYMENT_CREDENTIALS_TIMEOUT_MS) val params = Arguments.createMap().apply { @@ -350,8 +354,11 @@ class WalletModule internal constructor(context: ReactApplicationContext) : if (tspOpc == null) { throw Exception("opaquePaymentCard is required") } + if (googleOpc == null) { + throw Exception("googleOpaquePaymentCard is required") + } val response = GeneratePaymentCredentialsResponse.Builder() - .setGoogleOpaquePaymentCard(googleOpc?.toByteArray(Charsets.UTF_8)) + .setGoogleOpaquePaymentCard(googleOpc.toByteArray(Charsets.UTF_8)) .setOpaquePaymentCard(tspOpc.toByteArray(Charsets.UTF_8)) .build() @@ -359,7 +366,7 @@ class WalletModule internal constructor(context: ReactApplicationContext) : promise.resolve(true) } catch (e: Exception) { - future.completeExceptionally(e) + future.completeExceptionally(paymentCredentialsError()) promise.reject(E_OPERATION_FAILED, e.message) } } @@ -371,7 +378,7 @@ class WalletModule internal constructor(context: ReactApplicationContext) : promise.reject(E_OPERATION_FAILED, "Request ID not found or timed out") return } - future.completeExceptionally(Exception(errorMessage)) + future.completeExceptionally(paymentCredentialsError()) promise.resolve(true) } diff --git a/docs/UAPP.md b/docs/UAPP.md index 15b63709..b0dd3201 100644 --- a/docs/UAPP.md +++ b/docs/UAPP.md @@ -120,7 +120,7 @@ Initiates the Google Wallet card provisioning flow with UAPP support. **Returns:** `Promise` - Status of the tokenization process ('success', 'canceled', or 'error') -The handler is only called when Google Wallet requests a Google OPC. If it throws, the request is reported to Google Wallet as failed. If it does not resolve within 60 seconds, the request fails with a timeout. +The handler is only called when Google Wallet requests a Google OPC, and it must return both `opaquePaymentCard` and `googleOpaquePaymentCard`. If it throws, returns no Google OPC, or does not resolve within 60 seconds, the request is reported to Google Wallet as failed (`GeneratePaymentCredentialsException.UNKNOWN_ERROR`). Google does not guarantee a fallback to TSP-only provisioning in that case, so do not rely on the card being added. **Platform:** Android only From b219673a84f3948e86e7ef9af141e8b523fd444e Mon Sep 17 00:00:00 2001 From: Jakub Korytko Date: Mon, 12 Oct 2026 13:44:41 +0200 Subject: [PATCH 021/118] feat: pass tokenRequestorId to the payment credentials handler --- README.md | 2 +- android/src/main/java/com/expensify/wallet/WalletModule.kt | 1 + docs/UAPP.md | 5 +++-- src/NativeWallet.ts | 1 + 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 57add64c..5d50856d 100644 --- a/README.md +++ b/README.md @@ -193,7 +193,7 @@ The library offers functions for seamless integration and use of the Apple Walle | **AndroidCardData** | Data related to a card that is to be added on Android platform wallets. | `network: string`,
`opaquePaymentCard: string`,
`cardHolderName: string`,
`lastDigits: string`,
`userAddress: UserAddress`,
`isVirtualCard?: boolean`,
`isBounceProvisioned?: boolean` | | **AndroidResumeCardData** | Simplified data structure for resuming card addition to Google Wallet using existing token reference ID. | `network: string`,
`tokenReferenceID: string`,
`cardHolderName?: string`,
`lastDigits?: string` | | **AndroidPaymentCredentialsResponse** | Response object containing encrypted payment credentials for UAPP flow. [Learn more β†’](./docs/UAPP.md) | `opaquePaymentCard: string`,
`googleOpaquePaymentCard: string` | -| **onPaymentCredentialsRequestPayload** | Payload received when Google Wallet requests payment credentials during UAPP provisioning. [Learn more β†’](./docs/UAPP.md) | `requestId: string`,
`serverSessionId: string`,
`walletId: string`,
`opaquePaymentCard: string` | +| **onPaymentCredentialsRequestPayload** | Payload received when Google Wallet requests payment credentials during UAPP provisioning. [Learn more β†’](./docs/UAPP.md) | `requestId: string`,
`serverSessionId: string`,
`walletId: string`,
`tokenRequestorId: string \| null`,
`opaquePaymentCard: string` | | **UserAddress** | Structured address used for cardholder verification. | `name: string`,
`addressOne: string`,
`addressTwo: string`,
`city: string`,
`administrativeArea: string`,
`countryCode: string`,
`postalCode: string`,
`phoneNumber: string` | | **IOSCardData** | Data related to a card that is to be added on iOS platform. | `network: string`,
`activationData: string`,
`encryptedPassData: string`,
`ephemeralPublicKey: string`,
`cardHolderTitle: string`,
`cardHolderName: string`,
`lastDigits: string`,
`cardDescription: string`,
`cardDescriptionComment: string` | | **onCardActivatedPayload** | Data used by listener to notice when a card's status changes. | `tokenId?: string \| null`,
`status: 'activated' \| 'canceled'`
| diff --git a/android/src/main/java/com/expensify/wallet/WalletModule.kt b/android/src/main/java/com/expensify/wallet/WalletModule.kt index 3cb54f74..c7293407 100644 --- a/android/src/main/java/com/expensify/wallet/WalletModule.kt +++ b/android/src/main/java/com/expensify/wallet/WalletModule.kt @@ -324,6 +324,7 @@ class WalletModule internal constructor(context: ReactApplicationContext) : putString("requestId", requestId) putString("serverSessionId", request.serverSessionId) putString("walletId", request.walletId) + putString("tokenRequestorId", request.tokenRequestorId) putString("opaquePaymentCard", cardData.opaquePaymentCard) } diff --git a/docs/UAPP.md b/docs/UAPP.md index b0dd3201..ca5748fe 100644 --- a/docs/UAPP.md +++ b/docs/UAPP.md @@ -62,10 +62,10 @@ const status = await addCardToGoogleWallet(cardData, handlePaymentCredentials); Your backend needs to: -1. Receive the `serverSessionId`, `opaquePaymentCard`, and `walletId` +1. Receive the `serverSessionId`, `opaquePaymentCard`, `walletId`, and `tokenRequestorId` 2. Call Google's UAPP API with these parameters 3. Use Google's encryption keys to generate the Google OPC -4. Return both the TSP OPC and Google OPC to your app +4. Return both the TSP OPC and Google OPC to your app. For Visa tokenization on Fitbit, the TSP OPC must include the `tokenRequestorId`, or the request fails Example backend response: ```json @@ -86,6 +86,7 @@ Payload received when Google Wallet requests payment credentials during UAPP pro requestId: string; // Unique identifier for this request serverSessionId: string; // Session ID from Google (crucial for backend) walletId: string; // Google Wallet ID + tokenRequestorId: string | null; // Token Requestor ID of the tokenization target (needed for Visa on Fitbit) opaquePaymentCard: string; // Original OPC from TSP } ``` diff --git a/src/NativeWallet.ts b/src/NativeWallet.ts index 12f8e099..e85a0e22 100644 --- a/src/NativeWallet.ts +++ b/src/NativeWallet.ts @@ -54,6 +54,7 @@ type onPaymentCredentialsRequestPayload = { requestId: string; serverSessionId: string; walletId: string; + tokenRequestorId: string | null; opaquePaymentCard: string; }; From b4c059d6744c3e6a45a4d7d60a3e37b11f30f24a Mon Sep 17 00:00:00 2001 From: Jakub Korytko Date: Mon, 12 Oct 2026 14:09:02 +0200 Subject: [PATCH 022/118] fix: read separate push tokenize result extras when PushTokenizeResult is missing --- .../java/com/expensify/wallet/WalletModule.kt | 31 ++++++++++++++++--- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/android/src/main/java/com/expensify/wallet/WalletModule.kt b/android/src/main/java/com/expensify/wallet/WalletModule.kt index c7293407..dc9e959f 100644 --- a/android/src/main/java/com/expensify/wallet/WalletModule.kt +++ b/android/src/main/java/com/expensify/wallet/WalletModule.kt @@ -143,8 +143,27 @@ class WalletModule internal constructor(context: ReactApplicationContext) : } if (result == null) { + handlePushTokenizeResultExtras(resultCode, data, promise) + return + } + + val successfulOutcome = result.tokenizationOutcomes.firstOrNull { it.tokenResult } + if (result.cardResult || successfulOutcome != null) { + sendCardActivatedEvent("activated", successfulOutcome?.issuerTokenId) + promise?.resolve(TokenizationStatus.SUCCESS.code) + } else { + promise?.reject(E_OPERATION_FAILED, "Card not saved. Status: ${result.cardStatus}") + } + } + + // Google documents the result both as PushTokenizeResult and as separate extras, so read the extras when the object is missing + private fun handlePushTokenizeResultExtras(resultCode: Int, data: Intent?, promise: Promise?) { + val hasTokenResult = data?.hasExtra(TapAndPay.EXTRA_TOKEN_RESULT) == true + val hasCardResult = data?.hasExtra(TapAndPay.EXTRA_CARD_RESULT) == true + + if (!hasTokenResult && !hasCardResult) { if (resultCode == RESULT_OK) { - sendCardActivatedEvent("activated", null) + sendCardActivatedEvent("activated", data?.getStringExtra(TapAndPay.EXTRA_ISSUER_TOKEN_ID)) promise?.resolve(TokenizationStatus.SUCCESS.code) } else { promise?.resolve(TokenizationStatus.ERROR.code) @@ -152,12 +171,14 @@ class WalletModule internal constructor(context: ReactApplicationContext) : return } - val successfulOutcome = result.tokenizationOutcomes.firstOrNull { it.tokenResult } - if (result.cardResult || successfulOutcome != null) { - sendCardActivatedEvent("activated", successfulOutcome?.issuerTokenId) + val tokenResult = data?.getBooleanExtra(TapAndPay.EXTRA_TOKEN_RESULT, false) == true + val cardResult = data?.getBooleanExtra(TapAndPay.EXTRA_CARD_RESULT, false) == true + if (tokenResult || cardResult) { + val tokenId = if (tokenResult) data?.getStringExtra(TapAndPay.EXTRA_ISSUER_TOKEN_ID) else null + sendCardActivatedEvent("activated", tokenId) promise?.resolve(TokenizationStatus.SUCCESS.code) } else { - promise?.reject(E_OPERATION_FAILED, "Card not saved. Status: ${result.cardStatus}") + promise?.reject(E_OPERATION_FAILED, "Card not saved") } } From b6561ad8635501f4638cd0ded5cee5031dc979cd Mon Sep 17 00:00:00 2001 From: Jakub Korytko Date: Mon, 12 Oct 2026 14:09:25 +0200 Subject: [PATCH 023/118] chore: migrate deprecated TapAndPay calls to request variants --- .../java/com/expensify/wallet/WalletModule.kt | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/android/src/main/java/com/expensify/wallet/WalletModule.kt b/android/src/main/java/com/expensify/wallet/WalletModule.kt index dc9e959f..d469eb9c 100644 --- a/android/src/main/java/com/expensify/wallet/WalletModule.kt +++ b/android/src/main/java/com/expensify/wallet/WalletModule.kt @@ -32,10 +32,13 @@ import com.google.android.gms.tapandpay.TapAndPayClient import com.google.android.gms.tapandpay.issuer.GeneratePaymentCredentialsException import com.google.android.gms.tapandpay.issuer.GeneratePaymentCredentialsRequest import com.google.android.gms.tapandpay.issuer.GeneratePaymentCredentialsResponse +import com.google.android.gms.tapandpay.issuer.GetTokenStatusRequest +import com.google.android.gms.tapandpay.issuer.ListTokensRequest import com.google.android.gms.tapandpay.issuer.PaymentCredentialsGenerator import com.google.android.gms.tapandpay.issuer.PushTokenizeExtraOptions import com.google.android.gms.tapandpay.issuer.PushTokenizeRequest import com.google.android.gms.tapandpay.issuer.PushTokenizeResult +import com.google.android.gms.tapandpay.issuer.TokenizeRequest import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers @@ -227,7 +230,7 @@ class WalletModule internal constructor(context: ReactApplicationContext) : @ReactMethod override fun getCardStatusBySuffix(last4Digits: String, promise: Promise) { - tapAndPayClient.listTokens() + tapAndPayClient.listTokens(ListTokensRequest.Builder().build()) .addOnCompleteListener { task -> if (!task.isSuccessful || task.result == null) { promise.resolve(CardStatus.NOT_FOUND_IN_WALLET.code) @@ -246,7 +249,12 @@ class WalletModule internal constructor(context: ReactApplicationContext) : @ReactMethod override fun getCardStatusByIdentifier(identifier: String, tsp: String, promise: Promise) { - tapAndPayClient.getTokenStatus(getTokenServiceProvider(tsp), identifier) + tapAndPayClient.getTokenStatus( + GetTokenStatusRequest.Builder() + .setTokenServiceProvider(getTokenServiceProvider(tsp)) + .setIssuerTokenId(identifier) + .build() + ) .addOnCompleteListener { task -> if (!task.isSuccessful || task.result == null) { promise.resolve(CardStatus.NOT_FOUND_IN_WALLET.code) @@ -421,14 +429,13 @@ class WalletModule internal constructor(context: ReactApplicationContext) : val displayName = getDisplayName(data, network) pendingPushTokenizePromise = promise - tapAndPayClient.tokenize( - activity, - tokenReferenceID, - tokenServiceProvider, - displayName, - cardNetwork, - REQUEST_CODE_RESUME_TOKENIZE - ) + val tokenizeRequest = TokenizeRequest.Builder() + .setIssuerTokenId(tokenReferenceID) + .setTokenServiceProvider(tokenServiceProvider) + .setCardDisplayName(displayName) + .setCardNetwork(cardNetwork) + .build() + tapAndPayClient.tokenize(activity, tokenizeRequest, REQUEST_CODE_RESUME_TOKENIZE) } catch (e: java.lang.Exception) { pendingPushTokenizePromise = null promise.reject(e) @@ -437,7 +444,7 @@ class WalletModule internal constructor(context: ReactApplicationContext) : @ReactMethod override fun listTokens(promise: Promise) { - tapAndPayClient.listTokens() + tapAndPayClient.listTokens(ListTokensRequest.Builder().build()) .addOnCompleteListener { task -> if (!task.isSuccessful || task.result == null) { promise.resolve(Arguments.createArray()) From 63b3cfa18ae57d72ea3bbb1368ee53e96d35ca7e Mon Sep 17 00:00:00 2001 From: Jakub Korytko Date: Mon, 12 Oct 2026 14:10:35 +0200 Subject: [PATCH 024/118] feat: add hasEligibleTokenizationTarget --- README.md | 1 + .../java/com/expensify/wallet/WalletModule.kt | 20 ++++++++++++++++++ ios/RNWallet.mm | 9 ++++++++ src/NativeWallet.ts | 1 + src/index.tsx | 21 +++++++++++++++++++ 5 files changed, 52 insertions(+) diff --git a/README.md b/README.md index 5d50856d..a5c5088f 100644 --- a/README.md +++ b/README.md @@ -180,6 +180,7 @@ The library offers functions for seamless integration and use of the Apple Walle | **checkWalletAvailability** | Checks if the wallet is ready and initializes it if possible. | None | `boolean` | βœ… | βœ… | | **getCardStatusBySuffix** | Retrieves the current status of a card in the wallet. | `lastDigits: string`
(The last few digits of the card number) | `CardStatus` | βœ… | βœ… | | **getCardStatusByIdentifier** | Returns the state of a card based on a platform-specific identifier. On Android, it's `Token Reference ID` and on iOS, it's `Primary Account Identifier`. | `identifier: string`,
`tsp: string` | `CardStatus` | βœ… | βœ… | +| **hasEligibleTokenizationTarget** | Checks whether the card can still be added to a Google Wallet target (the phone or a paired wearable). Google recommends it for the "Add to Google Wallet" button when wearables are supported, because a card already on the phone can still be added to a watch. | `last4Digits: string`,
`network: string`,
`issuerName: string` | `boolean` | ❌ | βœ… | | **addCardToGoogleWallet** | Initiates native Push Provisioning flow for adding a card to the Google Wallet. Requires a handler that returns the payment credentials for the UAPP flow. [Learn more β†’](./docs/UAPP.md) | `data`: `AndroidCardData`,
`handlePaymentCredentials`: `(data: onPaymentCredentialsRequestPayload) => Promise` | `TokenizationStatus` | ❌ | βœ… | | **resumeAddCardToGoogleWallet** | Resumes the Push Provisioning flow for adding a card to the Google Wallet using existing token reference ID. | `data`: `AndroidResumeCardData` | `TokenizationStatus` | ❌ | βœ… | | **listTokens** | Lists all tokens currently stored in the Google Wallet. | None | `TokenInfo[]` | ❌ | βœ… | diff --git a/android/src/main/java/com/expensify/wallet/WalletModule.kt b/android/src/main/java/com/expensify/wallet/WalletModule.kt index d469eb9c..79365dae 100644 --- a/android/src/main/java/com/expensify/wallet/WalletModule.kt +++ b/android/src/main/java/com/expensify/wallet/WalletModule.kt @@ -33,6 +33,7 @@ import com.google.android.gms.tapandpay.issuer.GeneratePaymentCredentialsExcepti import com.google.android.gms.tapandpay.issuer.GeneratePaymentCredentialsRequest import com.google.android.gms.tapandpay.issuer.GeneratePaymentCredentialsResponse import com.google.android.gms.tapandpay.issuer.GetTokenStatusRequest +import com.google.android.gms.tapandpay.issuer.HasEligibleTokenizationTargetRequest import com.google.android.gms.tapandpay.issuer.ListTokensRequest import com.google.android.gms.tapandpay.issuer.PaymentCredentialsGenerator import com.google.android.gms.tapandpay.issuer.PushTokenizeExtraOptions @@ -271,6 +272,25 @@ class WalletModule internal constructor(context: ReactApplicationContext) : } } + @ReactMethod + override fun hasEligibleTokenizationTarget(last4Digits: String, network: String, issuerName: String, promise: Promise) { + try { + val request = HasEligibleTokenizationTargetRequest.Builder() + .setIdentifier(last4Digits) + .setNetwork(getCardNetwork(network)) + .setTokenServiceProvider(getTokenServiceProvider(network)) + .setIssuerName(issuerName) + .build() + tapAndPayClient.hasEligibleTokenizationTarget(request) + .addOnSuccessListener { promise.resolve(it) } + .addOnFailureListener { e -> + promise.reject(E_OPERATION_FAILED, "hasEligibleTokenizationTarget: ${e.localizedMessage}") + } + } catch (e: InvalidNetworkError) { + promise.reject(E_INVALID_DATA, e.message) + } + } + @ReactMethod override fun addCardToGoogleWallet( data: ReadableMap, promise: Promise diff --git a/ios/RNWallet.mm b/ios/RNWallet.mm index a17297c7..79d8b56d 100644 --- a/ios/RNWallet.mm +++ b/ios/RNWallet.mm @@ -135,6 +135,15 @@ - (void)addCardToGoogleWallet: // no-op } +- (void)hasEligibleTokenizationTarget:(NSString *)last4Digits + network:(NSString *)network + issuerName:(NSString *)issuerName + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject +{ + // no-op +} + - (void)AndroidProvidePaymentCredentials:(NSString *)requestId responseData: #ifdef RCT_NEW_ARCH_ENABLED diff --git a/src/NativeWallet.ts b/src/NativeWallet.ts index e85a0e22..e4100c74 100644 --- a/src/NativeWallet.ts +++ b/src/NativeWallet.ts @@ -90,6 +90,7 @@ export interface Spec extends TurboModule { getSecureWalletInfo(): Promise; getCardStatusBySuffix(last4Digits: string): Promise; getCardStatusByIdentifier(identifier: string, tsp: string): Promise; + hasEligibleTokenizationTarget(last4Digits: string, network: string, issuerName: string): Promise; addCardToGoogleWallet(cardData: AndroidCardData): Promise; AndroidProvidePaymentCredentials(requestId: string, responseData: AndroidPaymentCredentialsResponse): Promise; AndroidRejectPaymentCredentials(requestId: string, errorMessage: string): Promise; diff --git a/src/index.tsx b/src/index.tsx index 3e36ea17..ec3d62bb 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -106,6 +106,26 @@ async function getCardStatusByIdentifier(identifier: string, tsp: string): Promi return getCardState(tokenState); } +/** + * Checks whether the card can still be added to a Google Wallet target: the phone or a paired wearable. + * Use it to decide whether to show the "Add to Google Wallet" button when wearables are supported. + * @param last4Digits - The last 4 digits of the card number (FPAN, not the token) + * @param network - The card network, e.g. `VISA`, `MASTERCARD` + * @param issuerName - The issuer name used for matching, as in the token metadata (not the card display name) + * @returns `true` if at least one target can still receive the card + */ +async function hasEligibleTokenizationTarget(last4Digits: string, network: string, issuerName: string): Promise { + if (Platform.OS === 'ios') { + throw new Error('hasEligibleTokenizationTarget is not available on iOS'); + } + + if (!Wallet) { + return getModuleLinkingRejection(); + } + + return Wallet.hasEligibleTokenizationTarget(last4Digits, network.toUpperCase(), issuerName); +} + async function addCardToGoogleWallet( cardData: AndroidCardData, handlePaymentCredentialsGeneration: (data: onPaymentCredentialsRequestPayload) => Promise, @@ -214,6 +234,7 @@ export { getSecureWalletInfo, getCardStatusBySuffix, getCardStatusByIdentifier, + hasEligibleTokenizationTarget, addCardToGoogleWallet, resumeAddCardToGoogleWallet, listTokens, From 5c8e613086da8c316d5d30d872a5c6b8bd1cdaa3 Mon Sep 17 00:00:00 2001 From: Jakub Korytko Date: Mon, 12 Oct 2026 14:27:29 +0200 Subject: [PATCH 025/118] docs: align payload comments in UAPP.md --- docs/UAPP.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/UAPP.md b/docs/UAPP.md index ca5748fe..7f5d99ec 100644 --- a/docs/UAPP.md +++ b/docs/UAPP.md @@ -30,7 +30,7 @@ import { addCardToGoogleWallet } from '@expensify/react-native-wallet'; // Define your handler function const handlePaymentCredentials = async (data) => { const { requestId, serverSessionId, walletId, opaquePaymentCard } = data; - + // Call your backend to get encrypted credentials const response = await fetch('https://your-backend.com/api/wallet/credentials', { method: 'POST', @@ -41,9 +41,9 @@ const handlePaymentCredentials = async (data) => { walletId, }), }); - + const credentials = await response.json(); - + // Return the credentials - library will automatically send them to Google Wallet return { opaquePaymentCard: credentials.tspOpc, @@ -83,11 +83,11 @@ Payload received when Google Wallet requests payment credentials during UAPP pro ```typescript { - requestId: string; // Unique identifier for this request - serverSessionId: string; // Session ID from Google (crucial for backend) - walletId: string; // Google Wallet ID + requestId: string; // Unique identifier for this request + serverSessionId: string; // Session ID from Google (crucial for backend) + walletId: string; // Google Wallet ID tokenRequestorId: string | null; // Token Requestor ID of the tokenization target (needed for Visa on Fitbit) - opaquePaymentCard: string; // Original OPC from TSP + opaquePaymentCard: string; // Original OPC from TSP } ``` From 25154d3493183519a35103e0d9782be028f17b26 Mon Sep 17 00:00:00 2001 From: Bruno Nascimento Date: Mon, 12 Oct 2026 17:22:17 -0300 Subject: [PATCH 026/118] fix: reject when a card is already on every Apple device PassKit returns a nil enrollment controller in that case, which surfaced as a generic configuration error. --- ios/WalletManager.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ios/WalletManager.swift b/ios/WalletManager.swift index 39d104a4..b8209e1e 100644 --- a/ios/WalletManager.swift +++ b/ios/WalletManager.swift @@ -113,6 +113,12 @@ open class WalletManager: UIViewController { configuration.localizedDescription = String(card.cardDescription) if let primaryAccountIdentifier = card.primaryAccountIdentifier { configuration.primaryAccountIdentifier = primaryAccountIdentifier + if !passLibrary.canAddSecureElementPass(primaryAccountIdentifier: primaryAccountIdentifier) { + completion(.error, [ + "errorMessage": "This card has already been added to all available devices." + ]) + return + } } guard let enrollViewController = PKAddPaymentPassViewController(requestConfiguration: configuration, delegate: self) else { From 078464ef41b562ed1c356b243977297990b01f8b Mon Sep 17 00:00:00 2001 From: Bruno Nascimento Date: Mon, 12 Oct 2026 17:29:06 -0300 Subject: [PATCH 027/118] feat: tell apps which Apple device can still take the card PassKit does not report the device the user picked, so infer it from existing passes and pass it to the issuer callback. Expose canAddCardToAppleWallet so the Add button stays up until every device has the card. --- README.md | 18 +++++-- .../java/com/expensify/wallet/WalletModule.kt | 4 ++ .../expensify/wallet/NativeWalletSpec.java | 4 ++ ios/RNWallet.mm | 8 ++++ ios/WalletManager.swift | 47 ++++++++++++++++++- ios/stucts/AddPassResponse.swift | 23 +++++++-- src/NativeWallet.ts | 5 ++ src/index.tsx | 31 ++++++++++-- 8 files changed, 128 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index ee3b7fdc..9ecf8b11 100644 --- a/README.md +++ b/README.md @@ -166,9 +166,11 @@ Here you can find data elements used in the library, essential to work with Goog - **Certificates** - an array of NSData objects. Each object contains a DER encoded X.509 certificate, with the leaf first and root last. You must download the root CA to validate the entire chain. - **Encrypted Pass Data**- an encrypted JSON file containing the sensitive information needed to add a card to Apple Pay (Base 64 encoded). [More details here.](https://developer.apple.com/documentation/passkit/pkaddpaymentpassrequest/encryptedpassdata) - **Ephemeral Public Key** - a key used by elliptic curve cryptography (ECC) (Base 64 encoded). +- **Primary Account Identifier** - an opaque issuer id (also called the FPAN ID) that PassKit uses to recognize the same card across reissues and devices. Pass it in `IOSCardData` and to `canAddCardToAppleWallet`. On iOS, `getCardStatusByIdentifier` uses this same value. +- **Device type** - the device the card is being provisioned to: `phone` or `watch`. PassKit does not report the device the user selected, so the library infers it from where the card is already provisioned. When it cannot tell, the value is `unknown`. # API Reference -The library offers seven functions for seamless integration and use of the Apple Wallet and Google Wallet APIs. Additionally, it includes one listener that informs when the added card has been activated. Below, these functions are described along with the data types involved. +The library offers nine functions for seamless integration and use of the Apple Wallet and Google Wallet APIs. Additionally, it includes one listener that informs when the added card has been activated. Below, these functions are described along with the data types involved. ## Functions @@ -181,7 +183,8 @@ The library offers seven functions for seamless integration and use of the Apple | **addCardToGoogleWallet** | Initiates native Push Provisioning flow for adding a card to the Google Wallet. | `data`: `AndroidCardData` | `TokenizationStatus` | ❌ | βœ… | | **resumeAddCardToGoogleWallet** | Resumes the Push Provisioning flow for adding a card to the Google Wallet using existing token reference ID. | `data`: `AndroidResumeCardData` | `TokenizationStatus` | ❌ | βœ… | | **listTokens** | Lists all tokens currently stored in the Google Wallet. | None | `TokenInfo[]` | ❌ | βœ… | -| **addCardToAppleWallet** | Initiates native Push Provisioning flow for adding a card to the Apple Wallet. | `data`: `IOSCardData`,
`issuerEncrypt-`
`PayloadCallback: IOSIssuerCallback` | `void` | βœ… | ❌ | +| **addCardToAppleWallet** | Initiates native Push Provisioning flow for adding a card to the Apple Wallet. When `IOSCardData.primaryAccountIdentifier` is set, the device picker only lists devices that do not already have the card. The call fails with a clear error when the card is already on every device. | `data`: `IOSCardData`,
`issuerEncrypt-`
`PayloadCallback: IOSIssuerCallback` | `void` | βœ… | ❌ | +| **canAddCardToAppleWallet** | Checks whether the card can still be added to any of the user's devices (iPhone or paired Apple Watch). | `primaryAccountIdentifier: string` | `boolean` | βœ… | ❌ | ## Data Types @@ -192,9 +195,11 @@ The library offers seven functions for seamless integration and use of the Apple | **AndroidCardData** | Data related to a card that is to be added on Android platform wallets. | `network: string`,
`opaquePaymentCard: string`,
`cardHolderName: string`,
`lastDigits: string`,
`userAddress: UserAddress` | | **AndroidResumeCardData** | Simplified data structure for resuming card addition to Google Wallet using existing token reference ID. | `network: string`,
`tokenReferenceID: string`,
`cardHolderName?: string`,
`lastDigits?: string` | | **UserAddress** | Structured address used for cardholder verification. | `name: string`,
`addressOne: string`,
`addressTwo: string`,
`city: string`,
`administrativeArea: string`,
`countryCode: string`,
`postalCode: string`,
`phoneNumber: string` | -| **IOSCardData** | Data related to a card that is to be added on iOS platform. | `network: string`,
`activationData: string`,
`encryptedPassData: string`,
`ephemeralPublicKey: string`,
`cardHolderTitle: string`,
`cardHolderName: string`,
`lastDigits: string`,
`cardDescription: string`,
`cardDescriptionComment: string` | +| **IOSCardData** | Data related to a card that is to be added on iOS. Set `primaryAccountIdentifier` after the first provisioning so the next attempt targets devices that do not already have the card. | `network: string`,
`activationData: string`,
`encryptedPassData: string`,
`ephemeralPublicKey: string`,
`cardHolderTitle: string`,
`cardHolderName: string`,
`lastDigits: string`,
`cardDescription: string`,
`cardDescriptionComment: string`,
`primaryAccountIdentifier?: string` | | **onCardActivatedPayload** | Data used by listener to notice when a card's status changes. | `tokenId: string`,
`status: 'activated' \| 'canceled'`
| -| **IOSIssuerCallback** | This callback is invoked with a nonce, its signature, and a certificate array obtained from Apple. It is expected that you will forward these details to your server or the card issuer's API to securely encrypt the payload required for adding cards to the Apple Wallet. | `(nonce: string, nonceSignature: string, certificate: string[]) => IOSEncryptPayload` | +| **IOSIssuerCallback** | This callback is invoked with a nonce, its signature, a certificate array obtained from Apple, and the device the card is being added to. Forward these details to your server or the card issuer's API to securely encrypt the payload required for adding cards to the Apple Wallet. `deviceType` is a best-effort value; see `IOSDeviceType`. | `(nonce: string, nonceSignature: string, certificate: string[], deviceType: IOSDeviceType) => IOSEncryptPayload` | +| **IOSDeviceType** | Best-effort device the card is being provisioned to. PassKit does not report the selected device, so this is inferred from existing passes when `primaryAccountIdentifier` is set. `phone` means the card is already on the paired Apple Watch only. `watch` means it is already on the iPhone only. `unknown` means the card is on neither device, or no identifier was provided, so the user may still choose either device. Passes in the `deactivated` state count as not present. | `'phone' \| 'watch' \| 'unknown'` | +| **IOSAddPaymentPassData** | Data Apple returns when the user continues the provisioning flow, including the device the card is being added to. | `status: number`,
`nonce: string`,
`nonceSignature: string`,
`certificates: string[]`,
`deviceType?: string` | | **IOSEncryptPayload** | An object containing the necessary elements to complete the addition of a card to Apple Wallet. | `encryptedPassData: string`,
`activationData: string`,
`ephemeralPublicKey: string` | | **TokenInfo** | Information about a token stored in Google Wallet. | `identifier: string`,
`lastDigits: string`,
`tokenState: number` | @@ -267,6 +272,7 @@ const base64ToHex = (base64: string): string => Buffer.from(base64, "base64").to nonce: string, nonceSignature: string, certificate: string[], + deviceType: IOSDeviceType, ): Promise => { try { const payload = { @@ -275,6 +281,8 @@ const base64ToHex = (base64: string): string => Buffer.from(base64, "base64").to nonceSignature: base64ToHex(nonceSignature), wspCertificates: certificate, }, + // Map deviceType to the field your issuer expects, for example "phone" or "watch". + deviceType, serialNumber, wspId: "APPLE" as const, } @@ -301,7 +309,7 @@ The app will need to be reviewed by Google. During this process, it will need to ### iOS When implementing the In-App Push Provisioning feature in your App make sure that your app follows Apple's [branding guidelines connected to Apple Wallet](https://developer.apple.com/wallet/add-to-apple-wallet-guidelines/). Remember that you must not create your own buttons or your app could be rejected at revision. You can use the[ AddWalletButton component](#components) instead! -When the pass is already provisioned, make sure to hide this button and replace it with text like `Added to Apple Wallet`. The card is fully provisioned once it added to your main device (user's iPhone) and all linked devices (for example Apple Watch). +Keep the Add to Apple Wallet button visible while `canAddCardToAppleWallet(primaryAccountIdentifier)` returns `true`, including after the card is added to the iPhone but not yet to a paired Apple Watch. Show text like `Added to Apple Wallet` only once that call returns `false`. Pass `primaryAccountIdentifier` in `IOSCardData` so the next attempt targets the remaining device. This matches [Apple's in-app provisioning guidance](https://applepaydemo.apple.com/in-app-provisioning#3.3). Next to branding guidelines, please follow the instructions and best practices from [the In-App provisioning documentation]((#ios)) provided by Apple. diff --git a/android/src/main/java/com/expensify/wallet/WalletModule.kt b/android/src/main/java/com/expensify/wallet/WalletModule.kt index dfa1a398..152ab5e8 100644 --- a/android/src/main/java/com/expensify/wallet/WalletModule.kt +++ b/android/src/main/java/com/expensify/wallet/WalletModule.kt @@ -365,6 +365,10 @@ class WalletModule internal constructor(context: ReactApplicationContext) : // no-op } + override fun canAddCardToAppleWallet(primaryAccountIdentifier: String, promise: Promise) { + // no-op + } + override fun addListener(eventType: String?) { // no-op } diff --git a/android/src/paper/java/com/expensify/wallet/NativeWalletSpec.java b/android/src/paper/java/com/expensify/wallet/NativeWalletSpec.java index 999f0cb8..773b336f 100644 --- a/android/src/paper/java/com/expensify/wallet/NativeWalletSpec.java +++ b/android/src/paper/java/com/expensify/wallet/NativeWalletSpec.java @@ -73,6 +73,10 @@ public NativeWalletSpec(ReactApplicationContext reactContext) { @DoNotStrip public abstract void IOSHandleAddPaymentPassResponse(ReadableMap payload, Promise promise); + @ReactMethod + @DoNotStrip + public abstract void canAddCardToAppleWallet(String primaryAccountIdentifier, Promise promise); + @ReactMethod @DoNotStrip public abstract void addListener(String eventType); diff --git a/ios/RNWallet.mm b/ios/RNWallet.mm index ee1e9aca..bca330ef 100644 --- a/ios/RNWallet.mm +++ b/ios/RNWallet.mm @@ -131,6 +131,14 @@ - (instancetype)init { resolve([walletManager getCardStatusByIdentifierWithIdentifier:identifier]); } +RCT_REMAP_METHOD(canAddCardToAppleWallet, + canAddCardToAppleWallet:(NSString *)primaryAccountIdentifier + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) +{ + resolve(@([walletManager canAddCardToAppleWalletWithPrimaryAccountIdentifier:primaryAccountIdentifier])); +} + - (void)addCardToGoogleWallet: #ifdef RCT_NEW_ARCH_ENABLED (JS::NativeWallet::AndroidCardData &)cardData diff --git a/ios/WalletManager.swift b/ios/WalletManager.swift index b8209e1e..3e286bec 100644 --- a/ios/WalletManager.swift +++ b/ios/WalletManager.swift @@ -21,6 +21,8 @@ open class WalletManager: UIViewController { private var addPaymentPassCompletionHandler: (CompletionHandler)? private var addPassHandler: ((PKAddPaymentPassRequest) -> Void)? + + private var addPassTargetDevice: String? @objc public var packageName = "react-native-wallet" @@ -121,6 +123,8 @@ open class WalletManager: UIViewController { } } + let targetDevice = provisioningTargetDevice(for: card.primaryAccountIdentifier) + guard let enrollViewController = PKAddPaymentPassViewController(requestConfiguration: configuration, delegate: self) else { completion(.error, [ "errorMessage": "InApp enrollment controller configuration fails" @@ -143,6 +147,7 @@ open class WalletManager: UIViewController { self.presentAddPaymentPassCompletionHandler = nil self.addPaymentPassCompletionHandler = nil self.addPassHandler = nil + self.addPassTargetDevice = nil let errorResponse: NSDictionary = [ "errorMessage": "EnrollViewController is no longer presented." @@ -159,6 +164,7 @@ open class WalletManager: UIViewController { self.presentAddPaymentPassCompletionHandler = completion self.addPassViewController = enrollViewController + self.addPassTargetDevice = targetDevice presentingViewController.present(enrollViewController, animated: true, completion: nil) } } @@ -221,6 +227,44 @@ open class WalletManager: UIViewController { return pass.primaryAccountIdentifier == identifier as String } } + + @objc public func canAddCardToAppleWallet(primaryAccountIdentifier: NSString) -> Bool { + return passLibrary.canAddSecureElementPass(primaryAccountIdentifier: primaryAccountIdentifier as String) + } + + private func provisioningTargetDevice(for primaryAccountIdentifier: String?) -> String { + guard let primaryAccountIdentifier = primaryAccountIdentifier else { + return "unknown" + } + + let onPhone = hasProvisionedPass(identifier: primaryAccountIdentifier, in: passLibrary.passes(of: .secureElement)) + let onWatch = hasProvisionedPass(identifier: primaryAccountIdentifier, in: passLibrary.remoteSecureElementPasses) + + if onPhone && !onWatch { + return "watch" + } + if onWatch && !onPhone { + return "phone" + } + return "unknown" + } + + private func hasProvisionedPass(identifier: String, in passes: [PKPass]) -> Bool { + passes.contains { pass in + guard let securePass = pass.secureElementPass else { + return false + } + return isProvisioned(securePass, identifier: identifier) + } + } + + private func hasProvisionedPass(identifier: String, in passes: [PKSecureElementPass]) -> Bool { + passes.contains { isProvisioned($0, identifier: identifier) } + } + + private func isProvisioned(_ pass: PKSecureElementPass, identifier: String) -> Bool { + pass.primaryAccountIdentifier == identifier && pass.passActivationState != .deactivated + } private func isPassKitAvailable() -> Bool { return PKAddPaymentPassViewController.canAddPaymentPass() @@ -270,7 +314,7 @@ extension WalletManager: PKAddPaymentPassViewControllerDelegate { let stringCertificates = certificates.map { $0.base64EncodedString() as NSString } - let reqestCardData = AddPassResponse(status: .completed, nonce: stringNonce, nonceSignature: stringNonceSignature, certificates: stringCertificates) + let reqestCardData = AddPassResponse(status: .completed, nonce: stringNonce, nonceSignature: stringNonceSignature, certificates: stringCertificates, deviceType: addPassTargetDevice as NSString?) self.addPassHandler = handler // Retry the JS issuer callback if the user tries again to add a payment pass @@ -302,6 +346,7 @@ extension WalletManager: PKAddPaymentPassViewControllerDelegate { presentAddPaymentPassCompletionHandler = nil addPaymentPassCompletionHandler = nil addPassHandler = nil + addPassTargetDevice = nil let errorMessage = error?.localizedDescription ?? "" diff --git a/ios/stucts/AddPassResponse.swift b/ios/stucts/AddPassResponse.swift index 1df45b4b..327afea8 100644 --- a/ios/stucts/AddPassResponse.swift +++ b/ios/stucts/AddPassResponse.swift @@ -5,12 +5,25 @@ struct AddPassResponse { let nonce: NSString? let nonceSignature: NSString? let certificates: [NSString]? + let deviceType: NSString? + + init(status: OperationResult, nonce: NSString?, nonceSignature: NSString?, certificates: [NSString]?, deviceType: NSString? = nil) { + self.status = status + self.nonce = nonce + self.nonceSignature = nonceSignature + self.certificates = certificates + self.deviceType = deviceType + } func toNSDictionary() -> NSDictionary { if status.rawValue > 0 { - return [ + var response: [String: Any] = [ "status": status.rawValue, ] + if let deviceType = deviceType { + response["deviceType"] = deviceType + } + return response as NSDictionary } guard let nonce = nonce, @@ -19,12 +32,16 @@ struct AddPassResponse { return [:] } - return [ + var response: [String: Any] = [ "status": status.rawValue, "nonce": nonce, "nonceSignature": nonceSignature, - "certificates": certificates + "certificates": certificates, ] + if let deviceType = deviceType { + response["deviceType"] = deviceType + } + return response as NSDictionary } } diff --git a/src/NativeWallet.ts b/src/NativeWallet.ts index c0149b8a..9dad9c0c 100644 --- a/src/NativeWallet.ts +++ b/src/NativeWallet.ts @@ -55,8 +55,11 @@ type IOSAddPaymentPassData = { nonce: string; nonceSignature: string; certificates: string[]; + deviceType?: string; }; +type IOSDeviceType = 'phone' | 'watch' | 'unknown'; + type IOSEncryptPayload = { encryptedPassData: string; activationData: string; @@ -82,6 +85,7 @@ export interface Spec extends TurboModule { listTokens(): Promise; IOSPresentAddPaymentPassView(cardData: IOSCardData): Promise; IOSHandleAddPaymentPassResponse(payload: IOSEncryptPayload): Promise; + canAddCardToAppleWallet(primaryAccountIdentifier: string): Promise; addListener: (eventType: string) => void; removeListeners: (count: number) => void; } @@ -111,6 +115,7 @@ export type { onCardActivatedPayload, Platform, IOSAddPaymentPassData, + IOSDeviceType, IOSEncryptPayload, TokenizationStatus, TokenInfo, diff --git a/src/index.tsx b/src/index.tsx index d1801ffc..2cc3605d 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -12,6 +12,7 @@ import type { AndroidWalletData, onCardActivatedPayload, IOSAddPaymentPassData, + IOSDeviceType, TokenInfo, } from './NativeWallet'; import {getCardState, getTokenizationStatus} from './utils'; @@ -131,9 +132,32 @@ async function listTokens(): Promise { return Wallet.listTokens(); } +function toIOSDeviceType(deviceType: string | undefined): IOSDeviceType { + switch (deviceType) { + case 'phone': + case 'watch': + case 'unknown': + return deviceType; + default: + return 'unknown'; + } +} + +async function canAddCardToAppleWallet(primaryAccountIdentifier: string): Promise { + if (Platform.OS === 'android') { + throw new Error('canAddCardToAppleWallet is not available on Android'); + } + + if (!Wallet) { + return getModuleLinkingRejection(); + } + + return Wallet.canAddCardToAppleWallet(primaryAccountIdentifier); +} + async function addCardToAppleWallet( cardData: IOSCardData, - issuerEncryptPayloadCallback: (nonce: string, nonceSignature: string, certificate: string[]) => Promise, + issuerEncryptPayloadCallback: (nonce: string, nonceSignature: string, certificate: string[], deviceType: IOSDeviceType) => Promise, ): Promise { if (Platform.OS === 'android') { throw new Error('addCardToAppleWallet is not available on Android'); @@ -151,7 +175,7 @@ async function addCardToAppleWallet( } async function addPaymentPassToWallet(paymentPassData: IOSAddPaymentPassData): Promise { - const responseData = await issuerEncryptPayloadCallback(paymentPassData.nonce, paymentPassData.nonceSignature, paymentPassData.certificates); + const responseData = await issuerEncryptPayloadCallback(paymentPassData.nonce, paymentPassData.nonceSignature, paymentPassData.certificates, toIOSDeviceType(paymentPassData.deviceType)); const response = await wallet.IOSHandleAddPaymentPassResponse(responseData); // Response is null when a pass is successfully added to the wallet or the user cancels the process // In case the user presses the `Try again` option, new pass data is returned, and it should reenter the function @@ -164,7 +188,7 @@ async function addCardToAppleWallet( return getTokenizationStatus(status); } -export type {AndroidCardData, AndroidWalletData, CardStatus, IOSEncryptPayload, IOSCardData, IOSAddPaymentPassData, onCardActivatedPayload, TokenizationStatus, TokenInfo}; +export type {AndroidCardData, AndroidWalletData, CardStatus, IOSEncryptPayload, IOSCardData, IOSAddPaymentPassData, IOSDeviceType, onCardActivatedPayload, TokenizationStatus, TokenInfo}; export { AddToWalletButton, checkWalletAvailability, @@ -174,6 +198,7 @@ export { addCardToGoogleWallet, resumeAddCardToGoogleWallet, listTokens, + canAddCardToAppleWallet, addCardToAppleWallet, addListener, removeListener, From 32556d9dcccfe8642260c6deb7f4ac4a7e0e4487 Mon Sep 17 00:00:00 2001 From: Bruno Nascimento Date: Mon, 12 Oct 2026 17:36:20 -0300 Subject: [PATCH 028/118] chore: drop field comments that the rest of the types do not use --- README.md | 1 - src/NativeWallet.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/README.md b/README.md index 9ecf8b11..861dd3de 100644 --- a/README.md +++ b/README.md @@ -281,7 +281,6 @@ const base64ToHex = (base64: string): string => Buffer.from(base64, "base64").to nonceSignature: base64ToHex(nonceSignature), wspCertificates: certificate, }, - // Map deviceType to the field your issuer expects, for example "phone" or "watch". deviceType, serialNumber, wspId: "APPLE" as const, diff --git a/src/NativeWallet.ts b/src/NativeWallet.ts index 9dad9c0c..bc2abffc 100644 --- a/src/NativeWallet.ts +++ b/src/NativeWallet.ts @@ -41,7 +41,6 @@ type IOSCardData = { cardHolderName: string; lastDigits: string; cardDescription: string; - /** Opaque issuer id. PassKit uses it to recognize a reissued card. */ primaryAccountIdentifier?: string; }; From 934dc357250894d5e8b518d84e340ed7797aebb1 Mon Sep 17 00:00:00 2001 From: Bruno Nascimento Date: Mon, 12 Oct 2026 18:29:47 -0300 Subject: [PATCH 029/118] fix: match Apple Watch passes more reliably Ignore remote passes in the iPhone list, recover the stored account id from a unique last-digits match, and only use the all-devices check when PassKit itself refuses to present. --- README.md | 6 +++--- ios/WalletManager.swift | 43 +++++++++++++++++++++++++++++++---------- 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 861dd3de..fe28f02f 100644 --- a/README.md +++ b/README.md @@ -166,7 +166,7 @@ Here you can find data elements used in the library, essential to work with Goog - **Certificates** - an array of NSData objects. Each object contains a DER encoded X.509 certificate, with the leaf first and root last. You must download the root CA to validate the entire chain. - **Encrypted Pass Data**- an encrypted JSON file containing the sensitive information needed to add a card to Apple Pay (Base 64 encoded). [More details here.](https://developer.apple.com/documentation/passkit/pkaddpaymentpassrequest/encryptedpassdata) - **Ephemeral Public Key** - a key used by elliptic curve cryptography (ECC) (Base 64 encoded). -- **Primary Account Identifier** - an opaque issuer id (also called the FPAN ID) that PassKit uses to recognize the same card across reissues and devices. Pass it in `IOSCardData` and to `canAddCardToAppleWallet`. On iOS, `getCardStatusByIdentifier` uses this same value. +- **Primary Account Identifier** - an opaque issuer id (also called the FPAN ID) that PassKit uses to recognize the same card across reissues and devices. Pass it in `IOSCardData` and to `canAddCardToAppleWallet`. On iOS, `getCardStatusByIdentifier` uses this same value. If no pass matches the given id exactly, the library uses the id of the only pass with the same last digits. - **Device type** - the device the card is being provisioned to: `phone` or `watch`. PassKit does not report the device the user selected, so the library infers it from where the card is already provisioned. When it cannot tell, the value is `unknown`. # API Reference @@ -183,7 +183,7 @@ The library offers nine functions for seamless integration and use of the Apple | **addCardToGoogleWallet** | Initiates native Push Provisioning flow for adding a card to the Google Wallet. | `data`: `AndroidCardData` | `TokenizationStatus` | ❌ | βœ… | | **resumeAddCardToGoogleWallet** | Resumes the Push Provisioning flow for adding a card to the Google Wallet using existing token reference ID. | `data`: `AndroidResumeCardData` | `TokenizationStatus` | ❌ | βœ… | | **listTokens** | Lists all tokens currently stored in the Google Wallet. | None | `TokenInfo[]` | ❌ | βœ… | -| **addCardToAppleWallet** | Initiates native Push Provisioning flow for adding a card to the Apple Wallet. When `IOSCardData.primaryAccountIdentifier` is set, the device picker only lists devices that do not already have the card. The call fails with a clear error when the card is already on every device. | `data`: `IOSCardData`,
`issuerEncrypt-`
`PayloadCallback: IOSIssuerCallback` | `void` | βœ… | ❌ | +| **addCardToAppleWallet** | Initiates native Push Provisioning flow for adding a card to the Apple Wallet. When `IOSCardData.primaryAccountIdentifier` is set, the device picker only lists devices that do not already have the card. If PassKit cannot present the flow because the card is already on every device, the call fails with a clear error. | `data`: `IOSCardData`,
`issuerEncrypt-`
`PayloadCallback: IOSIssuerCallback` | `void` | βœ… | ❌ | | **canAddCardToAppleWallet** | Checks whether the card can still be added to any of the user's devices (iPhone or paired Apple Watch). | `primaryAccountIdentifier: string` | `boolean` | βœ… | ❌ | @@ -198,7 +198,7 @@ The library offers nine functions for seamless integration and use of the Apple | **IOSCardData** | Data related to a card that is to be added on iOS. Set `primaryAccountIdentifier` after the first provisioning so the next attempt targets devices that do not already have the card. | `network: string`,
`activationData: string`,
`encryptedPassData: string`,
`ephemeralPublicKey: string`,
`cardHolderTitle: string`,
`cardHolderName: string`,
`lastDigits: string`,
`cardDescription: string`,
`cardDescriptionComment: string`,
`primaryAccountIdentifier?: string` | | **onCardActivatedPayload** | Data used by listener to notice when a card's status changes. | `tokenId: string`,
`status: 'activated' \| 'canceled'`
| | **IOSIssuerCallback** | This callback is invoked with a nonce, its signature, a certificate array obtained from Apple, and the device the card is being added to. Forward these details to your server or the card issuer's API to securely encrypt the payload required for adding cards to the Apple Wallet. `deviceType` is a best-effort value; see `IOSDeviceType`. | `(nonce: string, nonceSignature: string, certificate: string[], deviceType: IOSDeviceType) => IOSEncryptPayload` | -| **IOSDeviceType** | Best-effort device the card is being provisioned to. PassKit does not report the selected device, so this is inferred from existing passes when `primaryAccountIdentifier` is set. `phone` means the card is already on the paired Apple Watch only. `watch` means it is already on the iPhone only. `unknown` means the card is on neither device, or no identifier was provided, so the user may still choose either device. Passes in the `deactivated` state count as not present. | `'phone' \| 'watch' \| 'unknown'` | +| **IOSDeviceType** | Best-effort device the card is being provisioned to. PassKit does not report the selected device, so this is inferred from existing passes when `primaryAccountIdentifier` is set. If no pass matches that id exactly, the library uses the id of the only pass with the same last digits. Passes on a paired Apple Watch are never counted as iPhone passes. `phone` means the card is already on the paired Apple Watch only. `watch` means it is already on the iPhone only. `unknown` means the card is on neither device, or no identifier was provided, so the user may still choose either device. Passes in the `deactivated` state count as not present. | `'phone' \| 'watch' \| 'unknown'` | | **IOSAddPaymentPassData** | Data Apple returns when the user continues the provisioning flow, including the device the card is being added to. | `status: number`,
`nonce: string`,
`nonceSignature: string`,
`certificates: string[]`,
`deviceType?: string` | | **IOSEncryptPayload** | An object containing the necessary elements to complete the addition of a card to Apple Wallet. | `encryptedPassData: string`,
`activationData: string`,
`ephemeralPublicKey: string` | | **TokenInfo** | Information about a token stored in Google Wallet. | `identifier: string`,
`lastDigits: string`,
`tokenState: number` | diff --git a/ios/WalletManager.swift b/ios/WalletManager.swift index 3e286bec..d5134a94 100644 --- a/ios/WalletManager.swift +++ b/ios/WalletManager.swift @@ -113,21 +113,23 @@ open class WalletManager: UIViewController { configuration.cardholderName = card.cardHolderName configuration.primaryAccountSuffix = card.lastDigits configuration.localizedDescription = String(card.cardDescription) - if let primaryAccountIdentifier = card.primaryAccountIdentifier { + let primaryAccountIdentifier = card.primaryAccountIdentifier.map { + resolvedPrimaryAccountIdentifier($0, lastDigits: card.lastDigits) + } + if let primaryAccountIdentifier = primaryAccountIdentifier { configuration.primaryAccountIdentifier = primaryAccountIdentifier - if !passLibrary.canAddSecureElementPass(primaryAccountIdentifier: primaryAccountIdentifier) { - completion(.error, [ - "errorMessage": "This card has already been added to all available devices." - ]) - return - } } - let targetDevice = provisioningTargetDevice(for: card.primaryAccountIdentifier) + let targetDevice = provisioningTargetDevice(for: primaryAccountIdentifier) guard let enrollViewController = PKAddPaymentPassViewController(requestConfiguration: configuration, delegate: self) else { + let alreadyOnEveryDevice = primaryAccountIdentifier.map { + !passLibrary.canAddSecureElementPass(primaryAccountIdentifier: $0) + } ?? false completion(.error, [ - "errorMessage": "InApp enrollment controller configuration fails" + "errorMessage": alreadyOnEveryDevice + ? "This card has already been added to all available devices." + : "InApp enrollment controller configuration fails" ]) return } @@ -249,9 +251,30 @@ open class WalletManager: UIViewController { return "unknown" } + private func resolvedPrimaryAccountIdentifier(_ identifier: String, lastDigits: String) -> String { + let passes = allSecureElementPasses() + if passes.contains(where: { $0.primaryAccountIdentifier == identifier }) { + return identifier + } + + let suffixMatches = passes.filter { $0.primaryAccountNumberSuffix.hasSuffix(lastDigits) } + let identifiers = Set(suffixMatches.map(\.primaryAccountIdentifier)) + if identifiers.count == 1, let resolvedIdentifier = identifiers.first { + return resolvedIdentifier + } + return identifier + } + + private func allSecureElementPasses() -> [PKSecureElementPass] { + let localPasses = passLibrary.passes(of: .secureElement) + .filter { !$0.isRemotePass } + .compactMap(\.secureElementPass) + return localPasses + passLibrary.remoteSecureElementPasses + } + private func hasProvisionedPass(identifier: String, in passes: [PKPass]) -> Bool { passes.contains { pass in - guard let securePass = pass.secureElementPass else { + guard !pass.isRemotePass, let securePass = pass.secureElementPass else { return false } return isProvisioned(securePass, identifier: identifier) From 229f4c6899fe39cadd4bc05fc6ce7901d068a431 Mon Sep 17 00:00:00 2001 From: Bruno Nascimento Date: Mon, 12 Oct 2026 18:48:48 -0300 Subject: [PATCH 030/118] feat: list and activate Apple Wallet passes Apps can read each pass's activation state on the iPhone and a paired Watch, then finish verification without leaving the app. --- README.md | 8 +- .../java/com/expensify/wallet/WalletModule.kt | 8 + .../expensify/wallet/NativeWalletSpec.java | 8 + ios/RNWallet.mm | 59 +++++++ ios/WalletManager.swift | 144 ++++++++++++++++++ src/NativeWallet.ts | 20 +++ src/index.tsx | 57 ++++++- 7 files changed, 301 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ee3b7fdc..93944041 100644 --- a/README.md +++ b/README.md @@ -168,7 +168,7 @@ Here you can find data elements used in the library, essential to work with Goog - **Ephemeral Public Key** - a key used by elliptic curve cryptography (ECC) (Base 64 encoded). # API Reference -The library offers seven functions for seamless integration and use of the Apple Wallet and Google Wallet APIs. Additionally, it includes one listener that informs when the added card has been activated. Below, these functions are described along with the data types involved. +The library offers ten functions for seamless integration and use of the Apple Wallet and Google Wallet APIs. Additionally, it includes one listener that informs when the added card has been activated. Below, these functions are described along with the data types involved. ## Functions @@ -181,7 +181,9 @@ The library offers seven functions for seamless integration and use of the Apple | **addCardToGoogleWallet** | Initiates native Push Provisioning flow for adding a card to the Google Wallet. | `data`: `AndroidCardData` | `TokenizationStatus` | ❌ | βœ… | | **resumeAddCardToGoogleWallet** | Resumes the Push Provisioning flow for adding a card to the Google Wallet using existing token reference ID. | `data`: `AndroidResumeCardData` | `TokenizationStatus` | ❌ | βœ… | | **listTokens** | Lists all tokens currently stored in the Google Wallet. | None | `TokenInfo[]` | ❌ | βœ… | +| **listAppleWalletPasses** | Lists Apple Wallet Secure Element passes available to the app, including activation state and whether the pass is remote (for example on Apple Watch). Filter the result for `activationState === 'requireActivation'` or `activationState === 'pending'` to build a pending-cards UI. | None | `IOSWalletPass[]` | βœ… | ❌ | | **addCardToAppleWallet** | Initiates native Push Provisioning flow for adding a card to the Apple Wallet. | `data`: `IOSCardData`,
`issuerEncrypt-`
`PayloadCallback: IOSIssuerCallback` | `void` | βœ… | ❌ | +| **activateAppleWalletPass** | Activates an existing Secure Element pass in Apple Wallet using activation data from your issuer backend. Best used for passes in the `requireActivation` state. | `data`: `IOSPassActivationData` | `boolean` | βœ… | ❌ | ## Data Types @@ -196,13 +198,15 @@ The library offers seven functions for seamless integration and use of the Apple | **onCardActivatedPayload** | Data used by listener to notice when a card's status changes. | `tokenId: string`,
`status: 'activated' \| 'canceled'`
| | **IOSIssuerCallback** | This callback is invoked with a nonce, its signature, and a certificate array obtained from Apple. It is expected that you will forward these details to your server or the card issuer's API to securely encrypt the payload required for adding cards to the Apple Wallet. | `(nonce: string, nonceSignature: string, certificate: string[]) => IOSEncryptPayload` | | **IOSEncryptPayload** | An object containing the necessary elements to complete the addition of a card to Apple Wallet. | `encryptedPassData: string`,
`activationData: string`,
`ephemeralPublicKey: string` | +| **IOSPassActivationData** | Identifies an existing Apple Wallet Secure Element pass and provides the activation payload returned by your issuer backend. `activationData` must be Base 64 encoded. | `passTypeIdentifier: string`,
`serialNumber: string`,
`activationData: string`,
`isRemote: boolean` | +| **IOSWalletPass** | Information about an Apple Wallet Secure Element pass currently available on the device or a paired remote device. | `passTypeIdentifier: string`,
`serialNumber: string`,
`primaryAccountIdentifier?: string`,
`lastDigits?: string`,
`activationState: CardStatus`,
`isRemote: boolean` | | **TokenInfo** | Information about a token stored in Google Wallet. | `identifier: string`,
`lastDigits: string`,
`tokenState: number` | ## Card Status | Type | Possible Values | |------|-----------------| -| **CardStatus** | `not found`, `active`, `requireAuthorization`, `pending`, `suspended`, `deactivated` | +| **CardStatus** | `not found`, `active`, `requireActivation`, `pending`, `suspended`, `deactivated` | ## Listeners diff --git a/android/src/main/java/com/expensify/wallet/WalletModule.kt b/android/src/main/java/com/expensify/wallet/WalletModule.kt index dfa1a398..366f86df 100644 --- a/android/src/main/java/com/expensify/wallet/WalletModule.kt +++ b/android/src/main/java/com/expensify/wallet/WalletModule.kt @@ -365,6 +365,14 @@ class WalletModule internal constructor(context: ReactApplicationContext) : // no-op } + override fun listAppleWalletPasses(promise: Promise) { + // no-op + } + + override fun activateAppleWalletPass(passData: ReadableMap?, promise: Promise?) { + // no-op + } + override fun addListener(eventType: String?) { // no-op } diff --git a/android/src/paper/java/com/expensify/wallet/NativeWalletSpec.java b/android/src/paper/java/com/expensify/wallet/NativeWalletSpec.java index 999f0cb8..551dd961 100644 --- a/android/src/paper/java/com/expensify/wallet/NativeWalletSpec.java +++ b/android/src/paper/java/com/expensify/wallet/NativeWalletSpec.java @@ -65,6 +65,10 @@ public NativeWalletSpec(ReactApplicationContext reactContext) { @DoNotStrip public abstract void listTokens(Promise promise); + @ReactMethod + @DoNotStrip + public abstract void listAppleWalletPasses(Promise promise); + @ReactMethod @DoNotStrip public abstract void IOSPresentAddPaymentPassView(ReadableMap cardData, Promise promise); @@ -73,6 +77,10 @@ public NativeWalletSpec(ReactApplicationContext reactContext) { @DoNotStrip public abstract void IOSHandleAddPaymentPassResponse(ReadableMap payload, Promise promise); + @ReactMethod + @DoNotStrip + public abstract void activateAppleWalletPass(ReadableMap passData, Promise promise); + @ReactMethod @DoNotStrip public abstract void addListener(String eventType); diff --git a/ios/RNWallet.mm b/ios/RNWallet.mm index 4981b78a..50903235 100644 --- a/ios/RNWallet.mm +++ b/ios/RNWallet.mm @@ -123,6 +123,51 @@ - (instancetype)init { resolve([walletManager getCardStatusByIdentifierWithIdentifier:identifier]); } +RCT_REMAP_METHOD(listAppleWalletPasses, + listAppleWalletPasses:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) +{ + resolve([walletManager listAppleWalletPasses]); +} + +RCT_REMAP_METHOD(activateAppleWalletPass, +#ifdef RCT_NEW_ARCH_ENABLED + activateAppleWalletPass:(JS::NativeWallet::IOSPassActivationData &)passData +#else + activateAppleWalletPass:(NSDictionary *)passData +#endif + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) +{ + @try { + NSDictionary *passDataDict = nil; + +#ifdef RCT_NEW_ARCH_ENABLED + passDataDict = @{ + @"passTypeIdentifier": [self safeString:passData.passTypeIdentifier()], + @"serialNumber": [self safeString:passData.serialNumber()], + @"activationData": [self safeString:passData.activationData()], + @"isRemote": @(passData.isRemote()), + }; +#else + id rawIsRemote = passData[@"isRemote"]; + BOOL isRemote = [rawIsRemote isKindOfClass:[NSNumber class]] && [rawIsRemote boolValue]; + passDataDict = @{ + @"passTypeIdentifier": [self safeString:passData[@"passTypeIdentifier"]], + @"serialNumber": [self safeString:passData[@"serialNumber"]], + @"activationData": [self safeString:passData[@"activationData"]], + @"isRemote": @(isRemote), + }; +#endif + + [self->walletManager activateAppleWalletPassWithPassData:passDataDict completion:^(OperationResult result, NSDictionary* data) { + [self handleBooleanWalletResponse:result data:data completedBlock:resolve errorPrefix:@"activate_card_failed" defaultErrorMessage:@"Failed to activate the Apple Wallet pass" rejecter:reject]; + }]; + } @catch (NSException *exception) { + [self rejectWithErrorType:@"activate_card_failed" code:500 description:exception.reason ?: @"An unexpected error occurred." rejecter:reject]; + } +} + - (void)addCardToGoogleWallet: #ifdef RCT_NEW_ARCH_ENABLED (JS::NativeWallet::AndroidCardData &)cardData @@ -175,6 +220,20 @@ - (void)handleWalletResponse:(OperationResult)result } } +- (void)handleBooleanWalletResponse:(OperationResult)result + data:(NSDictionary *)data + completedBlock:(RCTPromiseResolveBlock)resolve + errorPrefix:(NSString *)errorPrefix + defaultErrorMessage:(NSString *)defaultErrorMsg + rejecter:(RCTPromiseRejectBlock)reject { + if (result < 3) { + resolve(@([data[@"success"] boolValue])); + } else { + NSString *errorMessage = data[@"errorMessage"] ?: defaultErrorMsg ?: @"Operation failed"; + [self rejectWithErrorType:errorPrefix code:1001 description:errorMessage rejecter:reject]; + } +} + - (NSString *)safeString:(NSString *)value { return value ?: @""; } diff --git a/ios/WalletManager.swift b/ios/WalletManager.swift index 6b3e4c35..c289c7c3 100644 --- a/ios/WalletManager.swift +++ b/ios/WalletManager.swift @@ -212,10 +212,154 @@ open class WalletManager: UIViewController { return pass.primaryAccountIdentifier == identifier as String } } + + @objc public func listAppleWalletPasses() -> [NSDictionary] { + let localPasses = passLibrary.passes(of: .secureElement).compactMap { pass -> NSDictionary? in + guard pass.isRemotePass == false, + let secureElementPass = pass.secureElementPass else { + return nil + } + + return makeAppleWalletPassDictionary( + passTypeIdentifier: pass.passTypeIdentifier, + serialNumber: pass.serialNumber, + primaryAccountIdentifier: secureElementPass.primaryAccountIdentifier, + lastDigits: secureElementPass.primaryAccountNumberSuffix, + activationState: secureElementPass.passActivationState.rawValue, + isRemote: pass.isRemotePass + ) + } + + let remotePasses = passLibrary.remoteSecureElementPasses.map { pass in + makeAppleWalletPassDictionary( + passTypeIdentifier: pass.passTypeIdentifier, + serialNumber: pass.serialNumber, + primaryAccountIdentifier: pass.primaryAccountIdentifier, + lastDigits: pass.primaryAccountNumberSuffix, + activationState: pass.passActivationState.rawValue, + isRemote: true + ) + } + + return localPasses + remotePasses + } + + @objc public func activateAppleWalletPass(passData: NSDictionary, completion: @escaping CompletionHandler) { + guard passLibrary.isSecureElementPassActivationAvailable else { + completion(.error, [ + "errorMessage": "Secure Element pass activation is not available for this device" + ]) + return + } + + guard + let passTypeIdentifier = passData["passTypeIdentifier"] as? String, + !passTypeIdentifier.isEmpty, + let serialNumber = passData["serialNumber"] as? String, + !serialNumber.isEmpty, + let activationDataString = passData["activationData"] as? String, + !activationDataString.isEmpty + else { + completion(.error, [ + "errorMessage": "Invalid pass activation data" + ]) + return + } + + let isRemote = passData["isRemote"] as? Bool + + guard let activationData = Data(base64Encoded: activationDataString) else { + completion(.error, [ + "errorMessage": "activationData must be a valid base64 encoded string" + ]) + return + } + + guard let secureElementPass = findSecureElementPass( + passTypeIdentifier: passTypeIdentifier, + serialNumber: serialNumber, + isRemote: isRemote + ) else { + completion(.error, [ + "errorMessage": "Secure Element pass not found" + ]) + return + } + + if secureElementPass.passActivationState == .activated { + completion(.completed, ["success": true]) + return + } + + guard secureElementPass.passActivationState == .requiresActivation else { + completion(.error, [ + "errorMessage": "Secure Element pass is not eligible for activation. Current state: \(secureElementPass.passActivationState.rawValue)" + ]) + return + } + + passLibrary.activate(secureElementPass, activationData: activationData) { success, error in + if let error = error { + completion(.error, [ + "errorMessage": error.localizedDescription + ]) + return + } + + completion(.completed, ["success": success]) + } + } private func isPassKitAvailable() -> Bool { return PKAddPaymentPassViewController.canAddPaymentPass() } + + private func findSecureElementPass(passTypeIdentifier: String, serialNumber: String, isRemote: Bool?) -> PKSecureElementPass? { + if isRemote == true { + return passLibrary.remoteSecureElementPasses.first { pass in + pass.passTypeIdentifier == passTypeIdentifier && pass.serialNumber == serialNumber + } + } + + if let pass = passLibrary.pass(withPassTypeIdentifier: passTypeIdentifier, serialNumber: serialNumber), + pass.isRemotePass == false { + return pass.secureElementPass + } + + if isRemote == false { + return nil + } + + return passLibrary.remoteSecureElementPasses.first { pass in + pass.passTypeIdentifier == passTypeIdentifier && pass.serialNumber == serialNumber + } + } + + private func makeAppleWalletPassDictionary( + passTypeIdentifier: String, + serialNumber: String, + primaryAccountIdentifier: String?, + lastDigits: String?, + activationState: Int, + isRemote: Bool + ) -> NSDictionary { + let result = NSMutableDictionary(dictionary: [ + "passTypeIdentifier": passTypeIdentifier, + "serialNumber": serialNumber, + "activationState": activationState, + "isRemote": isRemote + ]) + + if let primaryAccountIdentifier = primaryAccountIdentifier, !primaryAccountIdentifier.isEmpty { + result["primaryAccountIdentifier"] = primaryAccountIdentifier + } + + if let lastDigits = lastDigits, !lastDigits.isEmpty { + result["lastDigits"] = lastDigits + } + + return result + } private func hideModal() { guard let enrollVC = addPassViewController else { diff --git a/src/NativeWallet.ts b/src/NativeWallet.ts index f15df6e9..0707c2c1 100644 --- a/src/NativeWallet.ts +++ b/src/NativeWallet.ts @@ -61,6 +61,22 @@ type IOSEncryptPayload = { ephemeralPublicKey: string; }; +type IOSPassActivationData = { + passTypeIdentifier: string; + serialNumber: string; + activationData: string; + isRemote: boolean; +}; + +type IOSWalletPassNative = { + passTypeIdentifier: string; + serialNumber: string; + primaryAccountIdentifier?: string; + lastDigits?: string; + activationState: number; + isRemote: boolean; +}; + type TokenizationStatus = 'canceled' | 'success' | 'error'; type TokenInfo = { @@ -78,8 +94,10 @@ export interface Spec extends TurboModule { addCardToGoogleWallet(cardData: AndroidCardData): Promise; resumeAddCardToGoogleWallet(cardData: AndroidResumeCardData): Promise; listTokens(): Promise; + listAppleWalletPasses(): Promise; IOSPresentAddPaymentPassView(cardData: IOSCardData): Promise; IOSHandleAddPaymentPassResponse(payload: IOSEncryptPayload): Promise; + activateAppleWalletPass(passData: IOSPassActivationData): Promise; addListener: (eventType: string) => void; removeListeners: (count: number) => void; } @@ -110,6 +128,8 @@ export type { Platform, IOSAddPaymentPassData, IOSEncryptPayload, + IOSPassActivationData, + IOSWalletPassNative, TokenizationStatus, TokenInfo, }; diff --git a/src/index.tsx b/src/index.tsx index d1801ffc..8268e34d 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -12,11 +12,22 @@ import type { AndroidWalletData, onCardActivatedPayload, IOSAddPaymentPassData, + IOSPassActivationData, + IOSWalletPassNative, TokenInfo, } from './NativeWallet'; import {getCardState, getTokenizationStatus} from './utils'; import AddToWalletButton from './AddToWalletButton'; +type IOSWalletPass = { + passTypeIdentifier: string; + serialNumber: string; + primaryAccountIdentifier?: string; + lastDigits?: string; + activationState: CardStatus; + isRemote: boolean; +}; + function getModuleLinkingRejection() { return Promise.reject(new Error(`Failed to load Wallet module, make sure to link ${PACKAGE_NAME} correctly`)); } @@ -131,6 +142,24 @@ async function listTokens(): Promise { return Wallet.listTokens(); } +async function listAppleWalletPasses(): Promise { + if (Platform.OS === 'android') { + return Promise.resolve([]); + } + + if (!Wallet) { + return getModuleLinkingRejection(); + } + + const passes = await Wallet.listAppleWalletPasses(); + return passes.map((pass: IOSWalletPassNative) => ({ + ...pass, + primaryAccountIdentifier: pass.primaryAccountIdentifier || undefined, + lastDigits: pass.lastDigits || undefined, + activationState: getCardState(pass.activationState), + })); +} + async function addCardToAppleWallet( cardData: IOSCardData, issuerEncryptPayloadCallback: (nonce: string, nonceSignature: string, certificate: string[]) => Promise, @@ -164,7 +193,31 @@ async function addCardToAppleWallet( return getTokenizationStatus(status); } -export type {AndroidCardData, AndroidWalletData, CardStatus, IOSEncryptPayload, IOSCardData, IOSAddPaymentPassData, onCardActivatedPayload, TokenizationStatus, TokenInfo}; +async function activateAppleWalletPass(passData: IOSPassActivationData): Promise { + if (Platform.OS === 'android') { + throw new Error('activateAppleWalletPass is not available on Android'); + } + + if (!Wallet) { + return getModuleLinkingRejection(); + } + + return Wallet.activateAppleWalletPass(passData); +} + +export type { + AndroidCardData, + AndroidWalletData, + CardStatus, + IOSEncryptPayload, + IOSCardData, + IOSAddPaymentPassData, + IOSPassActivationData, + IOSWalletPass, + onCardActivatedPayload, + TokenizationStatus, + TokenInfo, +}; export { AddToWalletButton, checkWalletAvailability, @@ -174,7 +227,9 @@ export { addCardToGoogleWallet, resumeAddCardToGoogleWallet, listTokens, + listAppleWalletPasses, addCardToAppleWallet, + activateAppleWalletPass, addListener, removeListener, }; From 0da090c4d006b82b26a1fa4b04fcd85821938f7e Mon Sep 17 00:00:00 2001 From: Jakub Korytko Date: Tue, 13 Oct 2026 13:10:21 +0200 Subject: [PATCH 031/118] refactor: use withTimeoutOrNull for payment credentials timeout --- .../java/com/expensify/wallet/WalletModule.kt | 44 +++++++++++-------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/android/src/main/java/com/expensify/wallet/WalletModule.kt b/android/src/main/java/com/expensify/wallet/WalletModule.kt index 79365dae..a46b4909 100644 --- a/android/src/main/java/com/expensify/wallet/WalletModule.kt +++ b/android/src/main/java/com/expensify/wallet/WalletModule.kt @@ -5,8 +5,6 @@ import android.app.Activity.RESULT_CANCELED import android.app.Activity.RESULT_OK import android.content.ActivityNotFoundException import android.content.Intent -import android.os.Handler -import android.os.Looper import androidx.core.content.IntentCompat import com.expensify.wallet.Utils.getAsyncResult import com.expensify.wallet.Utils.toCardData @@ -40,12 +38,14 @@ import com.google.android.gms.tapandpay.issuer.PushTokenizeExtraOptions import com.google.android.gms.tapandpay.issuer.PushTokenizeRequest import com.google.android.gms.tapandpay.issuer.PushTokenizeResult import com.google.android.gms.tapandpay.issuer.TokenizeRequest +import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel +import kotlinx.coroutines.withTimeoutOrNull import java.nio.charset.Charset import java.util.Locale import java.util.UUID @@ -78,8 +78,7 @@ class WalletModule internal constructor(context: ReactApplicationContext) : private var pendingPushTokenizePromise: Promise? = null private val moduleScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) - private val pendingProvisioningFutures = ConcurrentHashMap>() - private val mainHandler = Handler(Looper.getMainLooper()) + private val pendingPaymentCredentials = ConcurrentHashMap>() override fun initialize() { super.initialize() @@ -90,9 +89,7 @@ class WalletModule internal constructor(context: ReactApplicationContext) : super.invalidate() moduleScope.cancel() reactApplicationContext.removeActivityEventListener(cardListener) - mainHandler.removeCallbacksAndMessages(null) - pendingProvisioningFutures.values.forEach { it.completeExceptionally(paymentCredentialsError()) } - pendingProvisioningFutures.clear() + pendingPaymentCredentials.clear() } private val cardListener = object : ActivityEventListener { @@ -362,12 +359,21 @@ class WalletModule internal constructor(context: ReactApplicationContext) : } val future = CompletableFuture() + val credentials = CompletableDeferred() val requestId = UUID.randomUUID().toString() - pendingProvisioningFutures[requestId] = future - mainHandler.postDelayed({ - pendingProvisioningFutures.remove(requestId) - ?.completeExceptionally(paymentCredentialsError()) - }, PAYMENT_CREDENTIALS_TIMEOUT_MS) + pendingPaymentCredentials[requestId] = credentials + moduleScope.launch { + // null on timeout, on reject, or when invalidate() cancels the scope + val response = runCatching { + withTimeoutOrNull(PAYMENT_CREDENTIALS_TIMEOUT_MS) { credentials.await() } + }.getOrNull() + pendingPaymentCredentials.remove(requestId) + if (response != null) { + future.complete(response) + } else { + future.completeExceptionally(paymentCredentialsError()) + } + } val params = Arguments.createMap().apply { putString("requestId", requestId) @@ -391,8 +397,8 @@ class WalletModule internal constructor(context: ReactApplicationContext) : @ReactMethod override fun AndroidProvidePaymentCredentials(requestId: String, responseData: ReadableMap, promise: Promise) { - val future = pendingProvisioningFutures.remove(requestId) - if (future == null) { + val credentials = pendingPaymentCredentials.remove(requestId) + if (credentials == null) { promise.reject(E_OPERATION_FAILED, "Request ID not found or timed out") return } @@ -412,23 +418,23 @@ class WalletModule internal constructor(context: ReactApplicationContext) : .setOpaquePaymentCard(tspOpc.toByteArray(Charsets.UTF_8)) .build() - future.complete(response) + credentials.complete(response) promise.resolve(true) } catch (e: Exception) { - future.completeExceptionally(paymentCredentialsError()) + credentials.completeExceptionally(paymentCredentialsError()) promise.reject(E_OPERATION_FAILED, e.message) } } @ReactMethod override fun AndroidRejectPaymentCredentials(requestId: String, errorMessage: String, promise: Promise) { - val future = pendingProvisioningFutures.remove(requestId) - if (future == null) { + val credentials = pendingPaymentCredentials.remove(requestId) + if (credentials == null) { promise.reject(E_OPERATION_FAILED, "Request ID not found or timed out") return } - future.completeExceptionally(paymentCredentialsError()) + credentials.completeExceptionally(paymentCredentialsError()) promise.resolve(true) } From d3208ec5c9787d3d0ba892d9d0f9d81cb0ce4f8b Mon Sep 17 00:00:00 2001 From: Jakub Korytko Date: Tue, 13 Oct 2026 14:54:18 +0200 Subject: [PATCH 032/118] fix: start payment credentials coroutine undispatched --- android/src/main/java/com/expensify/wallet/WalletModule.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/com/expensify/wallet/WalletModule.kt b/android/src/main/java/com/expensify/wallet/WalletModule.kt index a46b4909..a77dee0a 100644 --- a/android/src/main/java/com/expensify/wallet/WalletModule.kt +++ b/android/src/main/java/com/expensify/wallet/WalletModule.kt @@ -40,6 +40,7 @@ import com.google.android.gms.tapandpay.issuer.PushTokenizeResult import com.google.android.gms.tapandpay.issuer.TokenizeRequest import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -362,7 +363,7 @@ class WalletModule internal constructor(context: ReactApplicationContext) : val credentials = CompletableDeferred() val requestId = UUID.randomUUID().toString() pendingPaymentCredentials[requestId] = credentials - moduleScope.launch { + moduleScope.launch(start = CoroutineStart.UNDISPATCHED) { // null on timeout, on reject, or when invalidate() cancels the scope val response = runCatching { withTimeoutOrNull(PAYMENT_CREDENTIALS_TIMEOUT_MS) { credentials.await() } From 2cc9c2a00b7c9df7cf60b3d86eefc46b462d4caf Mon Sep 17 00:00:00 2001 From: Jakub Korytko Date: Fri, 16 Oct 2026 14:03:03 +0200 Subject: [PATCH 033/118] docs: apply review suggestions --- README.md | 2 +- docs/UAPP.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a5c5088f..7ad2db33 100644 --- a/README.md +++ b/README.md @@ -217,7 +217,7 @@ When set to `true`, this flag informs Google Wallet that the card being provisio #### `isBounceProvisioned?: boolean` -Indicates whether the card has been provisioned through "bounce provisioning." Bounce provisioning is a two-step provisioning flow where: +Indicates whether the card has been provisioned through "bounce provisioning". Bounce provisioning is a two-step provisioning flow where: 1. The user initially provisions a card directly through Google Wallet (outside your app) 2. Your app later "bounces" or redirects the user back to complete additional verification or setup steps diff --git a/docs/UAPP.md b/docs/UAPP.md index 7f5d99ec..ffb22dad 100644 --- a/docs/UAPP.md +++ b/docs/UAPP.md @@ -7,7 +7,7 @@ UAPP (Unified Android Push Provisioning) is an enhanced security flow for Google This library handles the UAPP flow automatically. You just need to set up a handler that communicates with your backend to retrieve the encrypted credentials. > [!IMPORTANT] -> Please familiarize yourself with [official Google docs ](https://developers.google.com/pay/issuers/apis/push-provisioning/android)before starting the implementation! +> Please familiarize yourself with [official Google docs](https://developers.google.com/pay/issuers/apis/push-provisioning/android) before starting the implementation! ## How UAPP Works From f7ef57ed6188a986d6b2b6efbe42d98206b0ee6a Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Tue, 20 Oct 2026 19:09:11 +0000 Subject: [PATCH 034/118] Update package-lock.json version to 0.2.0 --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index e3c8efbe..941c7781 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.0-0", + "version": "0.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@expensify/react-native-wallet", - "version": "0.2.0-0", + "version": "0.2.0", "license": "MIT", "workspaces": [ "apps/common-app", From 9c15903487d7464f44af2c88bb74faf82615881e Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Tue, 20 Oct 2026 19:09:14 +0000 Subject: [PATCH 035/118] Update package.json version to 0.2.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9450b664..9ee24e34 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.0-0", + "version": "0.2.0", "description": "A React Native module designed for seamless integration of Card Push Provisioning into Apple Wallet and Google Wallet, enabling easy and secure addition of payment cards directly from your application.", "source": "./src/index.tsx", "main": "lib/commonjs/index", From 7f2fd5c084873eeb90be50edd361c9db57ce09c0 Mon Sep 17 00:00:00 2001 From: Robert Chen Date: Wed, 21 Oct 2026 10:18:49 -0700 Subject: [PATCH 036/118] Bump Node to 24.22.1 to block the compromised eMudhra intermediates --- .nvmrc | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.nvmrc b/.nvmrc index 60ade1ae..ce62013c 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -24.19.0 +24.22.1 diff --git a/package.json b/package.json index 9ee24e34..57e14590 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "devEngines": { "runtime": { "name": "node", - "version": ">=24.19.0", + "version": ">=24.22.1", "onFail": "error" }, "packageManager": { From f5f04e1913b5a1013025c764f24958d42b9c99ad Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Wed, 21 Oct 2026 17:48:58 +0000 Subject: [PATCH 037/118] Update package-lock.json version to 0.2.1 --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 941c7781..577cdc64 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.0", + "version": "0.2.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@expensify/react-native-wallet", - "version": "0.2.0", + "version": "0.2.1", "license": "MIT", "workspaces": [ "apps/common-app", From 8eacf5a44dd463774e013871c0acaf49baa88cdc Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Wed, 21 Oct 2026 17:49:00 +0000 Subject: [PATCH 038/118] Update package.json version to 0.2.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 57e14590..7e48e1de 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.0", + "version": "0.2.1", "description": "A React Native module designed for seamless integration of Card Push Provisioning into Apple Wallet and Google Wallet, enabling easy and secure addition of payment cards directly from your application.", "source": "./src/index.tsx", "main": "lib/commonjs/index", From 784c69d1689f746c0c5da13d4ae143114a62a444 Mon Sep 17 00:00:00 2001 From: MelvinBot Date: Mon, 26 Oct 2026 13:16:17 +0000 Subject: [PATCH 039/118] Bump handlebars from 4.7.8 to 4.7.10 --- package-lock.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 577cdc64..a9e75723 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14418,13 +14418,13 @@ "license": "MIT" }, "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "version": "4.7.10", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.10.tgz", + "integrity": "sha512-P5VJMVM7qgBn6vjXMw8WG9uVI+ncf2pi72j4de4yz5ZULLj2RGqLYaKOYGsgyrViQ0tePOVlN1tDCCXXtFqXKg==", "dev": true, "license": "MIT", "dependencies": { - "minimist": "^1.2.5", + "minimist": "^1.2.8", "neo-async": "^2.6.2", "source-map": "^0.6.1", "wordwrap": "^1.0.0" From 5899829907d3161ddb1fe1f21ffb6526591ee312 Mon Sep 17 00:00:00 2001 From: MelvinBot Date: Mon, 26 Oct 2026 13:16:43 +0000 Subject: [PATCH 040/118] Bump launch-editor to 2.14.2 to address Dependabot alerts --- package-lock.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 577cdc64..9b6d2364 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17224,14 +17224,14 @@ } }, "node_modules/launch-editor": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.12.0.tgz", - "integrity": "sha512-giOHXoOtifjdHqUamwKq6c49GzBdLjvxrd2D+Q4V6uOHopJv7p9VJxikDsQ/CBXZbEITgUqSVHXLTG3VhPP1Dg==", + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.14.2.tgz", + "integrity": "sha512-NHJEb30MGBCkmZXLZwz25t37QR4aY03ZzPYUJXOJaOa92uWwExI9SBQUuIznBYc1xkD7cJNGX7oc3Nl3sq2kQQ==", "devOptional": true, "license": "MIT", "dependencies": { "picocolors": "^1.1.1", - "shell-quote": "^1.8.3" + "shell-quote": "^1.10.0" } }, "node_modules/leven": { @@ -22297,9 +22297,9 @@ } }, "node_modules/shell-quote": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", - "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.12.0.tgz", + "integrity": "sha512-PcByqNyT/38F2kDNi006HAMRJaULuBzq/FOsw3qdZvX/GA9W/jamDaRskgHjubHiftXK5sIFxLNkvrXUwcof6Q==", "license": "MIT", "engines": { "node": ">= 0.4" From aac2ae1273773816d2719f3541e9ee07a195447f Mon Sep 17 00:00:00 2001 From: MelvinBot Date: Mon, 26 Oct 2026 13:17:55 +0000 Subject: [PATCH 041/118] Bump compression to 1.8.2 to address GHSA-vc2v-76pw-4v95 --- apps/expo-example/package-lock.json | 7 ++++--- package-lock.json | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/apps/expo-example/package-lock.json b/apps/expo-example/package-lock.json index b30eea4f..325f5fa2 100644 --- a/apps/expo-example/package-lock.json +++ b/apps/expo-example/package-lock.json @@ -4797,14 +4797,15 @@ } }, "node_modules/compression": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", - "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.2.tgz", + "integrity": "sha512-o8vI5RE5A6EVVOd9o41jKp41aJom+QTEO/Bx8MYNjexMo/Bv2WOjUfZr+aL0WnYSgymUy6zeguqLTsIhV0gMvQ==", "license": "MIT", "dependencies": { "bytes": "3.1.2", "compressible": "~2.0.18", "debug": "2.6.9", + "destroy": "1.2.0", "negotiator": "~0.6.4", "on-headers": "~1.1.0", "safe-buffer": "5.2.1", diff --git a/package-lock.json b/package-lock.json index 577cdc64..62355c5f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7867,15 +7867,16 @@ } }, "node_modules/compression": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", - "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.2.tgz", + "integrity": "sha512-o8vI5RE5A6EVVOd9o41jKp41aJom+QTEO/Bx8MYNjexMo/Bv2WOjUfZr+aL0WnYSgymUy6zeguqLTsIhV0gMvQ==", "devOptional": true, "license": "MIT", "dependencies": { "bytes": "3.1.2", "compressible": "~2.0.18", "debug": "2.6.9", + "destroy": "1.2.0", "negotiator": "~0.6.4", "on-headers": "~1.1.0", "safe-buffer": "5.2.1", From 53d4ae369a3a93d4bd79db7b8542f73bcf54bdb8 Mon Sep 17 00:00:00 2001 From: MelvinBot Date: Mon, 26 Oct 2026 13:18:00 +0000 Subject: [PATCH 042/118] Bump vm2 from 3.10.4 to 3.12.2 --- package-lock.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 577cdc64..c29f4c95 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24395,13 +24395,13 @@ "license": "MIT" }, "node_modules/vm2": { - "version": "3.10.4", - "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.10.4.tgz", - "integrity": "sha512-Gl6r7MN3mkawGurFdp467yDJQ7HdragK2QVVWFbOCd3LPJXJLE2xZFwLCNhp04MTkHruPqLIOs3pYbJQJw/1aA==", + "version": "3.12.2", + "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.12.2.tgz", + "integrity": "sha512-LRHt4xU6eXshlZEcSZVrTehLGxRDjHvjmFPGwEp4xGpZX61QzH6Xm9wK2nU7XAp36V1w2xILAL1LwxVkgD1Ipw==", "dev": true, "license": "MIT", "dependencies": { - "acorn": "^8.14.1", + "acorn": "^8.15.0", "acorn-walk": "^8.3.4" }, "bin": { From 64e3715c987aaf3e5095a48e220b310be672d926 Mon Sep 17 00:00:00 2001 From: MelvinBot Date: Mon, 26 Oct 2026 13:18:04 +0000 Subject: [PATCH 043/118] Bump node-forge to 1.4.0 to address Dependabot alerts --- apps/expo-example/package-lock.json | 6 +++--- package-lock.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/expo-example/package-lock.json b/apps/expo-example/package-lock.json index b30eea4f..a119cc5f 100644 --- a/apps/expo-example/package-lock.json +++ b/apps/expo-example/package-lock.json @@ -9664,9 +9664,9 @@ "license": "MIT" }, "node_modules/node-forge": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.3.tgz", - "integrity": "sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz", + "integrity": "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==", "license": "(BSD-3-Clause OR GPL-2.0)", "engines": { "node": ">= 6.13.0" diff --git a/package-lock.json b/package-lock.json index 577cdc64..50067d3e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18930,9 +18930,9 @@ } }, "node_modules/node-forge": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.3.tgz", - "integrity": "sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz", + "integrity": "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==", "dev": true, "license": "(BSD-3-Clause OR GPL-2.0)", "peer": true, From 9fe4cffb3acc0db6f39af20d1cb008a1fab923b8 Mon Sep 17 00:00:00 2001 From: MelvinBot Date: Mon, 26 Oct 2026 13:18:38 +0000 Subject: [PATCH 044/118] Bump http-cache-semantics from 4.2.0 to 4.3.0 --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 577cdc64..d624d9a2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14633,9 +14633,9 @@ "license": "MIT" }, "node_modules/http-cache-semantics": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", - "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.3.0.tgz", + "integrity": "sha512-M5t5LlJpS1UHMjvwRQVdFHvPISGeLAxNcrWuJkeGh0KxsqCHZ1O3NXZU/8x7cD0BDcGW8kapxMKTvwlqrNkHkA==", "dev": true, "license": "BSD-2-Clause" }, From 77c93489c1c557a0fd1d786eedc2b70c043548a4 Mon Sep 17 00:00:00 2001 From: MelvinBot Date: Mon, 26 Oct 2026 13:19:16 +0000 Subject: [PATCH 045/118] chore(deps): bump brace-expansion to 1.1.21 and 2.1.7 to address Dependabot alerts --- apps/expo-example/package-lock.json | 30 ++++++------- package-lock.json | 66 ++++++++++++++--------------- 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/apps/expo-example/package-lock.json b/apps/expo-example/package-lock.json index b30eea4f..b86d66eb 100644 --- a/apps/expo-example/package-lock.json +++ b/apps/expo-example/package-lock.json @@ -1885,9 +1885,9 @@ } }, "node_modules/@expo/fingerprint/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.7.tgz", + "integrity": "sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -3371,9 +3371,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.7.tgz", + "integrity": "sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==", "dev": true, "license": "MIT", "dependencies": { @@ -4416,9 +4416,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -6540,9 +6540,9 @@ } }, "node_modules/expo/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.7.tgz", + "integrity": "sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -7512,9 +7512,9 @@ } }, "node_modules/glob/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.7.tgz", + "integrity": "sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" diff --git a/package-lock.json b/package-lock.json index 577cdc64..ece2a489 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2455,9 +2455,9 @@ "license": "Python-2.0" }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, "license": "MIT", "dependencies": { @@ -3450,9 +3450,9 @@ } }, "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, "license": "MIT", "dependencies": { @@ -7219,9 +7219,9 @@ } }, "node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.7.tgz", + "integrity": "sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==", "dev": true, "license": "MIT", "dependencies": { @@ -10090,9 +10090,9 @@ "license": "Apache-2.0" }, "node_modules/eslint-config-expensify/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, "license": "MIT", "dependencies": { @@ -11062,9 +11062,9 @@ } }, "node_modules/eslint-plugin-import/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, "license": "MIT", "dependencies": { @@ -11296,9 +11296,9 @@ } }, "node_modules/eslint-plugin-jsx-a11y/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, "license": "MIT", "dependencies": { @@ -11424,9 +11424,9 @@ "license": "MIT" }, "node_modules/eslint-plugin-react/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, "license": "MIT", "dependencies": { @@ -11567,9 +11567,9 @@ "license": "Python-2.0" }, "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, "license": "MIT", "dependencies": { @@ -12489,9 +12489,9 @@ } }, "node_modules/expo-module-scripts/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, "license": "MIT", "dependencies": { @@ -14257,9 +14257,9 @@ } }, "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -23345,9 +23345,9 @@ } }, "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", From e99c4473ca5d68c1fdefaf0b4cfa50ae6419e9a6 Mon Sep 17 00:00:00 2001 From: MelvinBot Date: Mon, 26 Oct 2026 13:20:03 +0000 Subject: [PATCH 046/118] Bump ip-address from 10.1.0 to 10.7.5 --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 577cdc64..89d9e78b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14976,9 +14976,9 @@ "license": "MIT" }, "node_modules/ip-address": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", - "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "version": "10.7.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.5.tgz", + "integrity": "sha512-t2v0rkILYv9WXmSL7TpAaU3HXgaFYOPUcH3QHgwQ++gpJuH3bqyMy4Fvt/DJCTKwzH3Cf+bAp5W/mFL39KPLzA==", "dev": true, "license": "MIT", "engines": { From 3edbbe204292ba85edff0454c48fbdd8c5b1c514 Mon Sep 17 00:00:00 2001 From: MelvinBot Date: Mon, 26 Oct 2026 13:20:23 +0000 Subject: [PATCH 047/118] fix(deps): bump @xmldom/xmldom to 0.8.15 --- apps/expo-example/package-lock.json | 25 +++---------------------- apps/expo-example/package.json | 3 +++ package-lock.json | 6 +++--- 3 files changed, 9 insertions(+), 25 deletions(-) diff --git a/apps/expo-example/package-lock.json b/apps/expo-example/package-lock.json index b30eea4f..5f047e08 100644 --- a/apps/expo-example/package-lock.json +++ b/apps/expo-example/package-lock.json @@ -3763,10 +3763,9 @@ } }, "node_modules/@xmldom/xmldom": { - "version": "0.7.13", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.7.13.tgz", - "integrity": "sha512-lm2GW5PkosIzccsaZIz7tp8cPADSIlIHWDFTR1N0SzfinhhYgeIQjFMz4rYzanCScr3DqQLeomUDArp6MWKm+g==", - "deprecated": "this version is no longer supported, please update to at least 0.8.*", + "version": "0.8.15", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.15.tgz", + "integrity": "sha512-/5NV/vDALVFDXgLmfsy9TRCBlKwO2LNBFzpzvb9iIj+jR+eSc6DLYYvVOdivT/jm7MtU6TebYuRmzEOI7w40UA==", "license": "MIT", "engines": { "node": ">=10.0.0" @@ -6487,15 +6486,6 @@ "integrity": "sha512-0HuJ8YtqlTVRXGZuGeBejLE04wSQsibpTI+RGOyVqxZvgtlLLC/Ssw0UmbHhT4lYMp2fhdtvKZSs5emWB1zR/g==", "license": "MIT" }, - "node_modules/expo/node_modules/@xmldom/xmldom": { - "version": "0.8.11", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz", - "integrity": "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/expo/node_modules/babel-preset-expo": { "version": "54.0.10", "resolved": "https://registry.npmjs.org/babel-preset-expo/-/babel-preset-expo-54.0.10.tgz", @@ -10239,15 +10229,6 @@ "node": ">=10.4.0" } }, - "node_modules/plist/node_modules/@xmldom/xmldom": { - "version": "0.8.11", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz", - "integrity": "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/plist/node_modules/xmlbuilder": { "version": "15.1.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", diff --git a/apps/expo-example/package.json b/apps/expo-example/package.json index d6a71d7b..605bf2dd 100644 --- a/apps/expo-example/package.json +++ b/apps/expo-example/package.json @@ -23,5 +23,8 @@ "eslint": "^8.19.0", "eslint-config-expo": "^10.0.0" }, + "overrides": { + "@xmldom/xmldom": "^0.8.15" + }, "private": true } diff --git a/package-lock.json b/package-lock.json index 577cdc64..c37c3f89 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5998,9 +5998,9 @@ "license": "MIT" }, "node_modules/@xmldom/xmldom": { - "version": "0.8.11", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz", - "integrity": "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==", + "version": "0.8.15", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.15.tgz", + "integrity": "sha512-/5NV/vDALVFDXgLmfsy9TRCBlKwO2LNBFzpzvb9iIj+jR+eSc6DLYYvVOdivT/jm7MtU6TebYuRmzEOI7w40UA==", "dev": true, "license": "MIT", "engines": { From 7c4da88fa83cb5ad87ae8973dddb5e9dd8c7d6e4 Mon Sep 17 00:00:00 2001 From: MelvinBot Date: Mon, 26 Oct 2026 13:20:32 +0000 Subject: [PATCH 048/118] Bump nanoid to 3.3.20 to address Dependabot alerts --- apps/expo-example/package-lock.json | 6 +++--- package-lock.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/expo-example/package-lock.json b/apps/expo-example/package-lock.json index b30eea4f..51ef752e 100644 --- a/apps/expo-example/package-lock.json +++ b/apps/expo-example/package-lock.json @@ -9608,9 +9608,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", "funding": [ { "type": "github", diff --git a/package-lock.json b/package-lock.json index 577cdc64..0370dbf7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18782,9 +18782,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", "dev": true, "funding": [ { From 7cfdd4d3bf2721926db551e45a192b69cf15aa33 Mon Sep 17 00:00:00 2001 From: MelvinBot Date: Mon, 26 Oct 2026 13:20:44 +0000 Subject: [PATCH 049/118] Bump shell-quote to 1.12.0 in expo-example lockfile --- apps/expo-example/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/expo-example/package-lock.json b/apps/expo-example/package-lock.json index b30eea4f..ef11f139 100644 --- a/apps/expo-example/package-lock.json +++ b/apps/expo-example/package-lock.json @@ -11254,9 +11254,9 @@ } }, "node_modules/shell-quote": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", - "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.12.0.tgz", + "integrity": "sha512-PcByqNyT/38F2kDNi006HAMRJaULuBzq/FOsw3qdZvX/GA9W/jamDaRskgHjubHiftXK5sIFxLNkvrXUwcof6Q==", "license": "MIT", "engines": { "node": ">= 0.4" From 7a7837a197582c7a33c82a8ebd9762a32ef735d0 Mon Sep 17 00:00:00 2001 From: MelvinBot Date: Mon, 26 Oct 2026 13:21:08 +0000 Subject: [PATCH 050/118] Bump browserslist to 4.28.7 --- apps/expo-example/package-lock.json | 50 ++++++++++++++++------------- package-lock.json | 50 ++++++++++++++++------------- 2 files changed, 56 insertions(+), 44 deletions(-) diff --git a/apps/expo-example/package-lock.json b/apps/expo-example/package-lock.json index b30eea4f..c64de71e 100644 --- a/apps/expo-example/package-lock.json +++ b/apps/expo-example/package-lock.json @@ -4348,12 +4348,15 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.9.19", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", - "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", + "version": "2.11.33", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.33.tgz", + "integrity": "sha512-yiVKDX+qCMRKBE084lXvq9EcwFUhKZ2nU8TK1EQQuOgPNR/lmbGxXOeDdlGUqC2Zz11Cs9rk1d/8NfgBmlkt0w==", "license": "Apache-2.0", "bin": { - "baseline-browser-mapping": "dist/cli.js" + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" } }, "node_modules/better-opn": { @@ -4438,9 +4441,9 @@ } }, "node_modules/browserslist": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "version": "4.28.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.7.tgz", + "integrity": "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==", "funding": [ { "type": "opencollective", @@ -4457,11 +4460,11 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" + "baseline-browser-mapping": "^2.10.44", + "caniuse-lite": "^1.0.30001806", + "electron-to-chromium": "^1.5.393", + "node-releases": "^2.0.51", + "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" @@ -4591,9 +4594,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001769", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz", - "integrity": "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==", + "version": "1.0.30001819", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001819.tgz", + "integrity": "sha512-LFUqIIAwpzsIuevnKnsR9qnStBcgzPShTX61lBwN9uFkkf8o8zLiebEjbW8AYtZiF+1YKDWfdt/pWDtMuG8Xwg==", "funding": [ { "type": "opencollective", @@ -5168,9 +5171,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.286", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", - "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==", + "version": "1.5.464", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.464.tgz", + "integrity": "sha512-nRa65aFge40viPAKxMKULIY8tcXKwKboJWNn4RoKwyWXJ6SP7BnrDGKUTzc64818jIciH0PK7LSF8QzZes19GA==", "license": "ISC" }, "node_modules/emoji-regex": { @@ -9679,10 +9682,13 @@ "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.27", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", - "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", - "license": "MIT" + "version": "2.0.60", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.60.tgz", + "integrity": "sha512-OmO8+GM1JUZqXET720VrJCwkjUCXmCMQm0Z3UDDRbfNLHS4NEAvY60j5jiFv0tkKw2PNNlqOLhahVmwJIOLDIQ==", + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/normalize-path": { "version": "3.0.0", diff --git a/package-lock.json b/package-lock.json index 577cdc64..a490dbb5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6905,12 +6905,15 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.9.19", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", - "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", + "version": "2.11.33", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.33.tgz", + "integrity": "sha512-yiVKDX+qCMRKBE084lXvq9EcwFUhKZ2nU8TK1EQQuOgPNR/lmbGxXOeDdlGUqC2Zz11Cs9rk1d/8NfgBmlkt0w==", "license": "Apache-2.0", "bin": { - "baseline-browser-mapping": "dist/cli.js" + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" } }, "node_modules/basic-ftp": { @@ -7241,9 +7244,9 @@ } }, "node_modules/browserslist": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "version": "4.28.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.7.tgz", + "integrity": "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==", "funding": [ { "type": "opencollective", @@ -7260,11 +7263,11 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" + "baseline-browser-mapping": "^2.10.44", + "caniuse-lite": "^1.0.30001806", + "electron-to-chromium": "^1.5.393", + "node-releases": "^2.0.51", + "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" @@ -7469,9 +7472,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001769", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz", - "integrity": "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==", + "version": "1.0.30001819", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001819.tgz", + "integrity": "sha512-LFUqIIAwpzsIuevnKnsR9qnStBcgzPShTX61lBwN9uFkkf8o8zLiebEjbW8AYtZiF+1YKDWfdt/pWDtMuG8Xwg==", "funding": [ { "type": "opencollective", @@ -9459,9 +9462,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.286", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", - "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==", + "version": "1.5.464", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.464.tgz", + "integrity": "sha512-nRa65aFge40viPAKxMKULIY8tcXKwKboJWNn4RoKwyWXJ6SP7BnrDGKUTzc64818jIciH0PK7LSF8QzZes19GA==", "license": "ISC" }, "node_modules/emittery": { @@ -18947,10 +18950,13 @@ "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.27", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", - "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", - "license": "MIT" + "version": "2.0.60", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.60.tgz", + "integrity": "sha512-OmO8+GM1JUZqXET720VrJCwkjUCXmCMQm0Z3UDDRbfNLHS4NEAvY60j5jiFv0tkKw2PNNlqOLhahVmwJIOLDIQ==", + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/node-stream-zip": { "version": "1.15.0", From 48bcc9b86977438fd3b34adf70a14c28aa6130ad Mon Sep 17 00:00:00 2001 From: MelvinBot Date: Mon, 26 Oct 2026 13:21:11 +0000 Subject: [PATCH 051/118] Override metro to 0.83.8 to drop vulnerable image-size --- apps/expo-example/package-lock.json | 321 +++++++++++++++------------- apps/expo-example/package.json | 16 ++ package-lock.json | 320 ++++++++++++++------------- package.json | 16 ++ 4 files changed, 371 insertions(+), 302 deletions(-) diff --git a/apps/expo-example/package-lock.json b/apps/expo-example/package-lock.json index b30eea4f..591ca94c 100644 --- a/apps/expo-example/package-lock.json +++ b/apps/expo-example/package-lock.json @@ -24,8 +24,7 @@ }, "../..": { "name": "@expensify/react-native-wallet", - "version": "0.1.15", - "hasInstallScript": true, + "version": "0.2.1", "license": "MIT", "workspaces": [ "apps/common-app", @@ -49,7 +48,7 @@ "@typescript-eslint/parser": "^6.15.0", "del-cli": "^5.0.0", "eslint": "^8.57.1", - "eslint-config-expensify": "^2.0.24", + "eslint-config-expensify": "2.0.24", "eslint-config-prettier": "^8.5.0", "eslint-import-resolver-alias": "^1.1.2", "eslint-plugin-deprecation": "^2.0.0", @@ -1520,25 +1519,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/traverse--for-generate-function-map": { - "name": "@babel/traverse", - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", - "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/types": { "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", @@ -4874,6 +4854,19 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, + "node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -7699,18 +7692,18 @@ } }, "node_modules/hermes-estree": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.32.0.tgz", - "integrity": "sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ==", + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.35.0.tgz", + "integrity": "sha512-xVx5Opwy8Oo1I5yGpVRhCvWL/iV3M+ylksSKVNlxxD90cpDpR/AR1jLYqK8HWihm065a6UI3HeyAmYzwS8NOOg==", "license": "MIT" }, "node_modules/hermes-parser": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.32.0.tgz", - "integrity": "sha512-g4nBOWFpuiTqjR3LZdRxKUkij9iyveWeuks7INEsMX741f3r9xxrOe8TeQfUxtda0eXmiIFiMQzoeSQEno33Hw==", + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.35.0.tgz", + "integrity": "sha512-9JLjeHxBx8T4CAsydZR49PNZUaix+WpQJwu9p2010lu+7Kwl6D/7wYFFJxoz+aXkaaClp9Zfg6W6/zVlSJORaA==", "license": "MIT", "dependencies": { - "hermes-estree": "0.32.0" + "hermes-estree": "0.35.0" } }, "node_modules/hosted-git-info": { @@ -7802,21 +7795,6 @@ "node": ">= 4" } }, - "node_modules/image-size": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.2.1.tgz", - "integrity": "sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==", - "license": "MIT", - "dependencies": { - "queue": "6.0.2" - }, - "bin": { - "image-size": "bin/image-size.js" - }, - "engines": { - "node": ">=16.x" - } - }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", @@ -9213,45 +9191,43 @@ "license": "MIT" }, "node_modules/metro": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro/-/metro-0.83.3.tgz", - "integrity": "sha512-+rP+/GieOzkt97hSJ0MrPOuAH/jpaS21ZDvL9DJ35QYRDlQcwzcvUlGUf79AnQxq/2NPiS/AULhhM4TKutIt8Q==", + "version": "0.83.8", + "resolved": "https://registry.npmjs.org/metro/-/metro-0.83.8.tgz", + "integrity": "sha512-ZbJDJCDlvv0O3QHVbhZkyP2/DtebfEoDZ3wVyYHgu6Uoy6DmmE/bis6lIDBYh+kWPuaueJDegyutPjkIBLtZDQ==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.24.7", + "@babel/code-frame": "^7.29.0", "@babel/core": "^7.25.2", - "@babel/generator": "^7.25.0", - "@babel/parser": "^7.25.3", - "@babel/template": "^7.25.0", - "@babel/traverse": "^7.25.3", - "@babel/types": "^7.25.2", - "accepts": "^1.3.7", - "chalk": "^4.0.0", + "@babel/generator": "^7.29.1", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "accepts": "^2.0.0", "ci-info": "^2.0.0", "connect": "^3.6.5", "debug": "^4.4.0", "error-stack-parser": "^2.0.6", "flow-enums-runtime": "^0.0.6", "graceful-fs": "^4.2.4", - "hermes-parser": "0.32.0", - "image-size": "^1.0.2", + "hermes-parser": "0.35.0", "invariant": "^2.2.4", "jest-worker": "^29.7.0", "jsc-safe-url": "^0.2.2", "lodash.throttle": "^4.1.1", - "metro-babel-transformer": "0.83.3", - "metro-cache": "0.83.3", - "metro-cache-key": "0.83.3", - "metro-config": "0.83.3", - "metro-core": "0.83.3", - "metro-file-map": "0.83.3", - "metro-resolver": "0.83.3", - "metro-runtime": "0.83.3", - "metro-source-map": "0.83.3", - "metro-symbolicate": "0.83.3", - "metro-transform-plugins": "0.83.3", - "metro-transform-worker": "0.83.3", - "mime-types": "^2.1.27", + "metro-babel-transformer": "0.83.8", + "metro-cache": "0.83.8", + "metro-cache-key": "0.83.8", + "metro-config": "0.83.8", + "metro-core": "0.83.8", + "metro-file-map": "0.83.8", + "metro-resolver": "0.83.8", + "metro-runtime": "0.83.8", + "metro-source-map": "0.83.8", + "metro-symbolicate": "0.83.8", + "metro-transform-plugins": "0.83.8", + "metro-transform-worker": "0.83.8", + "mime-types": "^3.0.1", "nullthrows": "^1.1.1", "serialize-error": "^2.1.0", "source-map": "^0.5.6", @@ -9267,14 +9243,15 @@ } }, "node_modules/metro-babel-transformer": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.83.3.tgz", - "integrity": "sha512-1vxlvj2yY24ES1O5RsSIvg4a4WeL7PFXgKOHvXTXiW0deLvQr28ExXj6LjwCCDZ4YZLhq6HddLpZnX4dEdSq5g==", + "version": "0.83.8", + "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.83.8.tgz", + "integrity": "sha512-tnn0J5wzgTgTx2OJy3Cwr1y79bJz4eNgFQd+2HENOs5Vz6QOMnt05z7J+BedIo9wIbpEa0iN9U1nerxyvMRE9g==", "license": "MIT", "dependencies": { "@babel/core": "^7.25.2", "flow-enums-runtime": "^0.0.6", - "hermes-parser": "0.32.0", + "hermes-parser": "0.35.0", + "metro-cache-key": "0.83.8", "nullthrows": "^1.1.1" }, "engines": { @@ -9282,24 +9259,24 @@ } }, "node_modules/metro-cache": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.83.3.tgz", - "integrity": "sha512-3jo65X515mQJvKqK3vWRblxDEcgY55Sk3w4xa6LlfEXgQ9g1WgMh9m4qVZVwgcHoLy0a2HENTPCCX4Pk6s8c8Q==", + "version": "0.83.8", + "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.83.8.tgz", + "integrity": "sha512-aogMG5WbKzW5000otNjYrS9hIoORzkCI1faPJK+vxQLaf2BorJKBBFe/jl2Tfsi1mZUglS2EBuBt8B3JB7MYDQ==", "license": "MIT", "dependencies": { "exponential-backoff": "^3.1.1", "flow-enums-runtime": "^0.0.6", "https-proxy-agent": "^7.0.5", - "metro-core": "0.83.3" + "metro-core": "0.83.8" }, "engines": { "node": ">=20.19.4" } }, "node_modules/metro-cache-key": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.83.3.tgz", - "integrity": "sha512-59ZO049jKzSmvBmG/B5bZ6/dztP0ilp0o988nc6dpaDsU05Cl1c/lRf+yx8m9WW/JVgbmfO5MziBU559XjI5Zw==", + "version": "0.83.8", + "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.83.8.tgz", + "integrity": "sha512-I38PtcjT4crS5HY9UQ8i6z8S7tJ2WewtPGr/OwS6FcLKfy5T/1hlTaFw+wozUZkEtNpR6Gc0oJuvuKCbSoSN5A==", "license": "MIT", "dependencies": { "flow-enums-runtime": "^0.0.6" @@ -9309,18 +9286,18 @@ } }, "node_modules/metro-config": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.83.3.tgz", - "integrity": "sha512-mTel7ipT0yNjKILIan04bkJkuCzUUkm2SeEaTads8VfEecCh+ltXchdq6DovXJqzQAXuR2P9cxZB47Lg4klriA==", + "version": "0.83.8", + "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.83.8.tgz", + "integrity": "sha512-crNbNy+/B4tCne2+HjUshwvC57gNBQj+V9fFSy3lHH2RlKcLHib3Mil/SfTX8Pfh2fCY5pPuSu2OKa7YiadB+Q==", "license": "MIT", "dependencies": { "connect": "^3.6.5", "flow-enums-runtime": "^0.0.6", "jest-validate": "^29.7.0", - "metro": "0.83.3", - "metro-cache": "0.83.3", - "metro-core": "0.83.3", - "metro-runtime": "0.83.3", + "metro": "0.83.8", + "metro-cache": "0.83.8", + "metro-core": "0.83.8", + "metro-runtime": "0.83.8", "yaml": "^2.6.1" }, "engines": { @@ -9328,23 +9305,23 @@ } }, "node_modules/metro-core": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.83.3.tgz", - "integrity": "sha512-M+X59lm7oBmJZamc96usuF1kusd5YimqG/q97g4Ac7slnJ3YiGglW5CsOlicTR5EWf8MQFxxjDoB6ytTqRe8Hw==", + "version": "0.83.8", + "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.83.8.tgz", + "integrity": "sha512-NTyOUOQaQKvQgJG9VI2ymN6KTM7gHEqkVFhkPc9bK4BsHSTz/EaXuFBXtC+wtwINLeH9tbusL/jfsSmdEmuU7A==", "license": "MIT", "dependencies": { "flow-enums-runtime": "^0.0.6", "lodash.throttle": "^4.1.1", - "metro-resolver": "0.83.3" + "metro-resolver": "0.83.8" }, "engines": { "node": ">=20.19.4" } }, "node_modules/metro-file-map": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.83.3.tgz", - "integrity": "sha512-jg5AcyE0Q9Xbbu/4NAwwZkmQn7doJCKGW0SLeSJmzNB9Z24jBe0AL2PHNMy4eu0JiKtNWHz9IiONGZWq7hjVTA==", + "version": "0.83.8", + "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.83.8.tgz", + "integrity": "sha512-+W++EUuzEXIfWQEFTWQMVThzhWbnJL4gRNJ9WSHzIAM5pT7gsprUxo9+2hrfirURm/TLvrKwhq37oCECJcDSyQ==", "license": "MIT", "dependencies": { "debug": "^4.4.0", @@ -9362,9 +9339,9 @@ } }, "node_modules/metro-minify-terser": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.83.3.tgz", - "integrity": "sha512-O2BmfWj6FSfzBLrNCXt/rr2VYZdX5i6444QJU0fFoc7Ljg+Q+iqebwE3K0eTvkI6TRjELsXk1cjU+fXwAR4OjQ==", + "version": "0.83.8", + "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.83.8.tgz", + "integrity": "sha512-7tU0J5/c7LZaZJwTlOb1xq0NepTFvGzRxigDhZOq4jSE6g0BRHmBqe7XvLH3WcRiJNGy+eshcZr34RpMjb6mmg==", "license": "MIT", "dependencies": { "flow-enums-runtime": "^0.0.6", @@ -9375,9 +9352,9 @@ } }, "node_modules/metro-resolver": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.83.3.tgz", - "integrity": "sha512-0js+zwI5flFxb1ktmR///bxHYg7OLpRpWZlBBruYG8OKYxeMP7SV0xQ/o/hUelrEMdK4LJzqVtHAhBm25LVfAQ==", + "version": "0.83.8", + "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.83.8.tgz", + "integrity": "sha512-piU0NVTI9i37YztDVF5rtn9uxP3NebVT0xZM9NKJ9z0jbigrctUQksi3NoYIeJMvL6Wn2dgAehpcmmnfn+gUwA==", "license": "MIT", "dependencies": { "flow-enums-runtime": "^0.0.6" @@ -9387,9 +9364,9 @@ } }, "node_modules/metro-runtime": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.83.3.tgz", - "integrity": "sha512-JHCJb9ebr9rfJ+LcssFYA2x1qPYuSD/bbePupIGhpMrsla7RCwC/VL3yJ9cSU+nUhU4c9Ixxy8tBta+JbDeZWw==", + "version": "0.83.8", + "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.83.8.tgz", + "integrity": "sha512-f7FfeM0pamq8vrvs8aO9KvIUabbUKe0WkHFpLt6Q9yIIIsORqNFwlgJeHGraOFPU7Cxqj5yLXkJu5bT1uwDXvw==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.25.0", @@ -9400,19 +9377,18 @@ } }, "node_modules/metro-source-map": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.83.3.tgz", - "integrity": "sha512-xkC3qwUBh2psVZgVavo8+r2C9Igkk3DibiOXSAht1aYRRcztEZNFtAMtfSB7sdO2iFMx2Mlyu++cBxz/fhdzQg==", + "version": "0.83.8", + "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.83.8.tgz", + "integrity": "sha512-60Uor7bM+KsVewLkLCcZfkPFCbqjPDdoSmeBuT3+ye+ac80BuLWbbR8DpyxWpUqQeZPdaAT5ZqFgDIs8BNEcVA==", "license": "MIT", "dependencies": { - "@babel/traverse": "^7.25.3", - "@babel/traverse--for-generate-function-map": "npm:@babel/traverse@^7.25.3", - "@babel/types": "^7.25.2", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", - "metro-symbolicate": "0.83.3", + "metro-symbolicate": "0.83.8", "nullthrows": "^1.1.1", - "ob1": "0.83.3", + "ob1": "0.83.8", "source-map": "^0.5.6", "vlq": "^1.0.0" }, @@ -9421,14 +9397,14 @@ } }, "node_modules/metro-symbolicate": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.83.3.tgz", - "integrity": "sha512-F/YChgKd6KbFK3eUR5HdUsfBqVsanf5lNTwFd4Ca7uuxnHgBC3kR/Hba/RGkenR3pZaGNp5Bu9ZqqP52Wyhomw==", + "version": "0.83.8", + "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.83.8.tgz", + "integrity": "sha512-ZLrbqOqQ+m+Mpv7zo65XEXkNqVYvgUbY7tIsb9e1zSSmU/4C3D6DjaJvRHqcAdl0mkg8TI/csKooAEa5xcZNwg==", "license": "MIT", "dependencies": { "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", - "metro-source-map": "0.83.3", + "metro-source-map": "0.83.8", "nullthrows": "^1.1.1", "source-map": "^0.5.6", "vlq": "^1.0.0" @@ -9441,15 +9417,15 @@ } }, "node_modules/metro-transform-plugins": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.83.3.tgz", - "integrity": "sha512-eRGoKJU6jmqOakBMH5kUB7VitEWiNrDzBHpYbkBXW7C5fUGeOd2CyqrosEzbMK5VMiZYyOcNFEphvxk3OXey2A==", + "version": "0.83.8", + "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.83.8.tgz", + "integrity": "sha512-9JRPkvi+m0QH2Y/w5RjCF9mHqOUNFpMFLDDLhKUjhcKESh8Wm3HKDdHXHVldTN1lTToCIabv81nF0zyJzKEJ5g==", "license": "MIT", "dependencies": { "@babel/core": "^7.25.2", - "@babel/generator": "^7.25.0", - "@babel/template": "^7.25.0", - "@babel/traverse": "^7.25.3", + "@babel/generator": "^7.29.1", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", "flow-enums-runtime": "^0.0.6", "nullthrows": "^1.1.1" }, @@ -9458,29 +9434,83 @@ } }, "node_modules/metro-transform-worker": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.83.3.tgz", - "integrity": "sha512-Ztekew9t/gOIMZX1tvJOgX7KlSLL5kWykl0Iwu2cL2vKMKVALRl1hysyhUw0vjpAvLFx+Kfq9VLjnHIkW32fPA==", + "version": "0.83.8", + "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.83.8.tgz", + "integrity": "sha512-Pa2hOfhUmWpI/dmkhsLq8uGyFHK2opoEK7j/YCiRtqNSe0YzzxYguXTNgg8AMiuZfc9LPcB1AhGENS10O5wcIw==", "license": "MIT", "dependencies": { "@babel/core": "^7.25.2", - "@babel/generator": "^7.25.0", - "@babel/parser": "^7.25.3", - "@babel/types": "^7.25.2", + "@babel/generator": "^7.29.1", + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", "flow-enums-runtime": "^0.0.6", - "metro": "0.83.3", - "metro-babel-transformer": "0.83.3", - "metro-cache": "0.83.3", - "metro-cache-key": "0.83.3", - "metro-minify-terser": "0.83.3", - "metro-source-map": "0.83.3", - "metro-transform-plugins": "0.83.3", + "metro": "0.83.8", + "metro-babel-transformer": "0.83.8", + "metro-cache": "0.83.8", + "metro-cache-key": "0.83.8", + "metro-minify-terser": "0.83.8", + "metro-source-map": "0.83.8", + "metro-transform-plugins": "0.83.8", "nullthrows": "^1.1.1" }, "engines": { "node": ">=20.19.4" } }, + "node_modules/metro/node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/metro/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/metro/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/metro/node_modules/negotiator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.1.0.tgz", + "integrity": "sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg==", + "license": "MIT", + "dependencies": { + "content-type": "^2.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -9727,9 +9757,9 @@ "license": "MIT" }, "node_modules/ob1": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.83.3.tgz", - "integrity": "sha512-egUxXCDwoWG06NGCS5s5AdcpnumHKJlfd3HH06P3m9TEMwwScfcY35wpQxbm9oHof+dM/lVH9Rfyu1elTVelSA==", + "version": "0.83.8", + "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.83.8.tgz", + "integrity": "sha512-pk7el+eTOzfSKMAY4QBiiwKzegXn633JQj13y+pW5E5IdS+yV2CfDJ9Hf20K/LKy8nCrP9dAmd7XOk52OJxifA==", "license": "MIT", "dependencies": { "flow-enums-runtime": "^0.0.6" @@ -10428,15 +10458,6 @@ "qrcode-terminal": "bin/qrcode-terminal.js" } }, - "node_modules/queue": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", - "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", - "license": "MIT", - "dependencies": { - "inherits": "~2.0.3" - } - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -12785,9 +12806,9 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", - "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.1.tgz", + "integrity": "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==", "license": "ISC", "bin": { "yaml": "bin.mjs" diff --git a/apps/expo-example/package.json b/apps/expo-example/package.json index d6a71d7b..6ca3ec8f 100644 --- a/apps/expo-example/package.json +++ b/apps/expo-example/package.json @@ -23,5 +23,21 @@ "eslint": "^8.19.0", "eslint-config-expo": "^10.0.0" }, + "overrides": { + "metro": "0.83.8", + "metro-babel-transformer": "0.83.8", + "metro-cache": "0.83.8", + "metro-cache-key": "0.83.8", + "metro-config": "0.83.8", + "metro-core": "0.83.8", + "metro-file-map": "0.83.8", + "metro-minify-terser": "0.83.8", + "metro-resolver": "0.83.8", + "metro-runtime": "0.83.8", + "metro-source-map": "0.83.8", + "metro-symbolicate": "0.83.8", + "metro-transform-plugins": "0.83.8", + "metro-transform-worker": "0.83.8" + }, "private": true } diff --git a/package-lock.json b/package-lock.json index 577cdc64..c4f74dc7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2329,25 +2329,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/traverse--for-generate-function-map": { - "name": "@babel/traverse", - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", - "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/types": { "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", @@ -6031,6 +6012,7 @@ "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "devOptional": true, "license": "MIT", "dependencies": { "mime-types": "~2.1.34", @@ -6044,6 +6026,7 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "devOptional": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -14776,21 +14759,6 @@ "node": ">= 4" } }, - "node_modules/image-size": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.2.1.tgz", - "integrity": "sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==", - "license": "MIT", - "dependencies": { - "queue": "6.0.2" - }, - "bin": { - "image-size": "bin/image-size.js" - }, - "engines": { - "node": ">=16.x" - } - }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", @@ -18213,45 +18181,43 @@ } }, "node_modules/metro": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro/-/metro-0.83.3.tgz", - "integrity": "sha512-+rP+/GieOzkt97hSJ0MrPOuAH/jpaS21ZDvL9DJ35QYRDlQcwzcvUlGUf79AnQxq/2NPiS/AULhhM4TKutIt8Q==", + "version": "0.83.8", + "resolved": "https://registry.npmjs.org/metro/-/metro-0.83.8.tgz", + "integrity": "sha512-ZbJDJCDlvv0O3QHVbhZkyP2/DtebfEoDZ3wVyYHgu6Uoy6DmmE/bis6lIDBYh+kWPuaueJDegyutPjkIBLtZDQ==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.24.7", + "@babel/code-frame": "^7.29.0", "@babel/core": "^7.25.2", - "@babel/generator": "^7.25.0", - "@babel/parser": "^7.25.3", - "@babel/template": "^7.25.0", - "@babel/traverse": "^7.25.3", - "@babel/types": "^7.25.2", - "accepts": "^1.3.7", - "chalk": "^4.0.0", + "@babel/generator": "^7.29.1", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "accepts": "^2.0.0", "ci-info": "^2.0.0", "connect": "^3.6.5", "debug": "^4.4.0", "error-stack-parser": "^2.0.6", "flow-enums-runtime": "^0.0.6", "graceful-fs": "^4.2.4", - "hermes-parser": "0.32.0", - "image-size": "^1.0.2", + "hermes-parser": "0.35.0", "invariant": "^2.2.4", "jest-worker": "^29.7.0", "jsc-safe-url": "^0.2.2", "lodash.throttle": "^4.1.1", - "metro-babel-transformer": "0.83.3", - "metro-cache": "0.83.3", - "metro-cache-key": "0.83.3", - "metro-config": "0.83.3", - "metro-core": "0.83.3", - "metro-file-map": "0.83.3", - "metro-resolver": "0.83.3", - "metro-runtime": "0.83.3", - "metro-source-map": "0.83.3", - "metro-symbolicate": "0.83.3", - "metro-transform-plugins": "0.83.3", - "metro-transform-worker": "0.83.3", - "mime-types": "^2.1.27", + "metro-babel-transformer": "0.83.8", + "metro-cache": "0.83.8", + "metro-cache-key": "0.83.8", + "metro-config": "0.83.8", + "metro-core": "0.83.8", + "metro-file-map": "0.83.8", + "metro-resolver": "0.83.8", + "metro-runtime": "0.83.8", + "metro-source-map": "0.83.8", + "metro-symbolicate": "0.83.8", + "metro-transform-plugins": "0.83.8", + "metro-transform-worker": "0.83.8", + "mime-types": "^3.0.1", "nullthrows": "^1.1.1", "serialize-error": "^2.1.0", "source-map": "^0.5.6", @@ -18267,14 +18233,15 @@ } }, "node_modules/metro-babel-transformer": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.83.3.tgz", - "integrity": "sha512-1vxlvj2yY24ES1O5RsSIvg4a4WeL7PFXgKOHvXTXiW0deLvQr28ExXj6LjwCCDZ4YZLhq6HddLpZnX4dEdSq5g==", + "version": "0.83.8", + "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.83.8.tgz", + "integrity": "sha512-tnn0J5wzgTgTx2OJy3Cwr1y79bJz4eNgFQd+2HENOs5Vz6QOMnt05z7J+BedIo9wIbpEa0iN9U1nerxyvMRE9g==", "license": "MIT", "dependencies": { "@babel/core": "^7.25.2", "flow-enums-runtime": "^0.0.6", - "hermes-parser": "0.32.0", + "hermes-parser": "0.35.0", + "metro-cache-key": "0.83.8", "nullthrows": "^1.1.1" }, "engines": { @@ -18282,39 +18249,39 @@ } }, "node_modules/metro-babel-transformer/node_modules/hermes-estree": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.32.0.tgz", - "integrity": "sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ==", + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.35.0.tgz", + "integrity": "sha512-xVx5Opwy8Oo1I5yGpVRhCvWL/iV3M+ylksSKVNlxxD90cpDpR/AR1jLYqK8HWihm065a6UI3HeyAmYzwS8NOOg==", "license": "MIT" }, "node_modules/metro-babel-transformer/node_modules/hermes-parser": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.32.0.tgz", - "integrity": "sha512-g4nBOWFpuiTqjR3LZdRxKUkij9iyveWeuks7INEsMX741f3r9xxrOe8TeQfUxtda0eXmiIFiMQzoeSQEno33Hw==", + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.35.0.tgz", + "integrity": "sha512-9JLjeHxBx8T4CAsydZR49PNZUaix+WpQJwu9p2010lu+7Kwl6D/7wYFFJxoz+aXkaaClp9Zfg6W6/zVlSJORaA==", "license": "MIT", "dependencies": { - "hermes-estree": "0.32.0" + "hermes-estree": "0.35.0" } }, "node_modules/metro-cache": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.83.3.tgz", - "integrity": "sha512-3jo65X515mQJvKqK3vWRblxDEcgY55Sk3w4xa6LlfEXgQ9g1WgMh9m4qVZVwgcHoLy0a2HENTPCCX4Pk6s8c8Q==", + "version": "0.83.8", + "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.83.8.tgz", + "integrity": "sha512-aogMG5WbKzW5000otNjYrS9hIoORzkCI1faPJK+vxQLaf2BorJKBBFe/jl2Tfsi1mZUglS2EBuBt8B3JB7MYDQ==", "license": "MIT", "dependencies": { "exponential-backoff": "^3.1.1", "flow-enums-runtime": "^0.0.6", "https-proxy-agent": "^7.0.5", - "metro-core": "0.83.3" + "metro-core": "0.83.8" }, "engines": { "node": ">=20.19.4" } }, "node_modules/metro-cache-key": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.83.3.tgz", - "integrity": "sha512-59ZO049jKzSmvBmG/B5bZ6/dztP0ilp0o988nc6dpaDsU05Cl1c/lRf+yx8m9WW/JVgbmfO5MziBU559XjI5Zw==", + "version": "0.83.8", + "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.83.8.tgz", + "integrity": "sha512-I38PtcjT4crS5HY9UQ8i6z8S7tJ2WewtPGr/OwS6FcLKfy5T/1hlTaFw+wozUZkEtNpR6Gc0oJuvuKCbSoSN5A==", "license": "MIT", "dependencies": { "flow-enums-runtime": "^0.0.6" @@ -18324,18 +18291,18 @@ } }, "node_modules/metro-config": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.83.3.tgz", - "integrity": "sha512-mTel7ipT0yNjKILIan04bkJkuCzUUkm2SeEaTads8VfEecCh+ltXchdq6DovXJqzQAXuR2P9cxZB47Lg4klriA==", + "version": "0.83.8", + "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.83.8.tgz", + "integrity": "sha512-crNbNy+/B4tCne2+HjUshwvC57gNBQj+V9fFSy3lHH2RlKcLHib3Mil/SfTX8Pfh2fCY5pPuSu2OKa7YiadB+Q==", "license": "MIT", "dependencies": { "connect": "^3.6.5", "flow-enums-runtime": "^0.0.6", "jest-validate": "^29.7.0", - "metro": "0.83.3", - "metro-cache": "0.83.3", - "metro-core": "0.83.3", - "metro-runtime": "0.83.3", + "metro": "0.83.8", + "metro-cache": "0.83.8", + "metro-core": "0.83.8", + "metro-runtime": "0.83.8", "yaml": "^2.6.1" }, "engines": { @@ -18343,23 +18310,23 @@ } }, "node_modules/metro-core": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.83.3.tgz", - "integrity": "sha512-M+X59lm7oBmJZamc96usuF1kusd5YimqG/q97g4Ac7slnJ3YiGglW5CsOlicTR5EWf8MQFxxjDoB6ytTqRe8Hw==", + "version": "0.83.8", + "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.83.8.tgz", + "integrity": "sha512-NTyOUOQaQKvQgJG9VI2ymN6KTM7gHEqkVFhkPc9bK4BsHSTz/EaXuFBXtC+wtwINLeH9tbusL/jfsSmdEmuU7A==", "license": "MIT", "dependencies": { "flow-enums-runtime": "^0.0.6", "lodash.throttle": "^4.1.1", - "metro-resolver": "0.83.3" + "metro-resolver": "0.83.8" }, "engines": { "node": ">=20.19.4" } }, "node_modules/metro-file-map": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.83.3.tgz", - "integrity": "sha512-jg5AcyE0Q9Xbbu/4NAwwZkmQn7doJCKGW0SLeSJmzNB9Z24jBe0AL2PHNMy4eu0JiKtNWHz9IiONGZWq7hjVTA==", + "version": "0.83.8", + "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.83.8.tgz", + "integrity": "sha512-+W++EUuzEXIfWQEFTWQMVThzhWbnJL4gRNJ9WSHzIAM5pT7gsprUxo9+2hrfirURm/TLvrKwhq37oCECJcDSyQ==", "license": "MIT", "dependencies": { "debug": "^4.4.0", @@ -18377,9 +18344,9 @@ } }, "node_modules/metro-minify-terser": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.83.3.tgz", - "integrity": "sha512-O2BmfWj6FSfzBLrNCXt/rr2VYZdX5i6444QJU0fFoc7Ljg+Q+iqebwE3K0eTvkI6TRjELsXk1cjU+fXwAR4OjQ==", + "version": "0.83.8", + "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.83.8.tgz", + "integrity": "sha512-7tU0J5/c7LZaZJwTlOb1xq0NepTFvGzRxigDhZOq4jSE6g0BRHmBqe7XvLH3WcRiJNGy+eshcZr34RpMjb6mmg==", "license": "MIT", "dependencies": { "flow-enums-runtime": "^0.0.6", @@ -18390,9 +18357,9 @@ } }, "node_modules/metro-resolver": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.83.3.tgz", - "integrity": "sha512-0js+zwI5flFxb1ktmR///bxHYg7OLpRpWZlBBruYG8OKYxeMP7SV0xQ/o/hUelrEMdK4LJzqVtHAhBm25LVfAQ==", + "version": "0.83.8", + "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.83.8.tgz", + "integrity": "sha512-piU0NVTI9i37YztDVF5rtn9uxP3NebVT0xZM9NKJ9z0jbigrctUQksi3NoYIeJMvL6Wn2dgAehpcmmnfn+gUwA==", "license": "MIT", "dependencies": { "flow-enums-runtime": "^0.0.6" @@ -18402,9 +18369,9 @@ } }, "node_modules/metro-runtime": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.83.3.tgz", - "integrity": "sha512-JHCJb9ebr9rfJ+LcssFYA2x1qPYuSD/bbePupIGhpMrsla7RCwC/VL3yJ9cSU+nUhU4c9Ixxy8tBta+JbDeZWw==", + "version": "0.83.8", + "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.83.8.tgz", + "integrity": "sha512-f7FfeM0pamq8vrvs8aO9KvIUabbUKe0WkHFpLt6Q9yIIIsORqNFwlgJeHGraOFPU7Cxqj5yLXkJu5bT1uwDXvw==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.25.0", @@ -18415,19 +18382,18 @@ } }, "node_modules/metro-source-map": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.83.3.tgz", - "integrity": "sha512-xkC3qwUBh2psVZgVavo8+r2C9Igkk3DibiOXSAht1aYRRcztEZNFtAMtfSB7sdO2iFMx2Mlyu++cBxz/fhdzQg==", + "version": "0.83.8", + "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.83.8.tgz", + "integrity": "sha512-60Uor7bM+KsVewLkLCcZfkPFCbqjPDdoSmeBuT3+ye+ac80BuLWbbR8DpyxWpUqQeZPdaAT5ZqFgDIs8BNEcVA==", "license": "MIT", "dependencies": { - "@babel/traverse": "^7.25.3", - "@babel/traverse--for-generate-function-map": "npm:@babel/traverse@^7.25.3", - "@babel/types": "^7.25.2", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", - "metro-symbolicate": "0.83.3", + "metro-symbolicate": "0.83.8", "nullthrows": "^1.1.1", - "ob1": "0.83.3", + "ob1": "0.83.8", "source-map": "^0.5.6", "vlq": "^1.0.0" }, @@ -18445,14 +18411,14 @@ } }, "node_modules/metro-symbolicate": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.83.3.tgz", - "integrity": "sha512-F/YChgKd6KbFK3eUR5HdUsfBqVsanf5lNTwFd4Ca7uuxnHgBC3kR/Hba/RGkenR3pZaGNp5Bu9ZqqP52Wyhomw==", + "version": "0.83.8", + "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.83.8.tgz", + "integrity": "sha512-ZLrbqOqQ+m+Mpv7zo65XEXkNqVYvgUbY7tIsb9e1zSSmU/4C3D6DjaJvRHqcAdl0mkg8TI/csKooAEa5xcZNwg==", "license": "MIT", "dependencies": { "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", - "metro-source-map": "0.83.3", + "metro-source-map": "0.83.8", "nullthrows": "^1.1.1", "source-map": "^0.5.6", "vlq": "^1.0.0" @@ -18474,15 +18440,15 @@ } }, "node_modules/metro-transform-plugins": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.83.3.tgz", - "integrity": "sha512-eRGoKJU6jmqOakBMH5kUB7VitEWiNrDzBHpYbkBXW7C5fUGeOd2CyqrosEzbMK5VMiZYyOcNFEphvxk3OXey2A==", + "version": "0.83.8", + "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.83.8.tgz", + "integrity": "sha512-9JRPkvi+m0QH2Y/w5RjCF9mHqOUNFpMFLDDLhKUjhcKESh8Wm3HKDdHXHVldTN1lTToCIabv81nF0zyJzKEJ5g==", "license": "MIT", "dependencies": { "@babel/core": "^7.25.2", - "@babel/generator": "^7.25.0", - "@babel/template": "^7.25.0", - "@babel/traverse": "^7.25.3", + "@babel/generator": "^7.29.1", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", "flow-enums-runtime": "^0.0.6", "nullthrows": "^1.1.1" }, @@ -18491,48 +18457,106 @@ } }, "node_modules/metro-transform-worker": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.83.3.tgz", - "integrity": "sha512-Ztekew9t/gOIMZX1tvJOgX7KlSLL5kWykl0Iwu2cL2vKMKVALRl1hysyhUw0vjpAvLFx+Kfq9VLjnHIkW32fPA==", + "version": "0.83.8", + "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.83.8.tgz", + "integrity": "sha512-Pa2hOfhUmWpI/dmkhsLq8uGyFHK2opoEK7j/YCiRtqNSe0YzzxYguXTNgg8AMiuZfc9LPcB1AhGENS10O5wcIw==", "license": "MIT", "dependencies": { "@babel/core": "^7.25.2", - "@babel/generator": "^7.25.0", - "@babel/parser": "^7.25.3", - "@babel/types": "^7.25.2", + "@babel/generator": "^7.29.1", + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", "flow-enums-runtime": "^0.0.6", - "metro": "0.83.3", - "metro-babel-transformer": "0.83.3", - "metro-cache": "0.83.3", - "metro-cache-key": "0.83.3", - "metro-minify-terser": "0.83.3", - "metro-source-map": "0.83.3", - "metro-transform-plugins": "0.83.3", + "metro": "0.83.8", + "metro-babel-transformer": "0.83.8", + "metro-cache": "0.83.8", + "metro-cache-key": "0.83.8", + "metro-minify-terser": "0.83.8", + "metro-source-map": "0.83.8", + "metro-transform-plugins": "0.83.8", "nullthrows": "^1.1.1" }, "engines": { "node": ">=20.19.4" } }, + "node_modules/metro/node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/metro/node_modules/ci-info": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", "license": "MIT" }, + "node_modules/metro/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/metro/node_modules/hermes-estree": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.32.0.tgz", - "integrity": "sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ==", + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.35.0.tgz", + "integrity": "sha512-xVx5Opwy8Oo1I5yGpVRhCvWL/iV3M+ylksSKVNlxxD90cpDpR/AR1jLYqK8HWihm065a6UI3HeyAmYzwS8NOOg==", "license": "MIT" }, "node_modules/metro/node_modules/hermes-parser": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.32.0.tgz", - "integrity": "sha512-g4nBOWFpuiTqjR3LZdRxKUkij9iyveWeuks7INEsMX741f3r9xxrOe8TeQfUxtda0eXmiIFiMQzoeSQEno33Hw==", + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.35.0.tgz", + "integrity": "sha512-9JLjeHxBx8T4CAsydZR49PNZUaix+WpQJwu9p2010lu+7Kwl6D/7wYFFJxoz+aXkaaClp9Zfg6W6/zVlSJORaA==", + "license": "MIT", + "dependencies": { + "hermes-estree": "0.35.0" + } + }, + "node_modules/metro/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/metro/node_modules/negotiator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.1.0.tgz", + "integrity": "sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg==", "license": "MIT", "dependencies": { - "hermes-estree": "0.32.0" + "content-type": "^2.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/metro/node_modules/source-map": { @@ -18595,7 +18619,6 @@ "version": "1.54.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "devOptional": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -18605,6 +18628,7 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "devOptional": true, "license": "MIT", "dependencies": { "mime-db": "1.52.0" @@ -18617,6 +18641,7 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "devOptional": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -19097,9 +19122,9 @@ "license": "MIT" }, "node_modules/ob1": { - "version": "0.83.3", - "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.83.3.tgz", - "integrity": "sha512-egUxXCDwoWG06NGCS5s5AdcpnumHKJlfd3HH06P3m9TEMwwScfcY35wpQxbm9oHof+dM/lVH9Rfyu1elTVelSA==", + "version": "0.83.8", + "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.83.8.tgz", + "integrity": "sha512-pk7el+eTOzfSKMAY4QBiiwKzegXn633JQj13y+pW5E5IdS+yV2CfDJ9Hf20K/LKy8nCrP9dAmd7XOk52OJxifA==", "license": "MIT", "dependencies": { "flow-enums-runtime": "^0.0.6" @@ -20261,15 +20286,6 @@ "dev": true, "license": "MIT" }, - "node_modules/queue": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", - "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", - "license": "MIT", - "dependencies": { - "inherits": "~2.0.3" - } - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", diff --git a/package.json b/package.json index 7e48e1de..63dcc8c3 100644 --- a/package.json +++ b/package.json @@ -119,6 +119,22 @@ "react": "*", "react-native": "*" }, + "overrides": { + "metro": "0.83.8", + "metro-babel-transformer": "0.83.8", + "metro-cache": "0.83.8", + "metro-cache-key": "0.83.8", + "metro-config": "0.83.8", + "metro-core": "0.83.8", + "metro-file-map": "0.83.8", + "metro-minify-terser": "0.83.8", + "metro-resolver": "0.83.8", + "metro-runtime": "0.83.8", + "metro-source-map": "0.83.8", + "metro-symbolicate": "0.83.8", + "metro-transform-plugins": "0.83.8", + "metro-transform-worker": "0.83.8" + }, "jest": { "preset": "react-native", "modulePathIgnorePatterns": [ From 827773fc446460d95179b26ba153eab0190d9f3e Mon Sep 17 00:00:00 2001 From: MelvinBot Date: Mon, 26 Oct 2026 13:21:52 +0000 Subject: [PATCH 052/118] Bump form-data to 4.0.6 --- package-lock.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 577cdc64..844c28b7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13718,17 +13718,17 @@ } }, "node_modules/form-data": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", - "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" + "hasown": "^2.0.4", + "mime-types": "^2.1.35" }, "engines": { "node": ">= 6" @@ -14553,9 +14553,9 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" From 2114d9de6fe75b856aa39a25578981dccecde4fc Mon Sep 17 00:00:00 2001 From: MelvinBot Date: Mon, 26 Oct 2026 13:21:55 +0000 Subject: [PATCH 053/118] Bump @babel/plugin-transform-modules-systemjs to 7.29.4 --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 577cdc64..8fde09ba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1560,9 +1560,9 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.0.tgz", - "integrity": "sha512-PrujnVFbOdUpw4UHiVwKvKRLMMic8+eC0CuNlxjsyZUiBjhFdPsewdXCkveh2KqBA9/waD0W1b4hXSOBQJezpQ==", + "version": "7.29.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.4.tgz", + "integrity": "sha512-N7QmZ0xRZfjHOfZeQLJjwgX2zS9pdGHSVl/cjSGlo4dXMqvurfxXDMKY4RqEKzPozV78VMcd0lxyG13mlbKc4w==", "dev": true, "license": "MIT", "dependencies": { From d3023d920a3342bbadbac763bc1372645fa01998 Mon Sep 17 00:00:00 2001 From: MelvinBot Date: Mon, 26 Oct 2026 13:22:09 +0000 Subject: [PATCH 054/118] fix: bump postcss to 8.5.31 via overrides --- apps/expo-example/package-lock.json | 22 +++++++++++----------- apps/expo-example/package.json | 3 +++ package-lock.json | 22 +++++++++++----------- package.json | 3 +++ 4 files changed, 28 insertions(+), 22 deletions(-) diff --git a/apps/expo-example/package-lock.json b/apps/expo-example/package-lock.json index b30eea4f..b9bf3cec 100644 --- a/apps/expo-example/package-lock.json +++ b/apps/expo-example/package-lock.json @@ -9608,9 +9608,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", "funding": [ { "type": "github", @@ -10277,9 +10277,9 @@ } }, "node_modules/postcss": { - "version": "8.4.49", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", - "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "version": "8.5.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.31.tgz", + "integrity": "sha512-YUbWYCgErwnMAVkjUnYa+KlMBzGpHwiIt/DE7f0G33AuX2LH9UcTk6KgWxd3zRU+pN3u/C+Xl3EnMLM4Hea7Sg==", "funding": [ { "type": "opencollective", @@ -10296,9 +10296,9 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.7", + "nanoid": "^3.3.20", "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" + "source-map-js": "^1.2.2" }, "engines": { "node": "^10 || ^12 || >=14" @@ -11398,9 +11398,9 @@ } }, "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" diff --git a/apps/expo-example/package.json b/apps/expo-example/package.json index d6a71d7b..cca3d7a3 100644 --- a/apps/expo-example/package.json +++ b/apps/expo-example/package.json @@ -23,5 +23,8 @@ "eslint": "^8.19.0", "eslint-config-expo": "^10.0.0" }, + "overrides": { + "postcss": "^8.5.23" + }, "private": true } diff --git a/package-lock.json b/package-lock.json index 577cdc64..55594b98 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18782,9 +18782,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", "dev": true, "funding": [ { @@ -19899,9 +19899,9 @@ } }, "node_modules/postcss": { - "version": "8.4.49", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", - "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "version": "8.5.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.31.tgz", + "integrity": "sha512-YUbWYCgErwnMAVkjUnYa+KlMBzGpHwiIt/DE7f0G33AuX2LH9UcTk6KgWxd3zRU+pN3u/C+Xl3EnMLM4Hea7Sg==", "dev": true, "funding": [ { @@ -19920,9 +19920,9 @@ "license": "MIT", "peer": true, "dependencies": { - "nanoid": "^3.3.7", + "nanoid": "^3.3.20", "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" + "source-map-js": "^1.2.2" }, "engines": { "node": "^10 || ^12 || >=14" @@ -22542,9 +22542,9 @@ } }, "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", "dev": true, "license": "BSD-3-Clause", "peer": true, diff --git a/package.json b/package.json index 7e48e1de..ecbbbe2a 100644 --- a/package.json +++ b/package.json @@ -119,6 +119,9 @@ "react": "*", "react-native": "*" }, + "overrides": { + "postcss": "^8.5.23" + }, "jest": { "preset": "react-native", "modulePathIgnorePatterns": [ From 14b0284fcee6b59e0bdd09ab5c33260d3f41ea7d Mon Sep 17 00:00:00 2001 From: MelvinBot Date: Mon, 26 Oct 2026 13:22:56 +0000 Subject: [PATCH 055/118] Bump lodash to 4.18.1 --- package-lock.json | 13 +++---------- package.json | 3 +++ 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index 577cdc64..e9f1353d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17629,9 +17629,9 @@ } }, "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "dev": true, "license": "MIT" }, @@ -21399,13 +21399,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/release-it/node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true, - "license": "MIT" - }, "node_modules/release-it/node_modules/log-symbols": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", diff --git a/package.json b/package.json index 7e48e1de..28d9dfb1 100644 --- a/package.json +++ b/package.json @@ -115,6 +115,9 @@ "turbo": "^1.10.7", "typescript": "^5.8.3" }, + "overrides": { + "lodash": "^4.18.1" + }, "peerDependencies": { "react": "*", "react-native": "*" From 7c8147c18b81a6691eb08c75a7efb96fe0395a02 Mon Sep 17 00:00:00 2001 From: MelvinBot Date: Mon, 26 Oct 2026 13:23:03 +0000 Subject: [PATCH 056/118] Override tmp to ^0.2.6 to fix Dependabot alerts --- package-lock.json | 21 ++++----------------- package.json | 3 +++ 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index 577cdc64..c320a614 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19350,16 +19350,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/own-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", @@ -23491,16 +23481,13 @@ } }, "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz", + "integrity": "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==", "dev": true, "license": "MIT", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, "engines": { - "node": ">=0.6.0" + "node": ">=14.14" } }, "node_modules/tmpl": { diff --git a/package.json b/package.json index 7e48e1de..321ea8b9 100644 --- a/package.json +++ b/package.json @@ -119,6 +119,9 @@ "react": "*", "react-native": "*" }, + "overrides": { + "tmp": "^0.2.6" + }, "jest": { "preset": "react-native", "modulePathIgnorePatterns": [ From de813aa8cb15a0329f4595dc3a2fe82c4932af46 Mon Sep 17 00:00:00 2001 From: MelvinBot Date: Mon, 26 Oct 2026 13:23:39 +0000 Subject: [PATCH 057/118] Bump transitive ws to 6.2.6, 7.5.13, 8.21.3 to patch GHSA-96hv-2xvq-fx4p --- apps/expo-example/package-lock.json | 30 ++++++++++++++--------------- package-lock.json | 30 ++++++++++++++--------------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/apps/expo-example/package-lock.json b/apps/expo-example/package-lock.json index b30eea4f..dd854960 100644 --- a/apps/expo-example/package-lock.json +++ b/apps/expo-example/package-lock.json @@ -2983,9 +2983,9 @@ } }, "node_modules/@react-native/dev-middleware/node_modules/ws": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz", - "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", + "version": "6.2.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.6.tgz", + "integrity": "sha512-XTrf1gv7kXoVf1hbC3PAyAiPgR8Wz1blcrYIjEsUmr08BLksT41R8KbjmS9408C2ERx7v1JDLD/BkpLEttjfKA==", "license": "MIT", "dependencies": { "async-limiter": "~1.0.0" @@ -6473,9 +6473,9 @@ } }, "node_modules/expo/node_modules/@react-native/dev-middleware/node_modules/ws": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz", - "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", + "version": "6.2.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.6.tgz", + "integrity": "sha512-XTrf1gv7kXoVf1hbC3PAyAiPgR8Wz1blcrYIjEsUmr08BLksT41R8KbjmS9408C2ERx7v1JDLD/BkpLEttjfKA==", "license": "MIT", "dependencies": { "async-limiter": "~1.0.0" @@ -7053,9 +7053,9 @@ } }, "node_modules/expo/node_modules/ws": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", - "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz", + "integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==", "license": "MIT", "engines": { "node": ">=10.0.0" @@ -10659,9 +10659,9 @@ } }, "node_modules/react-native/node_modules/ws": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz", - "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", + "version": "6.2.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.6.tgz", + "integrity": "sha512-XTrf1gv7kXoVf1hbC3PAyAiPgR8Wz1blcrYIjEsUmr08BLksT41R8KbjmS9408C2ERx7v1JDLD/BkpLEttjfKA==", "license": "MIT", "dependencies": { "async-limiter": "~1.0.0" @@ -12705,9 +12705,9 @@ "license": "ISC" }, "node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "version": "7.5.13", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.13.tgz", + "integrity": "sha512-rsKI6xDBFVf4r/x8XyChGK04QR/XHroxs/jUcoWvtEZM8TPU/X/uIY9B1CsSzYws9ZJb/6bbBu7dPhFW00CAoA==", "license": "MIT", "engines": { "node": ">=8.3.0" diff --git a/package-lock.json b/package-lock.json index 577cdc64..692a8948 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11944,9 +11944,9 @@ } }, "node_modules/expo-module-scripts/node_modules/@expo/cli/node_modules/ws": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", - "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz", + "integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==", "dev": true, "license": "MIT", "peer": true, @@ -17006,9 +17006,9 @@ } }, "node_modules/jsdom/node_modules/ws": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", - "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz", + "integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==", "dev": true, "license": "MIT", "engines": { @@ -18545,9 +18545,9 @@ } }, "node_modules/metro/node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "version": "7.5.13", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.13.tgz", + "integrity": "sha512-rsKI6xDBFVf4r/x8XyChGK04QR/XHroxs/jUcoWvtEZM8TPU/X/uIY9B1CsSzYws9ZJb/6bbBu7dPhFW00CAoA==", "license": "MIT", "engines": { "node": ">=8.3.0" @@ -20372,9 +20372,9 @@ } }, "node_modules/react-devtools-core/node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "version": "7.5.13", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.13.tgz", + "integrity": "sha512-rsKI6xDBFVf4r/x8XyChGK04QR/XHroxs/jUcoWvtEZM8TPU/X/uIY9B1CsSzYws9ZJb/6bbBu7dPhFW00CAoA==", "license": "MIT", "engines": { "node": ">=8.3.0" @@ -24825,9 +24825,9 @@ } }, "node_modules/ws": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz", - "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", + "version": "6.2.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.6.tgz", + "integrity": "sha512-XTrf1gv7kXoVf1hbC3PAyAiPgR8Wz1blcrYIjEsUmr08BLksT41R8KbjmS9408C2ERx7v1JDLD/BkpLEttjfKA==", "license": "MIT", "dependencies": { "async-limiter": "~1.0.0" From ffff68af635a1aa5d7d685c5c2a170d8afd5da0f Mon Sep 17 00:00:00 2001 From: MelvinBot Date: Mon, 26 Oct 2026 13:23:45 +0000 Subject: [PATCH 058/118] Bump undici to 6.29.1 to address Dependabot alerts --- apps/expo-example/package-lock.json | 6 +++--- package-lock.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/expo-example/package-lock.json b/apps/expo-example/package-lock.json index b30eea4f..b485642e 100644 --- a/apps/expo-example/package-lock.json +++ b/apps/expo-example/package-lock.json @@ -12219,9 +12219,9 @@ } }, "node_modules/undici": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz", - "integrity": "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==", + "version": "6.29.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.29.1.tgz", + "integrity": "sha512-EvRlUt/QqAEvXdQeRMYUfwOQsfzpDztykrG2owGEE/rBOBoYv9uF39ZzsNDh4lTFHhTr2XOZjKkYgueD12ZmGA==", "license": "MIT", "engines": { "node": ">=18.17" diff --git a/package-lock.json b/package-lock.json index 577cdc64..ee96c13d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24073,9 +24073,9 @@ "license": "MIT" }, "node_modules/undici": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz", - "integrity": "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==", + "version": "6.29.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.29.1.tgz", + "integrity": "sha512-EvRlUt/QqAEvXdQeRMYUfwOQsfzpDztykrG2owGEE/rBOBoYv9uF39ZzsNDh4lTFHhTr2XOZjKkYgueD12ZmGA==", "dev": true, "license": "MIT", "peer": true, From ac331fef7163b3578b239680e6606fa89b5f4570 Mon Sep 17 00:00:00 2001 From: MelvinBot Date: Mon, 26 Oct 2026 13:23:51 +0000 Subject: [PATCH 059/118] Bump basic-ftp from 5.1.0 to 5.3.1 --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 577cdc64..54be6dea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6914,9 +6914,9 @@ } }, "node_modules/basic-ftp": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.1.0.tgz", - "integrity": "sha512-RkaJzeJKDbaDWTIPiJwubyljaEPwpVWkm9Rt5h9Nd6h7tEXTJ3VB4qxdZBioV7JO5yLUaOKwz7vDOzlncUsegw==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.3.1.tgz", + "integrity": "sha512-bopVNp6ugyA150DDuZfPFdt1KZ5a94ZDiwX4hMgZDzF+GttD80lEy8kj98kbyhLXnPvhtIo93mdnLIjpCAeeOw==", "dev": true, "license": "MIT", "engines": { From 1217ef704569d83a95ac29a7e7b87c246ead4f20 Mon Sep 17 00:00:00 2001 From: MelvinBot Date: Mon, 26 Oct 2026 13:23:58 +0000 Subject: [PATCH 060/118] Bump picomatch to 2.3.2, 3.0.2, and 4.0.4 to patch ReDoS and glob-matching CVEs --- apps/expo-example/package-lock.json | 18 +++++++++--------- package-lock.json | 24 ++++++++++++------------ 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/apps/expo-example/package-lock.json b/apps/expo-example/package-lock.json index b30eea4f..efafa924 100644 --- a/apps/expo-example/package-lock.json +++ b/apps/expo-example/package-lock.json @@ -6976,9 +6976,9 @@ } }, "node_modules/expo/node_modules/picomatch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.1.tgz", - "integrity": "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.2.tgz", + "integrity": "sha512-cfDHL6LStTEKlNilboNtobT/kEa30PtAf2Q1OgszfrG/rpVl1xaFWT9ktfkS306GmHgmnad1Sw4wabhlvFtsTw==", "license": "MIT", "engines": { "node": ">=10" @@ -10205,9 +10205,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "license": "MIT", "engines": { "node": ">=8.6" @@ -11980,9 +11980,9 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "license": "MIT", "engines": { "node": ">=12" diff --git a/package-lock.json b/package-lock.json index 577cdc64..cacbeae8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13206,9 +13206,9 @@ } }, "node_modules/expo-module-scripts/node_modules/picomatch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.1.tgz", - "integrity": "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.2.tgz", + "integrity": "sha512-cfDHL6LStTEKlNilboNtobT/kEa30PtAf2Q1OgszfrG/rpVl1xaFWT9ktfkS306GmHgmnad1Sw4wabhlvFtsTw==", "dev": true, "license": "MIT", "peer": true, @@ -19684,9 +19684,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "license": "MIT", "engines": { "node": ">=8.6" @@ -23465,9 +23465,9 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { @@ -23610,9 +23610,9 @@ } }, "node_modules/ts-declaration-location/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { From 2456c8274b7912f190e478501707d6b676c8859b Mon Sep 17 00:00:00 2001 From: MelvinBot Date: Mon, 26 Oct 2026 13:24:04 +0000 Subject: [PATCH 061/118] chore(deps): bump tar from 7.5.7 to 7.5.22 --- apps/expo-example/package-lock.json | 6 +++--- package-lock.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/expo-example/package-lock.json b/apps/expo-example/package-lock.json index b30eea4f..8968cc2b 100644 --- a/apps/expo-example/package-lock.json +++ b/apps/expo-example/package-lock.json @@ -11804,9 +11804,9 @@ } }, "node_modules/tar": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.7.tgz", - "integrity": "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==", + "version": "7.5.22", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.22.tgz", + "integrity": "sha512-MFO/QzvtAOmJbkhOaCTvbGcFN9L9b+JunIsDwaKljSOdcLMea3NJ1k9Usz/rjdfSXTq4dfzfeS7W4p4YOAAHeA==", "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", diff --git a/package-lock.json b/package-lock.json index 577cdc64..08acbe8f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23228,9 +23228,9 @@ } }, "node_modules/tar": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.7.tgz", - "integrity": "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==", + "version": "7.5.22", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.22.tgz", + "integrity": "sha512-MFO/QzvtAOmJbkhOaCTvbGcFN9L9b+JunIsDwaKljSOdcLMea3NJ1k9Usz/rjdfSXTq4dfzfeS7W4p4YOAAHeA==", "dev": true, "license": "BlueOak-1.0.0", "peer": true, From da2ec2be0cee952266cdd806f00060ed0125460f Mon Sep 17 00:00:00 2001 From: MelvinBot Date: Mon, 26 Oct 2026 13:24:26 +0000 Subject: [PATCH 062/118] chore(deps): bump flatted to 3.4.4 to patch GHSA-rf6f-7fwh-wjgh --- apps/expo-example/package-lock.json | 6 +++--- package-lock.json | 13 +++---------- package.json | 3 +++ 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/apps/expo-example/package-lock.json b/apps/expo-example/package-lock.json index b30eea4f..3ed5e450 100644 --- a/apps/expo-example/package-lock.json +++ b/apps/expo-example/package-lock.json @@ -7218,9 +7218,9 @@ } }, "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", "dev": true, "license": "ISC" }, diff --git a/package-lock.json b/package-lock.json index 577cdc64..ad711998 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10460,13 +10460,6 @@ "node": ">=4" } }, - "node_modules/eslint-config-expensify/node_modules/flatted": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", - "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", - "dev": true, - "license": "ISC" - }, "node_modules/eslint-config-expensify/node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -13681,9 +13674,9 @@ } }, "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", "dev": true, "license": "ISC" }, diff --git a/package.json b/package.json index 7e48e1de..5e951cd6 100644 --- a/package.json +++ b/package.json @@ -119,6 +119,9 @@ "react": "*", "react-native": "*" }, + "overrides": { + "flatted": "^3.4.2" + }, "jest": { "preset": "react-native", "modulePathIgnorePatterns": [ From 47c2bdfc178fd148ce6c944199178c82dd202685 Mon Sep 17 00:00:00 2001 From: MelvinBot Date: Mon, 26 Oct 2026 13:24:39 +0000 Subject: [PATCH 063/118] Bump addressable to 2.9.0 in basic-example --- apps/basic-example/Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/basic-example/Gemfile.lock b/apps/basic-example/Gemfile.lock index 0c412bfe..a84677e7 100644 --- a/apps/basic-example/Gemfile.lock +++ b/apps/basic-example/Gemfile.lock @@ -15,8 +15,8 @@ GEM minitest (>= 5.1) mutex_m tzinfo (~> 2.0) - addressable (2.8.7) - public_suffix (>= 2.0.2, < 7.0) + addressable (2.9.0) + public_suffix (>= 2.0.2, < 8.0) algoliasearch (1.27.5) httpclient (~> 2.8, >= 2.8.3) json (>= 1.5.1) From 472698fdf3c1cca19c68f058bd056267b6b10013 Mon Sep 17 00:00:00 2001 From: MelvinBot Date: Mon, 26 Oct 2026 13:25:56 +0000 Subject: [PATCH 064/118] Bump rexml to 3.4.4 in basic-example --- apps/basic-example/Gemfile.lock | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/basic-example/Gemfile.lock b/apps/basic-example/Gemfile.lock index 0c412bfe..d50aa0eb 100644 --- a/apps/basic-example/Gemfile.lock +++ b/apps/basic-example/Gemfile.lock @@ -84,10 +84,8 @@ GEM netrc (0.11.0) nkf (0.2.0) public_suffix (4.0.7) - rexml (3.2.9) - strscan + rexml (3.4.4) ruby-macho (2.5.1) - strscan (3.1.0) typhoeus (1.4.1) ethon (>= 0.9.0) tzinfo (2.0.6) From e3d7b785fd635050bdafe7ff168e12633ce14f8d Mon Sep 17 00:00:00 2001 From: MelvinBot Date: Mon, 26 Oct 2026 13:26:13 +0000 Subject: [PATCH 065/118] Bump concurrent-ruby to 1.3.8 in basic-example --- apps/basic-example/Gemfile | 2 +- apps/basic-example/Gemfile.lock | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/apps/basic-example/Gemfile b/apps/basic-example/Gemfile index 2d625b45..4a3af07f 100644 --- a/apps/basic-example/Gemfile +++ b/apps/basic-example/Gemfile @@ -6,7 +6,7 @@ ruby ">= 2.6.10" # Exclude problematic versions of cocoapods and activesupport that causes build failures. gem 'cocoapods', '>= 1.13', '!= 1.15.0', '!= 1.15.1' gem 'activesupport', '>= 6.1.7.5', '!= 7.1.0' -gem 'concurrent-ruby', '< 1.3.4' +gem 'concurrent-ruby', '>= 1.3.7' # Ruby 3.4.0 has removed some libraries from the standard library. gem 'bigdecimal' diff --git a/apps/basic-example/Gemfile.lock b/apps/basic-example/Gemfile.lock index 0c412bfe..1f230f2c 100644 --- a/apps/basic-example/Gemfile.lock +++ b/apps/basic-example/Gemfile.lock @@ -22,6 +22,7 @@ GEM json (>= 1.5.1) atomos (0.1.3) base64 (0.2.0) + benchmark (0.5.0) bigdecimal (3.1.8) claide (1.1.0) cocoapods (1.15.2) @@ -62,7 +63,7 @@ GEM netrc (~> 0.11) cocoapods-try (1.2.0) colored2 (3.1.2) - concurrent-ruby (1.3.3) + concurrent-ruby (1.3.8) connection_pool (2.4.1) drb (2.2.1) escape (0.0.4) @@ -76,6 +77,7 @@ GEM i18n (1.14.5) concurrent-ruby (~> 1.0) json (2.7.2) + logger (1.7.0) minitest (5.24.1) molinillo (0.8.0) mutex_m (0.2.0) @@ -105,7 +107,12 @@ PLATFORMS DEPENDENCIES activesupport (>= 6.1.7.5, != 7.1.0) + benchmark + bigdecimal cocoapods (>= 1.13, != 1.15.1, != 1.15.0) + concurrent-ruby (>= 1.3.7) + logger + mutex_m RUBY VERSION ruby 3.2.1p31 From b22831686ed82875b58e7691f1ab90a71ef8b1f3 Mon Sep 17 00:00:00 2001 From: MelvinBot Date: Mon, 26 Oct 2026 13:26:33 +0000 Subject: [PATCH 066/118] Override release-it's proxy-agent to 6.5.0 to remove vulnerable ip package --- package-lock.json | 207 ++++++++++++---------------------------------- package.json | 5 ++ 2 files changed, 56 insertions(+), 156 deletions(-) diff --git a/package-lock.json b/package-lock.json index 577cdc64..229f6640 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5427,6 +5427,13 @@ "node": ">= 10" } }, + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "dev": true, + "license": "MIT" + }, "node_modules/@tsconfig/node18": { "version": "18.2.6", "resolved": "https://registry.npmjs.org/@tsconfig/node18/-/node18-18.2.6.tgz", @@ -6914,9 +6921,9 @@ } }, "node_modules/basic-ftp": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.1.0.tgz", - "integrity": "sha512-RkaJzeJKDbaDWTIPiJwubyljaEPwpVWkm9Rt5h9Nd6h7tEXTJ3VB4qxdZBioV7JO5yLUaOKwz7vDOzlncUsegw==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.3.1.tgz", + "integrity": "sha512-bopVNp6ugyA150DDuZfPFdt1KZ5a94ZDiwX4hMgZDzF+GttD80lEy8kj98kbyhLXnPvhtIo93mdnLIjpCAeeOw==", "dev": true, "license": "MIT", "engines": { @@ -8999,16 +9006,15 @@ } }, "node_modules/degenerator": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-4.0.4.tgz", - "integrity": "sha512-MTZdZsuNxSBL92rsjx3VFWe57OpRlikyLbcx2B5Dmdv6oScqpMrvpY7zHLMymrUxo3U5+suPUMsNgW/+SZB1lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", "dev": true, "license": "MIT", "dependencies": { "ast-types": "^0.13.4", - "escodegen": "^1.14.3", - "esprima": "^4.0.1", - "vm2": "^3.9.19" + "escodegen": "^2.1.0", + "esprima": "^4.0.1" }, "engines": { "node": ">= 14" @@ -9847,92 +9853,27 @@ } }, "node_modules/escodegen": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", - "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", "dev": true, "license": "BSD-2-Clause", "dependencies": { "esprima": "^4.0.1", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1" + "estraverse": "^5.2.0", + "esutils": "^2.0.2" }, "bin": { "escodegen": "bin/escodegen.js", "esgenerate": "bin/esgenerate.js" }, "engines": { - "node": ">=4.0" + "node": ">=6.0" }, "optionalDependencies": { "source-map": "~0.6.1" } }, - "node_modules/escodegen/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/escodegen/node_modules/levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/escodegen/node_modules/optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/escodegen/node_modules/prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/escodegen/node_modules/type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/eslint": { "version": "8.57.1", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", @@ -14968,17 +14909,10 @@ "loose-envify": "^1.0.0" } }, - "node_modules/ip": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.9.tgz", - "integrity": "sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ==", - "dev": true, - "license": "MIT" - }, "node_modules/ip-address": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", - "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "version": "10.7.4", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.4.tgz", + "integrity": "sha512-DWBMKyeM25D70nrIiTkgQth59/G+naKOTep6huF9j+GmeK3DMD60Zvxg5d5qQnz+38AHgou6ean2LECX+lrKDg==", "dev": true, "license": "MIT", "engines": { @@ -16917,28 +16851,6 @@ "node": ">= 6.0.0" } }, - "node_modules/jsdom/node_modules/escodegen": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, "node_modules/jsdom/node_modules/http-proxy-agent": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", @@ -18834,9 +18746,9 @@ "peer": true }, "node_modules/netmask": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", - "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.1.1.tgz", + "integrity": "sha512-eonl3sLUha+S1GzTPxychyhnUzKyeQkZ7jLjKrBagJgPla13F+uQ71HgpFefyHgqrjEbCPkDArxYsjY8/+gLKA==", "dev": true, "license": "MIT", "engines": { @@ -19446,33 +19358,33 @@ } }, "node_modules/pac-proxy-agent": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-6.0.4.tgz", - "integrity": "sha512-FbJYeusBOZNe6bmrC2/+r/HljwExryon16lNKEU82gWiwIPMCEktUPSEAcTkO9K3jd/YPGuX/azZel1ltmo6nQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", + "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", "dev": true, "license": "MIT", "dependencies": { - "agent-base": "^7.0.2", + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.1.2", "debug": "^4.3.4", "get-uri": "^6.0.1", "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.0", - "pac-resolver": "^6.0.1", - "socks-proxy-agent": "^8.0.1" + "https-proxy-agent": "^7.0.6", + "pac-resolver": "^7.0.1", + "socks-proxy-agent": "^8.0.5" }, "engines": { "node": ">= 14" } }, "node_modules/pac-resolver": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-6.0.2.tgz", - "integrity": "sha512-EQpuJ2ifOjpZY5sg1Q1ZeAxvtLwR7Mj3RgY8cysPGbsRu3RBXyJFWxnMus9PScjxya/0LzvVDxNh/gl0eXBU4w==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", + "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", "dev": true, "license": "MIT", "dependencies": { - "degenerator": "^4.0.4", - "ip": "^1.1.8", + "degenerator": "^5.0.0", "netmask": "^2.0.2" }, "engines": { @@ -20113,20 +20025,20 @@ "license": "MIT" }, "node_modules/proxy-agent": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.2.1.tgz", - "integrity": "sha512-OIbBKlRAT+ycCm6wAYIzMwPejzRtjy8F3QiDX0eKOA3e4pe3U9F/IvzcHP42bmgQxVv97juG+J8/gx+JIeCX/Q==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", + "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", "dev": true, "license": "MIT", "dependencies": { - "agent-base": "^7.0.2", + "agent-base": "^7.1.2", "debug": "^4.3.4", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.0", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.6", "lru-cache": "^7.14.1", - "pac-proxy-agent": "^6.0.3", + "pac-proxy-agent": "^7.1.0", "proxy-from-env": "^1.1.0", - "socks-proxy-agent": "^8.0.1" + "socks-proxy-agent": "^8.0.5" }, "engines": { "node": ">= 14" @@ -22503,13 +22415,13 @@ } }, "node_modules/socks": { - "version": "2.8.7", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", - "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", + "version": "2.8.10", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.10.tgz", + "integrity": "sha512-e0VyvkVTwVYViNovRkZ9aodhxVlyoMn7eJhVUPxZ+eK9P/7CBkxvvsBOHqFPEH416726W8tLXXXjKwqgTErrCQ==", "dev": true, "license": "MIT", "dependencies": { - "ip-address": "^10.0.1", + "ip-address": "^10.1.1", "smart-buffer": "^4.2.0" }, "engines": { @@ -24394,23 +24306,6 @@ "integrity": "sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==", "license": "MIT" }, - "node_modules/vm2": { - "version": "3.10.4", - "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.10.4.tgz", - "integrity": "sha512-Gl6r7MN3mkawGurFdp467yDJQ7HdragK2QVVWFbOCd3LPJXJLE2xZFwLCNhp04MTkHruPqLIOs3pYbJQJw/1aA==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.14.1", - "acorn-walk": "^8.3.4" - }, - "bin": { - "vm2": "bin/vm2" - }, - "engines": { - "node": ">=6.0" - } - }, "node_modules/w3c-xmlserializer": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", diff --git a/package.json b/package.json index 7e48e1de..228ab0f7 100644 --- a/package.json +++ b/package.json @@ -119,6 +119,11 @@ "react": "*", "react-native": "*" }, + "overrides": { + "release-it": { + "proxy-agent": "^6.5.0" + } + }, "jest": { "preset": "react-native", "modulePathIgnorePatterns": [ From 8fa3a12735e98d8b8667bbdcd5153233b58932af Mon Sep 17 00:00:00 2001 From: MelvinBot Date: Mon, 26 Oct 2026 13:27:08 +0000 Subject: [PATCH 067/118] Override fast-xml-parser to ^5.7.0 to fix Dependabot alerts --- package-lock.json | 112 ++++++++++++++++++++++++++++++++++++++++++---- package.json | 3 ++ 2 files changed, 107 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 577cdc64..20f2ea70 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4120,6 +4120,19 @@ "eslint-scope": "5.1.1" } }, + "node_modules/@nodable/entities": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-3.1.0.tgz", + "integrity": "sha512-LsS/DjHr+uDM647Gru/cA8+J3a3HfhttwCKLyuoyN7yXTFCxKBtSgMQBvrW9yNPa2/zDRUNuGPaH5QUFoa4arQ==", + "devOptional": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/nodable" + } + ], + "license": "MIT" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -6269,6 +6282,19 @@ "node": ">= 8" } }, + "node_modules/anynum": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/anynum/-/anynum-1.0.1.tgz", + "integrity": "sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==", + "devOptional": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, "node_modules/appdirsjs": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/appdirsjs/-/appdirsjs-1.2.7.tgz", @@ -13463,10 +13489,27 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-xml-builder": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.3.1.tgz", + "integrity": "sha512-pIM/1n3ntFXKYrUZwW7QCK0gAW7XY+wzj1YMIV3tLDvPj/V+zTGJK5e3/4WJfwj0qWw2ElNXiTixda/R+3YSug==", + "devOptional": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "path-expression-matcher": "^1.6.2", + "xml-naming": "^0.3.0" + } + }, "node_modules/fast-xml-parser": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz", - "integrity": "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==", + "version": "5.11.2", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.11.2.tgz", + "integrity": "sha512-R9iDuNrQYeQut46cn2r2wHKn4HYzVDvDm5J1wW+koZewykv0yuO2HChTYeZtrULyHIDM9cj9TUXloCsueCQUog==", "devOptional": true, "funding": [ { @@ -13476,7 +13519,12 @@ ], "license": "MIT", "dependencies": { - "strnum": "^1.1.1" + "@nodable/entities": "^3.0.1", + "fast-xml-builder": "^1.2.0", + "is-unsafe": "^2.0.0", + "path-expression-matcher": "^1.6.2", + "strnum": "^2.4.2", + "xml-naming": "^0.3.0" }, "bin": { "fxparser": "src/cli/cli.js" @@ -15784,6 +15832,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-unsafe": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-unsafe/-/is-unsafe-2.0.2.tgz", + "integrity": "sha512-HgbIHPBH0KHHCcjLfGsCvhtPTVxjaAZlXjwdz7/GQC40SjSe4sfQsar8J5VFo8JOSbarkpV0OLG95bbaNd9aAQ==", + "devOptional": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, "node_modules/is-weakmap": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", @@ -19608,6 +19669,22 @@ "node": ">=8" } }, + "node_modules/path-expression-matcher": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.6.2.tgz", + "integrity": "sha512-enSlaiat05iasnzmgNxRj8reFdj3puY2QpNgP1aPIaVfT6nn9ICuPoFlKHk8EN22HcwewshO+mN2DGbkCEOtqQ==", + "devOptional": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -23037,9 +23114,9 @@ } }, "node_modules/strnum": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz", - "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.5.1.tgz", + "integrity": "sha512-UM4nTLWzQiDBseVxUGxN0wJzIk0UU89Qi7pKXz9qISo+lhviJikTuikBsK/oFRlpvYHvSdz0TVoLyQ+sEpYwxA==", "devOptional": true, "funding": [ { @@ -23047,7 +23124,10 @@ "url": "https://github.com/sponsors/NaturalIntelligence" } ], - "license": "MIT" + "license": "MIT", + "dependencies": { + "anynum": "^1.0.1" + } }, "node_modules/structured-headers": { "version": "0.4.1", @@ -24870,6 +24950,22 @@ "node": ">=12" } }, + "node_modules/xml-naming": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.3.0.tgz", + "integrity": "sha512-ghig2TBE/H11aOVgmahA3MhimvkBr6JIYknH/Dhdk10nXwdbIqBJsbfMxpvFPG8bAw77gN29aQWvKpmVoPlvPQ==", + "devOptional": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/xml2js": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.0.tgz", diff --git a/package.json b/package.json index 7e48e1de..f86042d1 100644 --- a/package.json +++ b/package.json @@ -115,6 +115,9 @@ "turbo": "^1.10.7", "typescript": "^5.8.3" }, + "overrides": { + "fast-xml-parser": "^5.7.0" + }, "peerDependencies": { "react": "*", "react-native": "*" From c04809786f2202162d8ba2543b4272df5bd4c4e2 Mon Sep 17 00:00:00 2001 From: MelvinBot Date: Mon, 26 Oct 2026 13:27:55 +0000 Subject: [PATCH 068/118] Bump minimatch to patched versions --- apps/expo-example/package-lock.json | 121 +++++++------ package-lock.json | 253 ++++++++++++++++++---------- package.json | 5 + 3 files changed, 238 insertions(+), 141 deletions(-) diff --git a/apps/expo-example/package-lock.json b/apps/expo-example/package-lock.json index b30eea4f..6fc22d31 100644 --- a/apps/expo-example/package-lock.json +++ b/apps/expo-example/package-lock.json @@ -1919,16 +1919,37 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@expo/fingerprint/node_modules/glob/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@expo/fingerprint/node_modules/glob/node_modules/brace-expansion": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/@expo/fingerprint/node_modules/glob/node_modules/minimatch": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.2.tgz", - "integrity": "sha512-fu656aJ0n2kcXwsnwnv9g24tkU5uSmOlTjd6WyyaKm2Z+h1qmY6bAjrcaIxF/BslFqbZ8UBtbJi7KgQOZD2PTw==", + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", "license": "BlueOak-1.0.0", "dependencies": { - "@isaacs/brace-expansion": "^5.0.1" + "brace-expansion": "^5.0.8" }, "engines": { - "node": "20 || >=22" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -1944,12 +1965,12 @@ } }, "node_modules/@expo/fingerprint/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -2276,27 +2297,6 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@isaacs/balanced-match": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", - "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", - "license": "MIT", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@isaacs/brace-expansion": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.1.tgz", - "integrity": "sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==", - "license": "MIT", - "dependencies": { - "@isaacs/balanced-match": "^4.0.1" - }, - "engines": { - "node": "20 || >=22" - } - }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -3381,13 +3381,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -6676,16 +6676,37 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/expo/node_modules/glob/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/expo/node_modules/glob/node_modules/brace-expansion": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/expo/node_modules/glob/node_modules/minimatch": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.2.tgz", - "integrity": "sha512-fu656aJ0n2kcXwsnwnv9g24tkU5uSmOlTjd6WyyaKm2Z+h1qmY6bAjrcaIxF/BslFqbZ8UBtbJi7KgQOZD2PTw==", + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", "license": "BlueOak-1.0.0", "dependencies": { - "@isaacs/brace-expansion": "^5.0.1" + "brace-expansion": "^5.0.8" }, "engines": { - "node": "20 || >=22" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -6945,12 +6966,12 @@ } }, "node_modules/expo/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -7521,12 +7542,12 @@ } }, "node_modules/glob/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -9537,9 +9558,9 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" diff --git a/package-lock.json b/package-lock.json index 577cdc64..76501421 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2479,9 +2479,9 @@ } }, "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -2590,6 +2590,29 @@ "xml2js": "0.6.0" } }, + "node_modules/@expo/config-plugins/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@expo/config-plugins/node_modules/brace-expansion": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/@expo/config-plugins/node_modules/glob": { "version": "13.0.2", "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.2.tgz", @@ -2619,16 +2642,16 @@ } }, "node_modules/@expo/config-plugins/node_modules/minimatch": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.2.tgz", - "integrity": "sha512-fu656aJ0n2kcXwsnwnv9g24tkU5uSmOlTjd6WyyaKm2Z+h1qmY6bAjrcaIxF/BslFqbZ8UBtbJi7KgQOZD2PTw==", + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "@isaacs/brace-expansion": "^5.0.1" + "brace-expansion": "^5.0.8" }, "engines": { - "node": "20 || >=22" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -2691,6 +2714,29 @@ "@babel/highlight": "^7.10.4" } }, + "node_modules/@expo/config/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@expo/config/node_modules/brace-expansion": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/@expo/config/node_modules/glob": { "version": "13.0.2", "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.2.tgz", @@ -2720,16 +2766,16 @@ } }, "node_modules/@expo/config/node_modules/minimatch": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.2.tgz", - "integrity": "sha512-fu656aJ0n2kcXwsnwnv9g24tkU5uSmOlTjd6WyyaKm2Z+h1qmY6bAjrcaIxF/BslFqbZ8UBtbJi7KgQOZD2PTw==", + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "@isaacs/brace-expansion": "^5.0.1" + "brace-expansion": "^5.0.8" }, "engines": { - "node": "20 || >=22" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -2879,18 +2925,43 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@expo/fingerprint/node_modules/glob/node_modules/minimatch": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.2.tgz", - "integrity": "sha512-fu656aJ0n2kcXwsnwnv9g24tkU5uSmOlTjd6WyyaKm2Z+h1qmY6bAjrcaIxF/BslFqbZ8UBtbJi7KgQOZD2PTw==", + "node_modules/@expo/fingerprint/node_modules/glob/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "peer": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@expo/fingerprint/node_modules/glob/node_modules/brace-expansion": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", "dev": true, - "license": "BlueOak-1.0.0", "peer": true, + "license": "MIT", "dependencies": { - "@isaacs/brace-expansion": "^5.0.1" + "balanced-match": "^4.0.2" }, "engines": { "node": "20 || >=22" + } + }, + "node_modules/@expo/fingerprint/node_modules/glob/node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "peer": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -3461,9 +3532,9 @@ } }, "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -3512,29 +3583,6 @@ "dev": true, "license": "ISC" }, - "node_modules/@isaacs/balanced-match": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", - "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@isaacs/brace-expansion": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.1.tgz", - "integrity": "sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@isaacs/balanced-match": "^4.0.1" - }, - "engines": { - "node": "20 || >=22" - } - }, "node_modules/@isaacs/fs-minipass": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", @@ -5858,13 +5906,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -6746,9 +6794,9 @@ } }, "node_modules/babel-plugin-module-resolver/node_modules/minimatch": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.4.tgz", - "integrity": "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==", + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.7.tgz", + "integrity": "sha512-V+1uQNdzybxa14e/p00HZnQNNcTjnRJjDxg2V8wtkjFctq4M7hXFws4oekyTP0Jebeq7QYtpFyOeBAjc88zvYg==", "dev": true, "license": "ISC", "dependencies": { @@ -10646,9 +10694,9 @@ } }, "node_modules/eslint-config-expensify/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -11096,9 +11144,9 @@ } }, "node_modules/eslint-plugin-import/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -11307,9 +11355,9 @@ } }, "node_modules/eslint-plugin-jsx-a11y/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -11448,9 +11496,9 @@ } }, "node_modules/eslint-plugin-react/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -11631,9 +11679,9 @@ } }, "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -12174,9 +12222,9 @@ } }, "node_modules/expo-module-scripts/node_modules/@react-native/codegen/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -12758,9 +12806,9 @@ } }, "node_modules/expo-module-scripts/node_modules/eslint-config-universe/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -12928,17 +12976,40 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/expo-module-scripts/node_modules/glob/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/expo-module-scripts/node_modules/glob/node_modules/brace-expansion": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/expo-module-scripts/node_modules/glob/node_modules/minimatch": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.2.tgz", - "integrity": "sha512-fu656aJ0n2kcXwsnwnv9g24tkU5uSmOlTjd6WyyaKm2Z+h1qmY6bAjrcaIxF/BslFqbZ8UBtbJi7KgQOZD2PTw==", + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "@isaacs/brace-expansion": "^5.0.1" + "brace-expansion": "^5.0.8" }, "engines": { - "node": "20 || >=22" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -14267,9 +14338,9 @@ } }, "node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -18656,13 +18727,13 @@ } }, "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -20653,9 +20724,9 @@ } }, "node_modules/react-native-builder-bob/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", "dev": true, "license": "ISC", "dependencies": { @@ -23355,9 +23426,9 @@ } }, "node_modules/test-exclude/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" diff --git a/package.json b/package.json index 7e48e1de..4a2b94f3 100644 --- a/package.json +++ b/package.json @@ -169,5 +169,10 @@ "android": { "javaPackageName": "com.expensify.wallet" } + }, + "overrides": { + "@typescript-eslint/typescript-estree": { + "minimatch": "9.0.9" + } } } From 852077c0c9db5f43db1e061b99305a59e05f3556 Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Mon, 26 Oct 2026 15:24:32 +0000 Subject: [PATCH 069/118] Update package-lock.json version to 0.2.2 --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index c29f4c95..37cc2a52 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.1", + "version": "0.2.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@expensify/react-native-wallet", - "version": "0.2.1", + "version": "0.2.2", "license": "MIT", "workspaces": [ "apps/common-app", From ebe6b17cfff6477229ca5f8c240ab44c0ca829fc Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Mon, 26 Oct 2026 15:24:35 +0000 Subject: [PATCH 070/118] Update package.json version to 0.2.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7e48e1de..eb696c1c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.1", + "version": "0.2.2", "description": "A React Native module designed for seamless integration of Card Push Provisioning into Apple Wallet and Google Wallet, enabling easy and secure addition of payment cards directly from your application.", "source": "./src/index.tsx", "main": "lib/commonjs/index", From c0cf911a59229736de6ddcc217989eefc95c16d5 Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Mon, 26 Oct 2026 16:08:30 +0000 Subject: [PATCH 071/118] Update package-lock.json version to 0.2.3 --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 65b57cce..15c633c2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.2", + "version": "0.2.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@expensify/react-native-wallet", - "version": "0.2.2", + "version": "0.2.3", "license": "MIT", "workspaces": [ "apps/common-app", From 9bb2e2d01da148bffb89ebecca0c1868afddfdd6 Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Mon, 26 Oct 2026 16:08:32 +0000 Subject: [PATCH 072/118] Update package.json version to 0.2.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index eb696c1c..1edf3456 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.2", + "version": "0.2.3", "description": "A React Native module designed for seamless integration of Card Push Provisioning into Apple Wallet and Google Wallet, enabling easy and secure addition of payment cards directly from your application.", "source": "./src/index.tsx", "main": "lib/commonjs/index", From 5d05b275acd71e1c460a216be1797ef4696e96d0 Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Mon, 26 Oct 2026 18:42:03 +0000 Subject: [PATCH 073/118] Update package-lock.json version to 0.2.4 --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 04e4879e..068ed9d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.3", + "version": "0.2.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@expensify/react-native-wallet", - "version": "0.2.3", + "version": "0.2.4", "license": "MIT", "workspaces": [ "apps/common-app", From 519cf73f7464008cb43011eb8ce9c86a17c098c0 Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Mon, 26 Oct 2026 18:42:05 +0000 Subject: [PATCH 074/118] Update package.json version to 0.2.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1edf3456..4ec46ce7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.3", + "version": "0.2.4", "description": "A React Native module designed for seamless integration of Card Push Provisioning into Apple Wallet and Google Wallet, enabling easy and secure addition of payment cards directly from your application.", "source": "./src/index.tsx", "main": "lib/commonjs/index", From 6770b40b6799a72cb875d1f4773a42c568014eb8 Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Mon, 26 Oct 2026 18:58:58 +0000 Subject: [PATCH 075/118] Update package-lock.json version to 0.2.5 --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2d0b3c49..f7695783 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.4", + "version": "0.2.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@expensify/react-native-wallet", - "version": "0.2.4", + "version": "0.2.5", "license": "MIT", "workspaces": [ "apps/common-app", From 8cf31e0a83cd809c0a60dec39c9bd196ee7111dd Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Mon, 26 Oct 2026 18:59:00 +0000 Subject: [PATCH 076/118] Update package.json version to 0.2.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4ec46ce7..e70be576 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.4", + "version": "0.2.5", "description": "A React Native module designed for seamless integration of Card Push Provisioning into Apple Wallet and Google Wallet, enabling easy and secure addition of payment cards directly from your application.", "source": "./src/index.tsx", "main": "lib/commonjs/index", From 1c3c8b42fbaaab4919ee78815cb1ff0e0e1c194e Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Mon, 26 Oct 2026 19:19:25 +0000 Subject: [PATCH 077/118] Update package-lock.json version to 0.2.6 --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8656510c..3ccf7fd3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.5", + "version": "0.2.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@expensify/react-native-wallet", - "version": "0.2.5", + "version": "0.2.6", "license": "MIT", "workspaces": [ "apps/common-app", From 43d6b3700d37cfbaa9f272b1209b38f84e9a59a7 Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Mon, 26 Oct 2026 19:19:27 +0000 Subject: [PATCH 078/118] Update package.json version to 0.2.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e70be576..2517b94b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.5", + "version": "0.2.6", "description": "A React Native module designed for seamless integration of Card Push Provisioning into Apple Wallet and Google Wallet, enabling easy and secure addition of payment cards directly from your application.", "source": "./src/index.tsx", "main": "lib/commonjs/index", From ff3774b39958f81ae16a9c243d61f876a5ff33aa Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2026 06:41:51 +0000 Subject: [PATCH 079/118] Update package-lock.json version to 0.2.7 --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index b14d7ed8..3562f556 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.6", + "version": "0.2.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@expensify/react-native-wallet", - "version": "0.2.6", + "version": "0.2.7", "license": "MIT", "workspaces": [ "apps/common-app", From 3680dc2cb9176398012d526999ad5182b5cdfa24 Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2026 06:41:53 +0000 Subject: [PATCH 080/118] Update package.json version to 0.2.7 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0c894243..e5ad260e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.6", + "version": "0.2.7", "description": "A React Native module designed for seamless integration of Card Push Provisioning into Apple Wallet and Google Wallet, enabling easy and secure addition of payment cards directly from your application.", "source": "./src/index.tsx", "main": "lib/commonjs/index", From e7a69195b8b232789520a580c8349e4a318a48ad Mon Sep 17 00:00:00 2001 From: rory Date: Tue, 27 Oct 2026 01:10:19 -0700 Subject: [PATCH 081/118] chore(deps): bump js-yaml for security fixes Update transitive js-yaml versions to address GHSA-2883-xcg3-v3hh, GHSA-5p4m-2wfm-xmqj, GHSA-52cp-r559-cp3m, and GHSA-h67p-54hq-rp68. --- apps/expo-example/package-lock.json | 12 +++++----- package-lock.json | 36 ++++++++++++++--------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/apps/expo-example/package-lock.json b/apps/expo-example/package-lock.json index 1ba89688..2dfcfcf5 100644 --- a/apps/expo-example/package-lock.json +++ b/apps/expo-example/package-lock.json @@ -2410,9 +2410,9 @@ } }, "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "version": "3.15.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.2.tgz", + "integrity": "sha512-6EuL879VkRA+1Cz578mKMiKvjPNEuk6+r1JaFzoSWejZmtf7xWbIyw1e3KkxlkzTIt9Taw6JBhEppG7utc1P+w==", "license": "MIT", "dependencies": { "argparse": "^1.0.7", @@ -8616,9 +8616,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", "license": "MIT", "dependencies": { "argparse": "^2.0.1" diff --git a/package-lock.json b/package-lock.json index 3562f556..1c058b08 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2466,9 +2466,9 @@ } }, "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", "dev": true, "license": "MIT", "dependencies": { @@ -3403,9 +3403,9 @@ "peer": true }, "node_modules/@expo/xcpretty/node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", "dev": true, "license": "MIT", "peer": true, @@ -8367,9 +8367,9 @@ "license": "Python-2.0" }, "node_modules/cosmiconfig/node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", "devOptional": true, "license": "MIT", "dependencies": { @@ -11618,9 +11618,9 @@ } }, "node_modules/eslint/node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", "dev": true, "license": "MIT", "dependencies": { @@ -16840,9 +16840,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "version": "3.15.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.2.tgz", + "integrity": "sha512-6EuL879VkRA+1Cz578mKMiKvjPNEuk6+r1JaFzoSWejZmtf7xWbIyw1e3KkxlkzTIt9Taw6JBhEppG7utc1P+w==", "license": "MIT", "dependencies": { "argparse": "^1.0.7", @@ -21387,9 +21387,9 @@ } }, "node_modules/release-it/node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", "dev": true, "license": "MIT", "dependencies": { From 19db3fd3a0a0699fb3dc6dd2c3da1da2e867ab8a Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2026 10:05:40 +0000 Subject: [PATCH 082/118] Update package-lock.json version to 0.2.8 --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index c46818f5..8eda09de 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.7", + "version": "0.2.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@expensify/react-native-wallet", - "version": "0.2.7", + "version": "0.2.8", "license": "MIT", "workspaces": [ "apps/common-app", From 726744f909a1dcd9fd26beaf79fd506847f0b034 Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2026 10:05:42 +0000 Subject: [PATCH 083/118] Update package.json version to 0.2.8 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e5ad260e..3eeed86b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.7", + "version": "0.2.8", "description": "A React Native module designed for seamless integration of Card Push Provisioning into Apple Wallet and Google Wallet, enabling easy and secure addition of payment cards directly from your application.", "source": "./src/index.tsx", "main": "lib/commonjs/index", From 613f4eaa5b10cedf1fd402a3edcd7d5d2716bcdc Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2026 10:11:38 +0000 Subject: [PATCH 084/118] Update package-lock.json version to 0.2.9 --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index e9b7ffb2..38e1d115 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.8", + "version": "0.2.9", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@expensify/react-native-wallet", - "version": "0.2.8", + "version": "0.2.9", "license": "MIT", "workspaces": [ "apps/common-app", From f488ba10818d3559c2c8d4ad5d9390a2733fd4ae Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2026 10:11:40 +0000 Subject: [PATCH 085/118] Update package.json version to 0.2.9 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3eeed86b..0a660202 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.8", + "version": "0.2.9", "description": "A React Native module designed for seamless integration of Card Push Provisioning into Apple Wallet and Google Wallet, enabling easy and secure addition of payment cards directly from your application.", "source": "./src/index.tsx", "main": "lib/commonjs/index", From 87ed42f35642187d032c0bc44853cd5a48db1695 Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2026 10:14:53 +0000 Subject: [PATCH 086/118] Update package-lock.json version to 0.2.10 --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9271c4f2..c08d5558 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.9", + "version": "0.2.10", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@expensify/react-native-wallet", - "version": "0.2.9", + "version": "0.2.10", "license": "MIT", "workspaces": [ "apps/common-app", From 16bac7021151a3c7c71eef2917b76a5e7d3d8924 Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2026 10:14:55 +0000 Subject: [PATCH 087/118] Update package.json version to 0.2.10 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0a660202..305c858e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.9", + "version": "0.2.10", "description": "A React Native module designed for seamless integration of Card Push Provisioning into Apple Wallet and Google Wallet, enabling easy and secure addition of payment cards directly from your application.", "source": "./src/index.tsx", "main": "lib/commonjs/index", From 9e1201c5f0aae2288ffd7cd07decd986f276e6bc Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2026 10:20:04 +0000 Subject: [PATCH 088/118] Update package-lock.json version to 0.2.11 --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 46e939f2..963c1869 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.10", + "version": "0.2.11", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@expensify/react-native-wallet", - "version": "0.2.10", + "version": "0.2.11", "license": "MIT", "workspaces": [ "apps/common-app", From 9d27831c6ff92d530077d0fec09ae6a55f2fda8d Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2026 10:20:06 +0000 Subject: [PATCH 089/118] Update package.json version to 0.2.11 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 305c858e..7fcf79f9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.10", + "version": "0.2.11", "description": "A React Native module designed for seamless integration of Card Push Provisioning into Apple Wallet and Google Wallet, enabling easy and secure addition of payment cards directly from your application.", "source": "./src/index.tsx", "main": "lib/commonjs/index", From 7848aa3d39e1876aa8c4b354e67d2537dca47739 Mon Sep 17 00:00:00 2001 From: Jakub Korytko Date: Tue, 27 Oct 2026 12:07:57 +0100 Subject: [PATCH 090/118] fix: merge duplicate overrides blocks in package.json --- package.json | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index c9273241..a8d4195d 100644 --- a/package.json +++ b/package.json @@ -116,15 +116,16 @@ "typescript": "^5.8.3" }, "overrides": { + "@typescript-eslint/typescript-estree": { + "minimatch": "9.0.9" + }, + "flatted": "^3.4.2", "lodash": "^4.18.1" }, "peerDependencies": { "react": "*", "react-native": "*" }, - "overrides": { - "flatted": "^3.4.2" - }, "jest": { "preset": "react-native", "modulePathIgnorePatterns": [ @@ -175,10 +176,5 @@ "android": { "javaPackageName": "com.expensify.wallet" } - }, - "overrides": { - "@typescript-eslint/typescript-estree": { - "minimatch": "9.0.9" - } } } From cff2cf6399ff12b82994f6df97e316a2bdb1f11c Mon Sep 17 00:00:00 2001 From: Jakub Korytko Date: Tue, 27 Oct 2026 12:12:33 +0100 Subject: [PATCH 091/118] fix: sync expo-example lockfile with root package --- apps/expo-example/package-lock.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/expo-example/package-lock.json b/apps/expo-example/package-lock.json index e35c04f0..da0450eb 100644 --- a/apps/expo-example/package-lock.json +++ b/apps/expo-example/package-lock.json @@ -24,8 +24,7 @@ }, "../..": { "name": "@expensify/react-native-wallet", - "version": "0.1.15", - "hasInstallScript": true, + "version": "0.2.11", "license": "MIT", "workspaces": [ "apps/common-app", @@ -49,7 +48,7 @@ "@typescript-eslint/parser": "^6.15.0", "del-cli": "^5.0.0", "eslint": "^8.57.1", - "eslint-config-expensify": "^2.0.24", + "eslint-config-expensify": "2.0.24", "eslint-config-prettier": "^8.5.0", "eslint-import-resolver-alias": "^1.1.2", "eslint-plugin-deprecation": "^2.0.0", From bee965d0ae3d9750a6c958537f66e2fc75c63811 Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2026 11:24:05 +0000 Subject: [PATCH 092/118] Update package-lock.json version to 0.2.12 --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5204fdbc..7b0faac8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.11", + "version": "0.2.12", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@expensify/react-native-wallet", - "version": "0.2.11", + "version": "0.2.12", "license": "MIT", "workspaces": [ "apps/common-app", From 735bed88d3ad21c22471ce50dc7a2e8e00946908 Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2026 11:24:07 +0000 Subject: [PATCH 093/118] Update package.json version to 0.2.12 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a8d4195d..30eb7e60 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.11", + "version": "0.2.12", "description": "A React Native module designed for seamless integration of Card Push Provisioning into Apple Wallet and Google Wallet, enabling easy and secure addition of payment cards directly from your application.", "source": "./src/index.tsx", "main": "lib/commonjs/index", From 63cfde4d412624db476d048510a128836e6e87c7 Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2026 12:59:45 +0000 Subject: [PATCH 094/118] Update package-lock.json version to 0.2.13 --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4957feca..b8181f39 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.12", + "version": "0.2.13", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@expensify/react-native-wallet", - "version": "0.2.12", + "version": "0.2.13", "license": "MIT", "workspaces": [ "apps/common-app", From e35c472765db591b287e2bb78ec5c0e0a1b6dd76 Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2026 12:59:47 +0000 Subject: [PATCH 095/118] Update package.json version to 0.2.13 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1533487a..6cc91a41 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.12", + "version": "0.2.13", "description": "A React Native module designed for seamless integration of Card Push Provisioning into Apple Wallet and Google Wallet, enabling easy and secure addition of payment cards directly from your application.", "source": "./src/index.tsx", "main": "lib/commonjs/index", From 3cc401bdaa93ae57ac539b089c98fc6bc4101636 Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2026 13:07:53 +0000 Subject: [PATCH 096/118] Update package-lock.json version to 0.2.14 --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index fc9b88b3..ad720828 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.13", + "version": "0.2.14", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@expensify/react-native-wallet", - "version": "0.2.13", + "version": "0.2.14", "license": "MIT", "workspaces": [ "apps/common-app", From c0cfceda7d1f4d2d2288bddbca945ffb49dfe4b6 Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2026 13:07:55 +0000 Subject: [PATCH 097/118] Update package.json version to 0.2.14 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3e8d8c68..76e949c5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.13", + "version": "0.2.14", "description": "A React Native module designed for seamless integration of Card Push Provisioning into Apple Wallet and Google Wallet, enabling easy and secure addition of payment cards directly from your application.", "source": "./src/index.tsx", "main": "lib/commonjs/index", From ba00fb58c494bc5e30010f476942e067a6f21bb5 Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2026 14:01:58 +0000 Subject: [PATCH 098/118] Update package-lock.json version to 0.2.15 --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index d01e7cf6..60b1fab5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.14", + "version": "0.2.15", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@expensify/react-native-wallet", - "version": "0.2.14", + "version": "0.2.15", "license": "MIT", "workspaces": [ "apps/common-app", From b7e406f0d3251801ee7613d37eacbbdf0599f2f2 Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2026 14:02:01 +0000 Subject: [PATCH 099/118] Update package.json version to 0.2.15 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d8ceadc5..571b7284 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.14", + "version": "0.2.15", "description": "A React Native module designed for seamless integration of Card Push Provisioning into Apple Wallet and Google Wallet, enabling easy and secure addition of payment cards directly from your application.", "source": "./src/index.tsx", "main": "lib/commonjs/index", From 0a63c684683fdf05da1331ea14fe92dc40b643ab Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2026 14:16:18 +0000 Subject: [PATCH 100/118] Update package-lock.json version to 0.2.16 --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index f66030b5..b22a216d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.15", + "version": "0.2.16", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@expensify/react-native-wallet", - "version": "0.2.15", + "version": "0.2.16", "license": "MIT", "workspaces": [ "apps/common-app", From 6a97dee4d5f5d99f3bafee8983866d65db7f402a Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2026 14:16:20 +0000 Subject: [PATCH 101/118] Update package.json version to 0.2.16 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 79e71210..a1c17383 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.15", + "version": "0.2.16", "description": "A React Native module designed for seamless integration of Card Push Provisioning into Apple Wallet and Google Wallet, enabling easy and secure addition of payment cards directly from your application.", "source": "./src/index.tsx", "main": "lib/commonjs/index", From 511db97ae58e1e6c98eff6265cc8e47d852f443c Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2026 14:23:35 +0000 Subject: [PATCH 102/118] Update package-lock.json version to 0.2.17 --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5e5f95e8..8b4a00e5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.16", + "version": "0.2.17", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@expensify/react-native-wallet", - "version": "0.2.16", + "version": "0.2.17", "license": "MIT", "workspaces": [ "apps/common-app", From 03dad2bf07bbb2f447fd817e4ba78344f0ca968c Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2026 14:23:37 +0000 Subject: [PATCH 103/118] Update package.json version to 0.2.17 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2efb8ce4..81a96359 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.16", + "version": "0.2.17", "description": "A React Native module designed for seamless integration of Card Push Provisioning into Apple Wallet and Google Wallet, enabling easy and secure addition of payment cards directly from your application.", "source": "./src/index.tsx", "main": "lib/commonjs/index", From e24692bbef96f9e53d074d69ccd52fb2890d42f8 Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2026 14:33:38 +0000 Subject: [PATCH 104/118] Update package-lock.json version to 0.2.18 --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3e2baf89..df62b949 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.17", + "version": "0.2.18", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@expensify/react-native-wallet", - "version": "0.2.17", + "version": "0.2.18", "license": "MIT", "workspaces": [ "apps/common-app", From a005f7ab47ae6dc897c6c768116e2a58517e7a0c Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2026 14:33:41 +0000 Subject: [PATCH 105/118] Update package.json version to 0.2.18 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 81a96359..fe4d560c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.17", + "version": "0.2.18", "description": "A React Native module designed for seamless integration of Card Push Provisioning into Apple Wallet and Google Wallet, enabling easy and secure addition of payment cards directly from your application.", "source": "./src/index.tsx", "main": "lib/commonjs/index", From f62b79e6dbbbebfffc2a08b977c25ac1a7a69d1a Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2026 14:49:08 +0000 Subject: [PATCH 106/118] Update package-lock.json version to 0.2.19 --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index f5f504ac..98ec1aba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.18", + "version": "0.2.19", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@expensify/react-native-wallet", - "version": "0.2.18", + "version": "0.2.19", "license": "MIT", "workspaces": [ "apps/common-app", From fc46d31d77602e21ed3b5c00861c992724e0b4f8 Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2026 14:49:10 +0000 Subject: [PATCH 107/118] Update package.json version to 0.2.19 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fe4d560c..c5377795 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.18", + "version": "0.2.19", "description": "A React Native module designed for seamless integration of Card Push Provisioning into Apple Wallet and Google Wallet, enabling easy and secure addition of payment cards directly from your application.", "source": "./src/index.tsx", "main": "lib/commonjs/index", From 88ac208ae764a001d18fcca839e3eeb18edd5ec7 Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2026 15:00:35 +0000 Subject: [PATCH 108/118] Update package-lock.json version to 0.2.20 --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9cf196a7..8e3c76e0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.19", + "version": "0.2.20", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@expensify/react-native-wallet", - "version": "0.2.19", + "version": "0.2.20", "license": "MIT", "workspaces": [ "apps/common-app", From d078d75b5b8821fcba144b2f05526113e7156b2e Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2026 15:00:37 +0000 Subject: [PATCH 109/118] Update package.json version to 0.2.20 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c5377795..0101c266 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.19", + "version": "0.2.20", "description": "A React Native module designed for seamless integration of Card Push Provisioning into Apple Wallet and Google Wallet, enabling easy and secure addition of payment cards directly from your application.", "source": "./src/index.tsx", "main": "lib/commonjs/index", From b970b4cf9b8a34b03eeaa15edda03b97c7daffc3 Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2026 15:02:28 +0000 Subject: [PATCH 110/118] Update package-lock.json version to 0.2.21 --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8b9f8f3e..e1a8a1a0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.20", + "version": "0.2.21", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@expensify/react-native-wallet", - "version": "0.2.20", + "version": "0.2.21", "license": "MIT", "workspaces": [ "apps/common-app", From eff379174fbdd96a7cb0e7d4918a940fa0fc58f1 Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2026 15:02:30 +0000 Subject: [PATCH 111/118] Update package.json version to 0.2.21 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0101c266..f06019e4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.20", + "version": "0.2.21", "description": "A React Native module designed for seamless integration of Card Push Provisioning into Apple Wallet and Google Wallet, enabling easy and secure addition of payment cards directly from your application.", "source": "./src/index.tsx", "main": "lib/commonjs/index", From d962568f07c9ab560fc5e3ff4eae198ade0635f4 Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2026 15:05:54 +0000 Subject: [PATCH 112/118] Update package-lock.json version to 0.2.22 --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 40c43932..15c77c15 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.21", + "version": "0.2.22", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@expensify/react-native-wallet", - "version": "0.2.21", + "version": "0.2.22", "license": "MIT", "workspaces": [ "apps/common-app", From 39842cebfb7bc51e1127433c7b577521dfa56839 Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2026 15:05:56 +0000 Subject: [PATCH 113/118] Update package.json version to 0.2.22 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f06019e4..e235b8c5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.21", + "version": "0.2.22", "description": "A React Native module designed for seamless integration of Card Push Provisioning into Apple Wallet and Google Wallet, enabling easy and secure addition of payment cards directly from your application.", "source": "./src/index.tsx", "main": "lib/commonjs/index", From c651417a702906d5f6b1ad00d868a09355d238cb Mon Sep 17 00:00:00 2001 From: Bruno Nascimento Date: Tue, 27 Oct 2026 17:39:46 -0300 Subject: [PATCH 114/118] fix: share one account id lookup across the add flow The button check, the add, and the inferred device now resolve the same id, and the device is read again when the user tries again. --- README.md | 10 ++--- .../java/com/expensify/wallet/WalletModule.kt | 4 +- .../expensify/wallet/NativeWalletSpec.java | 2 +- ios/RNWallet.mm | 3 +- ios/WalletManager.swift | 39 ++++++++++++------- ios/stucts/AddPassResponse.swift | 14 +++++-- src/NativeWallet.ts | 2 +- src/index.tsx | 6 +-- 8 files changed, 48 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 990f0950..79d334ff 100644 --- a/README.md +++ b/README.md @@ -168,7 +168,7 @@ Here you can find data elements used in the library, essential to work with Goog - **Certificates** - an array of NSData objects. Each object contains a DER encoded X.509 certificate, with the leaf first and root last. You must download the root CA to validate the entire chain. - **Encrypted Pass Data**- an encrypted JSON file containing the sensitive information needed to add a card to Apple Pay (Base 64 encoded). [More details here.](https://developer.apple.com/documentation/passkit/pkaddpaymentpassrequest/encryptedpassdata) - **Ephemeral Public Key** - a key used by elliptic curve cryptography (ECC) (Base 64 encoded). -- **Primary Account Identifier** - an opaque issuer id (also called the FPAN ID) that PassKit uses to recognize the same card across reissues and devices. Pass it in `IOSCardData` and to `canAddCardToAppleWallet`. On iOS, `getCardStatusByIdentifier` uses this same value. If no pass matches the given id exactly, the library uses the id of the only pass with the same last digits. +- **Primary Account Identifier** - an opaque issuer id (also called the FPAN ID) that PassKit uses to recognize the same card across reissues and devices. Pass it in `IOSCardData` and to `canAddCardToAppleWallet`. `getCardStatusByIdentifier` matches this value exactly and does not fall back. `addCardToAppleWallet`, the inferred `deviceType`, and `canAddCardToAppleWallet` when you also pass `lastDigits` share one lookup: an exact id match wins, and otherwise the library uses the id of the only pass that is not `deactivated` and has the same last digits. A different card that is still active and ends in the same digits can be picked. Pass the id PassKit stored on the pass (the `primaryAccountIdentifier` from `listAppleWalletPasses`) to skip that lookup. - **Device type** - the device the card is being provisioned to: `phone` or `watch`. PassKit does not report the device the user selected, so the library infers it from where the card is already provisioned. When it cannot tell, the value is `unknown`. # API Reference @@ -187,7 +187,7 @@ The library offers functions for seamless integration and use of the Apple Walle | **resumeAddCardToGoogleWallet** | Resumes the Push Provisioning flow for adding a card to the Google Wallet using existing token reference ID. | `data`: `AndroidResumeCardData` | `TokenizationStatus` | ❌ | βœ… | | **listTokens** | Lists all tokens currently stored in the Google Wallet. | None | `TokenInfo[]` | ❌ | βœ… | | **addCardToAppleWallet** | Initiates native Push Provisioning flow for adding a card to the Apple Wallet. When `IOSCardData.primaryAccountIdentifier` is set, the device picker only lists devices that do not already have the card. If PassKit cannot present the flow because the card is already on every device, the call fails with a clear error. | `data`: `IOSCardData`,
`issuerEncrypt-`
`PayloadCallback: IOSIssuerCallback` | `void` | βœ… | ❌ | -| **canAddCardToAppleWallet** | Checks whether the card can still be added to any of the user's devices (iPhone or paired Apple Watch). | `primaryAccountIdentifier: string` | `boolean` | βœ… | ❌ | +| **canAddCardToAppleWallet** | Checks whether the card can still be added to the iPhone or a paired Apple Watch. Pass `lastDigits` to use the same id lookup as `addCardToAppleWallet`. Returns `false` on Android. | `primaryAccountIdentifier: string`,
`lastDigits?: string` | `boolean` | βœ… | βœ… | ## Data Types @@ -202,8 +202,8 @@ The library offers functions for seamless integration and use of the Apple Walle | **IOSCardData** | Data related to a card that is to be added on iOS. Set `primaryAccountIdentifier` after the first provisioning so the next attempt targets devices that do not already have the card. | `network: string`,
`activationData: string`,
`encryptedPassData: string`,
`ephemeralPublicKey: string`,
`cardHolderTitle: string`,
`cardHolderName: string`,
`lastDigits: string`,
`cardDescription: string`,
`cardDescriptionComment: string`,
`primaryAccountIdentifier?: string` | | **onCardActivatedPayload** | Data used by listener to notice when a card's status changes. | `tokenId?: string \| null`,
`status: 'activated' \| 'canceled'`
| | **IOSIssuerCallback** | This callback is invoked with a nonce, its signature, a certificate array obtained from Apple, and the device the card is being added to. Forward these details to your server or the card issuer's API to securely encrypt the payload required for adding cards to the Apple Wallet. `deviceType` is a best-effort value; see `IOSDeviceType`. | `(nonce: string, nonceSignature: string, certificate: string[], deviceType: IOSDeviceType) => IOSEncryptPayload` | -| **IOSDeviceType** | Best-effort device the card is being provisioned to. PassKit does not report the selected device, so this is inferred from existing passes when `primaryAccountIdentifier` is set. If no pass matches that id exactly, the library uses the id of the only pass with the same last digits. Passes on a paired Apple Watch are never counted as iPhone passes. `phone` means the card is already on the paired Apple Watch only. `watch` means it is already on the iPhone only. `unknown` means the card is on neither device, or no identifier was provided, so the user may still choose either device. Passes in the `deactivated` state count as not present. | `'phone' \| 'watch' \| 'unknown'` | -| **IOSAddPaymentPassData** | Data Apple returns when the user continues the provisioning flow, including the device the card is being added to. | `status: number`,
`nonce: string`,
`nonceSignature: string`,
`certificates: string[]`,
`deviceType?: string` | +| **IOSDeviceType** | Best-effort device the card is being provisioned to. PassKit does not report the selected device, so this is inferred from existing passes when `primaryAccountIdentifier` is set, using the same id lookup as `addCardToAppleWallet`. The value is computed when Apple asks for the encrypted payload, including when the user tries again. Passes on a paired Apple Watch are never counted as iPhone passes. `phone` means the card is already on the paired Apple Watch only. `watch` means it is already on the iPhone only. `unknown` means the card is on neither device, or no identifier was provided, so the user may still choose either device. Passes in the `deactivated` state count as not present. | `'phone' \| 'watch' \| 'unknown'` | +| **IOSAddPaymentPassData** | Data returned when the user continues the provisioning flow. Apple supplies the nonce, its signature, and the certificates. The library adds `deviceType`; Apple does not return it. See `IOSDeviceType`. | `status: number`,
`nonce: string`,
`nonceSignature: string`,
`certificates: string[]`,
`deviceType?: string` | | **IOSEncryptPayload** | An object containing the necessary elements to complete the addition of a card to Apple Wallet. | `encryptedPassData: string`,
`activationData: string`,
`ephemeralPublicKey: string` | | **TokenInfo** | Information about a token stored in Google Wallet. | `identifier: string`,
`lastDigits: string`,
`tokenState: number` | @@ -360,7 +360,7 @@ Please refer to the [complete testing criteria documentation](https://developers ### iOS When implementing the In-App Push Provisioning feature in your App make sure that your app follows Apple's [branding guidelines connected to Apple Wallet](https://developer.apple.com/wallet/add-to-apple-wallet-guidelines/). Remember that you must not create your own buttons or your app could be rejected at revision. You can use the[ AddWalletButton component](#components) instead! -Keep the Add to Apple Wallet button visible while `canAddCardToAppleWallet(primaryAccountIdentifier)` returns `true`, including after the card is added to the iPhone but not yet to a paired Apple Watch. Show text like `Added to Apple Wallet` only once that call returns `false`. Pass `primaryAccountIdentifier` in `IOSCardData` so the next attempt targets the remaining device. This matches [Apple's in-app provisioning guidance](https://applepaydemo.apple.com/in-app-provisioning#3.3). +Keep the Add to Apple Wallet button visible while `canAddCardToAppleWallet(primaryAccountIdentifier, lastDigits)` returns `true`, including after the card is added to the iPhone but not yet to a paired Apple Watch. Show text like `Added to Apple Wallet` only once that call returns `false`. Pass `primaryAccountIdentifier` in `IOSCardData` so the next attempt targets the remaining device, and pass the same `lastDigits` to both calls so they agree. This matches [Apple's in-app provisioning guidance](https://applepaydemo.apple.com/in-app-provisioning#3.3). Next to branding guidelines, please follow the instructions and best practices from [the In-App provisioning documentation]((#ios)) provided by Apple. diff --git a/android/src/main/java/com/expensify/wallet/WalletModule.kt b/android/src/main/java/com/expensify/wallet/WalletModule.kt index d27147aa..e2105a03 100644 --- a/android/src/main/java/com/expensify/wallet/WalletModule.kt +++ b/android/src/main/java/com/expensify/wallet/WalletModule.kt @@ -588,8 +588,8 @@ class WalletModule internal constructor(context: ReactApplicationContext) : // no-op } - override fun canAddCardToAppleWallet(primaryAccountIdentifier: String, promise: Promise) { - // no-op + override fun canAddCardToAppleWallet(primaryAccountIdentifier: String, lastDigits: String, promise: Promise) { + promise.resolve(false) } override fun addListener(eventType: String?) { diff --git a/android/src/paper/java/com/expensify/wallet/NativeWalletSpec.java b/android/src/paper/java/com/expensify/wallet/NativeWalletSpec.java index db83292e..0d98ee13 100644 --- a/android/src/paper/java/com/expensify/wallet/NativeWalletSpec.java +++ b/android/src/paper/java/com/expensify/wallet/NativeWalletSpec.java @@ -83,7 +83,7 @@ public NativeWalletSpec(ReactApplicationContext reactContext) { @ReactMethod @DoNotStrip - public abstract void canAddCardToAppleWallet(String primaryAccountIdentifier, Promise promise); + public abstract void canAddCardToAppleWallet(String primaryAccountIdentifier, String lastDigits, Promise promise); @ReactMethod @DoNotStrip diff --git a/ios/RNWallet.mm b/ios/RNWallet.mm index c67dbb16..05a47226 100644 --- a/ios/RNWallet.mm +++ b/ios/RNWallet.mm @@ -133,10 +133,11 @@ - (instancetype)init { RCT_REMAP_METHOD(canAddCardToAppleWallet, canAddCardToAppleWallet:(NSString *)primaryAccountIdentifier + lastDigits:(NSString *)lastDigits resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { - resolve(@([walletManager canAddCardToAppleWalletWithPrimaryAccountIdentifier:primaryAccountIdentifier])); + resolve(@([walletManager canAddCardToAppleWalletWithPrimaryAccountIdentifier:primaryAccountIdentifier lastDigits:lastDigits])); } - (void)addCardToGoogleWallet: diff --git a/ios/WalletManager.swift b/ios/WalletManager.swift index d5134a94..7080c609 100644 --- a/ios/WalletManager.swift +++ b/ios/WalletManager.swift @@ -22,7 +22,7 @@ open class WalletManager: UIViewController { private var addPassHandler: ((PKAddPaymentPassRequest) -> Void)? - private var addPassTargetDevice: String? + private var addPassPrimaryAccountIdentifier: String? @objc public var packageName = "react-native-wallet" @@ -120,8 +120,6 @@ open class WalletManager: UIViewController { configuration.primaryAccountIdentifier = primaryAccountIdentifier } - let targetDevice = provisioningTargetDevice(for: primaryAccountIdentifier) - guard let enrollViewController = PKAddPaymentPassViewController(requestConfiguration: configuration, delegate: self) else { let alreadyOnEveryDevice = primaryAccountIdentifier.map { !passLibrary.canAddSecureElementPass(primaryAccountIdentifier: $0) @@ -149,7 +147,7 @@ open class WalletManager: UIViewController { self.presentAddPaymentPassCompletionHandler = nil self.addPaymentPassCompletionHandler = nil self.addPassHandler = nil - self.addPassTargetDevice = nil + self.addPassPrimaryAccountIdentifier = nil let errorResponse: NSDictionary = [ "errorMessage": "EnrollViewController is no longer presented." @@ -166,7 +164,7 @@ open class WalletManager: UIViewController { self.presentAddPaymentPassCompletionHandler = completion self.addPassViewController = enrollViewController - self.addPassTargetDevice = targetDevice + self.addPassPrimaryAccountIdentifier = primaryAccountIdentifier presentingViewController.present(enrollViewController, animated: true, completion: nil) } } @@ -230,25 +228,29 @@ open class WalletManager: UIViewController { } } - @objc public func canAddCardToAppleWallet(primaryAccountIdentifier: NSString) -> Bool { - return passLibrary.canAddSecureElementPass(primaryAccountIdentifier: primaryAccountIdentifier as String) + @objc public func canAddCardToAppleWallet(primaryAccountIdentifier: NSString, lastDigits: NSString) -> Bool { + let suffix = lastDigits as String + let resolved = suffix.isEmpty + ? primaryAccountIdentifier as String + : resolvedPrimaryAccountIdentifier(primaryAccountIdentifier as String, lastDigits: suffix) + return passLibrary.canAddSecureElementPass(primaryAccountIdentifier: resolved) } - private func provisioningTargetDevice(for primaryAccountIdentifier: String?) -> String { + private func provisioningTargetDevice(for primaryAccountIdentifier: String?) -> ProvisioningDeviceType { guard let primaryAccountIdentifier = primaryAccountIdentifier else { - return "unknown" + return .unknown } let onPhone = hasProvisionedPass(identifier: primaryAccountIdentifier, in: passLibrary.passes(of: .secureElement)) let onWatch = hasProvisionedPass(identifier: primaryAccountIdentifier, in: passLibrary.remoteSecureElementPasses) if onPhone && !onWatch { - return "watch" + return .watch } if onWatch && !onPhone { - return "phone" + return .phone } - return "unknown" + return .unknown } private func resolvedPrimaryAccountIdentifier(_ identifier: String, lastDigits: String) -> String { @@ -257,7 +259,13 @@ open class WalletManager: UIViewController { return identifier } - let suffixMatches = passes.filter { $0.primaryAccountNumberSuffix.hasSuffix(lastDigits) } + guard !lastDigits.isEmpty else { + return identifier + } + + let suffixMatches = passes.filter { + $0.passActivationState != .deactivated && $0.primaryAccountNumberSuffix.hasSuffix(lastDigits) + } let identifiers = Set(suffixMatches.map(\.primaryAccountIdentifier)) if identifiers.count == 1, let resolvedIdentifier = identifiers.first { return resolvedIdentifier @@ -337,7 +345,8 @@ extension WalletManager: PKAddPaymentPassViewControllerDelegate { let stringCertificates = certificates.map { $0.base64EncodedString() as NSString } - let reqestCardData = AddPassResponse(status: .completed, nonce: stringNonce, nonceSignature: stringNonceSignature, certificates: stringCertificates, deviceType: addPassTargetDevice as NSString?) + let deviceType = provisioningTargetDevice(for: addPassPrimaryAccountIdentifier) + let reqestCardData = AddPassResponse(status: .completed, nonce: stringNonce, nonceSignature: stringNonceSignature, certificates: stringCertificates, deviceType: deviceType) self.addPassHandler = handler // Retry the JS issuer callback if the user tries again to add a payment pass @@ -369,7 +378,7 @@ extension WalletManager: PKAddPaymentPassViewControllerDelegate { presentAddPaymentPassCompletionHandler = nil addPaymentPassCompletionHandler = nil addPassHandler = nil - addPassTargetDevice = nil + addPassPrimaryAccountIdentifier = nil let errorMessage = error?.localizedDescription ?? "" diff --git a/ios/stucts/AddPassResponse.swift b/ios/stucts/AddPassResponse.swift index 327afea8..3512e45d 100644 --- a/ios/stucts/AddPassResponse.swift +++ b/ios/stucts/AddPassResponse.swift @@ -1,13 +1,19 @@ import PassKit +enum ProvisioningDeviceType: String { + case phone + case watch + case unknown +} + struct AddPassResponse { let status: OperationResult let nonce: NSString? let nonceSignature: NSString? let certificates: [NSString]? - let deviceType: NSString? + let deviceType: ProvisioningDeviceType? - init(status: OperationResult, nonce: NSString?, nonceSignature: NSString?, certificates: [NSString]?, deviceType: NSString? = nil) { + init(status: OperationResult, nonce: NSString?, nonceSignature: NSString?, certificates: [NSString]?, deviceType: ProvisioningDeviceType? = nil) { self.status = status self.nonce = nonce self.nonceSignature = nonceSignature @@ -21,7 +27,7 @@ struct AddPassResponse { "status": status.rawValue, ] if let deviceType = deviceType { - response["deviceType"] = deviceType + response["deviceType"] = deviceType.rawValue } return response as NSDictionary } @@ -39,7 +45,7 @@ struct AddPassResponse { "certificates": certificates, ] if let deviceType = deviceType { - response["deviceType"] = deviceType + response["deviceType"] = deviceType.rawValue } return response as NSDictionary } diff --git a/src/NativeWallet.ts b/src/NativeWallet.ts index 8e6b95e4..94d5787f 100644 --- a/src/NativeWallet.ts +++ b/src/NativeWallet.ts @@ -102,7 +102,7 @@ export interface Spec extends TurboModule { listTokens(): Promise; IOSPresentAddPaymentPassView(cardData: IOSCardData): Promise; IOSHandleAddPaymentPassResponse(payload: IOSEncryptPayload): Promise; - canAddCardToAppleWallet(primaryAccountIdentifier: string): Promise; + canAddCardToAppleWallet(primaryAccountIdentifier: string, lastDigits: string): Promise; addListener: (eventType: string) => void; removeListeners: (count: number) => void; } diff --git a/src/index.tsx b/src/index.tsx index 105ce1c1..de311703 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -194,16 +194,16 @@ function toIOSDeviceType(deviceType: string | undefined): IOSDeviceType { } } -async function canAddCardToAppleWallet(primaryAccountIdentifier: string): Promise { +async function canAddCardToAppleWallet(primaryAccountIdentifier: string, lastDigits?: string): Promise { if (Platform.OS === 'android') { - throw new Error('canAddCardToAppleWallet is not available on Android'); + return false; } if (!Wallet) { return getModuleLinkingRejection(); } - return Wallet.canAddCardToAppleWallet(primaryAccountIdentifier); + return Wallet.canAddCardToAppleWallet(primaryAccountIdentifier, lastDigits ?? ''); } async function addCardToAppleWallet( From 45dea91f40b7181590af01d7cc4ea4867e0247e9 Mon Sep 17 00:00:00 2001 From: Bruno Nascimento Date: Tue, 27 Oct 2026 17:41:23 -0300 Subject: [PATCH 115/118] fix: look up the Watch pass when isRemote is omitted A missing flag now reaches the iPhone-then-Watch lookup instead of being treated as the iPhone. --- README.md | 2 +- ios/RNWallet.mm | 22 +++++++++++++--------- src/NativeWallet.ts | 2 +- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index dfd06a5d..88c0bf96 100644 --- a/README.md +++ b/README.md @@ -202,7 +202,7 @@ The library offers functions for seamless integration and use of the Apple Walle | **onCardActivatedPayload** | Data used by listener to notice when a card's status changes. | `tokenId?: string \| null`,
`status: 'activated' \| 'canceled'`
| | **IOSIssuerCallback** | This callback is invoked with a nonce, its signature, and a certificate array obtained from Apple. It is expected that you will forward these details to your server or the card issuer's API to securely encrypt the payload required for adding cards to the Apple Wallet. | `(nonce: string, nonceSignature: string, certificate: string[]) => IOSEncryptPayload` | | **IOSEncryptPayload** | An object containing the necessary elements to complete the addition of a card to Apple Wallet. | `encryptedPassData: string`,
`activationData: string`,
`ephemeralPublicKey: string` | -| **IOSPassActivationData** | Identifies an existing Apple Wallet Secure Element pass and provides the activation payload returned by your issuer backend. `activationData` must be Base 64 encoded. | `passTypeIdentifier: string`,
`serialNumber: string`,
`activationData: string`,
`isRemote: boolean` | +| **IOSPassActivationData** | Identifies an existing Apple Wallet Secure Element pass and provides the activation payload returned by your issuer backend. `activationData` must be Base 64 encoded. Set `isRemote` to `true` for a paired Apple Watch pass or `false` for the iPhone pass when the same card is on both. Omit it to look up the iPhone pass first and then the Watch. | `passTypeIdentifier: string`,
`serialNumber: string`,
`activationData: string`,
`isRemote?: boolean` | | **IOSWalletPass** | Information about an Apple Wallet Secure Element pass currently available on the device or a paired remote device. | `passTypeIdentifier: string`,
`serialNumber: string`,
`primaryAccountIdentifier?: string`,
`lastDigits?: string`,
`activationState: CardStatus`,
`isRemote: boolean` | | **TokenInfo** | Information about a token stored in Google Wallet. | `identifier: string`,
`lastDigits: string`,
`tokenState: number` | diff --git a/ios/RNWallet.mm b/ios/RNWallet.mm index 19a3d11e..0ec4e83d 100644 --- a/ios/RNWallet.mm +++ b/ios/RNWallet.mm @@ -140,24 +140,28 @@ - (instancetype)init { reject:(RCTPromiseRejectBlock)reject) { @try { - NSDictionary *passDataDict = nil; + NSMutableDictionary *passDataDict = nil; #ifdef RCT_NEW_ARCH_ENABLED - passDataDict = @{ + passDataDict = [@{ @"passTypeIdentifier": [self safeString:passData.passTypeIdentifier()], @"serialNumber": [self safeString:passData.serialNumber()], @"activationData": [self safeString:passData.activationData()], - @"isRemote": @(passData.isRemote()), - }; + } mutableCopy]; + std::optional isRemote = passData.isRemote(); + if (isRemote.has_value()) { + passDataDict[@"isRemote"] = @(isRemote.value()); + } #else - id rawIsRemote = passData[@"isRemote"]; - BOOL isRemote = [rawIsRemote isKindOfClass:[NSNumber class]] && [rawIsRemote boolValue]; - passDataDict = @{ + passDataDict = [@{ @"passTypeIdentifier": [self safeString:passData[@"passTypeIdentifier"]], @"serialNumber": [self safeString:passData[@"serialNumber"]], @"activationData": [self safeString:passData[@"activationData"]], - @"isRemote": @(isRemote), - }; + } mutableCopy]; + id rawIsRemote = passData[@"isRemote"]; + if ([rawIsRemote isKindOfClass:[NSNumber class]]) { + passDataDict[@"isRemote"] = rawIsRemote; + } #endif [self->walletManager activateAppleWalletPassWithPassData:passDataDict completion:^(OperationResult result, NSDictionary* data) { diff --git a/src/NativeWallet.ts b/src/NativeWallet.ts index 37e3440e..442094e4 100644 --- a/src/NativeWallet.ts +++ b/src/NativeWallet.ts @@ -75,7 +75,7 @@ type IOSPassActivationData = { passTypeIdentifier: string; serialNumber: string; activationData: string; - isRemote: boolean; + isRemote?: boolean; }; type IOSWalletPassNative = { From b778ee17acf904a6c8f9978348c74594b9fe0151 Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Thu, 29 Oct 2026 10:04:57 +0000 Subject: [PATCH 116/118] Update package-lock.json version to 0.2.23 --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 15c77c15..6a6ad40d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.22", + "version": "0.2.23", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@expensify/react-native-wallet", - "version": "0.2.22", + "version": "0.2.23", "license": "MIT", "workspaces": [ "apps/common-app", From 44571cdc44b65725fca9a5182963aedb5044a0e5 Mon Sep 17 00:00:00 2001 From: "os-botify[bot]" <140437396+os-botify[bot]@users.noreply.github.com> Date: Thu, 29 Oct 2026 10:04:59 +0000 Subject: [PATCH 117/118] Update package.json version to 0.2.23 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e235b8c5..baffbbf7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@expensify/react-native-wallet", - "version": "0.2.22", + "version": "0.2.23", "description": "A React Native module designed for seamless integration of Card Push Provisioning into Apple Wallet and Google Wallet, enabling easy and secure addition of payment cards directly from your application.", "source": "./src/index.tsx", "main": "lib/commonjs/index", From 3a35bdd99e77254038ab610cb4dd85254af46444 Mon Sep 17 00:00:00 2001 From: Bruno Nascimento Date: Thu, 29 Oct 2026 09:48:33 -0300 Subject: [PATCH 118/118] fix: share one wallet response handler Activation resolves through handleWalletResponse, the public pass type lives with the other public types, and listAppleWalletPasses only maps activationState. --- README.md | 2 +- ios/RNWallet.mm | 23 +++++------------------ src/NativeWallet.ts | 10 ++++++++++ src/index.tsx | 15 ++------------- 4 files changed, 18 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index be79374f..40336594 100644 --- a/README.md +++ b/README.md @@ -189,7 +189,7 @@ The library offers functions for seamless integration and use of the Apple Walle | **listAppleWalletPasses** | Lists Apple Wallet Secure Element passes available to the app, including activation state and whether the pass is remote (for example on Apple Watch). Filter the result for `activationState === 'requireActivation'` or `activationState === 'pending'` to build a pending-cards UI. | None | `IOSWalletPass[]` | βœ… | ❌ | | **addCardToAppleWallet** | Initiates native Push Provisioning flow for adding a card to the Apple Wallet. When `IOSCardData.primaryAccountIdentifier` is set, the device picker only lists devices that do not already have the card. If PassKit cannot present the flow because the card is already on every device, the call fails with a clear error. | `data`: `IOSCardData`,
`issuerEncrypt-`
`PayloadCallback: IOSIssuerCallback` | `void` | βœ… | ❌ | | **canAddCardToAppleWallet** | Checks whether the card can still be added to the iPhone or a paired Apple Watch. Pass `lastDigits` to use the same id lookup as `addCardToAppleWallet`. Returns `false` on Android. | `primaryAccountIdentifier: string`,
`lastDigits?: string` | `boolean` | βœ… | βœ… | -| **activateAppleWalletPass** | Activates an existing Secure Element pass in Apple Wallet using activation data from your issuer backend. Best used for passes in the `requireActivation` state. | `data`: `IOSPassActivationData` | `boolean` | βœ… | ❌ | +| **activateAppleWalletPass** | Activates an existing Secure Element pass in Apple Wallet using activation data from your issuer backend. Resolves `true` when the pass is already active. For `requireActivation`, returns Apple's result, which can be `false`. Rejects for any other state (`pending`, `suspended`), a missing pass, unavailable activation, or invalid or non-base64 activation data. | `data`: `IOSPassActivationData` | `boolean` | βœ… | ❌ | ## Data Types diff --git a/ios/RNWallet.mm b/ios/RNWallet.mm index a5b8e4d0..a4d43022 100644 --- a/ios/RNWallet.mm +++ b/ios/RNWallet.mm @@ -70,7 +70,7 @@ - (instancetype)init { dispatch_async(dispatch_get_main_queue(), ^{ [self->walletManager IOSPresentAddPaymentPassViewWithCardData:cardDataDict completion:^(OperationResult result, NSDictionary* data) { - [self handleWalletResponse:result data:data completedBlock:resolve errorPrefix:@"present_payment_pass_view_failed" defaultErrorMessage:@"Failed to present the payment pass view" rejecter:reject]; + [self handleWalletResponse:result data:data resolveValue:data completedBlock:resolve errorPrefix:@"present_payment_pass_view_failed" defaultErrorMessage:@"Failed to present the payment pass view" rejecter:reject]; }]; }); } @catch (NSException *exception) { @@ -106,7 +106,7 @@ - (instancetype)init { dispatch_async(dispatch_get_main_queue(), ^{ [self->walletManager IOSHandleAddPaymentPassResponseWithPayload:payloadDict completion:^(OperationResult result, NSDictionary* data) { - [self handleWalletResponse:result data:data completedBlock:resolve errorPrefix:@"add_card_failed" defaultErrorMessage:@"Failed to add the card to the wallet" rejecter:reject]; + [self handleWalletResponse:result data:data resolveValue:data completedBlock:resolve errorPrefix:@"add_card_failed" defaultErrorMessage:@"Failed to add the card to the wallet" rejecter:reject]; }]; }); } @catch (NSException *exception) { @@ -173,7 +173,7 @@ - (instancetype)init { #endif [self->walletManager activateAppleWalletPassWithPassData:passDataDict completion:^(OperationResult result, NSDictionary* data) { - [self handleBooleanWalletResponse:result data:data completedBlock:resolve errorPrefix:@"activate_card_failed" defaultErrorMessage:@"Failed to activate the Apple Wallet pass" rejecter:reject]; + [self handleWalletResponse:result data:data resolveValue:@([data[@"success"] boolValue]) completedBlock:resolve errorPrefix:@"activate_card_failed" defaultErrorMessage:@"Failed to activate the Apple Wallet pass" rejecter:reject]; }]; } @catch (NSException *exception) { [self rejectWithErrorType:@"activate_card_failed" code:500 description:exception.reason ?: @"An unexpected error occurred." rejecter:reject]; @@ -259,26 +259,13 @@ - (void)rejectWithErrorType:(NSString *)type - (void)handleWalletResponse:(OperationResult)result data:(NSDictionary *)data + resolveValue:(id)resolveValue completedBlock:(RCTPromiseResolveBlock)resolve errorPrefix:(NSString *)errorPrefix defaultErrorMessage:(NSString *)defaultErrorMsg rejecter:(RCTPromiseRejectBlock)reject { if (result < 3) { - resolve(data); - } else { - NSString *errorMessage = data[@"errorMessage"] ?: defaultErrorMsg ?: @"Operation failed"; - [self rejectWithErrorType:errorPrefix code:1001 description:errorMessage rejecter:reject]; - } -} - -- (void)handleBooleanWalletResponse:(OperationResult)result - data:(NSDictionary *)data - completedBlock:(RCTPromiseResolveBlock)resolve - errorPrefix:(NSString *)errorPrefix - defaultErrorMessage:(NSString *)defaultErrorMsg - rejecter:(RCTPromiseRejectBlock)reject { - if (result < 3) { - resolve(@([data[@"success"] boolValue])); + resolve(resolveValue); } else { NSString *errorMessage = data[@"errorMessage"] ?: defaultErrorMsg ?: @"Operation failed"; [self rejectWithErrorType:errorPrefix code:1001 description:errorMessage rejecter:reject]; diff --git a/src/NativeWallet.ts b/src/NativeWallet.ts index ecbd8d27..9522e9f6 100644 --- a/src/NativeWallet.ts +++ b/src/NativeWallet.ts @@ -91,6 +91,15 @@ type IOSWalletPassNative = { isRemote: boolean; }; +type IOSWalletPass = { + passTypeIdentifier: string; + serialNumber: string; + primaryAccountIdentifier?: string; + lastDigits?: string; + activationState: CardStatus; + isRemote: boolean; +}; + type TokenizationStatus = 'canceled' | 'success' | 'error'; type TokenInfo = { @@ -154,6 +163,7 @@ export type { IOSDeviceType, IOSEncryptPayload, IOSPassActivationData, + IOSWalletPass, IOSWalletPassNative, TokenizationStatus, TokenInfo, diff --git a/src/index.tsx b/src/index.tsx index 901a3786..a08b373f 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -15,7 +15,7 @@ import type { onPaymentCredentialsRequestPayload, IOSAddPaymentPassData, IOSPassActivationData, - IOSWalletPassNative, + IOSWalletPass, IOSDeviceType, TokenInfo, AndroidPaymentCredentialsResponse, @@ -23,15 +23,6 @@ import type { import {getCardState, getTokenizationStatus} from './utils'; import AddToWalletButton from './AddToWalletButton'; -type IOSWalletPass = { - passTypeIdentifier: string; - serialNumber: string; - primaryAccountIdentifier?: string; - lastDigits?: string; - activationState: CardStatus; - isRemote: boolean; -}; - function getModuleLinkingRejection() { return Promise.reject(new Error(`Failed to load Wallet module, make sure to link ${PACKAGE_NAME} correctly`)); } @@ -227,10 +218,8 @@ async function listAppleWalletPasses(): Promise { } const passes = await Wallet.listAppleWalletPasses(); - return passes.map((pass: IOSWalletPassNative) => ({ + return passes.map((pass) => ({ ...pass, - primaryAccountIdentifier: pass.primaryAccountIdentifier || undefined, - lastDigits: pass.lastDigits || undefined, activationState: getCardState(pass.activationState), })); }