diff --git a/android/src/main/java/io/ionic/portals/reactnative/PortalView.kt b/android/src/main/java/io/ionic/portals/reactnative/PortalView.kt index 9c95c93..606b729 100644 --- a/android/src/main/java/io/ionic/portals/reactnative/PortalView.kt +++ b/android/src/main/java/io/ionic/portals/reactnative/PortalView.kt @@ -33,7 +33,7 @@ internal class PortalViewManager(private val context: ReactApplicationContext) : when (fragmentMap[viewGroup.id]) { null -> fragmentMap[viewGroup.id] = PortalViewState( fragment = null, - portal = RNPortalManager.getPortal(name) ?: RNPortalManager.createPortal(portal), + portal = RNPortalManager.createPortal(portal), initialContext = portal.getMap("initialContext")?.toHashMap() ) } diff --git a/android/src/main/java/io/ionic/portals/reactnative/ReactNativePortalManager.kt b/android/src/main/java/io/ionic/portals/reactnative/ReactNativePortalManager.kt index 10c5e70..71bd244 100644 --- a/android/src/main/java/io/ionic/portals/reactnative/ReactNativePortalManager.kt +++ b/android/src/main/java/io/ionic/portals/reactnative/ReactNativePortalManager.kt @@ -41,20 +41,11 @@ internal data class PortalPlugin(val androidClassPath: String, val iosClassName: internal object RNPortalManager { private val manager = PortalManager - @Deprecated("Will be removed in the next release.") - private val portals: ConcurrentHashMap = ConcurrentHashMap() private lateinit var reactApplicationContext: ReactApplicationContext private var usesSecureLiveUpdates = false fun register(key: String) = manager.register(key) - @Deprecated("Will be removed in the next release.") - fun addPortal(map: ReadableMap): RNPortal? { - val portal = createPortal(map) ?: return null - portals[portal.builder.name] = portal - return portal - } - fun createPortal(map: ReadableMap): RNPortal? { val name = map.getString("name") ?: return null val portalBuilder = PortalBuilder(name) @@ -147,11 +138,6 @@ internal object RNPortalManager { ) } - @Deprecated("Will be removed in the next release.") - fun getPortal(name: String): RNPortal? { - return portals[name] - } - fun enableSecureLiveUpdates(keyPath: String) { LiveUpdateManager.secureLiveUpdatePEM = keyPath usesSecureLiveUpdates = true @@ -183,13 +169,6 @@ internal object RNPortalManager { LiveUpdateManager.secureLiveUpdatePEM = it usesSecureLiveUpdates = true } - - val portalJsonArray = configJson.getJSONArray("portals") - - for (index in 0 until portalJsonArray.length()) { - val portalJson = portalJsonArray.getJSONObject(index) - addPortal(portalJson.toReactMap()) - } } } diff --git a/android/src/main/java/io/ionic/portals/reactnative/ReactNativePortalsModule.kt b/android/src/main/java/io/ionic/portals/reactnative/ReactNativePortalsModule.kt index 9f57566..2608441 100644 --- a/android/src/main/java/io/ionic/portals/reactnative/ReactNativePortalsModule.kt +++ b/android/src/main/java/io/ionic/portals/reactnative/ReactNativePortalsModule.kt @@ -16,39 +16,6 @@ internal class PortalManagerModule(reactContext: ReactApplicationContext) : promise.resolve(null) } - @ReactMethod - fun addPortal(map: ReadableMap, promise: Promise) { - val portal = RNPortalManager.addPortal(map) - if (portal == null) { - promise.reject(null, "Invalid Portal configuration.") - } else { - promise.resolve(portal.toReadableMap()) - } - } - - @ReactMethod - fun addPortals(array: ReadableArray, promise: Promise) { - val portals = WritableNativeArray() - - for (i in 0 until array.size()) { - val map = array.getMap(i) - val portal = RNPortalManager.addPortal(map) ?: continue - portals.pushMap(portal.toReadableMap()) - } - - promise.resolve(portals) - } - - @ReactMethod - fun getPortal(name: String, promise: Promise) { - try { - val portal = RNPortalManager.getPortal(name) - promise.resolve(portal?.toReadableMap()) - } catch (e: IllegalStateException) { - promise.reject(null, "Portal named $name not registered.") - } - } - @ReactMethod fun enableSecureLiveUpdates(keyPath: String, promise: Promise) { RNPortalManager.enableSecureLiveUpdates(keyPath) diff --git a/android/src/main/java/io/ionic/portals/reactnative/ReactNativeWebVitalsModule.kt b/android/src/main/java/io/ionic/portals/reactnative/ReactNativeWebVitalsModule.kt index aed792e..5af3756 100644 --- a/android/src/main/java/io/ionic/portals/reactnative/ReactNativeWebVitalsModule.kt +++ b/android/src/main/java/io/ionic/portals/reactnative/ReactNativeWebVitalsModule.kt @@ -8,32 +8,6 @@ import com.facebook.react.bridge.ReactMethod internal class PortalWebVitalsModule(reactContext: ReactApplicationContext): ReactContextBaseJavaModule(reactContext) { override fun getName() = "IONPortalsWebVitals" - private fun registerVital(portalName: String, vitalName: String) { - val portal = RNPortalManager.getPortal(portalName) ?: return - if (portal.vitals == null) { - portal.vitals = mutableListOf() - } - portal.vitals?.add(vitalName) - } - - @ReactMethod - fun registerOnFirstContentfulPaint(portalName: String, promise: Promise) { - registerVital(portalName,"fcp") - promise.resolve(null) - } - - @ReactMethod - fun registerOnFirstInputDelay(portalName: String, promise: Promise) { - registerVital(portalName,"fid") - promise.resolve(null) - } - - @ReactMethod - fun registerOnTimeToFirstByte(portalName: String, promise: Promise) { - registerVital(portalName,"ttfb") - promise.resolve(null) - } - @ReactMethod fun addListener(eventName: String) { } diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 03ebbce..882e503 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1171,7 +1171,7 @@ PODS: - React-logger (= 0.74.2) - React-perflogger (= 0.74.2) - React-utils (= 0.74.2) - - ReactNativePortals (0.5.2): + - ReactNativePortals (0.6.0): - DoubleConversion - glog - hermes-engine @@ -1435,10 +1435,10 @@ SPEC CHECKSUMS: React-runtimescheduler: 56b642bf605ba5afa500d35790928fc1d51565ad React-utils: 4476b7fcbbd95cfd002f3e778616155241d86e31 ReactCommon: ecad995f26e0d1e24061f60f4e5d74782f003f12 - ReactNativePortals: 9bfdb3928c55e58ba22b21d99775aa3bb33c4df8 + ReactNativePortals: 42f59afa6ed4119897d2dd7c9e5f66599c022684 SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d Yoga: ae3c32c514802d30f687a04a6a35b348506d411f PODFILE CHECKSUM: ce82c2dd878c18eaa5b59018ea58e2e1c0876c21 -COCOAPODS: 1.14.3 +COCOAPODS: 1.15.2 diff --git a/example/ios/PortalsReactNativeExample.xcodeproj/project.pbxproj b/example/ios/PortalsReactNativeExample.xcodeproj/project.pbxproj index c032cfb..8d48f66 100644 --- a/example/ios/PortalsReactNativeExample.xcodeproj/project.pbxproj +++ b/example/ios/PortalsReactNativeExample.xcodeproj/project.pbxproj @@ -608,11 +608,7 @@ "-DFOLLY_CFG_NO_COROUTINES=1", "-DFOLLY_HAVE_CLOCK_GETTIME=1", ); - OTHER_LDFLAGS = ( - "$(inherited)", - "-Wl", - "-ld_classic", - ); + OTHER_LDFLAGS = "$(inherited)"; REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; USE_HERMES = true; @@ -684,11 +680,7 @@ "-DFOLLY_CFG_NO_COROUTINES=1", "-DFOLLY_HAVE_CLOCK_GETTIME=1", ); - OTHER_LDFLAGS = ( - "$(inherited)", - "-Wl", - "-ld_classic", - ); + OTHER_LDFLAGS = "$(inherited)"; REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; USE_HERMES = true; diff --git a/example/src/App.tsx b/example/src/App.tsx index 63adc4e..e7f5613 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -48,19 +48,19 @@ export default function App() { {/* {ready ? ( */} - - // console.log('firstContentfulPaint', duration), - // firstInputDelay: (duration: number) => - // console.log('firstInputDelay', duration), - // timeToFirstByte: (duration: number) => { - // console.log('timeToFirstByte', duration); - // }, - // }} - /> + + console.log('firstContentfulPaint', duration), + firstInputDelay: (duration: number) => + console.log('firstInputDelay', duration), + timeToFirstByte: (duration: number) => { + console.log('timeToFirstByte', duration); + }, + }} + /> {/* ) : ( <> )} */} diff --git a/ios/AssetMap+Codable.swift b/ios/AssetMap+Codable.swift new file mode 100644 index 0000000..18515aa --- /dev/null +++ b/ios/AssetMap+Codable.swift @@ -0,0 +1,33 @@ +// +// IonicPortals+Codable.swift +// ReactNativePortals +// +// Created by Trevor Lambert on 6/5/24. +// Copyright © 2024 Facebook. All rights reserved. +// + +import Foundation +import IonicPortals + +extension AssetMap: Encodable { + enum CodingKeys: String, CodingKey { + case startDir, virtualPath, name + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(startDir, forKey: .startDir) + try container.encode(virtualPath, forKey: .virtualPath) + try container.encode(name, forKey: .name) + } +} + +extension AssetMap: Decodable { + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let name = try container.decode(String.self, forKey: .name) + let startDir = try container.decodeIfPresent(String.self, forKey: .startDir) ?? "" + let virtualPath = try container.decodeIfPresent(String.self, forKey: .virtualPath) + self.init(name: name, virtualPath: virtualPath, startDir: startDir) + } +} diff --git a/ios/AssetMap+Dict.swift b/ios/AssetMap+Dict.swift deleted file mode 100644 index 034f989..0000000 --- a/ios/AssetMap+Dict.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// AssetMap+Dict.swift -// ReactNativePortals -// -// Created by Steven Sherry on 3/29/23. -// Copyright © 2023 Facebook. All rights reserved. -// - -import IonicPortals - -extension AssetMap { - init?(_ dict: [String: Any]) { - guard let name = dict["name"] as? String else { return nil } - self.init( - name: name, - virtualPath: dict["virtualPath"] as? String, - startDir: dict["startDir"] as? String ?? "" - ) - } - - var dict: [String: Any] { - return [ - "name": name, - "virtualPath": virtualPath, - "startDir": startDir - ] - } -} diff --git a/ios/IonicPortals+Codable.swift b/ios/IonicPortals+Codable.swift deleted file mode 100644 index 0d6c2ad..0000000 --- a/ios/IonicPortals+Codable.swift +++ /dev/null @@ -1,77 +0,0 @@ -// -// IonicPortals+Codable.swift -// ReactNativePortals -// -// Created by Trevor Lambert on 6/5/24. -// Copyright © 2024 Facebook. All rights reserved. -// - -import Foundation -import IonicPortals - -extension AssetMap: Encodable { - enum CodingKeys: String, CodingKey { - case startDir, virtualPath, name - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(startDir, forKey: .startDir) - try container.encode(virtualPath, forKey: .virtualPath) - try container.encode(name, forKey: .name) - } -} - -extension AssetMap: Decodable { - public init(from decoder: Decoder) throws { - let container = try decoder.container(keyedBy: CodingKeys.self) - let name = try container.decode(String.self, forKey: .name) - let startDir = try container.decodeIfPresent(String.self, forKey: .startDir) ?? "" - let virtualPath = try container.decodeIfPresent(String.self, forKey: .virtualPath) - self.init(name: name, virtualPath: virtualPath, startDir: startDir) - } -} - -//export interface Portal { -// /** The name of the Portal to be referenced. Must be **unique** */ -// name: string; -// /** Any Capacitor plugins to be made available to the Portal */ -// plugins?: CapacitorPlugin[]; -// /** -// * The root directory of the web application relative to Bundle.main on iOS -// * and src/main/assets on Android. If omitted, `name` is used. -// */ -// startDir?: string; -// /** The name of the initial file to load. If omitted, 'index.html' is used. */ -// index?: string; -// /** Any data needed at initial render when a portal is loaded. */ -// initialContext?: { -// [key: string]: any; -// }; -// assetMaps?: AssetMap[]; -// liveUpdate?: LiveUpdateConfig; -//} - -//export interface LiveUpdate { -// /** The AppFlow application ID */ -// appId: string; -// /** The AppFlow distribution channel */ -// channel: string; -//} -// -///** Data needed to register a live update to be managed */ -//export type LiveUpdateConfig = LiveUpdate & { syncOnAdd: boolean }; - -//export interface AssetMap { -// /** The name to index the asset map by */ -// name: string; -// /** Any path to match via the web. If omitted, {@link AssetMap#name} will be used. */ -// virtualPath?: string; -// /** The root directory of the assets relative to Bundle.main on iOS -// * and src/main/assets on Android. If omitted, the root of Bundle.main -// * and src/main/assets will be used. -// */ -// startDir?: string; -//} - - diff --git a/ios/PortalManager.mm b/ios/PortalManager.mm index 058d334..72c0144 100644 --- a/ios/PortalManager.mm +++ b/ios/PortalManager.mm @@ -11,9 +11,6 @@ @interface RCT_EXTERN_MODULE(IONPortalsReactNative, NSObject) RCT_EXTERN_METHOD(register: (NSString *) key resolver: (RCTPromiseResolveBlock) resolver rejector: (RCTPromiseRejectBlock) rejector) RCT_EXTERN_METHOD(enableSecureLiveUpdates: (NSString *) publicKeyPath resolver: (RCTPromiseResolveBlock) resolver rejector: (RCTPromiseRejectBlock) rejector) -RCT_EXTERN_METHOD(addPortal: (NSDictionary) portal resolver: (RCTPromiseResolveBlock) resolver rejector: (RCTPromiseRejectBlock) rejector) -RCT_EXTERN_METHOD(addPortals: (NSArray) portals resolver: (RCTPromiseResolveBlock) resolver rejector: (RCTPromiseRejectBlock) rejector) -RCT_EXTERN_METHOD(getPortal: (NSString *) name resolver: (RCTPromiseResolveBlock) resolver rejector: (RCTPromiseRejectBlock) rejector) RCT_EXTERN_METHOD(syncOne: (NSString *) appId resolver: (RCTPromiseResolveBlock) resolver rejector: (RCTPromiseRejectBlock) rejector) RCT_EXTERN_METHOD(syncSome: (NSArray) appIds resolver: (RCTPromiseResolveBlock) resolver rejector: (RCTPromiseRejectBlock) rejector) RCT_EXTERN_METHOD(syncAll: (RCTPromiseResolveBlock) resolver rejector: (RCTPromiseRejectBlock) rejector) diff --git a/ios/PortalView.swift b/ios/PortalView.swift index b56ea6e..3293077 100644 --- a/ios/PortalView.swift +++ b/ios/PortalView.swift @@ -27,28 +27,20 @@ class PortalView: UIView { } set { - guard let portalDict = newValue, - let name = portalDict["name"] as? String - else { return } - + guard let portalDict = newValue else { return } + var portal: Portal - if var deprecatedPortal = PortalsReactNative.getPortal(named: name) { - if let initialContext = portalDict["initialContext"] as? [String: Any] { - deprecatedPortal.initialContext = JSTypes.coerceDictionaryToJSObject(initialContext) ?? [:] - } - portal = deprecatedPortal - } else { + do { let jsObject = JSTypes.coerceDictionaryToJSObject(portalDict) ?? [:] - do { - portal = try Portal.decode(from: jsObject, with: JSValueDecoder()) - } catch { - print(error.localizedDescription) - return - } + portal = try Portal.decode(from: jsObject, with: JSValueDecoder()) + } catch { + print(error.localizedDescription) + return } + if portal.usesWebVitals { - var vitalsPlugin = WebVitalsPlugin { portalName, duration in + let vitalsPlugin = WebVitalsPlugin { portalName, duration in IonicPortals.PortalsPubSub .shared .publish( diff --git a/ios/PortalWebVitals.mm b/ios/PortalWebVitals.mm index 2d9bcce..7dce5b6 100644 --- a/ios/PortalWebVitals.mm +++ b/ios/PortalWebVitals.mm @@ -10,6 +10,5 @@ #import @interface RCT_EXTERN_MODULE(IONPortalsWebVitals, RCTEventEmitter) -RCT_EXTERN_METHOD(registerOnFirstContentfulPaint: (NSString *) portalName resolver: (RCTPromiseResolveBlock) resolver rejector: (RCTPromiseRejectBlock) rejector) @end diff --git a/ios/PortalsReactNative.swift b/ios/PortalsReactNative.swift index e1bba56..97f83c9 100644 --- a/ios/PortalsReactNative.swift +++ b/ios/PortalsReactNative.swift @@ -7,8 +7,6 @@ import React @objc(IONPortalsReactNative) public class PortalsReactNative: NSObject { internal private(set) static var lum: LiveUpdateManager = .shared - @available(*, deprecated, message: "This will be removed in the next release") - internal static var portals = ConcurrentDictionary(label: "com.portals.reactnative", dict: [:]) let encoder = JSValueEncoder(optionalEncodingStrategy: .undefined) let decoder = JSValueDecoder() @@ -41,44 +39,7 @@ public class PortalsReactNative: NSObject { Self.lum = SecureLiveUpdateManager(named: "secure-updates", publicKeyUrl: publicKeyUrl) resolver(()) } - - @available(*, deprecated, message: "This will be removed in the next release") - @objc func addPortal(_ portalDict: [String: Any], resolver: RCTPromiseResolveBlock, rejector: RCTPromiseRejectBlock) { - do { - let portal = try Portal.decode(from: JSTypes.coerceDictionaryToJSObject(portalDict) ?? [:], with: decoder) - Self.portals[portal.name] = portal - resolver(try encoder.encode(portal)) - } catch { - rejector(nil, "Invalid Portal configuration", error) - } - } - - @available(*, deprecated, message: "This will be removed in the next release") - @objc func addPortals(_ portalsArray: [[String: Any]], resolver: RCTPromiseResolveBlock, rejector: RCTPromiseRejectBlock) { - do { - let portals = try decoder.decode([Portal].self, from: JSTypes.coerceArrayToJSArray(portalsArray) ?? []) - for portal in portals { - Self.portals[portal.name] = portal - } - resolver(try encoder.encode(portals)) - } catch { - rejector(nil, "Invalid Portal configuration", error) - } - } - - @available(*, deprecated, message: "This will be removed in the next release") - static func getPortal(named name: String) -> Portal? { portals[name] } - - @available(*, deprecated, message: "This will be removed in the next release") - @objc func getPortal(_ name: String, resolver: RCTPromiseResolveBlock, rejector: RCTPromiseRejectBlock) { - guard let portal = Self.getPortal(named: name) else { return rejector(nil, "Portal named \(name) not registered", nil) } - do { - resolver(try encoder.encode(portal)) - } catch { - rejector(nil, "Invalid Portal configuration", error) - } - } - + @objc func syncOne(_ appId: String, resolver: @escaping RCTPromiseResolveBlock, rejector: @escaping RCTPromiseRejectBlock) { Task { do { diff --git a/ios/WebVitals.swift b/ios/WebVitals.swift index 0cf09ff..281c81c 100644 --- a/ios/WebVitals.swift +++ b/ios/WebVitals.swift @@ -33,16 +33,6 @@ class WebVitals: RCTEventEmitter { override func supportedEvents() -> [String] { [fcp] } - - @objc func registerOnFirstContentfulPaint(_ portalName: String, resolver: @escaping RCTPromiseResolveBlock, rejector: RCTPromiseRejectBlock) { - guard var portal = PortalsReactNative.portals[portalName] else { - return resolver(()) - } - - portal.usesWebVitals = true - PortalsReactNative.portals[portalName] = portal - resolver(()) - } - + override class func requiresMainQueueSetup() -> Bool { true } } diff --git a/package.json b/package.json index a39fa8a..53bb793 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ionic/portals-react-native", - "version": "0.6.0", + "version": "0.7.0", "description": "Portals for React Native", "main": "lib/commonjs/index", "module": "lib/module/index", diff --git a/src/index.tsx b/src/index.tsx index 9070e5b..3aa95cf 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -53,8 +53,6 @@ export interface Message { const PortalsPubSub = new NativeEventEmitter(IONPortalPubSub); -// const subscriptionMap = new Map(); - /** * Subscribes to messages for a topic * @@ -111,57 +109,6 @@ const registerVital = ( webVitalsMap.set(`${portalName}-vitals:${vital}`, listener); }; -/** - * @deprecated Use {@link WebVitals} prop on {@link PortalView} instead. This will be removed in the next release. - */ -export const onFirstContentfulPaint = async ( - portalName: string, - callback: (duration: number) => void -): Promise => { - registerVital(portalName, 'fcp', callback); - await IONPortalsWebVitals.registerOnFirstContentfulPaint(portalName); -}; - -/** - * @deprecated Use {@link WebVitals} prop on {@link PortalView} instead. This will be removed in the next release. - */ -export const onFirstInputDelay = async ( - portalName: string, - callback: (duration: number) => void -) => { - if (Platform.OS === 'android') { - registerVital(portalName, 'fid', callback); - await IONPortalsWebVitals.registerOnFirstInputDelay(portalName); - } -}; - -/** - * @deprecated Use {@link WebVitals} prop on {@link PortalView} instead. This will be removed in the next release. - */ -export const onTimeToFirstByte = async ( - portalName: string, - callback: (duration: number) => void -) => { - if (Platform.OS === 'android') { - registerVital(portalName, 'ttfb', callback); - await IONPortalsWebVitals.registerOnTimeToFirstByte(portalName); - } -}; - -/** - * @deprecated Use {@link WebVitals} prop on {@link PortalView} instead. This will be removed in the next release. - */ -export const registerWebVitals = async ( - portalName: string, - firstContentfulPaint: (duration: number) => void, - firstInputDelay: (duration: number) => void, - timeToFirstByte: (duration: number) => void -) => { - onFirstContentfulPaint(portalName, firstContentfulPaint); - onFirstInputDelay(portalName, firstInputDelay); - onTimeToFirstByte(portalName, timeToFirstByte); -}; - export type WebVitals = { firstContentfulPaint?: (duration: number) => void; firstInputDelay?: (duration: number) => void; @@ -227,44 +174,6 @@ export interface AssetMap { */ export type PortalProps = { portal: Portal; webVitals?: WebVitals } & ViewProps; -/** - * @deprecated Portals are no longer centrally managed natively. - * Pass a {@link Portal} directly to {@link PortalView} instead. - * This will be removed in the next release. - * - * Adds a Portal to an internal registry. - * - * @param portal The portal to add to the internal registry. - * @returns Promise containing the Portal that was added to the registry. - */ -export const addPortal = async (portal: Portal): Promise => { - return IONPortalsReactNative.addPortal(portal); -}; - -/** - * @deprecated Portals are no longer centrally managed natively. - * Pass a {@link Portal} directly to {@link PortalView} instead. - * This will be removed in the next release. - * - * @param portals The portals to add to the internal registry. - * @returns Promise containing the Portals that were added to the registry. - */ -export const addPortals = async (portals: Portal[]): Promise => { - return IONPortalsReactNative.addPortals(portals); -}; - -/** - * @deprecated Portals are no longer centrally managed natively. - * Pass a {@link Portal} directly to {@link PortalView} instead. - * This will be removed in the next release. - * - * @param name The portal name to retrieve from the internal registry. - * @returns Promise containing the registered {@link Portal}. If the {@link Portal} was not registered, the Promise will fail. - */ -export const getPortal = async (name: string): Promise => { - return IONPortalsReactNative.getPortal(name); -}; - export interface LiveUpdate { /** The AppFlow application ID */ appId: string;