web-launch-kit 0.0.7 → 0.0.9
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 +61 -5
- package/README.md +63 -3
- package/dist/index.d.ts +9 -2
- package/dist/launch-kit.cjs +196 -37
- package/dist/launch-kit.cjs.map +1 -1
- package/dist/launch-kit.min.cjs +1 -1
- package/dist/launch-kit.min.cjs.map +1 -1
- package/dist/launch-kit.min.mjs +1 -1
- package/dist/launch-kit.min.mjs.map +1 -1
- package/dist/launch-kit.mjs +196 -37
- package/dist/launch-kit.mjs.map +1 -1
- package/dist/launch-kit.umd.js +196 -37
- package/dist/launch-kit.umd.js.map +1 -1
- package/dist/launch-kit.umd.min.js +1 -1
- package/dist/launch-kit.umd.min.js.map +1 -1
- package/dist/utils/get-id.d.ts +2 -2
- package/package.json +2 -2
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.
|
|
36
|
+
| `LaunchKit.escapeWebview(options?)` | `Promise<void>` | URL(기본값은 현재 페이지)을 외부 브라우저에서 다시 열어 인앱 웹뷰에서 탈출 |
|
|
37
|
+
| `LaunchKit.utils` | object | `canOpenIntent` / `canOpenUniversal` / `canOpenSetting` / `canEscapeWebview` getter와 비동기 `getTrackId(bundleId)` / `getProductId(packageFamilyName, language?, country?)` |
|
|
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, {
|
|
@@ -146,6 +147,18 @@ flowchart TD
|
|
|
146
147
|
M2 --> M3["Priority list:<br/>scheme ⭢ fallback<br/>⭢ app store ⭢ web store (by trackId)"]
|
|
147
148
|
end
|
|
148
149
|
|
|
150
|
+
subgraph LOC["web store · createWebStoreURL"]
|
|
151
|
+
L1{"country given?"}
|
|
152
|
+
L1 -->|yes| L3["Play · MS Store: hl / gl parameters<br/>App Store: /country/ path + l parameter"]
|
|
153
|
+
L1 -->|no| L2["Derive from language:<br/>region subtag ⭢ Intl.Locale.maximize()<br/>⭢ omit (store geolocation)"]
|
|
154
|
+
L2 --> L3
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
A5 -.->|"web store"| L1
|
|
158
|
+
I3 -.->|"web store"| L1
|
|
159
|
+
W3 -.->|"web store"| L1
|
|
160
|
+
M3 -.->|"web store"| L1
|
|
161
|
+
|
|
149
162
|
AND --> P
|
|
150
163
|
IOS --> P
|
|
151
164
|
WIN --> P
|
|
@@ -190,6 +203,8 @@ const openedBy = await LaunchKit.app({
|
|
|
190
203
|
packageName: 'com.example.myapp',
|
|
191
204
|
allowAppStore: true,
|
|
192
205
|
allowWebStore: true,
|
|
206
|
+
language: 'ko', // 웹스토어 UI 언어
|
|
207
|
+
country: 'KR', // 생략하면 language 에서 유도됩니다 (region 서브태그 → Intl.Locale.maximize())
|
|
193
208
|
},
|
|
194
209
|
ios: {
|
|
195
210
|
universal: 'https://example.com/profile/42',
|
|
@@ -205,7 +220,17 @@ console.log(openedBy) // "universal" | "scheme" | "intent" | "fallback" | "store
|
|
|
205
220
|
플랫폼별 필드: 안드로이드는 `intent` / `scheme` / `packageName` / `fallback` /
|
|
206
221
|
`allowIntent`(scheme ⇄ intent는 서로에게서 유도됩니다), iOS는 `universal` / `scheme` / `bundleId` / `trackId`, Windows는
|
|
207
222
|
`scheme` / `packageFamilyName` / `productId`, macOS는 `scheme` / `bundleId` / `trackId`를
|
|
208
|
-
받습니다. 모두 `fallback`, `timeout`, `allowAppStore`, `allowWebStore
|
|
223
|
+
받습니다. 모두 `fallback`, `timeout`, `allowAppStore`, `allowWebStore`, `language`,
|
|
224
|
+
`country`를 함께 받습니다.
|
|
225
|
+
|
|
226
|
+
`language` / `country`는 **웹스토어** 후보에만 적용됩니다 — Google Play와 Microsoft
|
|
227
|
+
Store에서는 `hl` / `gl` 파라미터, App Store에서는 국가 경로 세그먼트와 `l` 파라미터로
|
|
228
|
+
반영됩니다. `country`를 생략하면 `language`에서 유도합니다. region 서브태그가 우선하고
|
|
229
|
+
(`ko-KR` → `KR`), 없으면 런타임이 지원하는 환경에서 `Intl.Locale.maximize()`를
|
|
230
|
+
사용합니다. 둘 다 실패하면 국가를 붙이지 않고 스토어의 자체 위치 추정에 맡깁니다 —
|
|
231
|
+
잘못된 국가는 "이 지역에서 사용할 수 없음" 페이지로 이어질 수 있어 생략이 안전한
|
|
232
|
+
기본값입니다. 네이티브 앱스토어 스킴(`market://`, `itms-apps://` 등)은 건드리지
|
|
233
|
+
않습니다. OS가 기기 로케일로 알아서 렌더링합니다.
|
|
209
234
|
|
|
210
235
|
`onAttempt`는 각 후보 시도 직전에 동기로 호출됩니다. 대기 UI를 여기서 그리세요 — 그러지
|
|
211
236
|
않으면 타임아웃 동안 화면에 아무 변화가 없습니다.
|
|
@@ -313,6 +338,32 @@ if (LaunchKit.utils.canOpenSetting) {
|
|
|
313
338
|
}
|
|
314
339
|
```
|
|
315
340
|
|
|
341
|
+
## 인앱 브라우저 탈출
|
|
342
|
+
|
|
343
|
+
`escapeWebview()`는 URL — 기본값은 현재 페이지 — 을 외부 기본 브라우저에서 다시 열어,
|
|
344
|
+
페이지가 갇혀 있는 인앱 웹뷰에서 탈출합니다.
|
|
345
|
+
|
|
346
|
+
```js
|
|
347
|
+
import LaunchKit from 'web-launch-kit'
|
|
348
|
+
|
|
349
|
+
if (LaunchKit.utils.canEscapeWebview) {
|
|
350
|
+
await LaunchKit.escapeWebview()
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// 현재 페이지 대신 특정 URL을 내보낼 수도 있습니다
|
|
354
|
+
await LaunchKit.escapeWebview({ url: 'https://example.com/checkout' })
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
경로는 환경에 따라 결정됩니다. 카카오톡은 전용 `kakaotalk://web/openExternal` 스킴을
|
|
358
|
+
제공하고, LINE은 공식 문서화된 `openExternalBrowser=1` 쿼리 파라미터를 지원합니다 —
|
|
359
|
+
둘 다 안드로이드와 iOS에서 동작합니다. 그 외 웹뷰는 OS 경로로 폴백합니다. 안드로이드는
|
|
360
|
+
Chrome을 고정한 `intent://` URL(원본 URL이 `S.browser_fallback_url`로 실려서 Chrome이
|
|
361
|
+
없는 기기는 제자리에 머뭅니다), iOS는 비공개 `x-safari-` 스킴입니다 — iOS 15와 17+에서
|
|
362
|
+
동작하고 16에서는 동작하지 않으며, 기본 브라우저 설정과 무관하게 항상 Safari를 열고,
|
|
363
|
+
Meta 계열 웹뷰(Facebook, Instagram) 안에서는 차단됩니다. http(s) URL만 다시 열 수
|
|
364
|
+
있습니다. 웹뷰 밖이거나 사용 가능한 경로가 없으면(완전 차단 웹뷰, iOS 16, 데스크톱)
|
|
365
|
+
프로미스가 reject됩니다 — 버튼 노출은 `utils.canEscapeWebview`로 게이트하세요.
|
|
366
|
+
|
|
316
367
|
---
|
|
317
368
|
|
|
318
369
|
## 참고
|
|
@@ -326,7 +377,12 @@ if (LaunchKit.utils.canOpenSetting) {
|
|
|
326
377
|
- **`utils.getTrackId` / `getProductId`는 비동기**이고 1시간 캐시됩니다. 페이지 초기화 때
|
|
327
378
|
한 번 호출해두면 `app()`이 id를 인라인으로 조회하지 않습니다. `app()`은 사용자 제스처 안에서
|
|
328
379
|
실행돼야 하므로, id가 없으면 `await`으로 user activation을 잃는 대신 동기로 조회합니다.
|
|
329
|
-
`trackId` / `productId`를 직접 주면 조회 자체가 생략됩니다.
|
|
380
|
+
`trackId` / `productId`를 직접 주면 조회 자체가 생략됩니다. `getProductId`는 선택적
|
|
381
|
+
`language` / `country`를 추가로 받아 Microsoft 카탈로그의 언어와 마켓을 고정합니다 —
|
|
382
|
+
조회 성공 여부가 마켓에 따라 달라질 수 있으니 `app()`에 넘길 값과 동일하게 주어야
|
|
383
|
+
프리워밍된 캐시가 같은 마켓을 커버합니다. 생략하면 웹스토어 country와 같은 방식으로
|
|
384
|
+
유도됩니다: 명시 region 서브태그 → 런타임이 지원하면 `Intl.Locale.maximize()` →
|
|
385
|
+
`en-US` / `US`. `getTrackId`는 로케일이 필요 없습니다 — iTunes lookup은 마켓과 무관합니다.
|
|
330
386
|
- **스토어 id 조회는 외부 API에 의존합니다**(iTunes Lookup, Microsoft display catalog).
|
|
331
387
|
1시간 캐시되며 실패는 예외가 아니라 `undefined`로 resolve됩니다.
|
|
332
388
|
- **인앱 브라우저는 후보 단위로 걸러집니다.** 실행이 차단된 웹뷰(WeChat, QQ, Qzone, Baidu,
|
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.
|
|
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(bundleId)` / `getProductId(packageFamilyName, language?, country?)` |
|
|
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;
|
|
88
|
+
`FilepickerOptions`, `EscapeWebviewOptions`. `SettingType` is a value export;
|
|
89
|
+
`AppOpenedBy` is a type.
|
|
88
90
|
|
|
89
91
|
```ts
|
|
90
92
|
import LaunchKit, {
|
|
@@ -147,6 +149,18 @@ flowchart TD
|
|
|
147
149
|
M2 --> M3["Priority list:<br/>scheme ⭢ fallback<br/>⭢ app store ⭢ web store (by trackId)"]
|
|
148
150
|
end
|
|
149
151
|
|
|
152
|
+
subgraph LOC["web store · createWebStoreURL"]
|
|
153
|
+
L1{"country given?"}
|
|
154
|
+
L1 -->|yes| L3["Play · MS Store: hl / gl parameters<br/>App Store: /country/ path + l parameter"]
|
|
155
|
+
L1 -->|no| L2["Derive from language:<br/>region subtag ⭢ Intl.Locale.maximize()<br/>⭢ omit (store geolocation)"]
|
|
156
|
+
L2 --> L3
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
A5 -.->|"web store"| L1
|
|
160
|
+
I3 -.->|"web store"| L1
|
|
161
|
+
W3 -.->|"web store"| L1
|
|
162
|
+
M3 -.->|"web store"| L1
|
|
163
|
+
|
|
150
164
|
AND --> P
|
|
151
165
|
IOS --> P
|
|
152
166
|
WIN --> P
|
|
@@ -191,6 +205,8 @@ const openedBy = await LaunchKit.app({
|
|
|
191
205
|
packageName: 'com.example.myapp',
|
|
192
206
|
allowAppStore: true,
|
|
193
207
|
allowWebStore: true,
|
|
208
|
+
language: 'ko', // web-store UI language
|
|
209
|
+
country: 'KR', // omit to derive from language (region subtag → Intl.Locale.maximize())
|
|
194
210
|
},
|
|
195
211
|
ios: {
|
|
196
212
|
universal: 'https://example.com/profile/42',
|
|
@@ -207,7 +223,17 @@ Per-platform fields: Android accepts `intent` / `scheme` / `packageName` / `fall
|
|
|
207
223
|
`allowIntent` (scheme ⇄ intent are derived from each other); iOS accepts `universal` / `scheme` /
|
|
208
224
|
`bundleId` / `trackId`; Windows accepts `scheme` / `packageFamilyName` / `productId`;
|
|
209
225
|
macOS accepts `scheme` / `bundleId` / `trackId`. All accept `fallback`, `timeout`,
|
|
210
|
-
`allowAppStore`, `allowWebStore`.
|
|
226
|
+
`allowAppStore`, `allowWebStore`, `language`, `country`.
|
|
227
|
+
|
|
228
|
+
`language` / `country` localize the **web-store** candidate only — `hl` / `gl` on
|
|
229
|
+
Google Play and the Microsoft Store, a country path segment plus `l` on the App
|
|
230
|
+
Store. When `country` is omitted it is derived from `language`: a region subtag
|
|
231
|
+
wins (`ko-KR` → `KR`), then `Intl.Locale.maximize()` where the runtime supports
|
|
232
|
+
it. When neither yields a region, the country is simply dropped and the store
|
|
233
|
+
falls back to its own geolocation — a wrong country can land on an
|
|
234
|
+
"not available in your region" page, so omission is the safe default. Native
|
|
235
|
+
app-store schemes (`market://`, `itms-apps://`, …) are untouched; the OS renders
|
|
236
|
+
those in the device locale.
|
|
211
237
|
|
|
212
238
|
`onAttempt` fires synchronously before each candidate, which is where you drive the waiting
|
|
213
239
|
UI — there is otherwise nothing on screen for the length of the timeout.
|
|
@@ -316,6 +342,34 @@ if (LaunchKit.utils.canOpenSetting) {
|
|
|
316
342
|
}
|
|
317
343
|
```
|
|
318
344
|
|
|
345
|
+
## Escaping in-app browsers
|
|
346
|
+
|
|
347
|
+
`escapeWebview()` reopens a URL — the current page by default — in the external
|
|
348
|
+
default browser, escaping the in-app webview the page is trapped in.
|
|
349
|
+
|
|
350
|
+
```js
|
|
351
|
+
import LaunchKit from 'web-launch-kit'
|
|
352
|
+
|
|
353
|
+
if (LaunchKit.utils.canEscapeWebview) {
|
|
354
|
+
await LaunchKit.escapeWebview()
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Or send a specific URL out instead of the current page
|
|
358
|
+
await LaunchKit.escapeWebview({ url: 'https://example.com/checkout' })
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
The route depends on the environment. KakaoTalk exposes a dedicated
|
|
362
|
+
`kakaotalk://web/openExternal` scheme and LINE honors the documented
|
|
363
|
+
`openExternalBrowser=1` query parameter — both work on Android and iOS. Other
|
|
364
|
+
webviews fall back to the OS route: a Chrome-pinned `intent://` URL on Android
|
|
365
|
+
(the original URL rides along as `S.browser_fallback_url`, so a Chrome-less
|
|
366
|
+
device stays in place), and the undocumented `x-safari-` scheme on iOS — which
|
|
367
|
+
works on iOS 15 and 17+ but not 16, always opens Safari regardless of the
|
|
368
|
+
default browser, and is blocked inside Meta's webviews (Facebook, Instagram).
|
|
369
|
+
Only http(s) URLs can be reopened. Outside a webview, or where no route is
|
|
370
|
+
available (hard-blocked webviews, iOS 16, desktop), the promise rejects — gate
|
|
371
|
+
the button on `utils.canEscapeWebview`.
|
|
372
|
+
|
|
319
373
|
---
|
|
320
374
|
|
|
321
375
|
## Notes
|
|
@@ -330,6 +384,12 @@ if (LaunchKit.utils.canOpenSetting) {
|
|
|
330
384
|
during page setup keeps `app()` from resolving the id inline. `app()` must run inside a
|
|
331
385
|
user gesture, so when the id is missing it looks it up synchronously rather than awaiting
|
|
332
386
|
and losing user activation. Passing `trackId` / `productId` directly skips it entirely.
|
|
387
|
+
`getProductId` also takes optional `language` / `country` to pin the Microsoft catalog
|
|
388
|
+
language and market — pass the same values you will give `app()`, since whether a lookup
|
|
389
|
+
succeeds can depend on the market and the pre-warmed cache should cover the same one.
|
|
390
|
+
When omitted, the market is derived the same way as the web-store country: explicit region
|
|
391
|
+
subtag first, then `Intl.Locale.maximize()` where the runtime supports it, then `en-US` / `US`.
|
|
392
|
+
`getTrackId` needs no locale — iTunes lookup is market-independent.
|
|
333
393
|
- **Store-id lookups depend on remote APIs** (iTunes Lookup, Microsoft display catalog)
|
|
334
394
|
and are cached for one hour; failures resolve to `undefined` rather than throwing.
|
|
335
395
|
- **In-app browsers are gated per candidate.** Inside webviews that block launches
|
package/dist/index.d.ts
CHANGED
|
@@ -32,6 +32,8 @@ declare interface AppInfo {
|
|
|
32
32
|
timeout?: number;
|
|
33
33
|
allowAppStore?: boolean;
|
|
34
34
|
allowWebStore?: boolean;
|
|
35
|
+
language?: string;
|
|
36
|
+
country?: string;
|
|
35
37
|
}
|
|
36
38
|
declare interface AndroidAppInfo extends AppInfo {
|
|
37
39
|
intent?: URLCandidate;
|
|
@@ -93,12 +95,16 @@ declare interface MapOptions {
|
|
|
93
95
|
fallback?: URLStringOrFallback;
|
|
94
96
|
onAttempt?: (attempt: LaunchAttempt) => void;
|
|
95
97
|
}
|
|
98
|
+
declare interface EscapeWebviewOptions {
|
|
99
|
+
url?: URLCandidate;
|
|
100
|
+
}
|
|
96
101
|
interface LaunchKitUtils {
|
|
97
102
|
get canOpenIntent(): boolean;
|
|
98
103
|
get canOpenUniversal(): boolean;
|
|
99
104
|
get canOpenSetting(): boolean;
|
|
105
|
+
get canEscapeWebview(): boolean;
|
|
100
106
|
getTrackId(bundleId: string): Promise<string | undefined>;
|
|
101
|
-
getProductId(packageFamilyName: string): Promise<string | undefined>;
|
|
107
|
+
getProductId(packageFamilyName: string, language?: string, country?: string): Promise<string | undefined>;
|
|
102
108
|
}
|
|
103
109
|
interface LaunchKitInstance {
|
|
104
110
|
readonly version: string;
|
|
@@ -113,8 +119,9 @@ interface LaunchKitInstance {
|
|
|
113
119
|
map(options?: MapOptions): Promise<AppOpenedBy>;
|
|
114
120
|
filepicker(options?: FilepickerOptions): Promise<File[]>;
|
|
115
121
|
setting(type?: SettingType): Promise<void>;
|
|
122
|
+
escapeWebview(options?: EscapeWebviewOptions): Promise<void>;
|
|
116
123
|
}
|
|
117
124
|
declare const LaunchKit: LaunchKitInstance;
|
|
118
125
|
|
|
119
126
|
export { SettingType, LaunchKit as default };
|
|
120
|
-
export type { AndroidAppInfo, AppInfo, AppOpenOptions, AppOpenedBy, FilepickerOptions, IOSAppInfo, LaunchAttempt, LaunchError, LaunchErrorCode, LaunchKitInstance, MacOSAppInfo, MailOptions, MapOptions, MessageOptions, OpenPickerStartIn, TelephoneOptions, WindowsAppInfo };
|
|
127
|
+
export type { AndroidAppInfo, AppInfo, AppOpenOptions, AppOpenedBy, EscapeWebviewOptions, FilepickerOptions, IOSAppInfo, LaunchAttempt, LaunchError, LaunchErrorCode, LaunchKitInstance, MacOSAppInfo, MailOptions, MapOptions, MessageOptions, OpenPickerStartIn, TelephoneOptions, WindowsAppInfo };
|