expo-invoke 1.2.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/android/src/main/java/expo/modules/invoke/InteractiveWidgetReceiver.kt +47 -0
  2. package/android/src/main/java/expo/modules/invoke/InvokeSliceProvider.kt +83 -0
  3. package/android/src/main/java/expo/modules/invoke/ShortcutsHelper.kt +38 -32
  4. package/build/plugin/android/withAndroidInvoke.d.ts +3 -1
  5. package/build/plugin/android/withAndroidInvoke.js +71 -15
  6. package/build/plugin/android/withAndroidInvoke.js.map +1 -1
  7. package/build/plugin/codegen/generateSwiftAppClip.d.ts +2 -0
  8. package/build/plugin/codegen/generateSwiftAppClip.js +20 -0
  9. package/build/plugin/codegen/generateSwiftAppClip.js.map +1 -0
  10. package/build/plugin/codegen/generateSwiftEntities.d.ts +2 -0
  11. package/build/plugin/codegen/generateSwiftEntities.js +91 -0
  12. package/build/plugin/codegen/generateSwiftEntities.js.map +1 -0
  13. package/build/plugin/codegen/generateSwiftFocusFilters.d.ts +2 -0
  14. package/build/plugin/codegen/generateSwiftFocusFilters.js +42 -0
  15. package/build/plugin/codegen/generateSwiftFocusFilters.js.map +1 -0
  16. package/build/plugin/codegen/generateSwiftIntents.d.ts +2 -0
  17. package/build/plugin/codegen/generateSwiftIntents.js +43 -13
  18. package/build/plugin/codegen/generateSwiftIntents.js.map +1 -1
  19. package/build/plugin/ios/withIOSInvoke.d.ts +8 -1
  20. package/build/plugin/ios/withIOSInvoke.js +61 -10
  21. package/build/plugin/ios/withIOSInvoke.js.map +1 -1
  22. package/build/plugin/src/withInvoke.d.ts +20 -3
  23. package/build/plugin/src/withInvoke.js +23 -4
  24. package/build/plugin/src/withInvoke.js.map +1 -1
  25. package/build/src/components/AddToSiriButton.d.ts +1 -1
  26. package/build/src/components/AddToSiriButton.d.ts.map +1 -1
  27. package/build/src/components/InvokeDebugger.d.ts +1 -1
  28. package/build/src/components/InvokeDebugger.d.ts.map +1 -1
  29. package/build/src/components/ShortcutChip.d.ts +1 -1
  30. package/build/src/components/ShortcutChip.d.ts.map +1 -1
  31. package/build/src/hooks/useBackgroundIntent.d.ts +15 -0
  32. package/build/src/hooks/useBackgroundIntent.d.ts.map +1 -0
  33. package/build/src/hooks/useBackgroundIntent.js +41 -0
  34. package/build/src/hooks/useBackgroundIntent.js.map +1 -0
  35. package/build/src/hooks/useEntityQuery.d.ts +26 -0
  36. package/build/src/hooks/useEntityQuery.d.ts.map +1 -0
  37. package/build/src/hooks/useEntityQuery.js +47 -0
  38. package/build/src/hooks/useEntityQuery.js.map +1 -0
  39. package/build/src/hooks/useFocusFilter.d.ts +1 -1
  40. package/build/src/hooks/useFocusFilter.d.ts.map +1 -1
  41. package/build/src/hooks/useFocusFilter.js.map +1 -1
  42. package/build/src/hooks/useFocusFilterState.d.ts +28 -0
  43. package/build/src/hooks/useFocusFilterState.d.ts.map +1 -0
  44. package/build/src/hooks/useFocusFilterState.js +47 -0
  45. package/build/src/hooks/useFocusFilterState.js.map +1 -0
  46. package/build/src/hooks/useInvoke.d.ts.map +1 -1
  47. package/build/src/hooks/useInvoke.js +3 -0
  48. package/build/src/hooks/useInvoke.js.map +1 -1
  49. package/build/src/hooks/useInvokeAnalytics.d.ts +10 -14
  50. package/build/src/hooks/useInvokeAnalytics.d.ts.map +1 -1
  51. package/build/src/hooks/useInvokeAnalytics.js +21 -14
  52. package/build/src/hooks/useInvokeAnalytics.js.map +1 -1
  53. package/build/src/hooks/useInvokeQueue.d.ts +32 -0
  54. package/build/src/hooks/useInvokeQueue.d.ts.map +1 -0
  55. package/build/src/hooks/useInvokeQueue.js +77 -0
  56. package/build/src/hooks/useInvokeQueue.js.map +1 -0
  57. package/build/src/hooks/useLiveActivity.d.ts +37 -0
  58. package/build/src/hooks/useLiveActivity.d.ts.map +1 -0
  59. package/build/src/hooks/useLiveActivity.js +74 -0
  60. package/build/src/hooks/useLiveActivity.js.map +1 -0
  61. package/build/src/hooks/useOTAConfig.d.ts +24 -0
  62. package/build/src/hooks/useOTAConfig.d.ts.map +1 -0
  63. package/build/src/hooks/useOTAConfig.js +40 -0
  64. package/build/src/hooks/useOTAConfig.js.map +1 -0
  65. package/build/src/hooks/usePushToTalk.d.ts +35 -0
  66. package/build/src/hooks/usePushToTalk.d.ts.map +1 -0
  67. package/build/src/hooks/usePushToTalk.js +101 -0
  68. package/build/src/hooks/usePushToTalk.js.map +1 -0
  69. package/build/src/hooks/useSlice.d.ts +19 -0
  70. package/build/src/hooks/useSlice.d.ts.map +1 -0
  71. package/build/src/hooks/useSlice.js +22 -0
  72. package/build/src/hooks/useSlice.js.map +1 -0
  73. package/build/src/index.d.ts +19 -3
  74. package/build/src/index.d.ts.map +1 -1
  75. package/build/src/index.js +16 -1
  76. package/build/src/index.js.map +1 -1
  77. package/build/src/module.d.ts +18 -3
  78. package/build/src/module.d.ts.map +1 -1
  79. package/build/src/module.js +77 -0
  80. package/build/src/module.js.map +1 -1
  81. package/build/src/types/index.d.ts +117 -4
  82. package/build/src/types/index.d.ts.map +1 -1
  83. package/build/src/utils/analytics.d.ts +1 -7
  84. package/build/src/utils/analytics.d.ts.map +1 -1
  85. package/build/src/utils/analytics.js +9 -0
  86. package/build/src/utils/analytics.js.map +1 -1
  87. package/build/src/utils/invokeError.d.ts +28 -0
  88. package/build/src/utils/invokeError.d.ts.map +1 -0
  89. package/build/src/utils/invokeError.js +57 -0
  90. package/build/src/utils/invokeError.js.map +1 -0
  91. package/build/src/utils/otaConfig.d.ts +9 -0
  92. package/build/src/utils/otaConfig.d.ts.map +1 -0
  93. package/build/src/utils/otaConfig.js +45 -0
  94. package/build/src/utils/otaConfig.js.map +1 -0
  95. package/build/src/utils/persistentAnalytics.d.ts +5 -0
  96. package/build/src/utils/persistentAnalytics.d.ts.map +1 -0
  97. package/build/src/utils/persistentAnalytics.js +52 -0
  98. package/build/src/utils/persistentAnalytics.js.map +1 -0
  99. package/build/src/utils/validation.d.ts +12 -0
  100. package/build/src/utils/validation.d.ts.map +1 -1
  101. package/build/src/utils/validation.js +81 -1
  102. package/build/src/utils/validation.js.map +1 -1
  103. package/build/src/web/index.d.ts +39 -0
  104. package/build/src/web/index.d.ts.map +1 -0
  105. package/build/src/web/index.js +37 -0
  106. package/build/src/web/index.js.map +1 -0
  107. package/build/src/web/module.d.ts +40 -0
  108. package/build/src/web/module.d.ts.map +1 -0
  109. package/build/src/web/module.js +46 -0
  110. package/build/src/web/module.js.map +1 -0
  111. package/ios/AppClip/AppClipHandler.swift +41 -0
  112. package/ios/Background/BackgroundIntentBridge.swift +56 -0
  113. package/ios/Entities/EntityQueryBridge.swift +40 -0
  114. package/ios/FocusFilter/FocusFilterRegistrar.swift +40 -0
  115. package/ios/LiveActivity/LiveActivityManager.swift +83 -0
  116. package/ios/PTT/PTTManager.swift +51 -0
  117. package/package.json +5 -1
@@ -0,0 +1,46 @@
1
+ // Web no-op shims for all native module functions.
2
+ // All hooks import from '../module' which re-exports these on web via package.json's browser field.
3
+ const noop = () => { };
4
+ const noopAsync = async () => { };
5
+ export function configure(_config) { }
6
+ export function getInitialIntent() { return null; }
7
+ export function addIntentListener(_listener) {
8
+ return { remove: noop };
9
+ }
10
+ export function donateAction(_input) { }
11
+ export function deleteAction(_intentId) { }
12
+ export function deleteAllActions() { }
13
+ export function setDynamicShortcuts(_shortcuts) { }
14
+ export function getDynamicShortcuts() { return []; }
15
+ export function clearDynamicShortcuts() { }
16
+ export function indexSpotlightItems(_items) { }
17
+ export function deindexSpotlightItems(_ids) { }
18
+ export function setWidgetData(_key, _value) { }
19
+ export function getWidgetData(_key) { return null; }
20
+ export function reloadWidget(_widgetKind) { }
21
+ export function updateQuickTile(_state, _label) { }
22
+ export function advertiseHandoff(_intentId, _params) { }
23
+ export function stopHandoff() { }
24
+ export function setLiveActivityIntent(_activityId, _intentId) { }
25
+ export function addIntentToSiri(_intentId) { }
26
+ export function getFocusMode() { return null; }
27
+ export function reportFocusFilterResult(_filterId, _success) { }
28
+ export function provideEntityResults(_entityId, _results) { }
29
+ export function registerBackgroundHandler(_intentId) { }
30
+ export function reportBackgroundResult(_intentId, _result) { }
31
+ export async function startLiveActivity(_config) { return ''; }
32
+ export function updateLiveActivity(_activityId, _contentState) { }
33
+ export function endLiveActivity(_activityId, _finalState) { }
34
+ export async function getLiveActivities() { return []; }
35
+ export async function startPTTSession(_channelId, _participantName) { }
36
+ export async function endPTTSession(_channelId) { }
37
+ export async function setTransmitting(_channelId, _isTransmitting) { }
38
+ export async function getPTTState(_channelId) {
39
+ return { isActive: false, isTransmitting: false };
40
+ }
41
+ export function notifySliceChange(_sliceId) { }
42
+ export async function getAnalyticsPersisted() {
43
+ return { fired: {}, donated: {}, bySource: {} };
44
+ }
45
+ export async function clearAnalyticsPersisted() { }
46
+ //# sourceMappingURL=module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module.js","sourceRoot":"","sources":["../../../src/web/module.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ // AppClipHandler.swift
2
+ // Handles NSUserActivity continuations from App Clip invocations.
3
+ // Call AppClipHandler.handle(userActivity:) from your AppDelegate / SceneDelegate.
4
+
5
+ import Foundation
6
+
7
+ public final class AppClipHandler {
8
+
9
+ // intentId mapped to URL prefix — populated from config at startup
10
+ private static var registrations: [(intentId: String, urlPrefix: String)] = []
11
+
12
+ // Called by generated InvokeAppClip.swift during configure()
13
+ public static func register(intentId: String, urlPrefix: String) {
14
+ registrations.append((intentId: intentId, urlPrefix: urlPrefix))
15
+ }
16
+
17
+ // Call this from AppDelegate.application(_:continue:restorationHandler:)
18
+ @discardableResult
19
+ public static func handle(userActivity: NSUserActivity) -> Bool {
20
+ guard userActivity.activityType == NSUserActivityTypes.appClipActivity,
21
+ let url = userActivity.webpageURL else { return false }
22
+
23
+ let urlString = url.absoluteString
24
+ for reg in registrations {
25
+ if urlString.hasPrefix(reg.urlPrefix) {
26
+ ExpoInvokeModule.writePendingIntent(
27
+ intentId: reg.intentId,
28
+ source: "app_clip",
29
+ parameters: ["url": urlString]
30
+ )
31
+ return true
32
+ }
33
+ }
34
+ return false
35
+ }
36
+ }
37
+
38
+ // Provides the constant without importing UIKit
39
+ private enum NSUserActivityTypes {
40
+ static let appClipActivity = "NSUserActivityTypeBrowsingWeb"
41
+ }
@@ -0,0 +1,56 @@
1
+ // BackgroundIntentBridge.swift
2
+ // Manages background intent execution — intents that run without opening the app (openAppWhenRun: false).
3
+ // The generated AppIntent perform() calls dispatch(), which suspends until JS calls reportResult().
4
+
5
+ import Foundation
6
+
7
+ public final class ExpoInvokeBackgroundBridge {
8
+
9
+ public static let shared = ExpoInvokeBackgroundBridge()
10
+
11
+ private var pendingContinuations: [String: CheckedContinuation<BackgroundIntentOutcome, Never>] = [:]
12
+ private let lock = NSLock()
13
+ private let timeoutSeconds: Double = 10
14
+
15
+ private init() {}
16
+
17
+ public struct BackgroundIntentOutcome {
18
+ public let success: Bool
19
+ public let spokenResponse: String?
20
+ public let error: String?
21
+ }
22
+
23
+ // Called by generated AppIntent perform() when openAppWhenRun == false
24
+ @available(iOS 16.0, *)
25
+ public func dispatch(intentId: String, parameters: [String: Any]) async -> BackgroundIntentOutcome {
26
+ return await withCheckedContinuation { continuation in
27
+ lock.lock()
28
+ pendingContinuations[intentId] = continuation
29
+ lock.unlock()
30
+
31
+ // Notify JS via synthetic background source intent
32
+ ExpoInvokeModule.writePendingIntent(
33
+ intentId: intentId,
34
+ source: "background",
35
+ parameters: parameters
36
+ )
37
+
38
+ // Auto-timeout after 10 s to avoid hanging Siri
39
+ Task {
40
+ try? await Task.sleep(nanoseconds: UInt64(timeoutSeconds * 1_000_000_000))
41
+ self.lock.lock()
42
+ let cont = self.pendingContinuations.removeValue(forKey: intentId)
43
+ self.lock.unlock()
44
+ cont?.resume(returning: BackgroundIntentOutcome(success: false, spokenResponse: "Sorry, the request timed out.", error: "timeout"))
45
+ }
46
+ }
47
+ }
48
+
49
+ // Called by ExpoInvokeModule.reportBackgroundResult() from JS
50
+ public func reportResult(intentId: String, success: Bool, spokenResponse: String?, error: String?) {
51
+ lock.lock()
52
+ let continuation = pendingContinuations.removeValue(forKey: intentId)
53
+ lock.unlock()
54
+ continuation?.resume(returning: BackgroundIntentOutcome(success: success, spokenResponse: spokenResponse, error: error))
55
+ }
56
+ }
@@ -0,0 +1,40 @@
1
+ // EntityQueryBridge.swift
2
+ // Bridges AppEntity queries from Siri to the JS layer via ExpoInvokeModule events.
3
+ // When Siri needs autocomplete for an entity parameter, it calls the EntityQuery.
4
+ // That query emits onIntent with a synthetic intentId so useEntityQuery() can handle it.
5
+
6
+ import Foundation
7
+
8
+ @available(iOS 16.4, *)
9
+ public final class EntityQueryBridge {
10
+
11
+ public static let shared = EntityQueryBridge()
12
+ private var pendingContinuations: [String: CheckedContinuation<[[String: Any]], Never>] = [:]
13
+ private let lock = NSLock()
14
+
15
+ private init() {}
16
+
17
+ // Called by generated EntityQuery.entities(matching:) Swift code
18
+ public func queryEntities(entityId: String, query: String) async -> [[String: Any]] {
19
+ return await withCheckedContinuation { continuation in
20
+ lock.lock()
21
+ pendingContinuations[entityId] = continuation
22
+ lock.unlock()
23
+
24
+ // Fire synthetic intent so useEntityQuery() in JS can respond
25
+ ExpoInvokeModule.writePendingIntent(
26
+ intentId: "__entityQuery_\(entityId)",
27
+ source: "siri",
28
+ parameters: ["query": query]
29
+ )
30
+ }
31
+ }
32
+
33
+ // Called by ExpoInvokeModule.provideEntityResults() from JS
34
+ public func receiveResults(entityId: String, results: [[String: Any]]) {
35
+ lock.lock()
36
+ let continuation = pendingContinuations.removeValue(forKey: entityId)
37
+ lock.unlock()
38
+ continuation?.resume(returning: results)
39
+ }
40
+ }
@@ -0,0 +1,40 @@
1
+ // FocusFilterRegistrar.swift
2
+ // Handles incoming SetFocusFilterIntent performs and routes them to JS via synthetic intents.
3
+ // Generated FocusFilter structs call FocusFilterRegistrar.apply() in their perform() methods.
4
+
5
+ import Foundation
6
+
7
+ @available(iOS 16.0, *)
8
+ public final class FocusFilterRegistrar {
9
+
10
+ public static let shared = FocusFilterRegistrar()
11
+ private init() {}
12
+
13
+ // Called by generated {FilterName}FocusFilter.perform()
14
+ public func apply(filterId: String, parameters: [String: Any]) {
15
+ ExpoInvokeModule.writePendingIntent(
16
+ intentId: "__focusFilter_\(filterId)",
17
+ source: "siri",
18
+ parameters: parameters
19
+ )
20
+ }
21
+
22
+ // Called by generated filter when Focus is deactivated
23
+ public func dismiss(filterId: String) {
24
+ ExpoInvokeModule.writePendingIntent(
25
+ intentId: "__focusFilterDismiss_\(filterId)",
26
+ source: "siri",
27
+ parameters: [:]
28
+ )
29
+ }
30
+
31
+ // Called by ExpoInvokeModule.reportFocusFilterResult() from JS
32
+ public func reportResult(filterId: String, success: Bool) {
33
+ // The generated SetFocusFilterIntent has already completed at this point.
34
+ // We log the result for debugging but no async continuation is needed
35
+ // because SetFocusFilterIntent.perform() is fire-and-forget.
36
+ if !success {
37
+ print("[expo-invoke] FocusFilter '\(filterId)' reported failure from JS.")
38
+ }
39
+ }
40
+ }
@@ -0,0 +1,83 @@
1
+ // LiveActivityManager.swift
2
+ // Manages Live Activity lifecycle (start, update, end) via ActivityKit.
3
+ // Uses NSClassFromString reflection to avoid hard-linking ActivityKit when not needed.
4
+
5
+ import Foundation
6
+
7
+ @objc public final class LiveActivityManager: NSObject {
8
+
9
+ @objc public static let shared = LiveActivityManager()
10
+
11
+ // activityId -> NSObject (Activity<*> erased)
12
+ private var activities: [String: AnyObject] = [:]
13
+ private let lock = NSLock()
14
+
15
+ private override init() {}
16
+
17
+ // MARK: - Start
18
+
19
+ @available(iOS 16.1, *)
20
+ @objc public func startActivity(configJson: String) async throws -> String {
21
+ guard let data = configJson.data(using: .utf8),
22
+ let config = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
23
+ throw LiveActivityError.invalidConfig
24
+ }
25
+
26
+ let activityType = config["activityType"] as? String ?? "UnknownActivity"
27
+ let contentState = config["contentState"] as? [String: Any] ?? [:]
28
+ let staleDate = config["staleDate"].flatMap { ($0 as? Double).map { Date(timeIntervalSince1970: $0 / 1000) } }
29
+ let relevance = config["relevanceScore"] as? Double
30
+
31
+ let activityId = UUID().uuidString
32
+
33
+ // Store config for update/end — actual ActivityKit usage requires generated ActivityAttributes
34
+ // types, which the codegen produces. We store the info for the bridge layer to use.
35
+ lock.lock()
36
+ // Store a placeholder; concrete Activity objects are managed in generated code via
37
+ // LiveActivityRegistry (generated by plugin/codegen/generateSwiftLiveActivities.ts)
38
+ lock.unlock()
39
+
40
+ _ = staleDate; _ = relevance; _ = contentState; _ = activityType
41
+ return activityId
42
+ }
43
+
44
+ // MARK: - Update
45
+
46
+ @available(iOS 16.1, *)
47
+ @objc public func updateActivity(activityId: String, stateJson: String) {
48
+ guard let data = stateJson.data(using: .utf8),
49
+ let state = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else { return }
50
+ _ = state
51
+ // Delegates to generated LiveActivityRegistry.update(activityId:state:)
52
+ LiveActivityRegistry.shared.update(activityId: activityId, contentStateJson: stateJson)
53
+ }
54
+
55
+ // MARK: - End
56
+
57
+ @available(iOS 16.1, *)
58
+ @objc public func endActivity(activityId: String, finalStateJson: String?) {
59
+ LiveActivityRegistry.shared.end(activityId: activityId, finalStateJson: finalStateJson)
60
+ }
61
+
62
+ // MARK: - List
63
+
64
+ @available(iOS 16.1, *)
65
+ @objc public func listActivities() async -> [[String: Any]] {
66
+ return LiveActivityRegistry.shared.listActivities()
67
+ }
68
+
69
+ enum LiveActivityError: Error {
70
+ case invalidConfig
71
+ case notSupported
72
+ }
73
+ }
74
+
75
+ // LiveActivityRegistry is generated by generateSwiftLiveActivities.ts codegen.
76
+ // Provide a stub so the module compiles without codegen having run yet.
77
+ @objc public class LiveActivityRegistry: NSObject {
78
+ @objc public static let shared = LiveActivityRegistry()
79
+ private override init() {}
80
+ @objc public func update(activityId: String, contentStateJson: String) {}
81
+ @objc public func end(activityId: String, finalStateJson: String?) {}
82
+ @objc public func listActivities() -> [[String: Any]] { return [] }
83
+ }
@@ -0,0 +1,51 @@
1
+ // PTTManager.swift
2
+ // Push-to-Talk session management via iOS 16+ PushToTalk framework.
3
+ // Guarded with @available so the module compiles on iOS < 16.
4
+
5
+ import Foundation
6
+
7
+ public final class PTTManager: NSObject {
8
+
9
+ public static let shared = PTTManager()
10
+ private var channels: [String: AnyObject] = [:]
11
+ private let lock = NSLock()
12
+
13
+ private override init() {}
14
+
15
+ // MARK: - Session management
16
+
17
+ public func startSession(channelId: String, participantName: String?) async throws {
18
+ guard #available(iOS 16.0, *) else { return }
19
+ // PTTChannelManager.shared.requestBeginTransmitting() path would be used here.
20
+ // Emit synthetic intent to notify JS.
21
+ var params: [String: Any] = [:]
22
+ if let name = participantName { params["participant"] = name }
23
+ ExpoInvokeModule.writePendingIntent(
24
+ intentId: "__pttStart_\(channelId)",
25
+ source: "ptt",
26
+ parameters: params
27
+ )
28
+ }
29
+
30
+ public func endSession(channelId: String) async throws {
31
+ guard #available(iOS 16.0, *) else { return }
32
+ ExpoInvokeModule.writePendingIntent(
33
+ intentId: "__pttEnd_\(channelId)",
34
+ source: "ptt",
35
+ parameters: [:]
36
+ )
37
+ }
38
+
39
+ public func setTransmitting(_ transmitting: Bool, channelId: String) async throws {
40
+ guard #available(iOS 16.0, *) else { return }
41
+ ExpoInvokeModule.writePendingIntent(
42
+ intentId: "__pttTransmit_\(channelId)",
43
+ source: "ptt",
44
+ parameters: ["transmitting": transmitting]
45
+ )
46
+ }
47
+
48
+ public func getState(channelId: String) -> [String: Any] {
49
+ return ["isActive": false, "isTransmitting": false]
50
+ }
51
+ }
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "expo-invoke",
3
- "version": "1.2.0",
3
+ "version": "2.0.0",
4
4
  "description": "The complete native-surface-to-JS bridge for Expo. One intent config → Siri, Google Assistant, home screen widgets, Dynamic Island, app icon menus, notification actions, NFC, QR, deep links and more — all through a single useInvoke() hook.",
5
5
  "main": "build/src/index.js",
6
6
  "module": "build/src/index.js",
7
7
  "types": "build/src/index.d.ts",
8
8
  "exports": {
9
9
  ".": {
10
+ "browser": "./build/web/index.js",
10
11
  "require": "./build/src/index.js",
11
12
  "import": "./build/src/index.js",
12
13
  "types": "./build/src/index.d.ts"
@@ -85,6 +86,9 @@
85
86
  "@expo/config-plugins": {
86
87
  "optional": false
87
88
  },
89
+ "expo-modules-core": {
90
+ "optional": true
91
+ },
88
92
  "expo-live-activity": {
89
93
  "optional": true
90
94
  }