web-launch-kit 0.0.6 → 0.0.8

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.
package/README.ko.md CHANGED
@@ -33,7 +33,8 @@ npm install web-launch-kit
33
33
  | `LaunchKit.map(options?)` | `Promise<AppOpenedBy>` | 검색어·좌표·경로로 지도 열기. OS별 네이티브 앱과 Google Maps 웹 폴백 |
34
34
  | `LaunchKit.filepicker(options?)` | `Promise<File[]>` | 파일 또는 디렉터리 선택 (File System Access API, input 폴백) |
35
35
  | `LaunchKit.setting(type?)` | `Promise<void>` | 지원되는 환경에서 시스템 설정 화면 열기 |
36
- | `LaunchKit.utils` | object | `canOpenIntent` / `canOpenUniversal` / `canOpenSetting` getter와 비동기 `getTrackId` / `getProductId` |
36
+ | `LaunchKit.escapeWebview(options?)` | `Promise<void>` | URL(기본값은 현재 페이지)을 외부 브라우저에서 다시 열어 인앱 웹뷰에서 탈출 |
37
+ | `LaunchKit.utils` | object | `canOpenIntent` / `canOpenUniversal` / `canOpenSetting` / `canEscapeWebview` getter와 비동기 `getTrackId` / `getProductId` |
37
38
 
38
39
  `AppOpenedBy`는 `"scheme"`, `"universal"`, `"intent"`, `"fallback"`, `"store"` 중 하나입니다.
39
40
 
@@ -82,8 +83,8 @@ LaunchKit.telephone({ to: '+821012345678' })
82
83
 
83
84
  싱글톤의 형태는 `LaunchKitInstance`로 export되고, 모든 옵션 객체에 이름이 붙어 있습니다 —
84
85
  `AppOpenOptions`, `AndroidAppInfo`, `IOSAppInfo`, `WindowsAppInfo`, `MacOSAppInfo`,
85
- `TelephoneOptions`, `MessageOptions`, `MailOptions`, `MapOptions`, `FilepickerOptions`.
86
- `SettingType`은 값 export이고 `AppOpenedBy`는 타입입니다.
86
+ `TelephoneOptions`, `MessageOptions`, `MailOptions`, `MapOptions`, `FilepickerOptions`,
87
+ `EscapeWebviewOptions`. `SettingType`은 값 export이고 `AppOpenedBy`는 타입입니다.
87
88
 
88
89
  ```ts
89
90
  import LaunchKit, {
@@ -119,10 +120,10 @@ flowchart TD
119
120
  A1{"intent given, but scheme /<br/>packageName / fallback missing?"}
120
121
  A1 -->|yes| A2["parseIntentURL:<br/>derive scheme · packageName · fallback"]
121
122
  A1 -->|no| A3
122
- A2 --> A3{"scheme given, but intent missing?"}
123
+ A2 --> A3{"allowIntent, and scheme given<br/>but intent missing?"}
123
124
  A3 -->|yes| A4["createIntentURL:<br/>scheme + packageName + fallback"]
124
125
  A3 -->|no| A5
125
- A4 --> A5["Priority list:<br/>intent (if canOpenIntent) ⭢ scheme (if canOpenScheme)<br/>⭢ fallback ⭢ app store (if canOpenScheme) ⭢ web store"]
126
+ A4 --> A5["Priority list:<br/>intent (if allowIntent and canOpenIntent) ⭢ scheme (if canOpenScheme)<br/>⭢ fallback ⭢ app store (if canOpenScheme) ⭢ web store"]
126
127
  end
127
128
 
128
129
  subgraph IOS["ios · resolveOptions"]
@@ -202,8 +203,8 @@ const openedBy = await LaunchKit.app({
202
203
  console.log(openedBy) // "universal" | "scheme" | "intent" | "fallback" | "store"
203
204
  ```
204
205
 
205
- 플랫폼별 필드: 안드로이드는 `intent` / `scheme` / `packageName` / `fallback`(scheme ⇄ intent는
206
- 서로에게서 유도됩니다), iOS는 `universal` / `scheme` / `bundleId` / `trackId`, Windows는
206
+ 플랫폼별 필드: 안드로이드는 `intent` / `scheme` / `packageName` / `fallback` /
207
+ `allowIntent`(scheme ⇄ intent는 서로에게서 유도됩니다), iOS는 `universal` / `scheme` / `bundleId` / `trackId`, Windows는
207
208
  `scheme` / `packageFamilyName` / `productId`, macOS는 `scheme` / `bundleId` / `trackId`를
208
209
  받습니다. 모두 `fallback`, `timeout`, `allowAppStore`, `allowWebStore`를 함께 받습니다.
209
210
 
@@ -313,6 +314,32 @@ if (LaunchKit.utils.canOpenSetting) {
313
314
  }
314
315
  ```
315
316
 
317
+ ## 인앱 브라우저 탈출
318
+
319
+ `escapeWebview()`는 URL — 기본값은 현재 페이지 — 을 외부 기본 브라우저에서 다시 열어,
320
+ 페이지가 갇혀 있는 인앱 웹뷰에서 탈출합니다.
321
+
322
+ ```js
323
+ import LaunchKit from 'web-launch-kit'
324
+
325
+ if (LaunchKit.utils.canEscapeWebview) {
326
+ await LaunchKit.escapeWebview()
327
+ }
328
+
329
+ // 현재 페이지 대신 특정 URL을 내보낼 수도 있습니다
330
+ await LaunchKit.escapeWebview({ url: 'https://example.com/checkout' })
331
+ ```
332
+
333
+ 경로는 환경에 따라 결정됩니다. 카카오톡은 전용 `kakaotalk://web/openExternal` 스킴을
334
+ 제공하고, LINE은 공식 문서화된 `openExternalBrowser=1` 쿼리 파라미터를 지원합니다 —
335
+ 둘 다 안드로이드와 iOS에서 동작합니다. 그 외 웹뷰는 OS 경로로 폴백합니다. 안드로이드는
336
+ Chrome을 고정한 `intent://` URL(원본 URL이 `S.browser_fallback_url`로 실려서 Chrome이
337
+ 없는 기기는 제자리에 머뭅니다), iOS는 비공개 `x-safari-` 스킴입니다 — iOS 15와 17+에서
338
+ 동작하고 16에서는 동작하지 않으며, 기본 브라우저 설정과 무관하게 항상 Safari를 열고,
339
+ Meta 계열 웹뷰(Facebook, Instagram) 안에서는 차단됩니다. http(s) URL만 다시 열 수
340
+ 있습니다. 웹뷰 밖이거나 사용 가능한 경로가 없으면(완전 차단 웹뷰, iOS 16, 데스크톱)
341
+ 프로미스가 reject됩니다 — 버튼 노출은 `utils.canEscapeWebview`로 게이트하세요.
342
+
316
343
  ---
317
344
 
318
345
  ## 참고
@@ -333,6 +360,13 @@ if (LaunchKit.utils.canOpenSetting) {
333
360
  그리고 파트너가 아닌 웨이보) 안에서는 커스텀 스킴·`intent://`·`market://` 같은 스토어 스킴이
334
361
  타임아웃을 소진하지 않고 미리 제외됩니다. 그래서 체인이 곧바로 `fallback` / 웹스토어로
335
362
  떨어집니다. 화이트리스트에 등록된 파트너 앱은 `assumeAllowedInApp: true`로 되돌립니다.
363
+ - **`allowIntent: false`는 안드로이드의 `intent://` 경로를 제외합니다.** 유도된 `intent://`에는
364
+ `S.browser_fallback_url`이 실립니다 — `fallback`에 URL을 주면 그 URL, 아니면 `packageName`이
365
+ 있을 때 스토어입니다. 그래서 그 이동은 브라우저가 첫 시도 안에서 직접 처리합니다. 앱이 없는
366
+ 사용자를 스토어로 보내는 가장 빠른 경로지만, 동시에 첫 시도가 항상 어딘가에 도달하므로 그
367
+ 뒤의 순수 스킴은 실행되지 않습니다. `allowIntent: false`로 이 경로를 아예 건너뛰면 순서를
368
+ 체인이 결정합니다 — 스킴, `fallback`, 그리고 허용한 스토어 순입니다. 직접 넘긴 `intent`도 같이
369
+ 제외되지만, 거기서 파싱한 scheme·`packageName`·`fallback`은 그대로 사용됩니다.
336
370
  - **`map()`은 각 후보에 OS별 타임아웃**을 사용하고, `app()`처럼 성공한 경로를 반환합니다.
337
371
  - **웹 폴백은 내비게이션 시점에 resolve합니다.** `fallback`이나 웹스토어 후보는 문서 자체를
338
372
  교체하므로 기다릴 앱 전환이 없습니다. 이동을 시작하는 즉시 resolve하며, 이동이 성공한 뒤에
package/README.md CHANGED
@@ -34,7 +34,8 @@ The bundle is self-contained (OS/locale detection is inlined) — no peer script
34
34
  | `LaunchKit.map(options?)` | `Promise<AppOpenedBy>` | Open a map by query, coordinate, or directions; native app per-OS with a Google Maps web fallback |
35
35
  | `LaunchKit.filepicker(options?)` | `Promise<File[]>` | Pick files or a directory (File System Access API, with input fallback) |
36
36
  | `LaunchKit.setting(type?)` | `Promise<void>` | Open a system-settings pane where supported |
37
- | `LaunchKit.utils` | object | `canOpenIntent` / `canOpenUniversal` / `canOpenSetting` getters, plus async `getTrackId` / `getProductId` |
37
+ | `LaunchKit.escapeWebview(options?)` | `Promise<void>` | Reopen a URL (the current page by default) in the external browser, escaping an in-app webview |
38
+ | `LaunchKit.utils` | object | `canOpenIntent` / `canOpenUniversal` / `canOpenSetting` / `canEscapeWebview` getters, plus async `getTrackId` / `getProductId` |
38
39
 
39
40
  `AppOpenedBy` is one of: `"scheme"`, `"universal"`, `"intent"`, `"fallback"`, `"store"`.
40
41
 
@@ -84,7 +85,8 @@ The global `LaunchKit` is a namespace object; the singleton is `LaunchKit.defaul
84
85
  The singleton shape is exported as `LaunchKitInstance`, and every option object has a
85
86
  named type: `AppOpenOptions`, `AndroidAppInfo`, `IOSAppInfo`, `WindowsAppInfo`,
86
87
  `MacOSAppInfo`, `TelephoneOptions`, `MessageOptions`, `MailOptions`, `MapOptions`,
87
- `FilepickerOptions`. `SettingType` is a value export; `AppOpenedBy` is a type.
88
+ `FilepickerOptions`, `EscapeWebviewOptions`. `SettingType` is a value export;
89
+ `AppOpenedBy` is a type.
88
90
 
89
91
  ```ts
90
92
  import LaunchKit, {
@@ -120,10 +122,10 @@ flowchart TD
120
122
  A1{"intent given, but scheme /<br/>packageName / fallback missing?"}
121
123
  A1 -->|yes| A2["parseIntentURL:<br/>derive scheme · packageName · fallback"]
122
124
  A1 -->|no| A3
123
- A2 --> A3{"scheme given, but intent missing?"}
125
+ A2 --> A3{"allowIntent, and scheme given<br/>but intent missing?"}
124
126
  A3 -->|yes| A4["createIntentURL:<br/>scheme + packageName + fallback"]
125
127
  A3 -->|no| A5
126
- A4 --> A5["Priority list:<br/>intent (if canOpenIntent) ⭢ scheme (if canOpenScheme)<br/>⭢ fallback ⭢ app store (if canOpenScheme) ⭢ web store"]
128
+ A4 --> A5["Priority list:<br/>intent (if allowIntent and canOpenIntent) ⭢ scheme (if canOpenScheme)<br/>⭢ fallback ⭢ app store (if canOpenScheme) ⭢ web store"]
127
129
  end
128
130
 
129
131
  subgraph IOS["ios · resolveOptions"]
@@ -203,8 +205,8 @@ const openedBy = await LaunchKit.app({
203
205
  console.log(openedBy) // "universal" | "scheme" | "intent" | "fallback" | "store"
204
206
  ```
205
207
 
206
- Per-platform fields: Android accepts `intent` / `scheme` / `packageName` / `fallback`
207
- (scheme ⇄ intent are derived from each other); iOS accepts `universal` / `scheme` /
208
+ Per-platform fields: Android accepts `intent` / `scheme` / `packageName` / `fallback` /
209
+ `allowIntent` (scheme ⇄ intent are derived from each other); iOS accepts `universal` / `scheme` /
208
210
  `bundleId` / `trackId`; Windows accepts `scheme` / `packageFamilyName` / `productId`;
209
211
  macOS accepts `scheme` / `bundleId` / `trackId`. All accept `fallback`, `timeout`,
210
212
  `allowAppStore`, `allowWebStore`.
@@ -216,7 +218,7 @@ UI — there is otherwise nothing on screen for the length of the timeout.
216
218
  await LaunchKit.app({
217
219
  android: { scheme: 'myapp://profile/42', packageName: 'com.example.myapp', allowWebStore: true },
218
220
  onAttempt({ by, index, total }) {
219
- setStatus(by === 'store' ? '스토어로 이동합니다' : '앱을 여는 중… (' + (index + 1) + '/' + total + ')')
221
+ setStatus(by === 'store' ? 'Go to the store' : 'Opening the app… (' + (index + 1) + '/' + total + ')')
220
222
  },
221
223
  })
222
224
  ```
@@ -316,6 +318,34 @@ if (LaunchKit.utils.canOpenSetting) {
316
318
  }
317
319
  ```
318
320
 
321
+ ## Escaping in-app browsers
322
+
323
+ `escapeWebview()` reopens a URL — the current page by default — in the external
324
+ default browser, escaping the in-app webview the page is trapped in.
325
+
326
+ ```js
327
+ import LaunchKit from 'web-launch-kit'
328
+
329
+ if (LaunchKit.utils.canEscapeWebview) {
330
+ await LaunchKit.escapeWebview()
331
+ }
332
+
333
+ // Or send a specific URL out instead of the current page
334
+ await LaunchKit.escapeWebview({ url: 'https://example.com/checkout' })
335
+ ```
336
+
337
+ The route depends on the environment. KakaoTalk exposes a dedicated
338
+ `kakaotalk://web/openExternal` scheme and LINE honors the documented
339
+ `openExternalBrowser=1` query parameter — both work on Android and iOS. Other
340
+ webviews fall back to the OS route: a Chrome-pinned `intent://` URL on Android
341
+ (the original URL rides along as `S.browser_fallback_url`, so a Chrome-less
342
+ device stays in place), and the undocumented `x-safari-` scheme on iOS — which
343
+ works on iOS 15 and 17+ but not 16, always opens Safari regardless of the
344
+ default browser, and is blocked inside Meta's webviews (Facebook, Instagram).
345
+ Only http(s) URLs can be reopened. Outside a webview, or where no route is
346
+ available (hard-blocked webviews, iOS 16, desktop), the promise rejects — gate
347
+ the button on `utils.canEscapeWebview`.
348
+
319
349
  ---
320
350
 
321
351
  ## Notes
@@ -338,6 +368,14 @@ if (LaunchKit.utils.canOpenSetting) {
338
368
  burning their timeouts, so the chain falls straight through to `fallback` / web store
339
369
  (the only routes that work there). Whitelisted partner apps opt back in with
340
370
  `assumeAllowedInApp: true`.
371
+ - **`allowIntent: false` drops the Android `intent://` route.** A derived `intent://` carries
372
+ `S.browser_fallback_url` — your `fallback` when you gave a URL, otherwise the store when a
373
+ `packageName` is present — so the browser handles that hop itself during the first attempt.
374
+ That is the fastest route to the store for a user without the app, but it also means the
375
+ first attempt always lands somewhere, and the plain scheme behind it never runs. Set
376
+ `allowIntent: false` to skip the route entirely and let the chain decide the order: scheme,
377
+ then `fallback`, then the store you allowed. An explicit `intent` is gated too, though the
378
+ scheme, `packageName` and `fallback` parsed out of it are still used.
341
379
  - **`map()` uses the per-OS timeout** for each candidate, and resolves with the route that
342
380
  worked just like `app()`.
343
381
  - **Web fallbacks resolve on navigation.** A `fallback` or web-store candidate replaces the
package/dist/index.d.ts CHANGED
@@ -36,6 +36,7 @@ declare interface AppInfo {
36
36
  declare interface AndroidAppInfo extends AppInfo {
37
37
  intent?: URLCandidate;
38
38
  packageName?: string;
39
+ allowIntent?: boolean;
39
40
  assumeAllowedInApp?: boolean;
40
41
  }
41
42
  declare interface IOSAppInfo extends AppInfo {
@@ -92,10 +93,14 @@ declare interface MapOptions {
92
93
  fallback?: URLStringOrFallback;
93
94
  onAttempt?: (attempt: LaunchAttempt) => void;
94
95
  }
96
+ declare interface EscapeWebviewOptions {
97
+ url?: URLCandidate;
98
+ }
95
99
  interface LaunchKitUtils {
96
100
  get canOpenIntent(): boolean;
97
101
  get canOpenUniversal(): boolean;
98
102
  get canOpenSetting(): boolean;
103
+ get canEscapeWebview(): boolean;
99
104
  getTrackId(bundleId: string): Promise<string | undefined>;
100
105
  getProductId(packageFamilyName: string): Promise<string | undefined>;
101
106
  }
@@ -112,8 +117,9 @@ interface LaunchKitInstance {
112
117
  map(options?: MapOptions): Promise<AppOpenedBy>;
113
118
  filepicker(options?: FilepickerOptions): Promise<File[]>;
114
119
  setting(type?: SettingType): Promise<void>;
120
+ escapeWebview(options?: EscapeWebviewOptions): Promise<void>;
115
121
  }
116
122
  declare const LaunchKit: LaunchKitInstance;
117
123
 
118
124
  export { SettingType, LaunchKit as default };
119
- export type { AndroidAppInfo, AppInfo, AppOpenOptions, AppOpenedBy, FilepickerOptions, IOSAppInfo, LaunchAttempt, LaunchError, LaunchErrorCode, LaunchKitInstance, MacOSAppInfo, MailOptions, MapOptions, MessageOptions, OpenPickerStartIn, TelephoneOptions, WindowsAppInfo };
125
+ export type { AndroidAppInfo, AppInfo, AppOpenOptions, AppOpenedBy, EscapeWebviewOptions, FilepickerOptions, IOSAppInfo, LaunchAttempt, LaunchError, LaunchErrorCode, LaunchKitInstance, MacOSAppInfo, MailOptions, MapOptions, MessageOptions, OpenPickerStartIn, TelephoneOptions, WindowsAppInfo };
@@ -2,7 +2,7 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var version$3 = "0.0.6";
5
+ var version$3 = "0.0.8";
6
6
  var packageJSON$3 = {
7
7
  version: version$3};
8
8
 
@@ -2347,6 +2347,7 @@ function resolveOptions(option) {
2347
2347
  var timeout;
2348
2348
  var intent;
2349
2349
  var packageName;
2350
+ var allowIntent = true;
2350
2351
  var bundleId;
2351
2352
  var trackId;
2352
2353
  var universal;
@@ -2356,6 +2357,7 @@ function resolveOptions(option) {
2356
2357
  case 'android':
2357
2358
  if (typeof option.android === 'undefined')
2358
2359
  return [[], 0];
2360
+ allowIntent = option.android.allowIntent !== false;
2359
2361
  intent = stripURL(option.android.intent);
2360
2362
  scheme = stripURL(option.android.scheme);
2361
2363
  fallback = stripURL(option.android.fallback);
@@ -2374,11 +2376,11 @@ function resolveOptions(option) {
2374
2376
  if (typeof parsed.fallback !== 'undefined' && typeof fallback === 'undefined')
2375
2377
  fallback = parsed.fallback;
2376
2378
  }
2377
- if (typeof scheme !== 'undefined' && typeof intent === 'undefined')
2379
+ if (allowIntent && typeof scheme !== 'undefined' && typeof intent === 'undefined')
2378
2380
  intent = createIntentURL(scheme, packageName, fallback);
2379
2381
  if (typeof timeout === 'undefined')
2380
2382
  timeout = ANDROID_DEFAULT_TIMEOUT;
2381
- if (typeof intent !== 'undefined' && canOpenIntent())
2383
+ if (allowIntent && typeof intent !== 'undefined' && canOpenIntent())
2382
2384
  resolved.push(['intent', intent]);
2383
2385
  if (typeof scheme !== 'undefined' && allowScheme)
2384
2386
  resolved.push(['scheme', scheme]);
@@ -2524,6 +2526,34 @@ function isFullyBlockedInAppBrowser() {
2524
2526
  function isPartnerAppOnly() {
2525
2527
  return /weibo.*weibo__[\d.]+/i.test(PlatformKit.userAgent);
2526
2528
  }
2529
+ function canOpenXSafari() {
2530
+ if (PlatformKit.os.name !== 'ios')
2531
+ return false;
2532
+ var version = PlatformKit.os.version;
2533
+ if (PlatformKit.compareVersion(version, '15.0') < 0)
2534
+ return false;
2535
+ if (PlatformKit.compareVersion(version, '16.0') >= 0 && PlatformKit.compareVersion(version, '17.0') < 0)
2536
+ return false;
2537
+ var inAppBrowser = PlatformKit.inAppBrowser;
2538
+ if (inAppBrowser === 'facebook' || inAppBrowser === 'instagram')
2539
+ return false;
2540
+ return canOpenScheme();
2541
+ }
2542
+ function isInWebview() {
2543
+ return PlatformKit.inAppBrowser !== null || PlatformKit.webview;
2544
+ }
2545
+ function canEscapeWebview() {
2546
+ if (!isInWebview())
2547
+ return false;
2548
+ var inAppBrowser = PlatformKit.inAppBrowser;
2549
+ if (inAppBrowser === 'kakaotalk' || inAppBrowser === 'line')
2550
+ return true;
2551
+ if (PlatformKit.os.name === 'android')
2552
+ return canOpenIntent();
2553
+ if (PlatformKit.os.name === 'ios')
2554
+ return canOpenXSafari();
2555
+ return false;
2556
+ }
2527
2557
  function canOpenWindowsSetting(version) {
2528
2558
  if (version.length === 0)
2529
2559
  return false;
@@ -2621,6 +2651,24 @@ function createWebStoreURL(id, os) {
2621
2651
  return os;
2622
2652
  }
2623
2653
  }
2654
+ function appendURLParameter(url, parameter) {
2655
+ var index = url.indexOf('#');
2656
+ var base = index === -1 ? url : url.substring(0, index);
2657
+ var hash = index === -1 ? '' : url.substring(index);
2658
+ return base + (base.indexOf('?') === -1 ? '?' : '&') + parameter + hash;
2659
+ }
2660
+ function createEscapeWebviewURL(url) {
2661
+ var inAppBrowser = PlatformKit.inAppBrowser;
2662
+ if (inAppBrowser === 'kakaotalk')
2663
+ return 'kakaotalk://web/openExternal?url=' + escapeURIComponentString(url);
2664
+ if (inAppBrowser === 'line')
2665
+ return appendURLParameter(url, 'openExternalBrowser=1');
2666
+ if (PlatformKit.os.name === 'android' && canOpenIntent())
2667
+ return createIntentURL(url, 'com.android.chrome', url);
2668
+ if (PlatformKit.os.name === 'ios' && canOpenXSafari())
2669
+ return 'x-safari-' + url;
2670
+ return undefined;
2671
+ }
2624
2672
  function escapeURIComponentString(value) {
2625
2673
  return encodeURIComponent(value)
2626
2674
  .replace(/[!'()*]/g, function (char) {
@@ -2894,6 +2942,9 @@ var LaunchKit = {
2894
2942
  get canOpenSetting() {
2895
2943
  return canOpenSetting();
2896
2944
  },
2945
+ get canEscapeWebview() {
2946
+ return canEscapeWebview();
2947
+ },
2897
2948
  getTrackId: getTrackIdAsync,
2898
2949
  getProductId: getProductIdAsync,
2899
2950
  },
@@ -3161,6 +3212,18 @@ var LaunchKit = {
3161
3212
  return openURLSequential();
3162
3213
  });
3163
3214
  },
3215
+ escapeWebview: function (options) {
3216
+ if (options === void 0) { options = {}; }
3217
+ if (!isInWebview())
3218
+ return Promise.reject(new Error('Escaping the webview is not needed: the current environment is not an in-app browser or webview. (userAgent: "' + PlatformKit.userAgent + '")'));
3219
+ var url = typeof options.url !== 'undefined' ? stripURL(options.url) : getTopmostWindow().location.href;
3220
+ if (!isWebURL(url))
3221
+ return Promise.reject(new Error('Cannot escape the webview: only http(s) URLs can be reopened in an external browser. (url: "' + url + '")'));
3222
+ var escape = createEscapeWebviewURL(url);
3223
+ if (typeof escape === 'undefined')
3224
+ return Promise.reject(new Error('Failed to escape the webview: no escape route is available in the current environment. (userAgent: "' + PlatformKit.userAgent + '")'));
3225
+ return openURL(0, escape, getDefaultTimeout(), isWebURL(escape));
3226
+ },
3164
3227
  };
3165
3228
 
3166
3229
  exports.default = LaunchKit;