@symbiote-native/web-browser 0.0.1

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 (41) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +160 -0
  3. package/build/angular/index.d.ts +1 -0
  4. package/build/angular/index.js +4 -0
  5. package/build/core/index.d.ts +3 -0
  6. package/build/core/index.js +2 -0
  7. package/build/core/native-module.d.ts +12 -0
  8. package/build/core/native-module.js +3 -0
  9. package/build/core/types.d.ts +193 -0
  10. package/build/core/types.js +37 -0
  11. package/build/core/web-browser.d.ts +83 -0
  12. package/build/core/web-browser.js +256 -0
  13. package/build/react/index.d.ts +1 -0
  14. package/build/react/index.js +7 -0
  15. package/build/vue/index.d.ts +1 -0
  16. package/build/vue/index.js +4 -0
  17. package/build-ngc/angular/index.d.ts +1 -0
  18. package/build-ngc/angular/index.js +5 -0
  19. package/build-ngc/angular/index.js.map +1 -0
  20. package/build-ngc/core/index.d.ts +3 -0
  21. package/build-ngc/core/index.js +3 -0
  22. package/build-ngc/core/index.js.map +1 -0
  23. package/build-ngc/core/native-module.d.ts +12 -0
  24. package/build-ngc/core/native-module.js +4 -0
  25. package/build-ngc/core/native-module.js.map +1 -0
  26. package/build-ngc/core/types.d.ts +193 -0
  27. package/build-ngc/core/types.js +38 -0
  28. package/build-ngc/core/types.js.map +1 -0
  29. package/build-ngc/core/web-browser.d.ts +83 -0
  30. package/build-ngc/core/web-browser.js +257 -0
  31. package/build-ngc/core/web-browser.js.map +1 -0
  32. package/native-link.json +12 -0
  33. package/package.json +107 -0
  34. package/src/angular/index.ts +4 -0
  35. package/src/core/index.ts +25 -0
  36. package/src/core/native-module.ts +43 -0
  37. package/src/core/types.ts +208 -0
  38. package/src/core/web-browser.test.ts +295 -0
  39. package/src/core/web-browser.ts +316 -0
  40. package/src/react/index.ts +7 -0
  41. package/src/vue/index.ts +4 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 A. Prokopenko
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,160 @@
1
+ # @symbiote-native/web-browser
2
+
3
+ A wrapper package for [SymbioteNative](../../README.md) that makes
4
+ [`expo-web-browser`](https://github.com/expo/expo/tree/main/packages/expo-web-browser) — an in-app
5
+ browser (`SFSafariViewController` on iOS, Chrome Custom Tabs on Android) plus the OAuth
6
+ auth-session flow — usable from **every** adapter, React, Vue, and Angular, not just React. Built
7
+ the same way as [`@symbiote-native/secure-store`](../secure-store): an `expo-modules-core`-based
8
+ wrapper (see the `symbiote-expo-native-module` project skill for the full mechanism — why
9
+ `expo-modules-core` is depended on directly and never the `expo` meta-package, why the upstream JS
10
+ is hand-ported into `core/` rather than imported, and how autolinking picks up the native module).
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ npm install @symbiote-native/web-browser
16
+ ```
17
+
18
+ `expo-web-browser` and `expo-modules-core` come along as regular, pinned dependencies — never
19
+ install either yourself, and never add the `expo` meta-package to this project (it bundles its own
20
+ Metro/Babel pipeline that conflicts with this project's own).
21
+
22
+ ### Required one-time step: native autolinking wiring
23
+
24
+ Unlike a plain RN native module, `expo-web-browser`'s native code is discovered by
25
+ `expo-modules-autolinking` — this needs wiring into the native host app **once**, covering this
26
+ package and every other `expo-modules-core` package with zero further changes:
27
+
28
+ | Platform | Touches |
29
+ |---|---|
30
+ | iOS | `ios/Podfile` — add `use_expo_modules!` |
31
+ | iOS | `AppDelegate.swift` — Expo's runtime-bootstrap hook |
32
+ | Android | `settings.gradle` / `app/build.gradle` — resolve and include the Expo Gradle projects |
33
+ | Android | `MainApplication.kt` — Expo's bootstrap hook, plus a native-module name map |
34
+
35
+ Full mechanics live in the `symbiote-expo-native-module` skill. The per-package half of that table
36
+ — the Gradle dependency and the module map entry — is generated by
37
+ [`@symbiote-native/expo-modules-link`](../expo-modules-link) from this package's `native-link.json`
38
+ on every install. No `Info.plist` usage description is needed, and the `<queries>` entry Android
39
+ needs to see the Custom Tabs service ships inside `expo-web-browser`'s own manifest and merges into
40
+ your app automatically.
41
+
42
+ ## Shape
43
+
44
+ ```
45
+ src/core/ the whole API: open/dismiss, the auth session, and the Custom Tabs
46
+ service functions. native-module.ts resolves ExpoWebBrowser through
47
+ expo-modules-core's requireNativeModule.
48
+ src/react/ @symbiote-native/web-browser/react
49
+ src/vue/ @symbiote-native/web-browser/vue
50
+ src/angular/ @symbiote-native/web-browser/angular
51
+ ```
52
+
53
+ All three adapter entries are plain re-exports of `core/`. Every export is a stateless free
54
+ function; the one piece of live state — the Android auth-session polyfill's redirect subscription —
55
+ belongs to a single in-flight promise inside the core and never surfaces as something a caller
56
+ subscribes to or tears down. So there is nothing for a hook, composable, or service to wrap, the
57
+ same reason [`@symbiote-native/secure-store`](../secure-store) re-exports rather than wraps. Import
58
+ from `@symbiote-native/web-browser` directly if you don't care which adapter you're on; the
59
+ per-adapter subpaths exist so every wrapper package has the same import surface.
60
+
61
+ ## Use it
62
+
63
+ ```ts
64
+ import * as WebBrowser from '@symbiote-native/web-browser';
65
+
66
+ await WebBrowser.openBrowserAsync('https://example.com');
67
+ ```
68
+
69
+ The in-app browser keeps the user inside the app, unlike `Linking.openURL`, which hands them off to
70
+ the system browser. iOS resolves once the browser closes (`{ type: 'cancel' }`, or
71
+ `{ type: 'dismiss' }` if you closed it yourself with `dismissBrowser()`); Android resolves with
72
+ `{ type: 'opened' }` as soon as the Custom Tab launches and never reports the close.
73
+
74
+ A login flow that redirects back into the app:
75
+
76
+ ```ts
77
+ const result = await WebBrowser.openAuthSessionAsync(
78
+ 'https://auth.example.com/authorize?…&redirect_uri=myapp://callback',
79
+ 'myapp://callback',
80
+ );
81
+
82
+ if (result.type === 'success') {
83
+ const code = new URL(result.url).searchParams.get('code');
84
+ }
85
+ ```
86
+
87
+ iOS uses `ASWebAuthenticationSession`, so the system asks the user whether the app may authenticate
88
+ with that url, and the redirect URI registered with the authorization server has to use the app's
89
+ own scheme (`myapp://`, not `https://`). Android has no equivalent, so it is polyfilled with a
90
+ Custom Tab racing `Linking` against `AppState` — adding your own `Linking` listener for the same
91
+ redirect is unnecessary on both, and on iOS can have side effects.
92
+
93
+ Warming the Custom Tabs service up before you need it, on Android:
94
+
95
+ ```ts
96
+ const { servicePackage } = await WebBrowser.warmUpAsync();
97
+ await WebBrowser.mayInitWithUrlAsync('https://example.com', servicePackage);
98
+ // …later
99
+ await WebBrowser.coolDownAsync(servicePackage);
100
+ ```
101
+
102
+ ## API
103
+
104
+ | Export | Signature | Notes |
105
+ |---|---|---|
106
+ | `openBrowserAsync` | `(url, options?) => Promise<IWebBrowserResult>` | Opens the in-app browser. |
107
+ | `dismissBrowser` | `() => Promise<IWebBrowserDismissResult>` | iOS only — throws elsewhere, since a Custom Tab cannot be closed programmatically. |
108
+ | `openAuthSessionAsync` | `(url, redirectUrl?, options?) => Promise<IWebBrowserAuthSessionResult>` | Resolves `{ type: 'success', url }` on the redirect. |
109
+ | `dismissAuthSession` | `() => void` | iOS only, for the same reason as `dismissBrowser`. |
110
+ | `warmUpAsync` | `(browserPackage?) => Promise<IWebBrowserWarmUpResult>` | Android. Resolves `{}` elsewhere. |
111
+ | `mayInitWithUrlAsync` | `(url, browserPackage?) => Promise<IWebBrowserMayInitWithUrlResult>` | Android. Resolves `{}` elsewhere. |
112
+ | `coolDownAsync` | `(browserPackage?) => Promise<IWebBrowserCoolDownResult>` | Android. Resolves `{}` elsewhere. |
113
+ | `getCustomTabsSupportingBrowsersAsync` | `() => Promise<IWebBrowserCustomTabsResults>` | Android. Throws on iOS — see the note below. |
114
+ | `WebBrowserResultType` | `enum` | `CANCEL`, `DISMISS`, `OPENED`, `LOCKED`. |
115
+ | `WebBrowserPresentationStyle` | `enum` | iOS modal presentation styles for `options.presentationStyle`. |
116
+
117
+ `IWebBrowserOpenOptions`: `toolbarColor`, `enableBarCollapsing`; Android's `browserPackage`,
118
+ `secondaryToolbarColor`, `showTitle`, `enableDefaultShareMenuItem`, `showInRecents`, `createTask`,
119
+ `useProxyActivity`; iOS's `controlsColor`, `dismissButtonStyle`, `readerMode`, `presentationStyle`.
120
+ `IAuthSessionOpenOptions` adds iOS's `preferEphemeralSession` and `preferUniversalLinks`.
121
+
122
+ ## Not ported
123
+
124
+ - **The `experimentalLauncherActivity` config plugin.** Upstream's
125
+ `plugin/src/withWebBrowserAndroid.ts` exists only for that opt-in flag: it writes a
126
+ `BrowserLauncherActivity.kt` into the consuming app and registers it as the launcher activity in
127
+ the app's `AndroidManifest.xml`, as a workaround for a specific redirect edge case. It is opt-in
128
+ upstream and unnecessary for `openBrowserAsync`, `openAuthSessionAsync`, or anything else on this
129
+ page, so this package does not reproduce it. An app that genuinely wants that workaround adds the
130
+ activity by hand.
131
+ - **`maybeCompleteAuthSession`.** Genuinely web-only: it closes the `window.open` popup the web
132
+ implementation of `openAuthSessionAsync` created. On a native platform upstream's own version can
133
+ only ever return `{ type: 'failed', message: 'Not supported on this platform' }`, so it is left
134
+ out rather than shipped as a function that can never succeed.
135
+ - **The web-only open options** `windowName` and `windowFeatures`, for the same reason —
136
+ SymbioteNative has no web target, so nothing could read them.
137
+
138
+ ## Notes
139
+
140
+ - **`getCustomTabsSupportingBrowsersAsync` throws on iOS rather than resolving empty.** iOS's
141
+ native module registers its no-op stub as `getCustomTabsSupportingBrowsers`, without the `Async`
142
+ suffix, so the availability check fires before the "not Android, return an empty result" branch
143
+ is reached. Upstream behaves identically; the guard order is kept deliberately so this package
144
+ and `expo-web-browser` cannot drift. Branch on `Platform.OS === 'android'` yourself if you call
145
+ it cross-platform.
146
+ - **Colors are marshalled before the native call.** `toolbarColor`, `secondaryToolbarColor` and
147
+ `controlsColor` go through `processColor`, so any React Native color format works.
148
+ - **Only one auth session at a time.** Starting a second one while the first is open rejects.
149
+
150
+ ## Test it
151
+
152
+ ```bash
153
+ pnpm vitest run packages/web-browser
154
+ ```
155
+
156
+ The core tests fake the native module in place of `requireNativeModule`'s runtime resolution —
157
+ `ExpoWebBrowser` only exists on a device — and drive fake `AppState`/`Linking` event sources so the
158
+ Android auth-session polyfill's race is exercised headlessly. The browser itself, the iOS
159
+ `ASWebAuthenticationSession` consent prompt, and real deep-link delivery can only be verified on a
160
+ device or simulator.
@@ -0,0 +1 @@
1
+ export * from '../core';
@@ -0,0 +1,4 @@
1
+ // @symbiote-native/web-browser/angular: the Angular entry over the framework-agnostic core.
2
+ // Nothing here holds per-instance state or hands back a subscription, so there is no lifecycle for
3
+ // an injectable service to own — see the React entry for the full reasoning.
4
+ export * from '../core';
@@ -0,0 +1,3 @@
1
+ export { getCustomTabsSupportingBrowsersAsync, warmUpAsync, mayInitWithUrlAsync, coolDownAsync, openBrowserAsync, dismissBrowser, openAuthSessionAsync, dismissAuthSession, } from './web-browser';
2
+ export { WebBrowserResultType, WebBrowserPresentationStyle } from './types';
3
+ export type { IAuthSessionOpenOptions, IRedirectEvent, IServiceActionResult, IWebBrowserAuthSessionResult, IWebBrowserCoolDownResult, IWebBrowserCustomTabsResults, IWebBrowserDismissResult, IWebBrowserMayInitWithUrlResult, IWebBrowserOpenOptions, IWebBrowserRedirectResult, IWebBrowserResult, IWebBrowserWarmUpResult, } from './types';
@@ -0,0 +1,2 @@
1
+ export { getCustomTabsSupportingBrowsersAsync, warmUpAsync, mayInitWithUrlAsync, coolDownAsync, openBrowserAsync, dismissBrowser, openAuthSessionAsync, dismissAuthSession, } from './web-browser';
2
+ export { WebBrowserResultType, WebBrowserPresentationStyle } from './types';
@@ -0,0 +1,12 @@
1
+ import type { IProcessedOpenOptions, IWebBrowserAuthSessionResult, IWebBrowserCoolDownResult, IWebBrowserCustomTabsResults, IWebBrowserDismissResult, IWebBrowserMayInitWithUrlResult, IWebBrowserResult, IWebBrowserWarmUpResult } from './types';
2
+ export type INativeWebBrowserModule = {
3
+ openBrowserAsync?(url: string, options: IProcessedOpenOptions): Promise<IWebBrowserResult>;
4
+ dismissBrowser?(): Promise<IWebBrowserDismissResult>;
5
+ openAuthSessionAsync?(url: string, redirectUrl: string | null | undefined, options: IProcessedOpenOptions): Promise<IWebBrowserAuthSessionResult>;
6
+ dismissAuthSession?(): void;
7
+ warmUpAsync?(browserPackage?: string): Promise<IWebBrowserWarmUpResult>;
8
+ coolDownAsync?(browserPackage?: string): Promise<IWebBrowserCoolDownResult>;
9
+ mayInitWithUrlAsync?(url: string, browserPackage?: string): Promise<IWebBrowserMayInitWithUrlResult>;
10
+ getCustomTabsSupportingBrowsersAsync?(): Promise<IWebBrowserCustomTabsResults>;
11
+ };
12
+ export declare const expoWebBrowser: INativeWebBrowserModule;
@@ -0,0 +1,3 @@
1
+ import { requireNativeModule } from 'expo-modules-core';
2
+ const EXPO_WEB_BROWSER_MODULE_NAME = 'ExpoWebBrowser';
3
+ export const expoWebBrowser = requireNativeModule(EXPO_WEB_BROWSER_MODULE_NAME);
@@ -0,0 +1,193 @@
1
+ import type { ProcessedColorValue } from 'react-native';
2
+ /** Payload of the deep-link event the Android auth-session polyfill listens for. */
3
+ export type IRedirectEvent = {
4
+ url: string;
5
+ };
6
+ /**
7
+ * Options for [`openBrowserAsync`](#openbrowserasync).
8
+ *
9
+ * Upstream's two `@platform web` fields (`windowName`, `windowFeatures`) are not part of this
10
+ * type — SymbioteNative has no web target, so they could never be read.
11
+ */
12
+ export type IWebBrowserOpenOptions = {
13
+ /** Color of the toolbar. Accepts any React Native color format. */
14
+ toolbarColor?: string;
15
+ /**
16
+ * Package name of the browser that should handle the Custom Tab. Pick one from
17
+ * `getCustomTabsSupportingBrowsersAsync`.
18
+ * @platform android
19
+ */
20
+ browserPackage?: string;
21
+ /** Whether the toolbar hides as the user scrolls the page. */
22
+ enableBarCollapsing?: boolean;
23
+ /**
24
+ * Color of the secondary toolbar. Accepts any React Native color format.
25
+ * @platform android
26
+ */
27
+ secondaryToolbarColor?: string;
28
+ /**
29
+ * Whether the toolbar shows the website's title.
30
+ * @platform android
31
+ */
32
+ showTitle?: boolean;
33
+ /**
34
+ * Whether a default share item is added to the browser's menu.
35
+ * @platform android
36
+ */
37
+ enableDefaultShareMenuItem?: boolean;
38
+ /**
39
+ * Whether the browsed website appears as its own entry in the recents/multitasking view.
40
+ * Requires `createTask` to stay `true`.
41
+ * @default false
42
+ * @platform android
43
+ */
44
+ showInRecents?: boolean;
45
+ /**
46
+ * Whether the browser opens in its own task rather than the app's.
47
+ * @default true
48
+ * @platform android
49
+ */
50
+ createTask?: boolean;
51
+ /**
52
+ * Whether to launch the browser through a transparent proxy activity with a different task
53
+ * affinity, which keeps the browser alive while the app is backgrounded. Only read when
54
+ * `createTask` is `true`, and forces `showInRecents` on. Set `false` for the legacy direct
55
+ * launch.
56
+ * @default true
57
+ * @platform android
58
+ */
59
+ useProxyActivity?: boolean;
60
+ /**
61
+ * Tint color for the controls in `SFSafariViewController`. Accepts any React Native color
62
+ * format.
63
+ * @platform ios
64
+ */
65
+ controlsColor?: string;
66
+ /**
67
+ * Style of the dismiss button.
68
+ * @platform ios
69
+ */
70
+ dismissButtonStyle?: 'done' | 'close' | 'cancel';
71
+ /**
72
+ * Whether Safari should enter Reader mode when the page supports it.
73
+ * @platform ios
74
+ */
75
+ readerMode?: boolean;
76
+ /**
77
+ * [Presentation style](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621355-modalpresentationstyle)
78
+ * of the browser window.
79
+ * @default WebBrowserPresentationStyle.OVER_FULL_SCREEN
80
+ * @platform ios
81
+ */
82
+ presentationStyle?: WebBrowserPresentationStyle;
83
+ };
84
+ /**
85
+ * Options for [`openAuthSessionAsync`](#openauthsessionasync). Android has no native auth-session
86
+ * API, so there the inherited `IWebBrowserOpenOptions` fields are passed to the Custom Tab the
87
+ * polyfill opens; on iOS `ASWebAuthenticationSession` ignores them and reads only the two fields
88
+ * below.
89
+ */
90
+ export type IAuthSessionOpenOptions = IWebBrowserOpenOptions & {
91
+ /**
92
+ * Ask the browser for a private authentication session, so it shares no cookies or browsing
93
+ * data with the user's normal session. Whether the request is honored is up to the user's
94
+ * default browser.
95
+ * @default false
96
+ * @platform ios
97
+ */
98
+ preferEphemeralSession?: boolean;
99
+ /**
100
+ * Use HTTPS universal-link callbacks instead of custom-URL-scheme callbacks. Needs the
101
+ * Associated Domains entitlement configured for the redirect URL's host, and iOS 17.4+. Left
102
+ * `false`, the legacy `callbackURLScheme` API is used, which needs no entitlement.
103
+ * @default false
104
+ * @platform ios
105
+ */
106
+ preferUniversalLinks?: boolean;
107
+ };
108
+ /**
109
+ * The open options after the three color fields have been run through `processColor`. This is the
110
+ * shape the native module actually receives — a color reaches native as a platform integer, never
111
+ * as the CSS string the caller wrote.
112
+ */
113
+ export type IProcessedOpenOptions = Omit<IAuthSessionOpenOptions, 'toolbarColor' | 'secondaryToolbarColor' | 'controlsColor'> & {
114
+ toolbarColor?: ProcessedColorValue | null;
115
+ secondaryToolbarColor?: ProcessedColorValue | null;
116
+ controlsColor?: ProcessedColorValue | null;
117
+ };
118
+ export type IWebBrowserCustomTabsResults = {
119
+ /**
120
+ * The browser the user picked as their device default, or `undefined` when there is none — which
121
+ * usually means the user will be prompted to choose.
122
+ */
123
+ defaultBrowserPackage?: string;
124
+ /**
125
+ * The browser `CustomTabsClient` prefers for Custom Tabs. Favors the user's default as long as
126
+ * it appears in both `browserPackages` and `servicePackages`; only such browsers fully support
127
+ * Custom Tabs. `undefined` when no installed browser qualifies.
128
+ */
129
+ preferredBrowserPackage?: string;
130
+ /**
131
+ * Every package `PackageManager` recognizes as able to handle Custom Tabs. Empty when the device
132
+ * has no supporting browser.
133
+ */
134
+ browserPackages: string[];
135
+ /**
136
+ * Every package `PackageManager` recognizes as able to handle the Custom Tabs *Service* — the
137
+ * one `warmUpAsync`, `mayInitWithUrlAsync` and `coolDownAsync` talk to.
138
+ */
139
+ servicePackages: string[];
140
+ };
141
+ /** How a browser session ended. */
142
+ export declare enum WebBrowserResultType {
143
+ /** The user dismissed the browser themselves. @platform ios */
144
+ CANCEL = "cancel",
145
+ /** The browser was closed by a `dismissBrowser` call. @platform ios */
146
+ DISMISS = "dismiss",
147
+ /** The browser was launched. Android resolves here without waiting for it to close. @platform android */
148
+ OPENED = "opened",
149
+ /** Another browser session is already in progress. */
150
+ LOCKED = "locked"
151
+ }
152
+ /**
153
+ * A browser presentation style, mapped directly onto
154
+ * [`UIModalPresentationStyle`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621355-modalpresentationstyle).
155
+ *
156
+ * @platform ios
157
+ */
158
+ export declare enum WebBrowserPresentationStyle {
159
+ /** The browser covers the screen. */
160
+ FULL_SCREEN = "fullScreen",
161
+ /** The browser partially covers the underlying content. */
162
+ PAGE_SHEET = "pageSheet",
163
+ /** The browser is centered on the screen. */
164
+ FORM_SHEET = "formSheet",
165
+ /** The browser is displayed over the app's content. */
166
+ CURRENT_CONTEXT = "currentContext",
167
+ /** The browser view covers the screen. */
168
+ OVER_FULL_SCREEN = "overFullScreen",
169
+ /** The browser is displayed over the app's content. */
170
+ OVER_CURRENT_CONTEXT = "overCurrentContext",
171
+ /** The browser is displayed in a popover view. */
172
+ POPOVER = "popover",
173
+ /** The system picks the style. Older iOS versions fall back to `FULL_SCREEN`. */
174
+ AUTOMATIC = "automatic"
175
+ }
176
+ export type IWebBrowserResult = {
177
+ type: WebBrowserResultType;
178
+ };
179
+ /** What `dismissBrowser` resolves with once the presented browser has been torn down. */
180
+ export type IWebBrowserDismissResult = {
181
+ type: WebBrowserResultType.DISMISS;
182
+ };
183
+ export type IWebBrowserRedirectResult = {
184
+ type: 'success';
185
+ url: string;
186
+ };
187
+ export type IServiceActionResult = {
188
+ servicePackage?: string;
189
+ };
190
+ export type IWebBrowserAuthSessionResult = IWebBrowserRedirectResult | IWebBrowserResult;
191
+ export type IWebBrowserMayInitWithUrlResult = IServiceActionResult;
192
+ export type IWebBrowserWarmUpResult = IServiceActionResult;
193
+ export type IWebBrowserCoolDownResult = IServiceActionResult;
@@ -0,0 +1,37 @@
1
+ /** How a browser session ended. */
2
+ export var WebBrowserResultType;
3
+ (function (WebBrowserResultType) {
4
+ /** The user dismissed the browser themselves. @platform ios */
5
+ WebBrowserResultType["CANCEL"] = "cancel";
6
+ /** The browser was closed by a `dismissBrowser` call. @platform ios */
7
+ WebBrowserResultType["DISMISS"] = "dismiss";
8
+ /** The browser was launched. Android resolves here without waiting for it to close. @platform android */
9
+ WebBrowserResultType["OPENED"] = "opened";
10
+ /** Another browser session is already in progress. */
11
+ WebBrowserResultType["LOCKED"] = "locked";
12
+ })(WebBrowserResultType || (WebBrowserResultType = {}));
13
+ /**
14
+ * A browser presentation style, mapped directly onto
15
+ * [`UIModalPresentationStyle`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621355-modalpresentationstyle).
16
+ *
17
+ * @platform ios
18
+ */
19
+ export var WebBrowserPresentationStyle;
20
+ (function (WebBrowserPresentationStyle) {
21
+ /** The browser covers the screen. */
22
+ WebBrowserPresentationStyle["FULL_SCREEN"] = "fullScreen";
23
+ /** The browser partially covers the underlying content. */
24
+ WebBrowserPresentationStyle["PAGE_SHEET"] = "pageSheet";
25
+ /** The browser is centered on the screen. */
26
+ WebBrowserPresentationStyle["FORM_SHEET"] = "formSheet";
27
+ /** The browser is displayed over the app's content. */
28
+ WebBrowserPresentationStyle["CURRENT_CONTEXT"] = "currentContext";
29
+ /** The browser view covers the screen. */
30
+ WebBrowserPresentationStyle["OVER_FULL_SCREEN"] = "overFullScreen";
31
+ /** The browser is displayed over the app's content. */
32
+ WebBrowserPresentationStyle["OVER_CURRENT_CONTEXT"] = "overCurrentContext";
33
+ /** The browser is displayed in a popover view. */
34
+ WebBrowserPresentationStyle["POPOVER"] = "popover";
35
+ /** The system picks the style. Older iOS versions fall back to `FULL_SCREEN`. */
36
+ WebBrowserPresentationStyle["AUTOMATIC"] = "automatic";
37
+ })(WebBrowserPresentationStyle || (WebBrowserPresentationStyle = {}));
@@ -0,0 +1,83 @@
1
+ import { type IAuthSessionOpenOptions, type IWebBrowserAuthSessionResult, type IWebBrowserCoolDownResult, type IWebBrowserCustomTabsResults, type IWebBrowserDismissResult, type IWebBrowserMayInitWithUrlResult, type IWebBrowserOpenOptions, type IWebBrowserResult, type IWebBrowserWarmUpResult } from './types';
2
+ /**
3
+ * The packages that support Custom Tabs, the Custom Tabs service, and the user's chosen and
4
+ * preferred browser. Only as reliable as `PackageManager.getResolvingActivities` underneath — a
5
+ * browser can be missing from `browserPackages` once another one is set as default.
6
+ *
7
+ * @platform android
8
+ */
9
+ export declare function getCustomTabsSupportingBrowsersAsync(): Promise<IWebBrowserCustomTabsResults>;
10
+ /**
11
+ * Calls `warmUp` on the [`CustomTabsClient`](https://developer.android.com/reference/androidx/browser/customtabs/CustomTabsClient#warmup(long))
12
+ * for the given package.
13
+ *
14
+ * @param browserPackage Browser to warm up. Defaults to the preferred one.
15
+ * @platform android
16
+ */
17
+ export declare function warmUpAsync(browserPackage?: string): Promise<IWebBrowserWarmUpResult>;
18
+ /**
19
+ * Starts a [`CustomTabsSession`](https://developer.android.com/reference/androidx/browser/customtabs/CustomTabsSession#mayLaunchUrl(android.net.Uri,android.os.Bundle,java.util.List%3Candroid.os.Bundle%3E))
20
+ * if needed and calls its `mayLaunchUrl`, so the browser can prefetch the page.
21
+ *
22
+ * @param url The page most likely to be opened first.
23
+ * @param browserPackage Browser to inform. Defaults to the preferred one.
24
+ * @platform android
25
+ */
26
+ export declare function mayInitWithUrlAsync(url: string, browserPackage?: string): Promise<IWebBrowserMayInitWithUrlResult>;
27
+ /**
28
+ * Drops every binding `warmUpAsync` and `mayInitWithUrlAsync` created. Call it once the bindings
29
+ * are no longer needed to avoid leaking them — though they are also released when the app is
30
+ * destroyed, which is often good enough.
31
+ *
32
+ * @param browserPackage Browser to cool down. Defaults to the preferred one.
33
+ * @returns The cooled service, or an empty object when there was no connection to dismiss.
34
+ * @platform android
35
+ */
36
+ export declare function coolDownAsync(browserPackage?: string): Promise<IWebBrowserCoolDownResult>;
37
+ /**
38
+ * Opens the url in a modal [`SFSafariViewController`](https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller)
39
+ * on iOS and a [Custom Tab](https://developer.chrome.com/docs/android/custom-tabs) on Android. The
40
+ * modal Safari does not share cookies with the system Safari — for a login flow that needs them,
41
+ * use `openAuthSessionAsync`.
42
+ *
43
+ * @returns Android resolves with `{ type: 'opened' }` as soon as the tab launches, without waiting
44
+ * for the user to close it. iOS resolves with `{ type: 'cancel' }` when the user dismissed the
45
+ * browser and `{ type: 'dismiss' }` when `dismissBrowser` closed it.
46
+ */
47
+ export declare function openBrowserAsync(url: string, browserParams?: IWebBrowserOpenOptions): Promise<IWebBrowserResult>;
48
+ /**
49
+ * Dismisses the presented browser. Throws where dismissing isn't available — Android's Custom Tabs
50
+ * offer no way to close a tab programmatically, so there the user has to press the tab's own close
51
+ * button.
52
+ *
53
+ * @platform ios
54
+ */
55
+ export declare function dismissBrowser(): Promise<IWebBrowserDismissResult>;
56
+ /**
57
+ * Opens a login page and resolves once the authentication provider redirects back to the app.
58
+ *
59
+ * On iOS this is `ASWebAuthenticationSession`, which asks the user whether the app may
60
+ * authenticate with the given url; the redirect URI registered with the authentication server has
61
+ * to use the app's own scheme (`demo://…`, not `https://…`). Adding a `Linking` listener of your
62
+ * own is unnecessary there and can have side effects.
63
+ *
64
+ * Android has no equivalent native API, so it is polyfilled: a Custom Tab plus `AppState` and
65
+ * `Linking`, racing "the deep link came back" against "the app returned to the foreground". Only
66
+ * one such session can be open at a time.
67
+ *
68
+ * @param url The login page to open.
69
+ * @param redirectUrl The url that deep-links back into the app. Without it the session can only
70
+ * end by the user closing the browser.
71
+ * @returns `{ type: 'success', url }` with the redirect url on a completed login,
72
+ * `{ type: 'cancel' }` when the user declined or closed the browser, and `{ type: 'dismiss' }` when
73
+ * `dismissBrowser` closed it.
74
+ */
75
+ export declare function openAuthSessionAsync(url: string, redirectUrl?: string | null, options?: IAuthSessionOpenOptions): Promise<IWebBrowserAuthSessionResult>;
76
+ /**
77
+ * Dismisses the current authentication session. Falls back to dismissing the plain browser where
78
+ * there is no native auth session, which on Android throws for the same reason `dismissBrowser`
79
+ * does.
80
+ *
81
+ * @platform ios
82
+ */
83
+ export declare function dismissAuthSession(): void;