web-launch-kit 0.0.2 → 0.0.4
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/LICENSE +21 -0
- package/README.ko.md +294 -0
- package/README.md +63 -17
- package/dist/index.d.ts +0 -57
- package/dist/launch-kit.cjs +1295 -304
- 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 +1295 -304
- package/dist/launch-kit.mjs.map +1 -1
- package/dist/launch-kit.umd.js +1295 -304
- 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-global.d.ts +37 -0
- package/dist/utils/get-id.d.ts +0 -12
- package/package.json +8 -4
- package/dist/utils/event-listener-utils.d.ts +0 -8
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Park Jungyoung
|
|
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.ko.md
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+

|
|
2
|
+

|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
*[English](./README.md) · 한국어*
|
|
6
|
+
|
|
7
|
+
# web-launch-kit
|
|
8
|
+
|
|
9
|
+
웹에서 **외부 앱**과 **통신 인텐트**를 실행하는 TypeScript 라이브러리. 딥링크·커스텀 스킴,
|
|
10
|
+
안드로이드 `intent://`, iOS 유니버설 링크, 앱스토어·웹스토어 폴백을 다루며 후보를 순서대로
|
|
11
|
+
시도해 하나가 성공할 때까지 진행합니다. `tel`, `sms`, `mailto`, 파일 선택기, 시스템 설정
|
|
12
|
+
딥링크도 포함합니다.
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install web-launch-kit
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
번들은 자체 완결형이라(OS·로케일 감지가 인라인됨) 다른 스크립트가 필요 없습니다.
|
|
19
|
+
|
|
20
|
+
## API 한눈에 보기
|
|
21
|
+
|
|
22
|
+
`LaunchKit`은 싱글톤입니다.
|
|
23
|
+
|
|
24
|
+
| 멤버 | 시그니처 | 설명 |
|
|
25
|
+
| --- | --- | --- |
|
|
26
|
+
| `LaunchKit.version` | `string` | 설치된 패키지 버전 |
|
|
27
|
+
| `LaunchKit.SettingType` | `enum` | 설정 화면: `General`, `Network`, `Display`, `Appearance`, `Accessibility`, `Battery`, `Datetime`, `Language`, `Accounts`, `Storage` |
|
|
28
|
+
| `LaunchKit.app(options?)` | `Promise<AppOpenedBy>` | 가능한 최선의 경로로 앱을 실행하고, 어떤 경로로 열렸는지 반환 |
|
|
29
|
+
| `LaunchKit.telephone(options?)` | `Promise<void>` | 전화 앱 열기 (`tel:`) |
|
|
30
|
+
| `LaunchKit.message(options?)` | `Promise<void>` | 문자 작성 열기 (`sms:`) |
|
|
31
|
+
| `LaunchKit.mail(options?)` | `Promise<void>` | 메일 작성 열기 (`mailto:`) |
|
|
32
|
+
| `LaunchKit.map(options?)` | `Promise<void>` | 검색어·좌표·경로로 지도 열기. OS별 네이티브 앱과 Google Maps 웹 폴백 |
|
|
33
|
+
| `LaunchKit.filepicker(options?)` | `Promise<File[]>` | 파일 또는 디렉터리 선택 (File System Access API, input 폴백) |
|
|
34
|
+
| `LaunchKit.setting(type?)` | `Promise<void>` | 지원되는 환경에서 시스템 설정 화면 열기 |
|
|
35
|
+
| `LaunchKit.utils` | object | `canOpenIntent` / `canOpenUniversal` / `canOpenSetting` getter와 비동기 `getTrackId` / `getProductId` |
|
|
36
|
+
|
|
37
|
+
`AppOpenedBy`는 `"scheme"`, `"universal"`, `"intent"`, `"fallback"`, `"store"` 중 하나입니다.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## ESM
|
|
42
|
+
|
|
43
|
+
```js
|
|
44
|
+
import LaunchKit from 'web-launch-kit'
|
|
45
|
+
|
|
46
|
+
await LaunchKit.telephone({ to: '+821012345678' })
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## CommonJS
|
|
50
|
+
|
|
51
|
+
번들이 `exports: "named"`로 빌드되어 싱글톤은 `.default` 아래에 있습니다.
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
const { default: LaunchKit } = require('web-launch-kit')
|
|
55
|
+
|
|
56
|
+
LaunchKit.telephone({ to: '+821012345678' })
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## UMD (브라우저 `<script>`)
|
|
60
|
+
|
|
61
|
+
전역 `LaunchKit`은 네임스페이스 객체이고 싱글톤은 `LaunchKit.default`입니다.
|
|
62
|
+
`SettingType`은 `LaunchKit.SettingType`으로도 접근할 수 있습니다.
|
|
63
|
+
|
|
64
|
+
```html
|
|
65
|
+
<script src="https://unpkg.com/web-launch-kit/dist/launch-kit.umd.min.js"></script>
|
|
66
|
+
<script>
|
|
67
|
+
document.querySelector('#call').addEventListener('click', function () {
|
|
68
|
+
window.LaunchKit.default.telephone({to: '+821012345678'})
|
|
69
|
+
})
|
|
70
|
+
</script>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## TypeScript
|
|
74
|
+
|
|
75
|
+
싱글톤의 형태는 `LaunchKitInstance`로 export되고, 모든 옵션 객체에 이름이 붙어 있습니다 —
|
|
76
|
+
`AppOpenOptions`, `AndroidAppInfo`, `IOSAppInfo`, `WindowsAppInfo`, `MacOSAppInfo`,
|
|
77
|
+
`TelephoneOptions`, `MessageOptions`, `MailOptions`, `MapOptions`, `FilepickerOptions`.
|
|
78
|
+
`SettingType`은 값 export이고 `AppOpenedBy`는 타입입니다.
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import LaunchKit, {
|
|
82
|
+
SettingType,
|
|
83
|
+
type AppOpenOptions,
|
|
84
|
+
type AppOpenedBy,
|
|
85
|
+
} from 'web-launch-kit'
|
|
86
|
+
|
|
87
|
+
const options: AppOpenOptions = {
|
|
88
|
+
ios: { universal: 'https://example.com/profile/42', bundleId: 'com.example.myapp' },
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const openedBy: AppOpenedBy = await LaunchKit.app(options)
|
|
92
|
+
|
|
93
|
+
if (LaunchKit.utils.canOpenSetting) await LaunchKit.setting(SettingType.Network)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## 앱 실행
|
|
97
|
+
|
|
98
|
+
`app()`은 플랫폼별 옵션을 받고 현재 OS에 해당하는 블록만 사용합니다. 후보 목록을 순서대로
|
|
99
|
+
만들어 하나가 앱을 실행할 때까지 시도하고, 성공한 경로를 반환합니다.
|
|
100
|
+
|
|
101
|
+
```mermaid
|
|
102
|
+
flowchart TD
|
|
103
|
+
A([LaunchKit.app called]) --> B{Detect OS via PlatformKit}
|
|
104
|
+
B -->|unknown| E1([Reject: unsupported OS])
|
|
105
|
+
B -->|android| AND
|
|
106
|
+
B -->|ios| IOS
|
|
107
|
+
B -->|windows| WIN
|
|
108
|
+
B -->|macos| MAC
|
|
109
|
+
|
|
110
|
+
subgraph AND["android · resolveOptions"]
|
|
111
|
+
A1{"intent given, but scheme /<br/>packageName / fallback missing?"}
|
|
112
|
+
A1 -->|yes| A2["parseIntentURL:<br/>derive scheme · packageName · fallback"]
|
|
113
|
+
A1 -->|no| A3
|
|
114
|
+
A2 --> A3{"scheme given, but intent missing?"}
|
|
115
|
+
A3 -->|yes| A4["createIntentURL:<br/>scheme + packageName + fallback"]
|
|
116
|
+
A3 -->|no| A5
|
|
117
|
+
A4 --> A5["Priority list:<br/>intent (if canOpenIntent) ⭢ scheme (if canOpenScheme)<br/>⭢ fallback ⭢ app store (if canOpenScheme) ⭢ web store"]
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
subgraph IOS["ios · resolveOptions"]
|
|
121
|
+
I1{"bundleId given, but trackId missing?"}
|
|
122
|
+
I1 -->|yes| I2["getTrackId:<br/>iTunes lookup API (bundleId ⭢ trackId)"]
|
|
123
|
+
I1 -->|no| I3
|
|
124
|
+
I2 --> I3["Priority list:<br/>universal (if canOpenUniversal) ⭢ scheme (if canOpenScheme)<br/>⭢ fallback ⭢ app store (if canOpenScheme) ⭢ web store"]
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
subgraph WIN["windows · resolveOptions"]
|
|
128
|
+
W1{"packageFamilyName given,<br/>but productId missing?"}
|
|
129
|
+
W1 -->|yes| W2["getProductId:<br/>packageFamilyName ⭢ productId"]
|
|
130
|
+
W1 -->|no| W3
|
|
131
|
+
W2 --> W3["Priority list:<br/>scheme ⭢ fallback<br/>⭢ app store ⭢ web store (by productId)"]
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
subgraph MAC["macos · resolveOptions"]
|
|
135
|
+
M1{"bundleId given, but trackId missing?"}
|
|
136
|
+
M1 -->|yes| M2["getTrackId:<br/>iTunes lookup API (bundleId ⭢ trackId)"]
|
|
137
|
+
M1 -->|no| M3
|
|
138
|
+
M2 --> M3["Priority list:<br/>scheme ⭢ fallback<br/>⭢ app store ⭢ web store (by trackId)"]
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
AND --> P
|
|
142
|
+
IOS --> P
|
|
143
|
+
WIN --> P
|
|
144
|
+
MAC --> P
|
|
145
|
+
|
|
146
|
+
P{"Any URL candidates?"} -->|no| E2([Reject: no openable URL candidates])
|
|
147
|
+
P -->|yes| Q{"Next candidate type?"}
|
|
148
|
+
Q -->|"function fallback"| R["Invoke fallback function"] --> G
|
|
149
|
+
Q -->|"URL string"| O["openURL(index, url, timeout)"]
|
|
150
|
+
O --> F{Opened?}
|
|
151
|
+
F -->|yes| G(["Resolve AppOpenedBy:<br/>'intent' · 'universal' · 'scheme' · 'fallback' · 'store'"])
|
|
152
|
+
F -->|no| H{"Candidates remaining?"}
|
|
153
|
+
H -->|yes| Q
|
|
154
|
+
H -->|no| E3(["Reject: all attempted URLs failed<br/>(error lists every tried URL)"])
|
|
155
|
+
|
|
156
|
+
subgraph openURL["openURL · app-switch detection"]
|
|
157
|
+
T0["Register blur + visibilitychange listeners"] --> T1{"Document focused?"}
|
|
158
|
+
T1 -->|no| T2["restoreFocus:<br/>window ⭢ body ⭢ hidden input"]
|
|
159
|
+
T1 -->|yes| T3
|
|
160
|
+
T2 --> T3{"Environment?"}
|
|
161
|
+
T3 -->|cordova| T4["InAppBrowser.open / window.open ('_system')"]
|
|
162
|
+
T3 -->|browser| T5{"userActivation active<br/>or first attempt?"}
|
|
163
|
+
T5 -->|yes| T6["top.location.href = url"]
|
|
164
|
+
T5 -->|no| T7["Hidden anchor + synthetic click"]
|
|
165
|
+
T6 --> T8["+ hidden iframe (removed after 500ms)"]
|
|
166
|
+
T7 --> T8
|
|
167
|
+
T4 --> T9
|
|
168
|
+
T8 --> T9{"blur / hidden fired?"}
|
|
169
|
+
T9 -->|"yes ⭢ wait focus"| T10([resolve: app opened])
|
|
170
|
+
T9 -->|"no, until timeout"| T11([reject: app not detected])
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
O -.->|delegates to| T0
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
```js
|
|
177
|
+
import LaunchKit from 'web-launch-kit'
|
|
178
|
+
|
|
179
|
+
const openedBy = await LaunchKit.app({
|
|
180
|
+
android: {
|
|
181
|
+
scheme: 'myapp://profile/42',
|
|
182
|
+
packageName: 'com.example.myapp',
|
|
183
|
+
allowAppStore: true,
|
|
184
|
+
allowWebStore: true,
|
|
185
|
+
},
|
|
186
|
+
ios: {
|
|
187
|
+
universal: 'https://example.com/profile/42',
|
|
188
|
+
scheme: 'myapp://profile/42',
|
|
189
|
+
bundleId: 'com.example.myapp', // 스토어 폴백용 trackId 로 변환됩니다
|
|
190
|
+
allowAppStore: true,
|
|
191
|
+
},
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
console.log(openedBy) // "universal" | "scheme" | "intent" | "fallback" | "store"
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
플랫폼별 필드: 안드로이드는 `intent` / `scheme` / `packageName` / `fallback`(scheme ⇄ intent는
|
|
198
|
+
서로에게서 유도됩니다), iOS는 `universal` / `scheme` / `bundleId` / `trackId`, Windows는
|
|
199
|
+
`scheme` / `packageFamilyName` / `productId`, macOS는 `scheme` / `bundleId` / `trackId`를
|
|
200
|
+
받습니다. 모두 `fallback`, `timeout`, `allowAppStore`, `allowWebStore`를 함께 받습니다.
|
|
201
|
+
|
|
202
|
+
안드로이드와 iOS는 `assumeAllowedInApp`을 추가로 받습니다. 파트너 화이트리스트로 동작하는
|
|
203
|
+
인앱 브라우저(예: 웨이보)에 앱이 등록되어 있을 때만 `true`로 선언하세요. 그러면 그 환경에서
|
|
204
|
+
scheme·유니버설 링크 후보가 유지됩니다. 완전히 차단된 웹뷰나 OS 버전 요구사항은 이 옵션으로
|
|
205
|
+
우회되지 않습니다.
|
|
206
|
+
|
|
207
|
+
## 통신 인텐트
|
|
208
|
+
|
|
209
|
+
```js
|
|
210
|
+
import LaunchKit from 'web-launch-kit'
|
|
211
|
+
|
|
212
|
+
await LaunchKit.telephone({ to: '+821012345678' })
|
|
213
|
+
|
|
214
|
+
await LaunchKit.message({ to: '+821012345678', body: 'hello' })
|
|
215
|
+
|
|
216
|
+
await LaunchKit.mail({
|
|
217
|
+
to: ['a@example.com', 'b@example.com'],
|
|
218
|
+
cc: 'c@example.com',
|
|
219
|
+
subject: 'Hi',
|
|
220
|
+
body: 'from web-launch-kit',
|
|
221
|
+
})
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## 파일 선택기
|
|
225
|
+
|
|
226
|
+
```js
|
|
227
|
+
import LaunchKit from 'web-launch-kit'
|
|
228
|
+
|
|
229
|
+
// 파일 (가능하면 showOpenFilePicker, 아니면 <input type=file> 폴백)
|
|
230
|
+
const files = await LaunchKit.filepicker({ accept: ['image/*', '.pdf'], multiple: true })
|
|
231
|
+
|
|
232
|
+
// 디렉터리 (재귀 탐색, webkitRelativePath 가 채워집니다)
|
|
233
|
+
const tree = await LaunchKit.filepicker({ directory: true })
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## 지도
|
|
237
|
+
|
|
238
|
+
`map()`은 검색어·좌표·경로 중 하나를 받아 현재 OS에 맞는 URL을 만듭니다
|
|
239
|
+
(iOS/macOS는 `maps://`, 안드로이드는 `geo:`, Windows는 `bingmaps:`). 웹에서는 Google Maps로
|
|
240
|
+
폴백합니다.
|
|
241
|
+
|
|
242
|
+
```js
|
|
243
|
+
import LaunchKit from 'web-launch-kit'
|
|
244
|
+
|
|
245
|
+
// 장소 검색
|
|
246
|
+
await LaunchKit.map({ query: 'Seoul City Hall' })
|
|
247
|
+
|
|
248
|
+
// 라벨이 붙은 핀으로 좌표 표시
|
|
249
|
+
await LaunchKit.map({ coordinate: [37.5665, 126.9780], label: 'Seoul City Hall', zoom: 15 })
|
|
250
|
+
|
|
251
|
+
// 경로 안내 (origin 을 생략하면 현재 위치가 기본값)
|
|
252
|
+
await LaunchKit.map({ directions: { destination: 'Seoul Station', origin: [37.5665, 126.9780] } })
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
`query` / `coordinate` / `directions` 중 하나만 주세요. 여러 개가 설정되면 `directions`,
|
|
256
|
+
`coordinate`, `query` 순으로 우선합니다. `coordinate`와 함께 준 `label`은 iOS/macOS/Windows에서
|
|
257
|
+
이름 붙은 핀으로 표시되고, 안드로이드에서는 무시됩니다 — Google Maps 외의 `geo:` 핸들러가
|
|
258
|
+
라벨 형식을 검색어로 잘못 해석하기 때문입니다. 안드로이드 `geo:`에는 표준 경로 안내가 없어서
|
|
259
|
+
경로는 Google Maps 폴백을 쓰고, Windows도 지도 앱이 단종되어 `bingmaps:`가 대개 웹 폴백으로
|
|
260
|
+
넘어갑니다.
|
|
261
|
+
|
|
262
|
+
## 시스템 설정
|
|
263
|
+
|
|
264
|
+
```js
|
|
265
|
+
import LaunchKit from 'web-launch-kit'
|
|
266
|
+
|
|
267
|
+
if (LaunchKit.utils.canOpenSetting) {
|
|
268
|
+
await LaunchKit.setting(LaunchKit.SettingType.Network)
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## 참고
|
|
275
|
+
|
|
276
|
+
- **딥링크에는 실제 사용자 제스처가 필요합니다.** 유니버설 링크와 커스텀 스킴은 프로그램에서
|
|
277
|
+
임의로 호출하거나 동일 출처 컨텍스트에서 호출하면 신뢰할 수 없습니다 — 앱을 여는 대신 웹으로
|
|
278
|
+
폴백합니다. `app()`은 클릭/탭 핸들러 안에서 호출하세요.
|
|
279
|
+
- **`app()`이 반환하는 것은 경로이지 실행 보증이 아닙니다.** 감지는 OS별 타임아웃을 둔
|
|
280
|
+
포커스·가시성 휴리스틱에 기반합니다. `AppOpenedBy`가 반환됐다는 건 그 후보가 시도되었고
|
|
281
|
+
페이지가 백그라운드로 전환된 것처럼 보였다는 뜻이지, 확정된 실행 확인은 아닙니다.
|
|
282
|
+
- **`utils.getTrackId` / `getProductId`는 비동기**이고 미리 호출해도 안전합니다. 제스처 전에
|
|
283
|
+
id를 확보해두면 `app()`이 그 조회를 인라인으로 하지 않습니다.
|
|
284
|
+
- **스토어 id 조회는 외부 API에 의존합니다**(iTunes Lookup, Microsoft display catalog).
|
|
285
|
+
1시간 캐시되며 실패는 예외가 아니라 `undefined`로 resolve됩니다.
|
|
286
|
+
- **인앱 브라우저는 후보 단위로 걸러집니다.** 실행이 차단된 웹뷰(WeChat, QQ, Qzone, Baidu,
|
|
287
|
+
그리고 파트너가 아닌 웨이보) 안에서는 커스텀 스킴·`intent://`·`market://` 같은 스토어 스킴이
|
|
288
|
+
타임아웃을 소진하지 않고 미리 제외됩니다. 그래서 체인이 곧바로 `fallback` / 웹스토어로
|
|
289
|
+
떨어집니다. 화이트리스트에 등록된 파트너 앱은 `assumeAllowedInApp: true`로 되돌립니다.
|
|
290
|
+
- **`map()`은 각 후보에 OS별 타임아웃**을 사용합니다.
|
|
291
|
+
|
|
292
|
+
## 브라우저 지원
|
|
293
|
+
|
|
294
|
+
**IE 9**까지 동작합니다.
|
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|

|
|
3
3
|

|
|
4
4
|
|
|
5
|
+
*English · [한국어](./README.ko.md)*
|
|
6
|
+
|
|
5
7
|
# web-launch-kit
|
|
6
8
|
|
|
7
9
|
A TypeScript library for launching **external apps** and **communication intents**
|
|
@@ -37,6 +39,61 @@ The bundle is self-contained (OS/locale detection is inlined) — no peer script
|
|
|
37
39
|
|
|
38
40
|
---
|
|
39
41
|
|
|
42
|
+
## ESM
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
import LaunchKit from 'web-launch-kit'
|
|
46
|
+
|
|
47
|
+
await LaunchKit.telephone({ to: '+821012345678' })
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## CommonJS
|
|
51
|
+
|
|
52
|
+
The bundle is built with `exports: "named"`, so the singleton lives under `.default`:
|
|
53
|
+
|
|
54
|
+
```js
|
|
55
|
+
const { default: LaunchKit } = require('web-launch-kit')
|
|
56
|
+
|
|
57
|
+
LaunchKit.telephone({ to: '+821012345678' })
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## UMD (browser `<script>`)
|
|
61
|
+
|
|
62
|
+
The global `LaunchKit` is a namespace object; the singleton is `LaunchKit.default`.
|
|
63
|
+
`SettingType` is also reachable as `LaunchKit.SettingType`.
|
|
64
|
+
|
|
65
|
+
```html
|
|
66
|
+
<script src="https://unpkg.com/web-launch-kit/dist/launch-kit.umd.min.js"></script>
|
|
67
|
+
<script>
|
|
68
|
+
document.querySelector('#call').addEventListener('click', function () {
|
|
69
|
+
window.LaunchKit.default.telephone({to: '+821012345678'})
|
|
70
|
+
})
|
|
71
|
+
</script>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## TypeScript
|
|
75
|
+
|
|
76
|
+
The singleton shape is exported as `LaunchKitInstance`, and every option object has a
|
|
77
|
+
named type: `AppOpenOptions`, `AndroidAppInfo`, `IOSAppInfo`, `WindowsAppInfo`,
|
|
78
|
+
`MacOSAppInfo`, `TelephoneOptions`, `MessageOptions`, `MailOptions`, `MapOptions`,
|
|
79
|
+
`FilepickerOptions`. `SettingType` is a value export; `AppOpenedBy` is a type.
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
import LaunchKit, {
|
|
83
|
+
SettingType,
|
|
84
|
+
type AppOpenOptions,
|
|
85
|
+
type AppOpenedBy,
|
|
86
|
+
} from 'web-launch-kit'
|
|
87
|
+
|
|
88
|
+
const options: AppOpenOptions = {
|
|
89
|
+
ios: { universal: 'https://example.com/profile/42', bundleId: 'com.example.myapp' },
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const openedBy: AppOpenedBy = await LaunchKit.app(options)
|
|
93
|
+
|
|
94
|
+
if (LaunchKit.utils.canOpenSetting) await LaunchKit.setting(SettingType.Network)
|
|
95
|
+
```
|
|
96
|
+
|
|
40
97
|
## Launching an app
|
|
41
98
|
|
|
42
99
|
`app()` takes per-platform options and only acts on the block matching the current
|
|
@@ -215,21 +272,6 @@ if (LaunchKit.utils.canOpenSetting) {
|
|
|
215
272
|
}
|
|
216
273
|
```
|
|
217
274
|
|
|
218
|
-
## CommonJS / UMD
|
|
219
|
-
|
|
220
|
-
The bundle is built with `exports: "named"`, so the singleton lives under `.default`:
|
|
221
|
-
|
|
222
|
-
```js
|
|
223
|
-
const { default: LaunchKit } = require('web-launch-kit')
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
```html
|
|
227
|
-
<script src="https://unpkg.com/web-launch-kit/dist/launch-kit.umd.min.js"></script>
|
|
228
|
-
<script>
|
|
229
|
-
window.LaunchKit.default.telephone({ to: '+821012345678' })
|
|
230
|
-
</script>
|
|
231
|
-
```
|
|
232
|
-
|
|
233
275
|
---
|
|
234
276
|
|
|
235
277
|
## Notes
|
|
@@ -240,8 +282,8 @@ const { default: LaunchKit } = require('web-launch-kit')
|
|
|
240
282
|
- **`app()` resolves with the route, not a guarantee of launch.** Detection relies on
|
|
241
283
|
focus/visibility heuristics with per-OS timeouts; a resolved `AppOpenedBy` means that
|
|
242
284
|
candidate was attempted and the page appeared to background, not a hard confirmation.
|
|
243
|
-
- **`utils.getTrackId` / `getProductId` are async
|
|
244
|
-
|
|
285
|
+
- **`utils.getTrackId` / `getProductId` are async** and safe to call ahead of time —
|
|
286
|
+
resolving an id before the gesture keeps `app()` from looking it up inline.
|
|
245
287
|
- **Store-id lookups depend on remote APIs** (iTunes Lookup, Microsoft display catalog)
|
|
246
288
|
and are cached for one hour; failures resolve to `undefined` rather than throwing.
|
|
247
289
|
- **In-app browsers are gated per candidate.** Inside webviews that block launches
|
|
@@ -251,3 +293,7 @@ const { default: LaunchKit } = require('web-launch-kit')
|
|
|
251
293
|
(the only routes that work there). Whitelisted partner apps opt back in with
|
|
252
294
|
`assumeAllowedInApp: true`.
|
|
253
295
|
- **`map()` uses the per-OS timeout** for each candidate.
|
|
296
|
+
|
|
297
|
+
## Browser support
|
|
298
|
+
|
|
299
|
+
Runs down to **IE 9**.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,54 +1,3 @@
|
|
|
1
|
-
declare global {
|
|
2
|
-
interface Document {
|
|
3
|
-
webkitVisibilityState?: 'hidden' | 'visible';
|
|
4
|
-
mozVisibilityState?: 'hidden' | 'visible';
|
|
5
|
-
msVisibilityState?: 'hidden' | 'visible';
|
|
6
|
-
webkitHidden?: boolean;
|
|
7
|
-
mozHidden?: boolean;
|
|
8
|
-
msHidden?: boolean;
|
|
9
|
-
}
|
|
10
|
-
interface FileSystemDirectoryHandle {
|
|
11
|
-
values(): AsyncIterableIterator<FileSystemHandle>;
|
|
12
|
-
}
|
|
13
|
-
interface SymbolConstructor {
|
|
14
|
-
readonly asyncIterator: symbol;
|
|
15
|
-
}
|
|
16
|
-
var showOpenFilePicker: (options?: OpenFilePickerOptions) => Promise<FileSystemFileHandle[]>;
|
|
17
|
-
var showDirectoryPicker: (options?: OpenDirectoryPickerOptions) => Promise<FileSystemDirectoryHandle>;
|
|
18
|
-
var cordova: CordovaPlugin | undefined;
|
|
19
|
-
}
|
|
20
|
-
interface OpenFilePickerOptions {
|
|
21
|
-
excludeAcceptAllOption?: boolean;
|
|
22
|
-
id?: string;
|
|
23
|
-
multiple?: boolean;
|
|
24
|
-
startIn?: OpenPickerStartIn;
|
|
25
|
-
types?: {
|
|
26
|
-
description?: string;
|
|
27
|
-
accept: Record<string, string[]>;
|
|
28
|
-
}[];
|
|
29
|
-
}
|
|
30
|
-
interface OpenDirectoryPickerOptions {
|
|
31
|
-
id?: string;
|
|
32
|
-
mode?: 'read' | 'readwrite';
|
|
33
|
-
startIn?: OpenPickerStartIn;
|
|
34
|
-
}
|
|
35
|
-
interface IteratorYieldResult<TYield> {
|
|
36
|
-
done?: false;
|
|
37
|
-
value: TYield;
|
|
38
|
-
}
|
|
39
|
-
interface IteratorReturnResult<TReturn> {
|
|
40
|
-
done: true;
|
|
41
|
-
value: TReturn;
|
|
42
|
-
}
|
|
43
|
-
interface AsyncIterator<T, TReturn = any, TNext = any> {
|
|
44
|
-
next(...[value]: [] | [TNext]): Promise<IteratorResult<T, TReturn>>;
|
|
45
|
-
return?(value?: TReturn | PromiseLike<TReturn>): Promise<IteratorResult<T, TReturn>>;
|
|
46
|
-
throw?(e?: any): Promise<IteratorResult<T, TReturn>>;
|
|
47
|
-
}
|
|
48
|
-
interface AsyncIterableIterator<T, TReturn = any, TNext = any> extends AsyncIterator<T, TReturn, TNext> {
|
|
49
|
-
[Symbol.asyncIterator](): AsyncIterableIterator<T, TReturn, TNext>;
|
|
50
|
-
}
|
|
51
|
-
type IteratorResult<T, TReturn = any> = IteratorYieldResult<T> | IteratorReturnResult<TReturn>;
|
|
52
1
|
declare type URLCandidate = URL | string;
|
|
53
2
|
declare type URLCandidateOrFallback = URLCandidate | (() => any);
|
|
54
3
|
declare type URLStringOrFallback = string | (() => any);
|
|
@@ -66,12 +15,6 @@ declare enum SettingType {
|
|
|
66
15
|
Accounts = "accounts",
|
|
67
16
|
Storage = "storage"
|
|
68
17
|
}
|
|
69
|
-
interface CordovaPlugin {
|
|
70
|
-
InAppBrowser?: CordovaInAppBrowser;
|
|
71
|
-
}
|
|
72
|
-
interface CordovaInAppBrowser {
|
|
73
|
-
open(url?: string | URL, target?: string, features?: string): WindowProxy | null;
|
|
74
|
-
}
|
|
75
18
|
declare interface AppInfo {
|
|
76
19
|
scheme?: URLCandidate;
|
|
77
20
|
fallback?: URLCandidateOrFallback;
|