@h-ai/capacitor 1.0.0-beta.2 → 1.0.0-beta.21
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.en.md +102 -27
- package/README.ja.md +101 -26
- package/README.md +101 -26
- package/dist/index.d.ts +3 -1
- package/dist/index.js +58 -27
- package/dist/index.js.map +1 -1
- package/images/android.Dockerfile +31 -0
- package/package.json +11 -10
package/README.en.md
CHANGED
|
@@ -4,11 +4,73 @@
|
|
|
4
4
|
|
|
5
5
|
[Framework home](../../README.en.md) · Related modules: [@h-ai/api-client](../api-client/README.en.md) · [@h-ai/core](../core/README.en.md)
|
|
6
6
|
|
|
7
|
-
## Introduction
|
|
8
|
-
|
|
9
7
|
A Capacitor native bridge for hai Framework mobile apps, with a unified API for secure token storage, device information, push notifications, camera, and status bar access.
|
|
10
8
|
|
|
11
|
-
##
|
|
9
|
+
## Quick Start
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { capacitor } from '@h-ai/capacitor'
|
|
13
|
+
|
|
14
|
+
const initialized = await capacitor.init()
|
|
15
|
+
if (!initialized.success)
|
|
16
|
+
throw new Error(initialized.error.message)
|
|
17
|
+
const failures: unknown[] = []
|
|
18
|
+
try {
|
|
19
|
+
const saved = await capacitor.preferences.set('theme', 'dark')
|
|
20
|
+
if (!saved.success)
|
|
21
|
+
throw new Error(saved.error.message)
|
|
22
|
+
const theme = await capacitor.preferences.get('theme')
|
|
23
|
+
if (!theme.success)
|
|
24
|
+
throw new Error(theme.error.message)
|
|
25
|
+
// theme.data is 'dark'; an absent key returns null.
|
|
26
|
+
}
|
|
27
|
+
catch (cause) {
|
|
28
|
+
failures.push(cause)
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
const closed = await capacitor.close()
|
|
32
|
+
if (!closed.success)
|
|
33
|
+
failures.push(closed.error)
|
|
34
|
+
}
|
|
35
|
+
if (failures.length)
|
|
36
|
+
throw new AggregateError(failures, 'Native preference operation failed')
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Features
|
|
40
|
+
|
|
41
|
+
### Purpose, inputs and outputs
|
|
42
|
+
|
|
43
|
+
Use capacitor subfeatures after successful initialization. Check HaiResult.success before reading data; void has no business data and Promises must be awaited. tokens is an independent adapter returned by createCapacitorTokenStorage(); its methods reject on native storage failure. Standalone Preferences helpers do not use module state and wrap the same plugin operations in HaiResult.
|
|
44
|
+
|
|
45
|
+
| Entry | Use case | Input (`—` means none) | Return value | Call example |
|
|
46
|
+
| -------------------------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
|
|
47
|
+
| `capacitor.init` | Detect and prepare the native host | `—` | `Promise<HaiResult<void>>` | `await capacitor.init()` |
|
|
48
|
+
| `capacitor.close` | Release owned native resources | `—` | `Promise<HaiResult<void>>` | `await capacitor.close()` |
|
|
49
|
+
| `capacitor.getPlatform` | Choose platform behavior | `—` | `string: ios / android / web` | `capacitor.getPlatform()` |
|
|
50
|
+
| `capacitor.isNative` | Check the native environment | `—` | `boolean` | `capacitor.isNative()` |
|
|
51
|
+
| `capacitor.isInitialized` | Check module readiness | `—` | `boolean` | `capacitor.isInitialized` |
|
|
52
|
+
| `capacitor.device.getInfo` | Read device diagnostics | `—` | `Promise<HaiResult<{ platform, osVersion, model, manufacturer, isVirtual, appVersion?, appBuild? }>>` | `await capacitor.device.getInfo()` |
|
|
53
|
+
| `capacitor.device.getAppVersion` | Read the installed app version | `—` | `Promise<HaiResult<{ version, build }>>` | `await capacitor.device.getAppVersion()` |
|
|
54
|
+
| `capacitor.camera.takePhoto` | Capture or select a photo | `{ source?: camera/photos/prompt, quality?: 0…100, width?, height?, resultType?: uri/base64/dataUrl }` | `Promise<HaiResult<{ data: string, format: string }>>` | `await capacitor.camera.takePhoto({ source: "photos", resultType: "dataUrl" })` |
|
|
55
|
+
| `capacitor.push.register` | Request permission and a push token | `{ signal?: AbortSignal }` | `Promise<HaiResult<{ token: string }>>` | `await capacitor.push.register()` |
|
|
56
|
+
| `capacitor.push.listen` | Observe notifications and taps | `{ onReceived?, onActionPerformed? }, { signal? }` | `Promise<HaiResult<() => Promise<HaiResult<void>>>>` | `await capacitor.push.listen(callbacks, { signal })` |
|
|
57
|
+
| `capacitor.statusBar.configure` | Configure the status bar | `{ style?: dark/light/default, backgroundColor?: color, overlay?: boolean }` | `Promise<HaiResult<void>>` | `await capacitor.statusBar.configure({ style: "dark", overlay: true })` |
|
|
58
|
+
| `capacitor.statusBar.show` | Show the status bar | `—` | `Promise<HaiResult<void>>` | `await capacitor.statusBar.show()` |
|
|
59
|
+
| `capacitor.statusBar.hide` | Hide the status bar | `—` | `Promise<HaiResult<void>>` | `await capacitor.statusBar.hide()` |
|
|
60
|
+
| `capacitor.preferences.get` | Read a nonsensitive preference | `key: string` | `Promise<HaiResult<string / null>>` | `await capacitor.preferences.get("theme")` |
|
|
61
|
+
| `capacitor.preferences.set` | Save a nonsensitive preference | `key: string, value: string` | `Promise<HaiResult<void>>` | `await capacitor.preferences.set("theme", "dark")` |
|
|
62
|
+
| `capacitor.preferences.remove` | Remove a nonsensitive preference | `key: string` | `Promise<HaiResult<void>>` | `await capacitor.preferences.remove("theme")` |
|
|
63
|
+
| `createCapacitorTokenStorage` | Create native secure token storage | `—` | `TokenStorage` | `createCapacitorTokenStorage()` |
|
|
64
|
+
| `tokens.getAccessToken` | Read the saved access token | `—` | `Promise<string / null>` | `await tokens.getAccessToken()` |
|
|
65
|
+
| `tokens.getRefreshToken` | Read the saved refresh token | `—` | `Promise<string / null>` | `await tokens.getRefreshToken()` |
|
|
66
|
+
| `tokens.setAccessToken` | Save an access token | `token: string` | `Promise<void>` | `await tokens.setAccessToken(accessToken)` |
|
|
67
|
+
| `tokens.setRefreshToken` | Save a refresh token | `token: string` | `Promise<void>` | `await tokens.setRefreshToken(refreshToken)` |
|
|
68
|
+
| `tokens.clear` | Remove both saved credentials | `—` | `Promise<void>` | `await tokens.clear()` |
|
|
69
|
+
| `safeGetPreference` | Read a preference without module lifecycle | `key: string` | `Promise<HaiResult<string / null>>` | `await safeGetPreference("theme")` |
|
|
70
|
+
| `safeSetPreference` | Save a preference without module lifecycle | `key: string, value: string` | `Promise<HaiResult<void>>` | `await safeSetPreference("theme", "dark")` |
|
|
71
|
+
| `safeRemovePreference` | Remove a preference without module lifecycle | `key: string` | `Promise<HaiResult<void>>` | `await safeRemovePreference("theme")` |
|
|
72
|
+
|
|
73
|
+
Photo source selects the camera, library or prompt; quality is 0–100 and width/height are pixel constraints. Successful data is a displayable webPath, raw Base64 or a MIME-prefixed Data URL according to resultType; format is the plugin image format. callbacks.onReceived/onActionPerformed receive notifications and signal comes from an application AbortController. A successful listen returns an async cleanup function whose result must be checked. Import standalone factories and Preferences helpers from the package root.
|
|
12
74
|
|
|
13
75
|
- **Token storage**: `@aparajita/capacitor-secure-storage` (required on native platforms). Android uses KeyStore + encrypted SharedPreferences; iOS uses Keychain. No Web fallback.
|
|
14
76
|
- **Preferences**: `@capacitor/preferences` (required). General preferences storage for nonsensitive settings, not authentication tokens.
|
|
@@ -18,21 +80,7 @@ A Capacitor native bridge for hai Framework mobile apps, with a unified API for
|
|
|
18
80
|
- **Camera/gallery**: `@capacitor/camera` (optional). Capture photos or select gallery images.
|
|
19
81
|
- **Status bar**: `@capacitor/status-bar` (optional). Immersive mode, background color, and style settings.
|
|
20
82
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
### Quick start
|
|
24
|
-
|
|
25
|
-
```ts
|
|
26
|
-
import { capacitor } from '@h-ai/capacitor'
|
|
27
|
-
|
|
28
|
-
// 应用启动时初始化(检测 Capacitor 环境)
|
|
29
|
-
const result = await capacitor.init()
|
|
30
|
-
if (!result.success) {
|
|
31
|
-
// 非 Capacitor 环境(纯 Web)
|
|
32
|
-
}
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
#### Secure token storage with api-client
|
|
83
|
+
### Secure token storage with api-client
|
|
36
84
|
|
|
37
85
|
```ts
|
|
38
86
|
import { apiClient } from '@h-ai/api-client'
|
|
@@ -53,7 +101,9 @@ await apiClient.init({
|
|
|
53
101
|
|
|
54
102
|
A failed credential write blocks both credentials, including an access token already saved before a refresh-token write failed. After failed login or registration persistence, attempt `auth.clear()` to remove partially saved data. Report cleanup failures explicitly instead of claiming device credentials were deleted.
|
|
55
103
|
|
|
56
|
-
|
|
104
|
+
All TokenStorage instances in the same module share a native mutation queue. `clear()` waits for earlier writes before removing credentials, preventing late writes from restoring old tokens after successful cleanup. Explicit later writes may save new credentials. This ordering does not coordinate independent WebViews or JavaScript runtimes.
|
|
105
|
+
|
|
106
|
+
### Device information and push notifications
|
|
57
107
|
|
|
58
108
|
```ts
|
|
59
109
|
import { capacitor } from '@h-ai/capacitor'
|
|
@@ -71,7 +121,7 @@ if (reg.success) {
|
|
|
71
121
|
|
|
72
122
|
Push requires an actual native platform. The setup phase of `push.register({ signal? })` and `push.listen(callbacks, { signal? })` has a 30-second deadline including permission requests and listener attachment. Failure, timeout, cancellation and `capacitor.close()` stop callbacks and remove owned handles; late handles are removed when they arrive. Other consumers are not affected through removeAllListeners. After listen succeeds, its signal and returned cleanup can still stop it. Cleanup and close return `HaiResult<void>`; native removal failures return `PUSH_CLEANUP_FAILED` (`capacitor:032`) and retain failed handles for retry without repeating successful removals. Closing disables the module immediately, and initialization retries pending cleanup first. Controlled plugin timing tests do not replace repeated registration, permission and FCM/APNs checks on Android/iOS devices.
|
|
73
123
|
|
|
74
|
-
|
|
124
|
+
### Status bar
|
|
75
125
|
|
|
76
126
|
```ts
|
|
77
127
|
import { capacitor } from '@h-ai/capacitor'
|
|
@@ -83,7 +133,7 @@ await capacitor.statusBar.configure({
|
|
|
83
133
|
})
|
|
84
134
|
```
|
|
85
135
|
|
|
86
|
-
|
|
136
|
+
### Camera
|
|
87
137
|
|
|
88
138
|
```ts
|
|
89
139
|
const photo = await capacitor.camera.takePhoto({
|
|
@@ -93,7 +143,7 @@ const photo = await capacitor.camera.takePhoto({
|
|
|
93
143
|
})
|
|
94
144
|
```
|
|
95
145
|
|
|
96
|
-
|
|
146
|
+
### Preferences
|
|
97
147
|
|
|
98
148
|
```ts
|
|
99
149
|
await capacitor.preferences.set('my_key', 'value')
|
|
@@ -103,11 +153,36 @@ await capacitor.preferences.remove('my_key')
|
|
|
103
153
|
|
|
104
154
|
`capacitor.preferences` is for general preferences such as language, theme, and drafts. Use `createCapacitorTokenStorage()` for authentication tokens.
|
|
105
155
|
|
|
106
|
-
###
|
|
156
|
+
### Build an Android APK in a container
|
|
157
|
+
|
|
158
|
+
Run `pnpm build --target app --platform android --runtime auto --debug` at the Hai project root. Omit `--debug` for a signed release. Successful batches appear under `.hai/build/app/android/<variant>/`. See the [native build and signing instructions](../../docs/usage/getting-started/build.md#原生和离线条件).
|
|
159
|
+
|
|
160
|
+
The build image includes Node.js 24, pnpm 12.11.2, JDK 21, Android SDK 36, and Build Tools 35.0.0 and 36.0.0. The build runs on `linux/amd64`. The app's Gradle project and native plugins determine the APK's target ABIs, such as `arm64-v8a` and `armeabi-v7a`. Building for iOS requires macOS and Xcode.
|
|
161
|
+
|
|
162
|
+
Build the image from the framework repository root. In a project with this package installed, set `image_dir` to the `images` directory in the installed `@h-ai/capacitor` package.
|
|
163
|
+
|
|
164
|
+
```sh
|
|
165
|
+
image_dir=packages/capacitor/images
|
|
166
|
+
docker build --platform linux/amd64 -t hai-capacitor-android:36 \
|
|
167
|
+
-f "$image_dir/android.Dockerfile" "$image_dir"
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Run the container from the project root. Start with a clean working copy, run `pnpm install --frozen-lockfile` in the container, and build any local workspace packages the app depends on. Then run the app's existing build, `cap sync android`, and APK packaging commands. The APK is written to the mounted project. With rootless Podman, replace `docker` with `podman` and add `--userns=keep-id` alongside `--user`.
|
|
171
|
+
|
|
172
|
+
```sh
|
|
173
|
+
docker run --rm -it --platform linux/amd64 \
|
|
174
|
+
--user "$(id -u):$(id -g)" -e HOME=/tmp \
|
|
175
|
+
-e GRADLE_USER_HOME=/tmp/.gradle -e ANDROID_USER_HOME=/tmp/.android \
|
|
176
|
+
-v "$PWD:/workspace" hai-capacitor-android:36 bash
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Downloading dependencies requires access to npm, Gradle distributions, Google Maven, and Maven Central. Use the app's signing workflow for release builds. Mount the keystore read-only and inject passwords through the build environment. See the [mobile-app packaging and acceptance example](../../apps/mobile-app/README.md#原生交付与验收).
|
|
180
|
+
|
|
181
|
+
## Configuration
|
|
107
182
|
|
|
108
183
|
This module has no configuration file. `capacitor.init()` only checks whether the Capacitor runtime is available.
|
|
109
184
|
|
|
110
|
-
|
|
185
|
+
Native WebViews load a client application. For SvelteKit, configure a static SPA as below; pure Svelte/Vite templates do not use this route file:
|
|
111
186
|
|
|
112
187
|
```ts
|
|
113
188
|
// src/routes/+layout.ts
|
|
@@ -115,9 +190,9 @@ export const prerender = true
|
|
|
115
190
|
export const ssr = false
|
|
116
191
|
```
|
|
117
192
|
|
|
118
|
-
|
|
193
|
+
## Error Handling
|
|
119
194
|
|
|
120
|
-
|
|
195
|
+
`init/close`, device/camera/push/statusBar/preferences operations and `safe*Preference` return `HaiResult<T>`; `getPlatform/isNative/isInitialized` return direct values. TokenStorage methods reject on platform or storage errors:
|
|
121
196
|
|
|
122
197
|
```ts
|
|
123
198
|
import { capacitor, HaiCapacitorError } from '@h-ai/capacitor'
|
|
@@ -148,7 +223,7 @@ Common error codes:
|
|
|
148
223
|
| `HaiCapacitorError.CAMERA_FAILED` | `hai:capacitor:040` | Camera operation failed |
|
|
149
224
|
| `HaiCapacitorError.STATUS_BAR_FAILED` | `hai:capacitor:050` | Status bar operation failed |
|
|
150
225
|
|
|
151
|
-
## Development
|
|
226
|
+
## Development
|
|
152
227
|
|
|
153
228
|
```bash
|
|
154
229
|
pnpm --filter @h-ai/capacitor test
|
package/README.ja.md
CHANGED
|
@@ -4,11 +4,73 @@
|
|
|
4
4
|
|
|
5
5
|
[フレームワークのホーム](../../README.ja.md) · 関連モジュール:[@h-ai/api-client](../api-client/README.ja.md) · [@h-ai/core](../core/README.ja.md)
|
|
6
6
|
|
|
7
|
-
## 概要
|
|
8
|
-
|
|
9
7
|
hai Framework のモバイルアプリ向け Capacitor ネイティブブリッジ。安全な Token ストレージ、端末情報、プッシュ通知、カメラ、ステータスバーを共通 API で提供します。
|
|
10
8
|
|
|
11
|
-
##
|
|
9
|
+
## クイックスタート
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { capacitor } from '@h-ai/capacitor'
|
|
13
|
+
|
|
14
|
+
const initialized = await capacitor.init()
|
|
15
|
+
if (!initialized.success)
|
|
16
|
+
throw new Error(initialized.error.message)
|
|
17
|
+
const failures: unknown[] = []
|
|
18
|
+
try {
|
|
19
|
+
const saved = await capacitor.preferences.set('theme', 'dark')
|
|
20
|
+
if (!saved.success)
|
|
21
|
+
throw new Error(saved.error.message)
|
|
22
|
+
const theme = await capacitor.preferences.get('theme')
|
|
23
|
+
if (!theme.success)
|
|
24
|
+
throw new Error(theme.error.message)
|
|
25
|
+
// theme.data は 'dark'。保存していないキーは null。
|
|
26
|
+
}
|
|
27
|
+
catch (cause) {
|
|
28
|
+
failures.push(cause)
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
const closed = await capacitor.close()
|
|
32
|
+
if (!closed.success)
|
|
33
|
+
failures.push(closed.error)
|
|
34
|
+
}
|
|
35
|
+
if (failures.length)
|
|
36
|
+
throw new AggregateError(failures, 'Native preference operation failed')
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## 機能
|
|
40
|
+
|
|
41
|
+
### 用途・入力・出力
|
|
42
|
+
|
|
43
|
+
capacitor のサブ機能は初期化成功後に使用します。HaiResult<T> は success の確認後に data を読み、void は業務データなし、Promise は await します。tokens は createCapacitorTokenStorage() が返す独立アダプターで、各メソッドはネイティブ保存に失敗すると Promise を拒否します。独立 Preferences ヘルパーはモジュール状態を使わず、同じプラグイン操作を HaiResult で返します。
|
|
44
|
+
|
|
45
|
+
| 入口 | 使用場面 | 入力(`—` は引数なし) | 戻り値 | 呼出例 |
|
|
46
|
+
| -------------------------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
|
|
47
|
+
| `capacitor.init` | ネイティブ環境を確認して準備する | `—` | `Promise<HaiResult<void>>` | `await capacitor.init()` |
|
|
48
|
+
| `capacitor.close` | 所有するリソースを解放する | `—` | `Promise<HaiResult<void>>` | `await capacitor.close()` |
|
|
49
|
+
| `capacitor.getPlatform` | プラットフォーム別に処理する | `—` | `string: ios / android / web` | `capacitor.getPlatform()` |
|
|
50
|
+
| `capacitor.isNative` | ネイティブ環境か確認する | `—` | `boolean` | `capacitor.isNative()` |
|
|
51
|
+
| `capacitor.isInitialized` | モジュールの状態を確認する | `—` | `boolean` | `capacitor.isInitialized` |
|
|
52
|
+
| `capacitor.device.getInfo` | 端末の診断情報を取得する | `—` | `Promise<HaiResult<{ platform, osVersion, model, manufacturer, isVirtual, appVersion?, appBuild? }>>` | `await capacitor.device.getInfo()` |
|
|
53
|
+
| `capacitor.device.getAppVersion` | インストール済みアプリの版を取得する | `—` | `Promise<HaiResult<{ version, build }>>` | `await capacitor.device.getAppVersion()` |
|
|
54
|
+
| `capacitor.camera.takePhoto` | 撮影または写真を選択する | `{ source?: camera/photos/prompt, quality?: 0…100, width?, height?, resultType?: uri/base64/dataUrl }` | `Promise<HaiResult<{ data: string, format: string }>>` | `await capacitor.camera.takePhoto({ source: "photos", resultType: "dataUrl" })` |
|
|
55
|
+
| `capacitor.push.register` | 権限を要求してプッシュトークンを取得する | `{ signal?: AbortSignal }` | `Promise<HaiResult<{ token: string }>>` | `await capacitor.push.register()` |
|
|
56
|
+
| `capacitor.push.listen` | 通知とタップを監視する | `{ onReceived?, onActionPerformed? }, { signal? }` | `Promise<HaiResult<() => Promise<HaiResult<void>>>>` | `await capacitor.push.listen(callbacks, { signal })` |
|
|
57
|
+
| `capacitor.statusBar.configure` | ステータスバーを設定する | `{ style?: dark/light/default, backgroundColor?: color, overlay?: boolean }` | `Promise<HaiResult<void>>` | `await capacitor.statusBar.configure({ style: "dark", overlay: true })` |
|
|
58
|
+
| `capacitor.statusBar.show` | ステータスバーを表示する | `—` | `Promise<HaiResult<void>>` | `await capacitor.statusBar.show()` |
|
|
59
|
+
| `capacitor.statusBar.hide` | ステータスバーを非表示にする | `—` | `Promise<HaiResult<void>>` | `await capacitor.statusBar.hide()` |
|
|
60
|
+
| `capacitor.preferences.get` | 機密でない設定を読む | `key: string` | `Promise<HaiResult<string / null>>` | `await capacitor.preferences.get("theme")` |
|
|
61
|
+
| `capacitor.preferences.set` | 機密でない設定を保存する | `key: string, value: string` | `Promise<HaiResult<void>>` | `await capacitor.preferences.set("theme", "dark")` |
|
|
62
|
+
| `capacitor.preferences.remove` | 機密でない設定を削除する | `key: string` | `Promise<HaiResult<void>>` | `await capacitor.preferences.remove("theme")` |
|
|
63
|
+
| `createCapacitorTokenStorage` | ネイティブの安全な認証保存を作成する | `—` | `TokenStorage` | `createCapacitorTokenStorage()` |
|
|
64
|
+
| `tokens.getAccessToken` | 保存したアクセストークンを読む | `—` | `Promise<string / null>` | `await tokens.getAccessToken()` |
|
|
65
|
+
| `tokens.getRefreshToken` | 保存した更新トークンを読む | `—` | `Promise<string / null>` | `await tokens.getRefreshToken()` |
|
|
66
|
+
| `tokens.setAccessToken` | アクセストークンを保存する | `token: string` | `Promise<void>` | `await tokens.setAccessToken(accessToken)` |
|
|
67
|
+
| `tokens.setRefreshToken` | 更新トークンを保存する | `token: string` | `Promise<void>` | `await tokens.setRefreshToken(refreshToken)` |
|
|
68
|
+
| `tokens.clear` | 保存した認証情報を削除する | `—` | `Promise<void>` | `await tokens.clear()` |
|
|
69
|
+
| `safeGetPreference` | モジュールのライフサイクル外で設定を読む | `key: string` | `Promise<HaiResult<string / null>>` | `await safeGetPreference("theme")` |
|
|
70
|
+
| `safeSetPreference` | モジュールのライフサイクル外で設定を保存する | `key: string, value: string` | `Promise<HaiResult<void>>` | `await safeSetPreference("theme", "dark")` |
|
|
71
|
+
| `safeRemovePreference` | モジュールのライフサイクル外で設定を削除する | `key: string` | `Promise<HaiResult<void>>` | `await safeRemovePreference("theme")` |
|
|
72
|
+
|
|
73
|
+
写真の source はカメラ・ライブラリ・選択画面、quality は 0–100、width/height はピクセル制約です。成功時の data は resultType に応じた webPath、生 Base64、MIME 接頭辞付き Data URL、format はプラグインの画像形式です。callbacks.onReceived/onActionPerformed は通知を受け、signal はアプリの AbortController から渡します。listen 成功時の data は await して結果を確認する解除関数です。独立工場と Preferences ヘルパーはパッケージのルートから import します。
|
|
12
74
|
|
|
13
75
|
- **Token ストレージ**:`@aparajita/capacitor-secure-storage`(ネイティブで必須)。Android は KeyStore + 暗号化 SharedPreferences、iOS は Keychain を使用。Web へのフォールバックはありません。
|
|
14
76
|
- **Preferences**:`@capacitor/preferences`(必須)。機密情報を含まない一般設定の保存用で、認証 Token には使用しません。
|
|
@@ -18,21 +80,7 @@ hai Framework のモバイルアプリ向け Capacitor ネイティブブリッ
|
|
|
18
80
|
- **カメラ/アルバム**:`@capacitor/camera`(任意)。撮影とアルバム画像の選択。
|
|
19
81
|
- **ステータスバー**:`@capacitor/status-bar`(任意)。没入型表示、背景色、スタイルの設定。
|
|
20
82
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
### クイックスタート
|
|
24
|
-
|
|
25
|
-
```ts
|
|
26
|
-
import { capacitor } from '@h-ai/capacitor'
|
|
27
|
-
|
|
28
|
-
// 应用启动时初始化(检测 Capacitor 环境)
|
|
29
|
-
const result = await capacitor.init()
|
|
30
|
-
if (!result.success) {
|
|
31
|
-
// 非 Capacitor 环境(纯 Web)
|
|
32
|
-
}
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
#### api-client と連携する安全な Token ストレージ
|
|
83
|
+
### api-client と連携する安全な Token ストレージ
|
|
36
84
|
|
|
37
85
|
```ts
|
|
38
86
|
import { apiClient } from '@h-ai/api-client'
|
|
@@ -53,7 +101,9 @@ await apiClient.init({
|
|
|
53
101
|
|
|
54
102
|
認証情報の一方の書き込みが失敗すると両方を読み取り不可にするため、access token 保存後に refresh token の保存が失敗しても不完全な認証状態は公開しません。ログイン・登録時の保存失敗後は `auth.clear()` で部分保存データの削除を試み、削除も失敗した場合は端末の認証情報を削除できたと表示しないでください。
|
|
55
103
|
|
|
56
|
-
|
|
104
|
+
同じモジュール内のすべての TokenStorage はネイティブ変更キューを共有します。`clear()` は先行書き込みの完了後に削除し、削除成功後に古い書き込みが認証情報を復活させることを防ぎます。その後の明示的な書き込みは新しい認証情報を保存できます。別の WebView や JavaScript 実行環境とのプロセス間調整は提供しません。
|
|
105
|
+
|
|
106
|
+
### 端末情報とプッシュ通知
|
|
57
107
|
|
|
58
108
|
```ts
|
|
59
109
|
import { capacitor } from '@h-ai/capacitor'
|
|
@@ -71,7 +121,7 @@ if (reg.success) {
|
|
|
71
121
|
|
|
72
122
|
Push は実際のネイティブ環境が必要です。`push.register({ signal? })` と `push.listen(callbacks, { signal? })` の設定段階は権限要求とリスナー登録を含め30秒で終了します。失敗、タイムアウト、中止、`capacitor.close()` はコールバックを停止し、所有するハンドルを除去します。遅延したハンドルも到着時に除去し、removeAllListeners で他の利用者を巻き込みません。listen 成功後も signal と cleanup が有効です。cleanup と close は `HaiResult<void>` を返し、ネイティブ除去失敗は `PUSH_CLEANUP_FAILED`(`capacitor:032`)です。失敗したハンドルを保持して再試行し、成功済みの除去は繰り返しません。終了直後にモジュールを無効化し、再初期化前に残存ハンドルを処理します。制御したプラグイン試験は Android/iOS 実機の権限、FCM/APNs、反復登録試験を代替しません。
|
|
73
123
|
|
|
74
|
-
|
|
124
|
+
### ステータスバー
|
|
75
125
|
|
|
76
126
|
```ts
|
|
77
127
|
import { capacitor } from '@h-ai/capacitor'
|
|
@@ -83,7 +133,7 @@ await capacitor.statusBar.configure({
|
|
|
83
133
|
})
|
|
84
134
|
```
|
|
85
135
|
|
|
86
|
-
|
|
136
|
+
### カメラ
|
|
87
137
|
|
|
88
138
|
```ts
|
|
89
139
|
const photo = await capacitor.camera.takePhoto({
|
|
@@ -93,7 +143,7 @@ const photo = await capacitor.camera.takePhoto({
|
|
|
93
143
|
})
|
|
94
144
|
```
|
|
95
145
|
|
|
96
|
-
|
|
146
|
+
### Preferences
|
|
97
147
|
|
|
98
148
|
```ts
|
|
99
149
|
await capacitor.preferences.set('my_key', 'value')
|
|
@@ -103,11 +153,36 @@ await capacitor.preferences.remove('my_key')
|
|
|
103
153
|
|
|
104
154
|
`capacitor.preferences` は言語、テーマ、下書きなどの一般的な設定用です。認証 Token には `createCapacitorTokenStorage()` を使用してください。
|
|
105
155
|
|
|
106
|
-
###
|
|
156
|
+
### コンテナで Android APK をビルドする
|
|
157
|
+
|
|
158
|
+
Hai プロジェクトルートで `pnpm build --target app --platform android --runtime auto --debug` を実行します。`--debug` を省略すると署名付きリリースになります。成功したビルドは `.hai/build/app/android/<variant>/` に格納します。[ネイティブビルドと署名](../../docs/usage/getting-started/build.md#原生和离线条件)を参照してください。
|
|
159
|
+
|
|
160
|
+
ビルドイメージには Node.js 24、pnpm 12.11.2、JDK 21、Android SDK 36、Build Tools 35.0.0 と 36.0.0 が含まれます。ビルドは `linux/amd64` で実行します。APK の `arm64-v8a`、`armeabi-v7a` などの対象 ABI は、アプリの Gradle プロジェクトとネイティブプラグインで決まります。iOS のビルドには macOS と Xcode が必要です。
|
|
161
|
+
|
|
162
|
+
フレームワークのリポジトリルートでイメージをビルドします。このパッケージをインストールしたプロジェクトでは、`image_dir` にインストール先の `@h-ai/capacitor` にある `images` ディレクトリを指定できます。
|
|
163
|
+
|
|
164
|
+
```sh
|
|
165
|
+
image_dir=packages/capacitor/images
|
|
166
|
+
docker build --platform linux/amd64 -t hai-capacitor-android:36 \
|
|
167
|
+
-f "$image_dir/android.Dockerfile" "$image_dir"
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
アプリのプロジェクトルートでコンテナを起動します。クリーンな作業コピーを使い、コンテナ内で `pnpm install --frozen-lockfile` を実行してから、アプリが依存するローカルワークスペースパッケージをビルドします。続けてアプリのビルド、`cap sync android`、既存の APK パッケージングを実行します。APK はマウントしたプロジェクトに出力されます。rootless Podman では `docker` を `podman` に変更し、`--user` に加えて `--userns=keep-id` を指定します。
|
|
171
|
+
|
|
172
|
+
```sh
|
|
173
|
+
docker run --rm -it --platform linux/amd64 \
|
|
174
|
+
--user "$(id -u):$(id -g)" -e HOME=/tmp \
|
|
175
|
+
-e GRADLE_USER_HOME=/tmp/.gradle -e ANDROID_USER_HOME=/tmp/.android \
|
|
176
|
+
-v "$PWD:/workspace" hai-capacitor-android:36 bash
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
依存関係の取得には npm、Gradle の配布元、Google Maven、Maven Central へのアクセスが必要です。リリースにはアプリの署名手順を使用し、キーストアを読み取り専用でマウントして、パスワードをビルド環境から渡します。[mobile-app のビルドと受け入れ確認](../../apps/mobile-app/README.md#原生交付与验收)を参照してください。
|
|
180
|
+
|
|
181
|
+
## 設定
|
|
107
182
|
|
|
108
183
|
このモジュールに設定ファイルはありません。`capacitor.init()` は Capacitor の実行環境が利用可能かどうかのみを確認します。
|
|
109
184
|
|
|
110
|
-
|
|
185
|
+
ネイティブ WebView はクライアントアプリを読み込みます。SvelteKit は次の静的 SPA 設定を使い、純粋な Svelte/Vite テンプレートではこのルートファイルを使いません:
|
|
111
186
|
|
|
112
187
|
```ts
|
|
113
188
|
// src/routes/+layout.ts
|
|
@@ -115,9 +190,9 @@ export const prerender = true
|
|
|
115
190
|
export const ssr = false
|
|
116
191
|
```
|
|
117
192
|
|
|
118
|
-
|
|
193
|
+
## エラー処理
|
|
119
194
|
|
|
120
|
-
`
|
|
195
|
+
`init/close`、device/camera/push/statusBar/preferences と `safe*Preference` は `HaiResult<T>`、`getPlatform/isNative/isInitialized` は直接値を返します。TokenStorage のメソッドはプラットフォームや保存エラーで Promise を reject します。
|
|
121
196
|
|
|
122
197
|
```ts
|
|
123
198
|
import { capacitor, HaiCapacitorError } from '@h-ai/capacitor'
|
package/README.md
CHANGED
|
@@ -4,11 +4,73 @@
|
|
|
4
4
|
|
|
5
5
|
[框架首页](../../README.md) · 相关模块:[@h-ai/api-client](../api-client/README.md) · [@h-ai/core](../core/README.md)
|
|
6
6
|
|
|
7
|
-
## 简介
|
|
8
|
-
|
|
9
7
|
Capacitor 原生桥接模块,为 hai Framework 移动应用提供原生安全 Token 存储、设备信息、推送通知、相机与状态栏等能力的统一封装。
|
|
10
8
|
|
|
11
|
-
##
|
|
9
|
+
## 快速开始
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { capacitor } from '@h-ai/capacitor'
|
|
13
|
+
|
|
14
|
+
const initialized = await capacitor.init()
|
|
15
|
+
if (!initialized.success)
|
|
16
|
+
throw new Error(initialized.error.message)
|
|
17
|
+
const failures: unknown[] = []
|
|
18
|
+
try {
|
|
19
|
+
const saved = await capacitor.preferences.set('theme', 'dark')
|
|
20
|
+
if (!saved.success)
|
|
21
|
+
throw new Error(saved.error.message)
|
|
22
|
+
const theme = await capacitor.preferences.get('theme')
|
|
23
|
+
if (!theme.success)
|
|
24
|
+
throw new Error(theme.error.message)
|
|
25
|
+
// theme.data 为 'dark';尚未保存的键为 null。
|
|
26
|
+
}
|
|
27
|
+
catch (cause) {
|
|
28
|
+
failures.push(cause)
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
const closed = await capacitor.close()
|
|
32
|
+
if (!closed.success)
|
|
33
|
+
failures.push(closed.error)
|
|
34
|
+
}
|
|
35
|
+
if (failures.length)
|
|
36
|
+
throw new AggregateError(failures, 'Native preference operation failed')
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## 功能
|
|
40
|
+
|
|
41
|
+
### 用途、输入与输出
|
|
42
|
+
|
|
43
|
+
capacitor 的子功能在初始化成功后使用。HaiResult<T> 先判断 success,再读成功 data;void 没有业务数据,Promise 必须 await。tokens 是 createCapacitorTokenStorage() 返回的独立适配器,其读写/清理在原生存储失败时拒绝 Promise。独立 Preferences 辅助函数不依赖模块状态,把同一插件操作包装为 HaiResult。
|
|
44
|
+
|
|
45
|
+
| 入口 | 使用场景 | 输入(`—` 表示无参数) | 返回值 | 调用示例 |
|
|
46
|
+
| -------------------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
|
|
47
|
+
| `capacitor.init` | 检测并准备原生宿主 | `—` | `Promise<HaiResult<void>>` | `await capacitor.init()` |
|
|
48
|
+
| `capacitor.close` | 释放推送监听等资源 | `—` | `Promise<HaiResult<void>>` | `await capacitor.close()` |
|
|
49
|
+
| `capacitor.getPlatform` | 选择平台行为 | `—` | `string: ios / android / web` | `capacitor.getPlatform()` |
|
|
50
|
+
| `capacitor.isNative` | 判断是否原生环境 | `—` | `boolean` | `capacitor.isNative()` |
|
|
51
|
+
| `capacitor.isInitialized` | 检查模块状态 | `—` | `boolean` | `capacitor.isInitialized` |
|
|
52
|
+
| `capacitor.device.getInfo` | 显示设备诊断信息 | `—` | `Promise<HaiResult<{ platform, osVersion, model, manufacturer, isVirtual, appVersion?, appBuild? }>>` | `await capacitor.device.getInfo()` |
|
|
53
|
+
| `capacitor.device.getAppVersion` | 显示当前安装版本 | `—` | `Promise<HaiResult<{ version, build }>>` | `await capacitor.device.getAppVersion()` |
|
|
54
|
+
| `capacitor.camera.takePhoto` | 拍照或选择相册图片 | `{ source?: camera/photos/prompt, quality?: 0…100, width?, height?, resultType?: uri/base64/dataUrl }` | `Promise<HaiResult<{ data: string, format: string }>>` | `await capacitor.camera.takePhoto({ source: "photos", resultType: "dataUrl" })` |
|
|
55
|
+
| `capacitor.push.register` | 申请权限并获取推送标识 | `{ signal?: AbortSignal }` | `Promise<HaiResult<{ token: string }>>` | `await capacitor.push.register()` |
|
|
56
|
+
| `capacitor.push.listen` | 接收推送与点击事件 | `{ onReceived?, onActionPerformed? }, { signal? }` | `Promise<HaiResult<() => Promise<HaiResult<void>>>>` | `await capacitor.push.listen(callbacks, { signal })` |
|
|
57
|
+
| `capacitor.statusBar.configure` | 设置状态栏显示 | `{ style?: dark/light/default, backgroundColor?: color, overlay?: boolean }` | `Promise<HaiResult<void>>` | `await capacitor.statusBar.configure({ style: "dark", overlay: true })` |
|
|
58
|
+
| `capacitor.statusBar.show` | 显示状态栏 | `—` | `Promise<HaiResult<void>>` | `await capacitor.statusBar.show()` |
|
|
59
|
+
| `capacitor.statusBar.hide` | 隐藏状态栏 | `—` | `Promise<HaiResult<void>>` | `await capacitor.statusBar.hide()` |
|
|
60
|
+
| `capacitor.preferences.get` | 读取普通偏好 | `key: string` | `Promise<HaiResult<string / null>>` | `await capacitor.preferences.get("theme")` |
|
|
61
|
+
| `capacitor.preferences.set` | 保存普通偏好 | `key: string, value: string` | `Promise<HaiResult<void>>` | `await capacitor.preferences.set("theme", "dark")` |
|
|
62
|
+
| `capacitor.preferences.remove` | 删除普通偏好 | `key: string` | `Promise<HaiResult<void>>` | `await capacitor.preferences.remove("theme")` |
|
|
63
|
+
| `createCapacitorTokenStorage` | 创建原生安全凭据适配器 | `—` | `TokenStorage` | `createCapacitorTokenStorage()` |
|
|
64
|
+
| `tokens.getAccessToken` | 读取已保存的访问令牌 | `—` | `Promise<string / null>` | `await tokens.getAccessToken()` |
|
|
65
|
+
| `tokens.getRefreshToken` | 读取已保存的刷新令牌 | `—` | `Promise<string / null>` | `await tokens.getRefreshToken()` |
|
|
66
|
+
| `tokens.setAccessToken` | 保存访问令牌 | `token: string` | `Promise<void>` | `await tokens.setAccessToken(accessToken)` |
|
|
67
|
+
| `tokens.setRefreshToken` | 保存刷新令牌 | `token: string` | `Promise<void>` | `await tokens.setRefreshToken(refreshToken)` |
|
|
68
|
+
| `tokens.clear` | 删除本机两类凭据 | `—` | `Promise<void>` | `await tokens.clear()` |
|
|
69
|
+
| `safeGetPreference` | 独立读取非敏感偏好 | `key: string` | `Promise<HaiResult<string / null>>` | `await safeGetPreference("theme")` |
|
|
70
|
+
| `safeSetPreference` | 独立保存非敏感偏好 | `key: string, value: string` | `Promise<HaiResult<void>>` | `await safeSetPreference("theme", "dark")` |
|
|
71
|
+
| `safeRemovePreference` | 独立删除非敏感偏好 | `key: string` | `Promise<HaiResult<void>>` | `await safeRemovePreference("theme")` |
|
|
72
|
+
|
|
73
|
+
照片的 source 选择相机、相册或交互选择器;quality 是 0–100 的质量值,width/height 是像素约束。成功时 data 随 resultType 返回可显示的 webPath、裸 Base64 或带 MIME 前缀的 Data URL,format 是插件返回的图片格式。callbacks 的 onReceived/onActionPerformed 接收推送对象,signal 来自应用创建的 AbortController;成功 listen 的 data 是需要 await 并检查结果的清理函数。独立工厂与 Preferences 辅助函数从 @h-ai/capacitor 根入口导入。
|
|
12
74
|
|
|
13
75
|
- **Token 存储**:`@aparajita/capacitor-secure-storage`(原生必需)。Android 使用 KeyStore + 加密 SharedPreferences,iOS 使用 Keychain;Web 不回退。
|
|
14
76
|
- **Preferences**:`@capacitor/preferences`(必需)。通用偏好数据存储,适合非敏感配置,不用于认证 Token。
|
|
@@ -18,21 +80,7 @@ Capacitor 原生桥接模块,为 hai Framework 移动应用提供原生安全
|
|
|
18
80
|
- **相机/相册**:`@capacitor/camera`(可选)。拍照、选取相册图片。
|
|
19
81
|
- **状态栏**:`@capacitor/status-bar`(可选)。沉浸式、背景色、样式配置。
|
|
20
82
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
### 快速开始
|
|
24
|
-
|
|
25
|
-
```ts
|
|
26
|
-
import { capacitor } from '@h-ai/capacitor'
|
|
27
|
-
|
|
28
|
-
// 应用启动时初始化(检测 Capacitor 环境)
|
|
29
|
-
const result = await capacitor.init()
|
|
30
|
-
if (!result.success) {
|
|
31
|
-
// 非 Capacitor 环境(纯 Web)
|
|
32
|
-
}
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
#### 与 api-client 配合的 Token 安全存储
|
|
83
|
+
### 与 api-client 配合的 Token 安全存储
|
|
36
84
|
|
|
37
85
|
```ts
|
|
38
86
|
import { apiClient } from '@h-ai/api-client'
|
|
@@ -53,7 +101,9 @@ await apiClient.init({
|
|
|
53
101
|
|
|
54
102
|
任一凭据写入失败都会屏蔽整对凭据,因此 access 已保存而 refresh 保存失败时也不会留下可读的半完成会话。登录/注册保存失败后应尝试 `auth.clear()` 清理部分持久数据;清理再次失败须明确提示,不得声称设备凭据已删除。
|
|
55
103
|
|
|
56
|
-
|
|
104
|
+
同一模块中的全部 TokenStorage 实例共享原生变更队列。`clear()` 等待更早调用的写入完成再删除,避免迟到写入在清理成功后恢复旧凭据;之后显式调用的写入可保存新凭据。该顺序不跨独立 WebView/JavaScript 运行环境提供进程间协调。
|
|
105
|
+
|
|
106
|
+
### 设备信息与推送
|
|
57
107
|
|
|
58
108
|
```ts
|
|
59
109
|
import { capacitor } from '@h-ai/capacitor'
|
|
@@ -71,7 +121,7 @@ if (reg.success) {
|
|
|
71
121
|
|
|
72
122
|
推送仅在实际原生平台调用插件。`push.register({ signal? })` 与 `push.listen(callbacks, { signal? })` 的设置阶段限时 30 秒(包含权限和监听器挂载);失败、超时、取消及 `capacitor.close()` 都会停止回调并移除本次拥有的句柄,迟到的句柄在完成后立即移除,不调用影响其他消费者的 removeAllListeners。listen 成功后信号仍有效,也可调用返回的 cleanup。cleanup 与 close 均返回 `HaiResult<void>`;原生 remove 失败返回 `PUSH_CLEANUP_FAILED`(`capacitor:032`),失败句柄保留供再次调用重试,成功移除的句柄不重复操作。模块关闭后立即不可用;重新初始化前先重试遗留清理。自动化验证控制插件时序,不能代替 Android/iOS 真机 FCM/APNs 权限、注册与反复挂载验收。
|
|
73
123
|
|
|
74
|
-
|
|
124
|
+
### 状态栏
|
|
75
125
|
|
|
76
126
|
```ts
|
|
77
127
|
import { capacitor } from '@h-ai/capacitor'
|
|
@@ -83,7 +133,7 @@ await capacitor.statusBar.configure({
|
|
|
83
133
|
})
|
|
84
134
|
```
|
|
85
135
|
|
|
86
|
-
|
|
136
|
+
### 相机
|
|
87
137
|
|
|
88
138
|
```ts
|
|
89
139
|
const photo = await capacitor.camera.takePhoto({
|
|
@@ -93,7 +143,7 @@ const photo = await capacitor.camera.takePhoto({
|
|
|
93
143
|
})
|
|
94
144
|
```
|
|
95
145
|
|
|
96
|
-
|
|
146
|
+
### Preferences
|
|
97
147
|
|
|
98
148
|
```ts
|
|
99
149
|
await capacitor.preferences.set('my_key', 'value')
|
|
@@ -103,11 +153,36 @@ await capacitor.preferences.remove('my_key')
|
|
|
103
153
|
|
|
104
154
|
`capacitor.preferences` 适合保存语言、主题、草稿等普通偏好数据;认证 Token 请使用 `createCapacitorTokenStorage()`。
|
|
105
155
|
|
|
106
|
-
###
|
|
156
|
+
### Android 容器打包
|
|
157
|
+
|
|
158
|
+
在 Hai 工程根运行 `pnpm build --target app --platform android --runtime auto --debug`。省略 `--debug` 构建签名发布包,成功后在 `.hai/build/app/android/<构建类型>/` 查看最新批次。签名设置见 [原生和离线条件](../../docs/usage/getting-started/build.md#原生和离线条件)。
|
|
159
|
+
|
|
160
|
+
构建镜像提供 Node.js 24、pnpm 12.11.2、JDK 21、Android SDK 36 和 Build Tools 35.0.0 / 36.0.0。构建机使用 `linux/amd64`,APK 的 `arm64-v8a`、`armeabi-v7a` 等目标 ABI 由应用的 Gradle 工程及其原生插件决定。iOS 打包需要 macOS 和 Xcode。
|
|
161
|
+
|
|
162
|
+
从框架仓库根目录构建镜像。已安装本包的项目可将 `image_dir` 指向 `@h-ai/capacitor` 实际安装目录下的 `images`。
|
|
163
|
+
|
|
164
|
+
```sh
|
|
165
|
+
image_dir=packages/capacitor/images
|
|
166
|
+
docker build --platform linux/amd64 -t hai-capacitor-android:36 \
|
|
167
|
+
-f "$image_dir/android.Dockerfile" "$image_dir"
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
在待打包项目的根目录运行容器。使用干净的工作副本,在容器内执行 `pnpm install --frozen-lockfile`,先构建应用依赖的本地 workspace 包,再执行应用已有的构建、`cap sync android` 和 APK 打包命令。APK 写入挂载的工程目录。使用 rootless Podman 时,将 `docker` 换成 `podman`,在 `--user` 之外加上 `--userns=keep-id`。
|
|
171
|
+
|
|
172
|
+
```sh
|
|
173
|
+
docker run --rm -it --platform linux/amd64 \
|
|
174
|
+
--user "$(id -u):$(id -g)" -e HOME=/tmp \
|
|
175
|
+
-e GRADLE_USER_HOME=/tmp/.gradle -e ANDROID_USER_HOME=/tmp/.android \
|
|
176
|
+
-v "$PWD:/workspace" hai-capacitor-android:36 bash
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
构建下载依赖时需要访问 npm、Gradle 分发站点、Google Maven 和 Maven Central。发布包沿用应用的签名流程,密钥库从容器外只读挂载,口令由构建环境注入。应用示例见 [mobile-app 原生交付与验收](../../apps/mobile-app/README.md#原生交付与验收)。
|
|
180
|
+
|
|
181
|
+
## 配置
|
|
107
182
|
|
|
108
183
|
本模块无配置文件。`capacitor.init()` 仅检测 Capacitor 运行环境是否可用。
|
|
109
184
|
|
|
110
|
-
|
|
185
|
+
原生 WebView 运行客户端页面。若应用使用 SvelteKit,配置静态 SPA;纯 Svelte/Vite 模板不使用下面的路由文件:
|
|
111
186
|
|
|
112
187
|
```ts
|
|
113
188
|
// src/routes/+layout.ts
|
|
@@ -115,9 +190,9 @@ export const prerender = true
|
|
|
115
190
|
export const ssr = false
|
|
116
191
|
```
|
|
117
192
|
|
|
118
|
-
|
|
193
|
+
## 错误处理
|
|
119
194
|
|
|
120
|
-
`
|
|
195
|
+
`init/close`、device/camera/push/statusBar/preferences 业务操作与 `safe*Preference` 返回 `HaiResult<T>`;`getPlatform/isNative/isInitialized` 返回直接值。TokenStorage 适配器方法以 Promise 拒绝报告平台与存储错误:
|
|
121
196
|
|
|
122
197
|
```ts
|
|
123
198
|
import { capacitor, HaiCapacitorError } from '@h-ai/capacitor'
|
package/dist/index.d.ts
CHANGED
|
@@ -415,6 +415,7 @@ declare const capacitor: CapacitorFunctions;
|
|
|
415
415
|
* `clear()` 一开始即屏蔽本实例旧凭据,删除失败时仍保持屏蔽;相应 key 成功写入后恢复读取。
|
|
416
416
|
* 任一凭据写入失败也会屏蔽整对凭据,避免 access 已保存、refresh 失败时暴露半完成登录。
|
|
417
417
|
* 本实例屏蔽不代表原生持久数据已删除;跨重启撤销仍依赖服务端退出/撤销会话。
|
|
418
|
+
* 同模块内全部适配器的原生变更按调用顺序完成;clear 等待更早写入,之后的显式写入可建立新凭据。
|
|
418
419
|
*
|
|
419
420
|
* @returns TokenStorage 实例
|
|
420
421
|
* @throws 返回适配器的方法在插件不可用或原生存储失败时拒绝 Promise。
|
|
@@ -501,4 +502,5 @@ declare function safeSetPreference(key: string, value: string): Promise<HaiResul
|
|
|
501
502
|
*/
|
|
502
503
|
declare function safeRemovePreference(key: string): Promise<HaiResult<void>>;
|
|
503
504
|
|
|
504
|
-
export {
|
|
505
|
+
export { HaiCapacitorError, capacitor, createCapacitorTokenStorage, safeGetPreference, safeRemovePreference, safeSetPreference };
|
|
506
|
+
export type { CameraOperations, CapacitorFunctions, DeviceInfo, DeviceOperations, DevicePlatform, PhotoOptions, PhotoResult, PhotoSource, PreferencesOperations, PushCleanup, PushNotification, PushNotificationCallbacks, PushOperations, PushOptions, PushRegistration, StatusBarConfig, StatusBarOperations, StatusBarStyle };
|
package/dist/index.js
CHANGED
|
@@ -246,7 +246,10 @@ function createPushScope(options) {
|
|
|
246
246
|
controller.signal.addEventListener("abort", interrupt, { once: true });
|
|
247
247
|
});
|
|
248
248
|
try {
|
|
249
|
-
return await Promise.race([
|
|
249
|
+
return await Promise.race([
|
|
250
|
+
Promise.resolve().then(operation),
|
|
251
|
+
aborted
|
|
252
|
+
]);
|
|
250
253
|
} finally {
|
|
251
254
|
if (interrupt)
|
|
252
255
|
controller.signal.removeEventListener("abort", interrupt);
|
|
@@ -300,7 +303,10 @@ async function registerPush(options) {
|
|
|
300
303
|
scope.assertOpen();
|
|
301
304
|
await push.register();
|
|
302
305
|
};
|
|
303
|
-
return (await Promise.all([
|
|
306
|
+
return (await Promise.all([
|
|
307
|
+
setup(),
|
|
308
|
+
tokenResult
|
|
309
|
+
]))[1];
|
|
304
310
|
});
|
|
305
311
|
await scope.dispose();
|
|
306
312
|
return ok({ token });
|
|
@@ -317,14 +323,26 @@ async function listenPush(callbacks, options) {
|
|
|
317
323
|
scope.assertOpen();
|
|
318
324
|
if (callbacks.onReceived) {
|
|
319
325
|
await scope.attach(() => push.addListener("pushNotificationReceived", (notification) => {
|
|
320
|
-
if (!scope.signal.aborted)
|
|
321
|
-
callbacks.onReceived?.({
|
|
326
|
+
if (!scope.signal.aborted) {
|
|
327
|
+
callbacks.onReceived?.({
|
|
328
|
+
id: notification.id,
|
|
329
|
+
title: notification.title,
|
|
330
|
+
body: notification.body,
|
|
331
|
+
data: notification.data
|
|
332
|
+
});
|
|
333
|
+
}
|
|
322
334
|
}));
|
|
323
335
|
}
|
|
324
336
|
if (callbacks.onActionPerformed) {
|
|
325
337
|
await scope.attach(() => push.addListener("pushNotificationActionPerformed", (action) => {
|
|
326
|
-
if (!scope.signal.aborted)
|
|
327
|
-
callbacks.onActionPerformed?.({
|
|
338
|
+
if (!scope.signal.aborted) {
|
|
339
|
+
callbacks.onActionPerformed?.({
|
|
340
|
+
id: action.notification.id,
|
|
341
|
+
title: action.notification.title,
|
|
342
|
+
body: action.notification.body,
|
|
343
|
+
data: action.notification.data
|
|
344
|
+
});
|
|
345
|
+
}
|
|
328
346
|
}));
|
|
329
347
|
}
|
|
330
348
|
});
|
|
@@ -397,6 +415,13 @@ async function hideStatusBar() {
|
|
|
397
415
|
}
|
|
398
416
|
var PREF_ACCESS_TOKEN = "hai_access_token";
|
|
399
417
|
var PREF_REFRESH_TOKEN = "hai_refresh_token";
|
|
418
|
+
var credentialMutationTail = Promise.resolve();
|
|
419
|
+
function mutateCredentials(operation) {
|
|
420
|
+
const pending = credentialMutationTail.then(operation);
|
|
421
|
+
credentialMutationTail = pending.catch(() => {
|
|
422
|
+
});
|
|
423
|
+
return pending;
|
|
424
|
+
}
|
|
400
425
|
async function loadSecureStoragePlugin() {
|
|
401
426
|
const { Capacitor } = await import('@capacitor/core');
|
|
402
427
|
if (!Capacitor.isNativePlatform())
|
|
@@ -415,24 +440,27 @@ function createCapacitorTokenStorage() {
|
|
|
415
440
|
}
|
|
416
441
|
async function writeToken(key, token) {
|
|
417
442
|
const writeGeneration = credentialGeneration;
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
if (
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
443
|
+
await mutateCredentials(async () => {
|
|
444
|
+
try {
|
|
445
|
+
const storage = await loadSecureStoragePlugin();
|
|
446
|
+
await storage.setItem(key, token);
|
|
447
|
+
if (writeGeneration === credentialGeneration) {
|
|
448
|
+
if (key === PREF_ACCESS_TOKEN)
|
|
449
|
+
accessBlocked = false;
|
|
450
|
+
else
|
|
451
|
+
refreshBlocked = false;
|
|
452
|
+
}
|
|
453
|
+
} catch (error) {
|
|
454
|
+
if (writeGeneration === credentialGeneration)
|
|
455
|
+
blockCredentials();
|
|
456
|
+
throw error;
|
|
426
457
|
}
|
|
427
|
-
}
|
|
428
|
-
if (writeGeneration === credentialGeneration)
|
|
429
|
-
blockCredentials();
|
|
430
|
-
throw error;
|
|
431
|
-
}
|
|
458
|
+
});
|
|
432
459
|
}
|
|
433
460
|
return {
|
|
434
461
|
async getAccessToken() {
|
|
435
462
|
const readGeneration = credentialGeneration;
|
|
463
|
+
await credentialMutationTail;
|
|
436
464
|
const storage = await loadSecureStoragePlugin();
|
|
437
465
|
if (accessBlocked)
|
|
438
466
|
return null;
|
|
@@ -441,6 +469,7 @@ function createCapacitorTokenStorage() {
|
|
|
441
469
|
},
|
|
442
470
|
async getRefreshToken() {
|
|
443
471
|
const readGeneration = credentialGeneration;
|
|
472
|
+
await credentialMutationTail;
|
|
444
473
|
const storage = await loadSecureStoragePlugin();
|
|
445
474
|
if (refreshBlocked)
|
|
446
475
|
return null;
|
|
@@ -455,14 +484,16 @@ function createCapacitorTokenStorage() {
|
|
|
455
484
|
},
|
|
456
485
|
async clear() {
|
|
457
486
|
blockCredentials();
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
487
|
+
await mutateCredentials(async () => {
|
|
488
|
+
const storage = await loadSecureStoragePlugin();
|
|
489
|
+
const results = await Promise.allSettled([
|
|
490
|
+
storage.removeItem(PREF_ACCESS_TOKEN),
|
|
491
|
+
storage.removeItem(PREF_REFRESH_TOKEN)
|
|
492
|
+
]);
|
|
493
|
+
const errors = results.filter((result) => result.status === "rejected").map((result) => result.reason);
|
|
494
|
+
if (errors.length > 0)
|
|
495
|
+
throw new AggregateError(errors, capacitorM("capacitor_secureStorageClearFailed"));
|
|
496
|
+
});
|
|
466
497
|
}
|
|
467
498
|
};
|
|
468
499
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../messages/en-US.json","../messages/ja-JP.json","../messages/zh-CN.json","../src/capacitor-i18n.ts","../src/capacitor-types.ts","../src/capacitor-camera.ts","../src/capacitor-device.ts","../src/capacitor-push.ts","../src/capacitor-status-bar.ts","../src/capacitor-token-storage.ts","../src/capacitor-main.ts"],"names":["core","ok","err","logger"],"mappings":";;;;;;AAAA,IAAA,aAAA,GAAA;AAAA,EACE,oBAAA,EAAwB,iCAAA;AAAA,EACxB,wBAAA,EAA4B,iDAAA;AAAA,EAC5B,sBAAA,EAA0B,gDAAA;AAAA,EAC1B,kCAAA,EAAsC,kFAAA;AAAA,EACtC,8BAAA,EAAkC,2CAAA;AAAA,EAClC,8BAAA,EAAkC,0CAAA;AAAA,EAClC,iCAAA,EAAqC,6CAAA;AAAA,EACrC,0BAAA,EAA8B,2BAAA;AAAA,EAC9B,0BAAA,EAA8B,2BAAA;AAAA,EAC9B,4BAAA,EAAgC,uCAAA;AAAA,EAChC,0BAAA,EAA8B,yCAAA;AAAA,EAC9B,sBAAA,EAA0B,yBAAA;AAAA,EAC1B,yBAAA,EAA6B,gCAAA;AAAA,EAC7B,wBAAA,EAA4B,qCAAA;AAAA,EAC5B,uBAAA,EAA2B,sEAAA;AAAA,EAC3B,2BAAA,EAA+B;AACjC,CAAA;;;ACjBA,IAAA,aAAA,GAAA;AAAA,EACE,oBAAA,EAAwB,6EAAA;AAAA,EACxB,wBAAA,EAA4B,mFAAA;AAAA,EAC5B,sBAAA,EAA0B,+FAAA;AAAA,EAC1B,kCAAA,EAAsC,4RAAA;AAAA,EACtC,8BAAA,EAAkC,2GAAA;AAAA,EAClC,8BAAA,EAAkC,qGAAA;AAAA,EAClC,iCAAA,EAAqC,+FAAA;AAAA,EACrC,0BAAA,EAA8B,kGAAA;AAAA,EAC9B,0BAAA,EAA8B,oHAAA;AAAA,EAC9B,4BAAA,EAAgC,kGAAA;AAAA,EAChC,0BAAA,EAA8B,8GAAA;AAAA,EAC9B,sBAAA,EAA0B,kGAAA;AAAA,EAC1B,yBAAA,EAA6B,wGAAA;AAAA,EAC7B,wBAAA,EAA4B,2GAAA;AAAA,EAC5B,uBAAA,EAA2B,4OAAA;AAAA,EAC3B,2BAAA,EAA+B;AACjC,CAAA;;;ACjBA,IAAA,aAAA,GAAA;AAAA,EACE,oBAAA,EAAwB,0CAAA;AAAA,EACxB,wBAAA,EAA4B,gDAAA;AAAA,EAC5B,sBAAA,EAA0B,sDAAA;AAAA,EAC1B,kCAAA,EAAsC,sIAAA;AAAA,EACtC,8BAAA,EAAkC,iDAAA;AAAA,EAClC,8BAAA,EAAkC,iDAAA;AAAA,EAClC,iCAAA,EAAqC,iDAAA;AAAA,EACrC,0BAAA,EAA8B,kDAAA;AAAA,EAC9B,0BAAA,EAA8B,kDAAA;AAAA,EAC9B,4BAAA,EAAgC,kDAAA;AAAA,EAChC,0BAAA,EAA8B,kDAAA;AAAA,EAC9B,sBAAA,EAA0B,sCAAA;AAAA,EAC1B,yBAAA,EAA6B,4CAAA;AAAA,EAC7B,wBAAA,EAA4B,gDAAA;AAAA,EAC5B,uBAAA,EAA2B,kGAAA;AAAA,EAC3B,2BAAA,EAA+B;AACjC,CAAA;;;ACiBO,IAAM,UAAA,GAAa,IAAA,CAAK,IAAA,CAAK,mBAAA,CAAyC;AAAA,EAC3E,OAAA,EAAS,aAAA;AAAA,EACT,OAAA,EAAS,aAAA;AAAA,EACT,OAAA,EAAS;AACX,CAAC,CAAA;AC5BD,IAAM,kBAAA,GAAqB;AAAA,EACzB,WAAA,EAAa,SAAA;AAAA,EACb,aAAA,EAAe,SAAA;AAAA,EACf,gBAAA,EAAkB,SAAA;AAAA,EAClB,eAAA,EAAiB,SAAA;AAAA,EACjB,sBAAA,EAAwB,SAAA;AAAA,EACxB,sBAAA,EAAwB,SAAA;AAAA,EACxB,yBAAA,EAA2B,SAAA;AAAA,EAC3B,kBAAA,EAAoB,SAAA;AAAA,EACpB,kBAAA,EAAoB,SAAA;AAAA,EACpB,oBAAA,EAAsB,SAAA;AAAA,EACtB,kBAAA,EAAoB,SAAA;AAAA,EACpB,mBAAA,EAAqB,SAAA;AAAA,EACrB,aAAA,EAAe,SAAA;AAAA,EACf,iBAAA,EAAmB;AACrB,CAAA;AAaO,IAAM,iBAAA,GAAoBA,IAAAA,CAAK,KAAA,CAAM,iBAAA,CAAkB,aAAa,kBAAkB;;;ACvB7F,IAAM,UAAA,GAAa;AAAA,EACjB,MAAA,EAAQ,QAAA;AAAA,EACR,MAAA,EAAQ,QAAA;AAAA,EACR,MAAA,EAAQ;AACV,CAAA;AAGA,IAAM,eAAA,GAAkB;AAAA,EACtB,GAAA,EAAK,KAAA;AAAA,EACL,MAAA,EAAQ,QAAA;AAAA,EACR,OAAA,EAAS;AACX,CAAA;AAyBA,eAAsB,UAAU,OAAA,EAAyD;AACvF,EAAA,IAAI;AACF,IAAA,MAAM,EAAE,MAAA,EAAQ,YAAA,EAAc,kBAAiB,GAAI,MAAM,OAAO,mBAAmB,CAAA;AAEnF,IAAA,MAAM,MAAA,GAAS,SAAS,MAAA,IAAU,QAAA;AAClC,IAAA,MAAM,UAAA,GAAa,SAAS,UAAA,IAAc,KAAA;AAE1C,IAAA,MAAM,KAAA,GAAQ,MAAM,MAAA,CAAO,QAAA,CAAS;AAAA,MAClC,OAAA,EAAS,SAAS,OAAA,IAAW,EAAA;AAAA,MAC7B,MAAA,EAAQ,YAAA,CAAa,UAAA,CAAW,MAAM,CAA8B,CAAA;AAAA,MACpE,UAAA,EAAY,gBAAA,CAAiB,eAAA,CAAgB,UAAU,CAAkC,CAAA;AAAA,MACzF,OAAO,OAAA,EAAS,KAAA;AAAA,MAChB,QAAQ,OAAA,EAAS;AAAA,KAClB,CAAA;AAGD,IAAA,IAAI,IAAA;AACJ,IAAA,IAAI,eAAe,QAAA,EAAU;AAC3B,MAAA,IAAA,GAAO,MAAM,YAAA,IAAgB,EAAA;AAAA,IAC/B,CAAA,MAAA,IACS,eAAe,SAAA,EAAW;AACjC,MAAA,IAAA,GAAO,MAAM,OAAA,IAAW,EAAA;AAAA,IAC1B,CAAA,MACK;AACH,MAAA,IAAA,GAAO,MAAM,OAAA,IAAW,EAAA;AAAA,IAC1B;AAEA,IAAA,OAAO,EAAA,CAAG;AAAA,MACR,IAAA;AAAA,MACA,QAAQ,KAAA,CAAM;AAAA,KACf,CAAA;AAAA,EACH,SACO,KAAA,EAAO;AACZ,IAAA,OAAO,GAAA;AAAA,MACL,iBAAA,CAAkB,aAAA;AAAA,MAClB,WAAW,wBAAwB,CAAA;AAAA,MACnC;AAAA,KACF;AAAA,EACF;AACF;ACtDA,eAAsB,aAAA,GAAgD;AACpE,EAAA,IAAI;AACF,IAAA,MAAM,EAAE,MAAA,EAAO,GAAI,MAAM,OAAO,mBAAmB,CAAA;AACnD,IAAA,MAAM,IAAA,GAAO,MAAM,MAAA,CAAO,OAAA,EAAQ;AAElC,IAAA,OAAOC,EAAAA,CAAG;AAAA,MACR,UAAU,IAAA,CAAK,QAAA;AAAA,MACf,WAAW,IAAA,CAAK,SAAA;AAAA,MAChB,OAAO,IAAA,CAAK,KAAA;AAAA,MACZ,cAAc,IAAA,CAAK,YAAA;AAAA,MACnB,WAAW,IAAA,CAAK;AAAA,KACjB,CAAA;AAAA,EACH,SACO,KAAA,EAAO;AACZ,IAAA,OAAOC,GAAAA;AAAA,MACL,iBAAA,CAAkB,kBAAA;AAAA,MAClB,WAAW,4BAA4B,CAAA;AAAA,MACvC;AAAA,KACF;AAAA,EACF;AACF;AAsBA,eAAsB,aAAA,GAAwE;AAC5F,EAAA,IAAI;AACF,IAAA,MAAM,EAAE,GAAA,EAAI,GAAI,MAAM,OAAO,gBAAgB,CAAA;AAC7C,IAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAAI,OAAA,EAAQ;AAE/B,IAAA,OAAOD,EAAAA,CAAG;AAAA,MACR,SAAS,IAAA,CAAK,OAAA;AAAA,MACd,OAAO,IAAA,CAAK;AAAA,KACb,CAAA;AAAA,EACH,SACO,KAAA,EAAO;AACZ,IAAA,OAAOC,GAAAA;AAAA,MACL,iBAAA,CAAkB,kBAAA;AAAA,MAClB,WAAW,4BAA4B,CAAA;AAAA,MACvC;AAAA,KACF;AAAA,EACF;AACF;ACpFA,IAAM,MAAA,GAASF,KAAK,MAAA,CAAO,KAAA,CAAM,EAAE,MAAA,EAAQ,WAAA,EAAa,KAAA,EAAO,MAAA,EAAQ,CAAA;AAyBvE,IAAM,YAAA,uBAAmB,GAAA,EAAe;AAGxC,SAAS,gBAAgB,OAAA,EAAkC;AACzD,EAAA,IAAI,KAAA;AACJ,EAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,EAAA,MAAM,OAAA,uBAAc,GAAA,EAA0B;AAC9C,EAAA,MAAM,QAAA,uBAAe,GAAA,EAAyC;AAC9D,EAAA,IAAI,OAAA,GAAU,CAAA;AACd,EAAA,IAAI,OAAA;AACJ,EAAA,IAAI,KAAA;AACJ,EAAA,MAAM,YAAY,MAAM,IAAI,KAAA,CAAM,UAAA,CAAW,yBAAyB,CAAC,CAAA;AAEvE,EAAA,SAAS,iBAAA,GAA0B;AACjC,IAAA,IAAI,WAAW,MAAA,CAAO,OAAA,IAAW,OAAA,KAAY,CAAA,IAAK,QAAQ,IAAA,KAAS,CAAA;AACjE,MAAA,YAAA,CAAa,OAAO,KAAK,CAAA;AAAA,EAC7B;AAEA,EAAA,SAAS,OAAO,MAAA,EAA6C;AAC3D,IAAA,MAAM,OAAA,GAAU,QAAA,CAAS,GAAA,CAAI,MAAM,CAAA;AACnC,IAAA,IAAI,OAAA;AACF,MAAA,OAAO,OAAA;AACT,IAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,OAAA,EAAQ,CAAE,IAAA,CAAK,MAAM,MAAA,CAAO,MAAA,EAAQ,CAAA,CAAE,IAAA,CAAK,MAAM;AACvE,MAAA,OAAA,CAAQ,OAAO,MAAM,CAAA;AAAA,IACvB,CAAC,CAAA,CAAE,OAAA,CAAQ,MAAM;AACf,MAAA,QAAA,CAAS,OAAO,MAAM,CAAA;AACtB,MAAA,iBAAA,EAAkB;AAAA,IACpB,CAAC,CAAA;AACD,IAAA,QAAA,CAAS,GAAA,CAAI,QAAQ,OAAO,CAAA;AAC5B,IAAA,OAAO,OAAA;AAAA,EACT;AAEA,EAAA,SAAS,WAAA,GAAoB;AAC3B,IAAA,IAAI,UAAU,MAAA,EAAW;AACvB,MAAA,YAAA,CAAa,KAAK,CAAA;AAClB,MAAA,KAAA,GAAQ,MAAA;AAAA,IACV;AAAA,EACF;AAEA,EAAA,SAAS,OAAA,GAAyB;AAChC,IAAA,UAAA,CAAW,KAAA,EAAM;AACjB,IAAA,WAAA,EAAY;AACZ,IAAA,OAAA,EAAS,MAAA,EAAQ,mBAAA,CAAoB,OAAA,EAAS,OAAO,CAAA;AACrD,IAAA,IAAI,OAAA;AACF,MAAA,OAAO,OAAA;AACT,IAAA,OAAA,GAAU,OAAA,CAAQ,UAAA,CAAW,CAAC,GAAG,OAAO,CAAA,CAAE,GAAA,CAAI,MAAM,CAAC,CAAA,CAAE,IAAA,CAAK,CAAC,OAAA,KAAY;AACvE,MAAA,iBAAA,EAAkB;AAClB,MAAA,IAAI,OAAA,CAAQ,IAAA,CAAK,CAAA,MAAA,KAAU,MAAA,CAAO,WAAW,UAAU,CAAA;AACrD,QAAA,MAAM,IAAI,KAAA,CAAM,UAAA,CAAW,6BAA6B,CAAC,CAAA;AAAA,IAC7D,CAAC,CAAA,CAAE,OAAA,CAAQ,MAAM;AAAE,MAAA,OAAA,GAAU,MAAA;AAAA,IAAU,CAAC,CAAA;AACxC,IAAA,OAAO,OAAA;AAAA,EACT;AAEA,EAAA,SAAS,OAAA,GAAgB;AACvB,IAAA,KAAK,SAAQ,CAAE,KAAA,CAAM,MAAM,MAAA,CAAO,IAAA,CAAK,gEAAgE,CAAC,CAAA;AAAA,EAC1G;AAEA,EAAA,KAAA,GAAQ;AAAA,IACN,QAAQ,UAAA,CAAW,MAAA;AAAA,IACnB,UAAA,GAAa;AACX,MAAA,IAAI,WAAW,MAAA,CAAO,OAAA;AACpB,QAAA,MAAM,SAAA,EAAU;AAAA,IACpB,CAAA;AAAA,IACA,MAAM,OAAO,GAAA,EAAK;AAChB,MAAA,KAAA,CAAM,UAAA,EAAW;AACjB,MAAA,OAAA,EAAA;AACA,MAAA,IAAI;AACF,QAAA,MAAM,MAAA,GAAS,MAAM,GAAA,EAAI;AACzB,QAAA,OAAA,CAAQ,IAAI,MAAM,CAAA;AAClB,QAAA,IAAI,UAAA,CAAW,OAAO,OAAA,EAAS;AAC7B,UAAA,MAAM,OAAO,MAAM,CAAA;AACnB,UAAA,MAAM,SAAA,EAAU;AAAA,QAClB;AAAA,MACF,CAAA,SACA;AACE,QAAA,OAAA,EAAA;AACA,QAAA,iBAAA,EAAkB;AAAA,MACpB;AAAA,IACF,CAAA;AAAA,IACA,MAAM,KAAK,SAAA,EAAW;AACpB,MAAA,KAAA,CAAM,UAAA,EAAW;AACjB,MAAA,IAAI,SAAA;AACJ,MAAA,MAAM,OAAA,GAAU,IAAI,OAAA,CAAe,CAAC,GAAG,MAAA,KAAW;AAChD,QAAA,SAAA,GAAY,MAAM,MAAA,CAAO,SAAA,EAAW,CAAA;AACpC,QAAA,UAAA,CAAW,OAAO,gBAAA,CAAiB,OAAA,EAAS,WAAW,EAAE,IAAA,EAAM,MAAM,CAAA;AAAA,MACvE,CAAC,CAAA;AACD,MAAA,IAAI;AACF,QAAA,OAAO,MAAM,OAAA,CAAQ,IAAA,CAAK,CAAC,OAAA,CAAQ,OAAA,EAAQ,CAAE,IAAA,CAAK,SAAS,CAAA,EAAG,OAAO,CAAC,CAAA;AAAA,MACxE,CAAA,SACA;AACE,QAAA,IAAI,SAAA;AACF,UAAA,UAAA,CAAW,MAAA,CAAO,mBAAA,CAAoB,OAAA,EAAS,SAAS,CAAA;AAAA,MAC5D;AAAA,IACF,CAAA;AAAA,IACA,WAAA;AAAA,IACA;AAAA,GACF;AACA,EAAA,YAAA,CAAa,IAAI,KAAK,CAAA;AACtB,EAAA,KAAA,GAAQ,UAAA,CAAW,SAAS,GAAM,CAAA;AAClC,EAAA,OAAA,EAAS,QAAQ,gBAAA,CAAiB,OAAA,EAAS,SAAS,EAAE,IAAA,EAAM,MAAM,CAAA;AAClE,EAAA,IAAI,SAAS,MAAA,EAAQ,OAAA;AACnB,IAAA,OAAA,EAAQ;AACV,EAAA,OAAO,KAAA;AACT;AAGA,eAAe,cAAA,GAAiB;AAC9B,EAAA,MAAM,EAAE,SAAA,EAAU,GAAI,MAAM,OAAO,iBAAiB,CAAA;AACpD,EAAA,IAAI,CAAC,UAAU,gBAAA,EAAiB;AAC9B,IAAA,MAAM,IAAI,KAAA,CAAM,UAAA,CAAW,wBAAwB,CAAC,CAAA;AACtD,EAAA,OAAA,CAAQ,MAAM,OAAO,+BAA+B,CAAA,EAAG,iBAAA;AACzD;AAcA,eAAsB,kBAAA,GAA+C;AACnE,EAAA,MAAM,OAAA,GAAU,MAAM,OAAA,CAAQ,UAAA,CAAW,CAAC,GAAG,YAAY,CAAA,CAAE,GAAA,CAAI,CAAA,KAAA,KAAS,KAAA,CAAM,OAAA,EAAS,CAAC,CAAA;AACxF,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,CAAA,MAAA,KAAU,MAAA,CAAO,WAAW,UAAU,CAAA,GACtDE,GAAAA,CAAI,iBAAA,CAAkB,qBAAqB,UAAA,CAAW,6BAA6B,CAAC,CAAA,GACpFD,GAAG,MAAS,CAAA;AAClB;AAuBA,eAAsB,aAAa,OAAA,EAA6D;AAC9F,EAAA,MAAM,KAAA,GAAQ,gBAAgB,OAAO,CAAA;AACrC,EAAA,IAAI;AACF,IAAA,MAAM,KAAA,GAAQ,MAAM,KAAA,CAAM,IAAA,CAAK,YAAY;AACzC,MAAA,MAAM,IAAA,GAAO,MAAM,cAAA,EAAe;AAClC,MAAA,KAAA,CAAM,UAAA,EAAW;AACjB,MAAA,MAAM,UAAA,GAAa,MAAM,IAAA,CAAK,kBAAA,EAAmB;AACjD,MAAA,KAAA,CAAM,UAAA,EAAW;AACjB,MAAA,IAAI,WAAW,OAAA,KAAY,SAAA;AACzB,QAAA,MAAM,IAAI,KAAA,CAAM,UAAA,CAAW,8BAA8B,CAAC,CAAA;AAC5D,MAAA,IAAI,YAAA;AACJ,MAAA,IAAI,WAAA;AACJ,MAAA,MAAM,WAAA,GAAc,IAAI,OAAA,CAAgB,CAAC,SAAS,MAAA,KAAW;AAC3D,QAAA,YAAA,GAAe,OAAA;AACf,QAAA,WAAA,GAAc,MAAA;AAAA,MAChB,CAAC,CAAA;AAED,MAAA,MAAM,QAAQ,YAAY;AACxB,QAAA,MAAM,MAAM,MAAA,CAAO,MAAM,KAAK,WAAA,CAAY,cAAA,EAAgB,CAAC,MAAA,KAAW;AACpE,UAAA,IAAI,CAAC,MAAM,MAAA,CAAO,OAAA;AAChB,YAAA,YAAA,CAAa,OAAO,KAAK,CAAA;AAAA,QAC7B,CAAC,CAAC,CAAA;AACF,QAAA,MAAM,MAAM,MAAA,CAAO,MAAM,IAAA,CAAK,WAAA,CAAY,qBAAqB,MAAM;AACnE,UAAA,IAAI,CAAC,MAAM,MAAA,CAAO,OAAA;AAChB,YAAA,WAAA,CAAY,IAAI,KAAA,CAAM,UAAA,CAAW,8BAA8B,CAAC,CAAC,CAAA;AAAA,QACrE,CAAC,CAAC,CAAA;AACF,QAAA,KAAA,CAAM,UAAA,EAAW;AACjB,QAAA,MAAM,KAAK,QAAA,EAAS;AAAA,MACtB,CAAA;AACA,MAAA,OAAA,CAAQ,MAAM,QAAQ,GAAA,CAAI,CAAC,OAAM,EAAG,WAAW,CAAC,CAAA,EAAG,CAAC,CAAA;AAAA,IACtD,CAAC,CAAA;AACD,IAAA,MAAM,MAAM,OAAA,EAAQ;AACpB,IAAA,OAAOA,EAAAA,CAAG,EAAE,KAAA,EAAO,CAAA;AAAA,EACrB,SACO,KAAA,EAAO;AACZ,IAAA,MAAM,KAAA,CAAM,SAAQ,CAAE,KAAA,CAAM,MAAM,MAAA,CAAO,IAAA,CAAK,yDAAyD,CAAC,CAAA;AACxG,IAAA,OAAOC,IAAI,iBAAA,CAAkB,oBAAA,EAAsB,UAAA,CAAW,8BAA8B,GAAG,KAAK,CAAA;AAAA,EACtG;AACF;AAwBA,eAAsB,UAAA,CAAW,WAAsC,OAAA,EAAwD;AAC7H,EAAA,MAAM,KAAA,GAAQ,gBAAgB,OAAO,CAAA;AACrC,EAAA,IAAI;AACF,IAAA,MAAM,KAAA,CAAM,KAAK,YAAY;AAC3B,MAAA,MAAM,IAAA,GAAO,MAAM,cAAA,EAAe;AAClC,MAAA,KAAA,CAAM,UAAA,EAAW;AACjB,MAAA,IAAI,UAAU,UAAA,EAAY;AACxB,QAAA,MAAM,MAAM,MAAA,CAAO,MAAM,KAAK,WAAA,CAAY,0BAAA,EAA4B,CAAC,YAAA,KAAiB;AACtF,UAAA,IAAI,CAAC,MAAM,MAAA,CAAO,OAAA;AAChB,YAAA,SAAA,CAAU,UAAA,GAAa,EAAE,EAAA,EAAI,YAAA,CAAa,IAAI,KAAA,EAAO,YAAA,CAAa,KAAA,EAAO,IAAA,EAAM,YAAA,CAAa,IAAA,EAAM,IAAA,EAAM,YAAA,CAAa,MAAiC,CAAA;AAAA,QAC1J,CAAC,CAAC,CAAA;AAAA,MACJ;AACA,MAAA,IAAI,UAAU,iBAAA,EAAmB;AAC/B,QAAA,MAAM,MAAM,MAAA,CAAO,MAAM,KAAK,WAAA,CAAY,iCAAA,EAAmC,CAAC,MAAA,KAAW;AACvF,UAAA,IAAI,CAAC,MAAM,MAAA,CAAO,OAAA;AAChB,YAAA,SAAA,CAAU,oBAAoB,EAAE,EAAA,EAAI,OAAO,YAAA,CAAa,EAAA,EAAI,OAAO,MAAA,CAAO,YAAA,CAAa,KAAA,EAAO,IAAA,EAAM,OAAO,YAAA,CAAa,IAAA,EAAM,MAAM,MAAA,CAAO,YAAA,CAAa,MAAiC,CAAA;AAAA,QAC7L,CAAC,CAAC,CAAA;AAAA,MACJ;AAAA,IACF,CAAC,CAAA;AACD,IAAA,KAAA,CAAM,UAAA,EAAW;AACjB,IAAA,KAAA,CAAM,WAAA,EAAY;AAClB,IAAA,OAAOD,GAAG,YAAY;AACpB,MAAA,IAAI;AACF,QAAA,MAAM,MAAM,OAAA,EAAQ;AACpB,QAAA,OAAOA,GAAG,KAAA,CAAS,CAAA;AAAA,MACrB,SACO,KAAA,EAAO;AACZ,QAAA,OAAOC,IAAI,iBAAA,CAAkB,mBAAA,EAAqB,UAAA,CAAW,6BAA6B,GAAG,KAAK,CAAA;AAAA,MACpG;AAAA,IACF,CAAC,CAAA;AAAA,EACH,SACO,KAAA,EAAO;AACZ,IAAA,MAAM,KAAA,CAAM,SAAQ,CAAE,KAAA,CAAM,MAAM,MAAA,CAAO,IAAA,CAAK,kDAAkD,CAAC,CAAA;AACjG,IAAA,OAAOA,IAAI,iBAAA,CAAkB,kBAAA,EAAoB,UAAA,CAAW,4BAA4B,GAAG,KAAK,CAAA;AAAA,EAClG;AACF;ACzPA,eAAsB,mBAAmB,MAAA,EAAmD;AAC1F,EAAA,IAAI;AACF,IAAA,MAAM,EAAE,SAAA,EAAW,KAAA,EAAM,GAAI,MAAM,OAAO,uBAAuB,CAAA;AAGjE,IAAA,IAAI,OAAO,KAAA,EAAO;AAChB,MAAA,MAAM,QAAA,GAA6D;AAAA,QACjE,MAAM,KAAA,CAAM,IAAA;AAAA,QACZ,OAAO,KAAA,CAAM,KAAA;AAAA,QACb,SAAS,KAAA,CAAM;AAAA,OACjB;AACA,MAAA,MAAM,SAAA,CAAU,QAAA,CAAS,EAAE,KAAA,EAAO,QAAA,CAAS,OAAO,KAAK,CAAA,IAAK,KAAA,CAAM,OAAA,EAAS,CAAA;AAAA,IAC7E;AAGA,IAAA,IAAI,OAAO,eAAA,EAAiB;AAC1B,MAAA,MAAM,UAAU,kBAAA,CAAmB,EAAE,KAAA,EAAO,MAAA,CAAO,iBAAiB,CAAA;AAAA,IACtE;AAGA,IAAA,IAAI,MAAA,CAAO,YAAY,KAAA,CAAA,EAAW;AAChC,MAAA,MAAM,UAAU,kBAAA,CAAmB,EAAE,OAAA,EAAS,MAAA,CAAO,SAAS,CAAA;AAAA,IAChE;AAEA,IAAA,OAAOD,GAAG,KAAA,CAAS,CAAA;AAAA,EACrB,SACO,KAAA,EAAO;AACZ,IAAA,OAAOC,GAAAA;AAAA,MACL,iBAAA,CAAkB,iBAAA;AAAA,MAClB,WAAW,2BAA2B,CAAA;AAAA,MACtC;AAAA,KACF;AAAA,EACF;AACF;AAsBA,eAAsB,aAAA,GAA0C;AAC9D,EAAA,IAAI;AACF,IAAA,MAAM,EAAE,SAAA,EAAU,GAAI,MAAM,OAAO,uBAAuB,CAAA;AAC1D,IAAA,MAAM,UAAU,IAAA,EAAK;AACrB,IAAA,OAAOD,GAAG,KAAA,CAAS,CAAA;AAAA,EACrB,SACO,KAAA,EAAO;AACZ,IAAA,OAAOC,GAAAA;AAAA,MACL,iBAAA,CAAkB,iBAAA;AAAA,MAClB,WAAW,2BAA2B,CAAA;AAAA,MACtC;AAAA,KACF;AAAA,EACF;AACF;AAsBA,eAAsB,aAAA,GAA0C;AAC9D,EAAA,IAAI;AACF,IAAA,MAAM,EAAE,SAAA,EAAU,GAAI,MAAM,OAAO,uBAAuB,CAAA;AAC1D,IAAA,MAAM,UAAU,IAAA,EAAK;AACrB,IAAA,OAAOD,GAAG,KAAA,CAAS,CAAA;AAAA,EACrB,SACO,KAAA,EAAO;AACZ,IAAA,OAAOC,GAAAA;AAAA,MACL,iBAAA,CAAkB,iBAAA;AAAA,MAClB,WAAW,2BAA2B,CAAA;AAAA,MACtC;AAAA,KACF;AAAA,EACF;AACF;AC1HA,IAAM,iBAAA,GAAoB,kBAAA;AAC1B,IAAM,kBAAA,GAAqB,mBAAA;AAG3B,eAAe,uBAAA,GAAwD;AACrE,EAAA,MAAM,EAAE,SAAA,EAAU,GAAI,MAAM,OAAO,iBAAiB,CAAA;AACpD,EAAA,IAAI,CAAC,UAAU,gBAAA,EAAiB;AAC9B,IAAA,MAAM,IAAI,KAAA,CAAM,UAAA,CAAW,wBAAwB,CAAC,CAAA;AACtD,EAAA,MAAM,EAAE,aAAA,EAAc,GAAI,MAAM,OAAO,qCAAqC,CAAA;AAC5E,EAAA,OAAO,aAAA;AACT;AA4BO,SAAS,2BAAA,GAA4C;AAC1D,EAAA,IAAI,oBAAA,GAAuB,CAAA;AAC3B,EAAA,IAAI,aAAA,GAAgB,KAAA;AACpB,EAAA,IAAI,cAAA,GAAiB,KAAA;AAGrB,EAAA,SAAS,gBAAA,GAAyB;AAChC,IAAA,oBAAA,EAAA;AACA,IAAA,aAAA,GAAgB,IAAA;AAChB,IAAA,cAAA,GAAiB,IAAA;AAAA,EACnB;AAGA,EAAA,eAAe,UAAA,CAAW,KAA2D,KAAA,EAA8B;AACjH,IAAA,MAAM,eAAA,GAAkB,oBAAA;AACxB,IAAA,IAAI;AACF,MAAA,MAAM,OAAA,GAAU,MAAM,uBAAA,EAAwB;AAC9C,MAAA,MAAM,OAAA,CAAQ,OAAA,CAAQ,GAAA,EAAK,KAAK,CAAA;AAChC,MAAA,IAAI,oBAAoB,oBAAA,EAAsB;AAC5C,QAAA,IAAI,GAAA,KAAQ,iBAAA;AACV,UAAA,aAAA,GAAgB,KAAA;AAAA;AAEhB,UAAA,cAAA,GAAiB,KAAA;AAAA,MACrB;AAAA,IACF,SACO,KAAA,EAAO;AACZ,MAAA,IAAI,eAAA,KAAoB,oBAAA;AACtB,QAAA,gBAAA,EAAiB;AACnB,MAAA,MAAM,KAAA;AAAA,IACR;AAAA,EACF;AAEA,EAAA,OAAO;AAAA,IACL,MAAM,cAAA,GAAyC;AAC7C,MAAA,MAAM,cAAA,GAAiB,oBAAA;AACvB,MAAA,MAAM,OAAA,GAAU,MAAM,uBAAA,EAAwB;AAC9C,MAAA,IAAI,aAAA;AACF,QAAA,OAAO,IAAA;AACT,MAAA,MAAM,KAAA,GAAQ,MAAM,OAAA,CAAQ,OAAA,CAAQ,iBAAiB,CAAA;AACrD,MAAA,OAAO,aAAA,IAAiB,cAAA,KAAmB,oBAAA,GAAuB,IAAA,GAAO,KAAA;AAAA,IAC3E,CAAA;AAAA,IAEA,MAAM,eAAA,GAA0C;AAC9C,MAAA,MAAM,cAAA,GAAiB,oBAAA;AACvB,MAAA,MAAM,OAAA,GAAU,MAAM,uBAAA,EAAwB;AAC9C,MAAA,IAAI,cAAA;AACF,QAAA,OAAO,IAAA;AACT,MAAA,MAAM,KAAA,GAAQ,MAAM,OAAA,CAAQ,OAAA,CAAQ,kBAAkB,CAAA;AACtD,MAAA,OAAO,cAAA,IAAkB,cAAA,KAAmB,oBAAA,GAAuB,IAAA,GAAO,KAAA;AAAA,IAC5E,CAAA;AAAA,IAEA,MAAM,eAAe,KAAA,EAA8B;AACjD,MAAA,MAAM,UAAA,CAAW,mBAAmB,KAAK,CAAA;AAAA,IAC3C,CAAA;AAAA,IAEA,MAAM,gBAAgB,KAAA,EAA8B;AAClD,MAAA,MAAM,UAAA,CAAW,oBAAoB,KAAK,CAAA;AAAA,IAC5C,CAAA;AAAA,IAEA,MAAM,KAAA,GAAuB;AAC3B,MAAA,gBAAA,EAAiB;AACjB,MAAA,MAAM,OAAA,GAAU,MAAM,uBAAA,EAAwB;AAE9C,MAAA,MAAM,OAAA,GAAU,MAAM,OAAA,CAAQ,UAAA,CAAW;AAAA,QACvC,OAAA,CAAQ,WAAW,iBAAiB,CAAA;AAAA,QACpC,OAAA,CAAQ,WAAW,kBAAkB;AAAA,OACtC,CAAA;AACD,MAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,MAAA,CAAO,CAAA,MAAA,KAAU,MAAA,CAAO,MAAA,KAAW,UAAU,CAAA,CAAE,GAAA,CAAI,CAAA,MAAA,KAAU,MAAA,CAAO,MAAM,CAAA;AACjG,MAAA,IAAI,OAAO,MAAA,GAAS,CAAA;AAClB,QAAA,MAAM,IAAI,cAAA,CAAe,MAAA,EAAQ,UAAA,CAAW,oCAAoC,CAAC,CAAA;AAAA,IACrF;AAAA,GACF;AACF;AAuBA,eAAsB,kBAAkB,GAAA,EAAgD;AACtF,EAAA,IAAI;AACF,IAAA,MAAM,EAAE,OAAM,GAAI,MAAM,YAAY,GAAA,CAAI,EAAE,KAAK,CAAA;AAC/C,IAAA,OAAOD,GAAG,KAAK,CAAA;AAAA,EACjB,SACO,KAAA,EAAO;AACZ,IAAA,OAAOC,GAAAA;AAAA,MACL,iBAAA,CAAkB,sBAAA;AAAA,MAClB,WAAW,gCAAgC,CAAA;AAAA,MAC3C;AAAA,KACF;AAAA,EACF;AACF;AAwBA,eAAsB,iBAAA,CAAkB,KAAa,KAAA,EAAyC;AAC5F,EAAA,IAAI;AACF,IAAA,MAAM,WAAA,CAAY,GAAA,CAAI,EAAE,GAAA,EAAK,OAAO,CAAA;AACpC,IAAA,OAAOD,GAAG,KAAA,CAAS,CAAA;AAAA,EACrB,SACO,KAAA,EAAO;AACZ,IAAA,OAAOC,GAAAA;AAAA,MACL,iBAAA,CAAkB,sBAAA;AAAA,MAClB,WAAW,gCAAgC,CAAA;AAAA,MAC3C;AAAA,KACF;AAAA,EACF;AACF;AAuBA,eAAsB,qBAAqB,GAAA,EAAuC;AAChF,EAAA,IAAI;AACF,IAAA,MAAM,WAAA,CAAY,MAAA,CAAO,EAAE,GAAA,EAAK,CAAA;AAChC,IAAA,OAAOD,GAAG,KAAA,CAAS,CAAA;AAAA,EACrB,SACO,KAAA,EAAO;AACZ,IAAA,OAAOC,GAAAA;AAAA,MACL,iBAAA,CAAkB,yBAAA;AAAA,MAClB,WAAW,mCAAmC,CAAA;AAAA,MAC9C;AAAA,KACF;AAAA,EACF;AACF;;;ACtNA,IAAMC,OAAAA,GAASH,KAAK,MAAA,CAAO,KAAA,CAAM,EAAE,MAAA,EAAQ,WAAA,EAAa,KAAA,EAAO,MAAA,EAAQ,CAAA;AAGvE,IAAI,WAAA,GAAc,KAAA;AAGlB,IAAI,cAAA,GAAiB,KAAA;AAGrB,IAAI,cAAA,GAAgC,IAAA;AAIpC,IAAM,cAAA,GAAiBA,KAAK,MAAA,CAAO,uBAAA;AAAA,EACjC,iBAAA,CAAkB,eAAA;AAAA,EAClB,MAAM,WAAW,0BAA0B;AAC7C,CAAA;AAEA,IAAM,oBAAA,GAAuB,eAAe,KAAA,EAAwB;AACpE,IAAM,oBAAA,GAAuB,eAAe,KAAA,EAAwB;AACpE,IAAM,kBAAA,GAAqB,eAAe,KAAA,EAAsB;AAChE,IAAM,uBAAA,GAA0B,eAAe,KAAA,EAA2B;AAC1E,IAAM,yBAAA,GAA4B,eAAe,KAAA,EAA6B;AAI9E,IAAM,SAAA,GAA8B;AAAA,EAClC,OAAA,EAAS,aAAA;AAAA,EACT;AACF,CAAA;AAEA,IAAM,SAAA,GAA8B;AAAA,EAClC;AACF,CAAA;AAEA,IAAM,OAAA,GAA0B;AAAA,EAC9B,QAAA,EAAU,YAAA;AAAA,EACV,MAAA,EAAQ;AACV,CAAA;AAEA,IAAM,YAAA,GAAoC;AAAA,EACxC,SAAA,EAAW,kBAAA;AAAA,EACX,IAAA,EAAM,aAAA;AAAA,EACN,IAAA,EAAM;AACR,CAAA;AAEA,IAAM,cAAA,GAAwC;AAAA,EAC5C,GAAA,EAAK,iBAAA;AAAA,EACL,GAAA,EAAK,iBAAA;AAAA,EACL,MAAA,EAAQ;AACV,CAAA;AAIA,SAAS,oBAAA,GAAgC;AACvC,EAAA,IAAI;AAEF,IAAA,OAAO,OAAO,MAAA,KAAW,WAAA,IACnB,MAAA,CAA8C,SAAA,KAAc,KAAA,CAAA;AAAA,EACpE,CAAA,CAAA,MACM;AACJ,IAAA,OAAO,KAAA;AAAA,EACT;AACF;AAsBO,IAAM,SAAA,GAAgC;AAAA,EAC3C,MAAM,IAAA,GAAiC;AAErC,IAAA,IAAI,cAAA,EAAgB;AAClB,MAAAG,OAAAA,CAAO,KAAK,8DAA8D,CAAA;AAC1E,MAAA,OAAOD,GAAAA;AAAA,QACL,iBAAA,CAAkB,gBAAA;AAAA,QAClB,WAAW,0BAA0B;AAAA,OACvC;AAAA,IACF;AACA,IAAA,cAAA,GAAiB,IAAA;AAEjB,IAAA,IAAI;AACF,MAAA,IAAI,WAAA,EAAa;AACf,QAAAC,OAAAA,CAAO,KAAK,yDAAyD,CAAA;AACrE,QAAA,MAAM,MAAA,GAAS,MAAM,SAAA,CAAU,KAAA,EAAM;AACrC,QAAA,IAAI,CAAC,MAAA,CAAO,OAAA;AACV,UAAA,OAAO,MAAA;AAAA,MACX;AAEA,MAAA,MAAM,OAAA,GAAU,MAAM,kBAAA,EAAmB;AACzC,MAAA,IAAI,CAAC,OAAA,CAAQ,OAAA;AACX,QAAA,OAAO,OAAA;AAET,MAAAA,OAAAA,CAAO,KAAK,+BAA+B,CAAA;AAE3C,MAAA,IAAI,CAAC,sBAAqB,EAAG;AAC3B,QAAAA,OAAAA,CAAO,MAAM,mDAAmD,CAAA;AAChE,QAAA,OAAOD,GAAAA;AAAA,UACL,iBAAA,CAAkB,aAAA;AAAA,UAClB,WAAW,wBAAwB;AAAA,SACrC;AAAA,MACF;AAEA,MAAA,MAAM,EAAE,SAAA,EAAU,GAAI,MAAM,OAAO,iBAAiB,CAAA;AACpD,MAAA,MAAM,QAAA,GAAW,UAAU,WAAA,EAAY;AAEvC,MAAA,IAAI,CAAC,QAAA,EAAU;AACb,QAAAC,OAAAA,CAAO,MAAM,wDAAwD,CAAA;AACrE,QAAA,OAAOD,GAAAA;AAAA,UACL,iBAAA,CAAkB,WAAA;AAAA,UAClB,WAAW,sBAAsB;AAAA,SACnC;AAAA,MACF;AAEA,MAAA,cAAA,GAAiB,QAAA;AACjB,MAAA,WAAA,GAAc,IAAA;AACd,MAAAC,OAAAA,CAAO,IAAA,CAAK,8BAAA,EAAgC,EAAE,UAAU,CAAA;AACxD,MAAA,OAAOF,GAAG,KAAA,CAAS,CAAA;AAAA,IACrB,SACO,KAAA,EAAO;AACZ,MAAAE,QAAO,KAAA,CAAM,wCAAA,EAA0C,EAAE,KAAA,EAAO,OAAO,CAAA;AACvE,MAAA,OAAOD,GAAAA;AAAA,QACL,iBAAA,CAAkB,WAAA;AAAA,QAClB,WAAW,sBAAsB,CAAA;AAAA,QACjC;AAAA,OACF;AAAA,IACF,CAAA,SACA;AACE,MAAA,cAAA,GAAiB,KAAA;AAAA,IACnB;AAAA,EACF,CAAA;AAAA,EAEA,MAAM,KAAA,GAAQ;AACZ,IAAAC,OAAAA,CAAO,KAAK,0BAA0B,CAAA;AACtC,IAAA,WAAA,GAAc,KAAA;AACd,IAAA,cAAA,GAAiB,IAAA;AACjB,IAAA,MAAM,OAAA,GAAU,MAAM,kBAAA,EAAmB;AACzC,IAAA,IAAI,CAAC,OAAA,CAAQ,OAAA;AACX,MAAA,OAAO,OAAA;AACT,IAAAA,OAAAA,CAAO,KAAK,yBAAyB,CAAA;AACrC,IAAA,OAAOF,GAAG,MAAS,CAAA;AAAA,EACrB,CAAA;AAAA,EAEA,WAAA,GAAsB;AACpB,IAAA,IAAI,cAAA,EAAgB;AAClB,MAAA,OAAO,cAAA;AAAA,IACT;AACA,IAAA,IAAI;AAEF,MAAA,MAAM,MAAO,MAAA,CAA8C,SAAA;AAC3D,MAAA,OAAO,GAAA,EAAK,eAAc,IAAK,KAAA;AAAA,IACjC,CAAA,CAAA,MACM;AACJ,MAAA,OAAO,KAAA;AAAA,IACT;AAAA,EACF,CAAA;AAAA,EAEA,QAAA,GAAoB;AAClB,IAAA,IAAI;AACF,MAAA,MAAM,MAAO,MAAA,CAA8C,SAAA;AAC3D,MAAA,OAAO,GAAA,EAAK,oBAAmB,IAAK,KAAA;AAAA,IACtC,CAAA,CAAA,MACM;AACJ,MAAA,OAAO,KAAA;AAAA,IACT;AAAA,EACF,CAAA;AAAA,EAEA,IAAI,aAAA,GAAyB;AAC3B,IAAA,OAAO,WAAA;AAAA,EACT,CAAA;AAAA,EAEA,IAAI,MAAA,GAA2B;AAC7B,IAAA,OAAO,cAAc,SAAA,GAAY,oBAAA;AAAA,EACnC,CAAA;AAAA,EAEA,IAAI,MAAA,GAA2B;AAC7B,IAAA,OAAO,cAAc,SAAA,GAAY,oBAAA;AAAA,EACnC,CAAA;AAAA,EAEA,IAAI,IAAA,GAAuB;AACzB,IAAA,OAAO,cAAc,OAAA,GAAU,kBAAA;AAAA,EACjC,CAAA;AAAA,EAEA,IAAI,SAAA,GAAiC;AACnC,IAAA,OAAO,cAAc,YAAA,GAAe,uBAAA;AAAA,EACtC,CAAA;AAAA,EAEA,IAAI,WAAA,GAAqC;AACvC,IAAA,OAAO,cAAc,cAAA,GAAiB,yBAAA;AAAA,EACxC;AACF","file":"index.js","sourcesContent":["{\n \"capacitor_initFailed\": \"Capacitor initialization failed\",\n \"capacitor_initInProgress\": \"Capacitor initialization is already in progress\",\n \"capacitor_notAvailable\": \"Capacitor is not available in this environment\",\n \"capacitor_secureStorageClearFailed\": \"Could not fully clear sign-in credentials from secure storage; retry signing out\",\n \"capacitor_preferencesGetFailed\": \"Failed to read from Capacitor Preferences\",\n \"capacitor_preferencesSetFailed\": \"Failed to write to Capacitor Preferences\",\n \"capacitor_preferencesRemoveFailed\": \"Failed to remove from Capacitor Preferences\",\n \"capacitor_deviceInfoFailed\": \"Failed to get device info\",\n \"capacitor_appVersionFailed\": \"Failed to get app version\",\n \"capacitor_pushRegisterFailed\": \"Failed to register push notifications\",\n \"capacitor_pushListenFailed\": \"Failed to listen for push notifications\",\n \"capacitor_cameraFailed\": \"Failed to access camera\",\n \"capacitor_statusBarFailed\": \"Failed to configure status bar\",\n \"capacitor_notInitialized\": \"Capacitor module is not initialized\",\n \"capacitor_pushCancelled\": \"The push operation was cancelled, timed out or the module was closed\",\n \"capacitor_pushCleanupFailed\": \"Push listeners could not be fully removed; retry cleanup or close the module\"\n}\n","{\n \"capacitor_initFailed\": \"Capacitorの初期化に失敗しました\",\n \"capacitor_initInProgress\": \"Capacitorの初期化は既に実行中です\",\n \"capacitor_notAvailable\": \"この環境ではCapacitorを利用できません\",\n \"capacitor_secureStorageClearFailed\": \"安全なストレージのログイン認証情報を完全に削除できませんでした。ログアウトを再試行してください\",\n \"capacitor_preferencesGetFailed\": \"Capacitor Preferencesからの読み取りに失敗しました\",\n \"capacitor_preferencesSetFailed\": \"Capacitor Preferencesへの書き込みに失敗しました\",\n \"capacitor_preferencesRemoveFailed\": \"Capacitor Preferencesからの削除に失敗しました\",\n \"capacitor_deviceInfoFailed\": \"デバイス情報の取得に失敗しました\",\n \"capacitor_appVersionFailed\": \"アプリのバージョンの取得に失敗しました\",\n \"capacitor_pushRegisterFailed\": \"プッシュ通知の登録に失敗しました\",\n \"capacitor_pushListenFailed\": \"プッシュ通知の受信設定に失敗しました\",\n \"capacitor_cameraFailed\": \"カメラへのアクセスに失敗しました\",\n \"capacitor_statusBarFailed\": \"ステータスバーの設定に失敗しました\",\n \"capacitor_notInitialized\": \"Capacitorモジュールが初期化されていません\",\n \"capacitor_pushCancelled\": \"プッシュ操作がキャンセルされたか、タイムアウトしたか、モジュールが終了しました\",\n \"capacitor_pushCleanupFailed\": \"プッシュリスナーを完全に削除できませんでした。クリーンアップを再試行するか、モジュールを終了してください\"\n}\n","{\n \"capacitor_initFailed\": \"Capacitor 初始化失败\",\n \"capacitor_initInProgress\": \"Capacitor 正在初始化中\",\n \"capacitor_notAvailable\": \"当前环境不支持 Capacitor\",\n \"capacitor_secureStorageClearFailed\": \"未能完全清除安全存储中的登录凭据,请重试退出\",\n \"capacitor_preferencesGetFailed\": \"读取 Capacitor Preferences 失败\",\n \"capacitor_preferencesSetFailed\": \"写入 Capacitor Preferences 失败\",\n \"capacitor_preferencesRemoveFailed\": \"删除 Capacitor Preferences 失败\",\n \"capacitor_deviceInfoFailed\": \"获取设备信息失败\",\n \"capacitor_appVersionFailed\": \"获取应用版本失败\",\n \"capacitor_pushRegisterFailed\": \"注册推送通知失败\",\n \"capacitor_pushListenFailed\": \"监听推送通知失败\",\n \"capacitor_cameraFailed\": \"访问相机失败\",\n \"capacitor_statusBarFailed\": \"配置状态栏失败\",\n \"capacitor_notInitialized\": \"Capacitor 模块未初始化\",\n \"capacitor_pushCancelled\": \"推送操作已取消、超时或模块已关闭\",\n \"capacitor_pushCleanupFailed\": \"推送监听器未完全移除,请重试清理或关闭模块\"\n}\n","/**\n * @h-ai/capacitor — i18n\n *\n * 模块 i18n 文案访问入口。\n * @module capacitor-i18n\n */\n\nimport { core } from '@h-ai/core'\nimport messagesEnUS from '../messages/en-US.json'\nimport messagesJaJP from '../messages/ja-JP.json'\nimport messagesZhCN from '../messages/zh-CN.json'\n\n/**\n * CapacitorMessageKey 的数据约束与操作契约。\n * @example 模块内部上下文\n * ```ts\n * const key: CapacitorMessageKey = 'capacitor_notAvailable'\n * const text = capacitorM(key)\n * void text\n * ```\n */\ntype CapacitorMessageKey = keyof typeof messagesZhCN\n\n/**\n * capacitor 模块 i18n 文案获取器\n *\n * @param key - 文案 Key\n * @returns 本地化后的文案字符串\n * @example 模块内部上下文\n * ```ts\n * const text = capacitorM('capacitor_notAvailable', { locale: 'en-US' })\n * void text\n * ```\n */\nexport const capacitorM = core.i18n.createMessageGetter<CapacitorMessageKey>({\n 'zh-CN': messagesZhCN,\n 'en-US': messagesEnUS,\n 'ja-JP': messagesJaJP,\n})\n","/**\n * @h-ai/capacitor — 类型定义\n *\n * Capacitor 原生桥接模块的公共类型。\n * @module capacitor-types\n */\n\nimport type { ErrorInfo, HaiResult } from '@h-ai/core'\nimport { core } from '@h-ai/core'\n\nconst CapacitorErrorInfo = {\n INIT_FAILED: '001:500',\n NOT_AVAILABLE: '002:400',\n INIT_IN_PROGRESS: '003:409',\n NOT_INITIALIZED: '010:500',\n PREFERENCES_GET_FAILED: '011:500',\n PREFERENCES_SET_FAILED: '012:500',\n PREFERENCES_REMOVE_FAILED: '013:500',\n DEVICE_INFO_FAILED: '020:500',\n APP_VERSION_FAILED: '021:500',\n PUSH_REGISTER_FAILED: '030:500',\n PUSH_LISTEN_FAILED: '031:500',\n PUSH_CLEANUP_FAILED: '032:500',\n CAMERA_FAILED: '040:500',\n STATUS_BAR_FAILED: '050:500',\n} as const satisfies ErrorInfo\n\n/**\n * Capacitor 模块的公共错误定义;业务操作通过 HaiResult 返回。\n * @example\n * ```ts\n * import { HaiCapacitorError } from '@h-ai/capacitor'\n * import { err } from '@h-ai/core'\n *\n * const result = err(HaiCapacitorError.NOT_AVAILABLE, 'Native bridge required')\n * if (!result.success) void result.error.code\n * ```\n */\nexport const HaiCapacitorError = core.error.buildHaiErrorsDef('capacitor', CapacitorErrorInfo)\n\n// ─── 子操作接口 ───\n\n/**\n * 设备信息操作\n * @example\n * ```ts\n * import type { DeviceOperations } from '@h-ai/capacitor'\n *\n * async function use(operations: DeviceOperations) {\n * const result = await operations.getInfo()\n * if (!result.success) return result.error.message\n * return result.data\n * }\n * // 由已初始化的 capacitor.device 提供这些操作。\n * ```\n */\nexport interface DeviceOperations {\n /** 获取设备信息(平台、型号、系统版本等) */\n getInfo: () => Promise<HaiResult<DeviceInfo>>\n /** 获取应用版本信息 */\n getAppVersion: () => Promise<HaiResult<{ version: string, build: string }>>\n}\n\n/**\n * 相机操作\n * @example\n * ```ts\n * import type { CameraOperations } from '@h-ai/capacitor'\n *\n * async function use(operations: CameraOperations) {\n * const result = await operations.takePhoto({ source: 'photos' })\n * if (!result.success) return result.error.message\n * return result.data\n * }\n * // 由已初始化的 capacitor.camera 提供这些操作。\n * ```\n */\nexport interface CameraOperations {\n /** 拍照或选取相册图片 */\n takePhoto: (options?: PhotoOptions) => Promise<HaiResult<PhotoResult>>\n}\n\n/**\n * 推送通知操作\n * @example\n * ```ts\n * import type { PushOperations } from '@h-ai/capacitor'\n *\n * async function use(operations: PushOperations) {\n * const result = await operations.register()\n * if (!result.success) return result.error.message\n * return result.data\n * }\n * // 由已初始化的 capacitor.push 提供这些操作。\n * ```\n */\nexport interface PushOperations {\n /** 注册原生 Token;30 秒总超时,可取消,结束时回收监听器。 */\n register: (options?: PushOptions) => Promise<HaiResult<PushRegistration>>\n /** 监听原生事件;设置阶段限时 30 秒,成功后由 cleanup/信号/模块关闭释放。 */\n listen: (callbacks: PushNotificationCallbacks, options?: PushOptions) => Promise<HaiResult<PushCleanup>>\n}\n\n/**\n * 状态栏操作\n * @example\n * ```ts\n * import type { StatusBarOperations } from '@h-ai/capacitor'\n *\n * async function use(operations: StatusBarOperations) {\n * const result = await operations.configure({ style: 'dark' })\n * if (!result.success) return result.error.message\n * return result.data\n * }\n * // 由已初始化的 capacitor.statusBar 提供这些操作。\n * ```\n */\nexport interface StatusBarOperations {\n /** 配置状态栏(样式、背景色、沉浸式) */\n configure: (config: StatusBarConfig) => Promise<HaiResult<void>>\n /** 显示状态栏 */\n show: () => Promise<HaiResult<void>>\n /** 隐藏状态栏 */\n hide: () => Promise<HaiResult<void>>\n}\n\n/**\n * Preferences 操作(安全读写,返回 HaiResult)\n * @example\n * ```ts\n * import type { PreferencesOperations } from '@h-ai/capacitor'\n *\n * async function use(operations: PreferencesOperations) {\n * const result = await operations.get('theme')\n * if (!result.success) return result.error.message\n * return result.data\n * }\n * // 由已初始化的 capacitor.preferences 提供这些操作。\n * ```\n */\nexport interface PreferencesOperations {\n /** 安全读取 Preference 值 */\n get: (key: string) => Promise<HaiResult<string | null>>\n /** 安全写入 Preference 值 */\n set: (key: string, value: string) => Promise<HaiResult<void>>\n /** 安全删除 Preference 值 */\n remove: (key: string) => Promise<HaiResult<void>>\n}\n\n// ─── 函数接口 ───\n\n/**\n * Capacitor 模块服务对象接口\n * @example\n * ```ts\n * import { capacitor } from '@h-ai/capacitor'\n *\n * const initialized = await capacitor.init()\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await capacitor.device.getInfo()\n * if (!result.success) throw new Error(result.error.message)\n * void result.data\n * } finally {\n * const closed = await capacitor.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport interface CapacitorFunctions {\n /** 初始化 Capacitor 模块 */\n init: () => Promise<HaiResult<void>>\n /** 关闭模块并回收推送监听;失败时保持不可用,保留句柄供再次 close 重试。 */\n close: () => Promise<HaiResult<void>>\n /** 获取当前平台 */\n getPlatform: () => string\n /** 是否运行在原生 App 中 */\n isNative: () => boolean\n /** 是否已初始化 */\n readonly isInitialized: boolean\n\n /** 设备信息操作 */\n readonly device: DeviceOperations\n /** 相机操作 */\n readonly camera: CameraOperations\n /** 推送通知操作 */\n readonly push: PushOperations\n /** 状态栏操作 */\n readonly statusBar: StatusBarOperations\n /** Preferences 安全读写操作 */\n readonly preferences: PreferencesOperations\n}\n\n// ─── 设备信息 ───\n\n/**\n * 设备平台\n * @example\n * ```ts\n * import type { DevicePlatform } from '@h-ai/capacitor'\n *\n * const value: DevicePlatform = 'android'\n * void value\n * ```\n */\nexport type DevicePlatform = 'ios' | 'android' | 'web'\n\n/**\n * 设备信息\n * @example\n * ```ts\n * import type { DeviceInfo } from '@h-ai/capacitor'\n *\n * const value: DeviceInfo = { platform: 'android', osVersion: '15', model: 'Example device', manufacturer: 'Example', isVirtual: false }\n * void value\n * ```\n */\nexport interface DeviceInfo {\n /** 平台(ios / android / web) */\n platform: DevicePlatform\n /** 操作系统版本(如 '14.5') */\n osVersion: string\n /** 设备型号(如 'iPhone 14 Pro') */\n model: string\n /** 设备制造商 */\n manufacturer: string\n /** 是否为虚拟设备(模拟器) */\n isVirtual: boolean\n /** 应用版本 */\n appVersion?: string\n /** 应用 Build 号 */\n appBuild?: string\n}\n\n// ─── 推送通知 ───\n\n/**\n * 推送流程的取消选项;listen 成功后信号仍可终止监听。\n * @example\n * ```ts\n * import type { PushOptions } from '@h-ai/capacitor'\n *\n * const value: PushOptions = { signal: new AbortController().signal }\n * void value\n * ```\n */\nexport interface PushOptions {\n /** 取消当前注册或监听,不影响其他消费者拥有的句柄。 */\n signal?: AbortSignal\n}\n\n/**\n * 幂等推送清理函数;原生 remove 失败返回错误,调用同一函数可重试。\n * @example\n * ```ts\n * import type { PushCleanup } from '@h-ai/capacitor'\n *\n * async function release(cleanup: PushCleanup) {\n * const result = await cleanup()\n * if (!result.success) return cleanup() // 同一所有权句柄允许重试清理。\n * return result\n * }\n * ```\n */\nexport type PushCleanup = () => Promise<HaiResult<void>>\n\n/**\n * 推送通知数据\n * @example\n * ```ts\n * import type { PushNotification } from '@h-ai/capacitor'\n *\n * const value: PushNotification = { id: 'notice_1', title: 'Order ready', data: { orderId: 'order_1' } }\n * void value\n * ```\n */\nexport interface PushNotification {\n /** 通知 ID */\n id: string\n /** 标题 */\n title?: string\n /** 正文 */\n body?: string\n /** 附加数据 */\n data?: Record<string, unknown>\n}\n\n/**\n * 推送注册结果\n * @example\n * ```ts\n * import type { PushRegistration } from '@h-ai/capacitor'\n *\n * const value: PushRegistration = { token: 'example-only-native-token' }\n * void value\n * ```\n */\nexport interface PushRegistration {\n /** 设备推送 Token */\n token: string\n}\n\n/**\n * 推送通知回调\n * @example\n * ```ts\n * import type { PushNotificationCallbacks } from '@h-ai/capacitor'\n *\n * const value: PushNotificationCallbacks = { onReceived: notification => { document.title = notification.title ?? 'Notice' } }\n * void value\n * ```\n */\nexport interface PushNotificationCallbacks {\n /** 收到推送时调用 */\n onReceived?: (notification: PushNotification) => void\n /** 用户点击推送时调用 */\n onActionPerformed?: (notification: PushNotification) => void\n}\n\n// ─── 相机 ───\n\n/**\n * 照片来源\n * @example\n * ```ts\n * import type { PhotoSource } from '@h-ai/capacitor'\n *\n * const value: PhotoSource = 'photos'\n * void value\n * ```\n */\nexport type PhotoSource = 'camera' | 'photos' | 'prompt'\n\n/**\n * 照片选项\n * @example\n * ```ts\n * import type { PhotoOptions } from '@h-ai/capacitor'\n *\n * const value: PhotoOptions = { source: 'photos', resultType: 'uri', quality: 90, width: 1200 }\n * void value\n * ```\n */\nexport interface PhotoOptions {\n /** 来源(默认 'prompt') */\n source?: PhotoSource\n /** 图片质量 0-100(默认 90) */\n quality?: number\n /** 最大宽度(像素) */\n width?: number\n /** 最大高度(像素) */\n height?: number\n /** 返回格式 */\n resultType?: 'uri' | 'base64' | 'dataUrl'\n}\n\n/**\n * 照片结果\n * @example\n * ```ts\n * import type { PhotoResult } from '@h-ai/capacitor'\n *\n * const value: PhotoResult = { data: 'file:///example/photo.jpeg', format: 'jpeg' }\n * void value\n * ```\n */\nexport interface PhotoResult {\n /** 图片路径或 base64 数据 */\n data: string\n /** 原生插件返回的格式扩展名(如 jpeg),不是 MIME 类型 */\n format: string\n}\n\n// ─── 状态栏 ───\n\n/**\n * 状态栏样式\n * @example\n * ```ts\n * import type { StatusBarStyle } from '@h-ai/capacitor'\n *\n * const value: StatusBarStyle = 'dark'\n * void value\n * ```\n */\nexport type StatusBarStyle = 'dark' | 'light' | 'default'\n\n/**\n * 状态栏配置\n * @example\n * ```ts\n * import type { StatusBarConfig } from '@h-ai/capacitor'\n *\n * const value: StatusBarConfig = { style: 'dark', overlay: false }\n * void value\n * ```\n */\nexport interface StatusBarConfig {\n /** 状态栏文字样式 */\n style?: StatusBarStyle\n /** 背景颜色(十六进制) */\n backgroundColor?: string\n /** 是否覆盖在内容上(沉浸式) */\n overlay?: boolean\n}\n","/**\n * @h-ai/capacitor — 相机 / 相册\n *\n * 封装 `@capacitor/camera` 插件,提供拍照与相册选择能力。\n *\n * @module capacitor-camera\n */\n\nimport type { HaiResult } from '@h-ai/core'\nimport type { PhotoOptions, PhotoResult } from './capacitor-types.js'\nimport { err, ok } from '@h-ai/core'\nimport { capacitorM } from './capacitor-i18n.js'\nimport { HaiCapacitorError } from './capacitor-types.js'\n\n/** 照片来源映射(内部使用) */\nconst SOURCE_MAP = {\n camera: 'Camera',\n photos: 'Photos',\n prompt: 'Prompt',\n} as const\n\n/** 结果类型映射(内部使用) */\nconst RESULT_TYPE_MAP = {\n uri: 'Uri',\n base64: 'Base64',\n dataUrl: 'DataUrl',\n} as const\n\n/**\n * 拍照或选取相册图片\n *\n * 需要安装 `@capacitor/camera` 插件。\n *\n * @param options - 照片选项\n * @returns 照片结果\n * @example\n * ```ts\n * import { capacitor } from '@h-ai/capacitor'\n *\n * const initialized = await capacitor.init()\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await capacitor.camera.takePhoto({ source: 'photos', width: 1200 })\n * if (!result.success) throw new Error(result.error.message)\n * void result.data\n * } finally {\n * const closed = await capacitor.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport async function takePhoto(options?: PhotoOptions): Promise<HaiResult<PhotoResult>> {\n try {\n const { Camera, CameraSource, CameraResultType } = await import('@capacitor/camera')\n\n const source = options?.source ?? 'prompt'\n const resultType = options?.resultType ?? 'uri'\n\n const photo = await Camera.getPhoto({\n quality: options?.quality ?? 90,\n source: CameraSource[SOURCE_MAP[source] as keyof typeof CameraSource],\n resultType: CameraResultType[RESULT_TYPE_MAP[resultType] as keyof typeof CameraResultType],\n width: options?.width,\n height: options?.height,\n })\n\n // 根据 resultType 返回对应数据\n let data: string\n if (resultType === 'base64') {\n data = photo.base64String ?? ''\n }\n else if (resultType === 'dataUrl') {\n data = photo.dataUrl ?? ''\n }\n else {\n data = photo.webPath ?? ''\n }\n\n return ok({\n data,\n format: photo.format,\n })\n }\n catch (cause) {\n return err(\n HaiCapacitorError.CAMERA_FAILED,\n capacitorM('capacitor_cameraFailed'),\n cause,\n )\n }\n}\n","/**\n * @h-ai/capacitor — 设备信息\n *\n * 封装 `@capacitor/device` 插件,提供统一的设备信息读取接口。\n *\n * @module capacitor-device\n */\n\nimport type { HaiResult } from '@h-ai/core'\nimport type { DeviceInfo } from './capacitor-types.js'\nimport { err, ok } from '@h-ai/core'\nimport { capacitorM } from './capacitor-i18n.js'\nimport { HaiCapacitorError } from './capacitor-types.js'\n\n/**\n * 获取设备信息\n *\n * 需要安装 `@capacitor/device` 插件。\n *\n * @returns 设备信息(平台、系统版本、型号等)\n * @example\n * ```ts\n * import { capacitor } from '@h-ai/capacitor'\n *\n * const initialized = await capacitor.init()\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await capacitor.device.getInfo()\n * if (!result.success) throw new Error(result.error.message)\n * void result.data\n * } finally {\n * const closed = await capacitor.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport async function getDeviceInfo(): Promise<HaiResult<DeviceInfo>> {\n try {\n const { Device } = await import('@capacitor/device')\n const info = await Device.getInfo()\n\n return ok({\n platform: info.platform as DeviceInfo['platform'],\n osVersion: info.osVersion,\n model: info.model,\n manufacturer: info.manufacturer,\n isVirtual: info.isVirtual,\n })\n }\n catch (cause) {\n return err(\n HaiCapacitorError.DEVICE_INFO_FAILED,\n capacitorM('capacitor_deviceInfoFailed'),\n cause,\n )\n }\n}\n\n/**\n * 获取应用版本信息\n *\n * @returns appVersion 与 appBuild\n * @example\n * ```ts\n * import { capacitor } from '@h-ai/capacitor'\n *\n * const initialized = await capacitor.init()\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await capacitor.device.getAppVersion()\n * if (!result.success) throw new Error(result.error.message)\n * void result.data\n * } finally {\n * const closed = await capacitor.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport async function getAppVersion(): Promise<HaiResult<{ version: string, build: string }>> {\n try {\n const { App } = await import('@capacitor/app')\n const info = await App.getInfo()\n\n return ok({\n version: info.version,\n build: info.build,\n })\n }\n catch (cause) {\n return err(\n HaiCapacitorError.APP_VERSION_FAILED,\n capacitorM('capacitor_appVersionFailed'),\n cause,\n )\n }\n}\n","/**\n * @h-ai/capacitor — 推送通知及其受管监听生命周期。\n * @module capacitor-push\n */\nimport type { PluginListenerHandle } from '@capacitor/core'\nimport type { HaiResult } from '@h-ai/core'\nimport type { PushCleanup, PushNotificationCallbacks, PushOptions, PushRegistration } from './capacitor-types.js'\nimport { core, err, ok } from '@h-ai/core'\nimport { capacitorM } from './capacitor-i18n.js'\nimport { HaiCapacitorError } from './capacitor-types.js'\n\nconst logger = core.logger.child({ module: 'capacitor', scope: 'push' })\n\n/**\n * 两类推送流程共用的资源所有权;关闭立即停止回调,迟到 handle 仍必须回收。\n * @example 模块内部上下文\n * ```ts\n * const scope: PushScope = createPushScope({ signal: new AbortController().signal })\n * try {\n * const state = await scope.wait(async () => 'ready')\n * scope.assertOpen()\n * void state\n * } finally {\n * await scope.dispose()\n * }\n * ```\n */\ninterface PushScope {\n readonly signal: AbortSignal\n assertOpen: () => void\n attach: (add: () => Promise<PluginListenerHandle>) => Promise<void>\n wait: <T>(operation: () => Promise<T>) => Promise<T>\n finishSetup: () => void\n dispose: () => Promise<void>\n}\n\nconst activeScopes = new Set<PushScope>()\n\n/** 为一次注册/监听建立所有权,不使用 removeAllListeners 影响其他消费者。 */\nfunction createPushScope(options?: PushOptions): PushScope {\n let scope: PushScope\n const controller = new AbortController()\n const handles = new Set<PluginListenerHandle>()\n const removals = new Map<PluginListenerHandle, Promise<void>>()\n let pending = 0\n let closing: Promise<void> | undefined\n let timer: ReturnType<typeof setTimeout> | undefined\n const cancelled = () => new Error(capacitorM('capacitor_pushCancelled'))\n\n function releaseIfComplete(): void {\n if (controller.signal.aborted && pending === 0 && handles.size === 0)\n activeScopes.delete(scope)\n }\n\n function remove(handle: PluginListenerHandle): Promise<void> {\n const current = removals.get(handle)\n if (current)\n return current\n const removal = Promise.resolve().then(() => handle.remove()).then(() => {\n handles.delete(handle)\n }).finally(() => {\n removals.delete(handle)\n releaseIfComplete()\n })\n removals.set(handle, removal)\n return removal\n }\n\n function finishSetup(): void {\n if (timer !== undefined) {\n clearTimeout(timer)\n timer = undefined\n }\n }\n\n function dispose(): Promise<void> {\n controller.abort()\n finishSetup()\n options?.signal?.removeEventListener('abort', onAbort)\n if (closing)\n return closing\n closing = Promise.allSettled([...handles].map(remove)).then((results) => {\n releaseIfComplete()\n if (results.some(result => result.status === 'rejected'))\n throw new Error(capacitorM('capacitor_pushCleanupFailed'))\n }).finally(() => { closing = undefined })\n return closing\n }\n\n function onAbort(): void {\n void dispose().catch(() => logger.warn('Push listener cleanup failed; retry cleanup or capacitor.close'))\n }\n\n scope = {\n signal: controller.signal,\n assertOpen() {\n if (controller.signal.aborted)\n throw cancelled()\n },\n async attach(add) {\n scope.assertOpen()\n pending++\n try {\n const handle = await add()\n handles.add(handle)\n if (controller.signal.aborted) {\n await remove(handle)\n throw cancelled()\n }\n }\n finally {\n pending--\n releaseIfComplete()\n }\n },\n async wait(operation) {\n scope.assertOpen()\n let interrupt: (() => void) | undefined\n const aborted = new Promise<never>((_, reject) => {\n interrupt = () => reject(cancelled())\n controller.signal.addEventListener('abort', interrupt, { once: true })\n })\n try {\n return await Promise.race([Promise.resolve().then(operation), aborted])\n }\n finally {\n if (interrupt)\n controller.signal.removeEventListener('abort', interrupt)\n }\n },\n finishSetup,\n dispose,\n }\n activeScopes.add(scope)\n timer = setTimeout(onAbort, 30_000)\n options?.signal?.addEventListener('abort', onAbort, { once: true })\n if (options?.signal?.aborted)\n onAbort()\n return scope\n}\n\n/** 原生能力必须实际存在;Web 插件不可作为 FCM/APNs 注册证明。 */\nasync function loadPushPlugin() {\n const { Capacitor } = await import('@capacitor/core')\n if (!Capacitor.isNativePlatform())\n throw new Error(capacitorM('capacitor_notAvailable'))\n return (await import('@capacitor/push-notifications')).PushNotifications\n}\n\n/**\n * 关闭模块所拥有的全部推送监听器;失败句柄保留以便再次 close 重试。\n * @returns 全部移除成功,或 PUSH_CLEANUP_FAILED;迟到句柄到达时立即移除。\n * @example\n * ```ts\n * import { capacitor } from '@h-ai/capacitor'\n *\n * // 关闭由本模块拥有的监听器,失败句柄可再次 close 重试。\n * const closed = await capacitor.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * ```\n */\nexport async function closePushListeners(): Promise<HaiResult<void>> {\n const results = await Promise.allSettled([...activeScopes].map(scope => scope.dispose()))\n return results.some(result => result.status === 'rejected')\n ? err(HaiCapacitorError.PUSH_CLEANUP_FAILED, capacitorM('capacitor_pushCleanupFailed'))\n : ok(undefined)\n}\n\n/**\n * 注册原生推送 Token;30 秒包含权限、挂载监听器与原生注册等待。\n * 部分失败、超时、AbortSignal 和模块关闭都会回收已知及迟到句柄。\n * @param options - 可选取消信号;取消返回失败结果,不注册后续原生操作。\n * @returns 真实原生插件返回的 token 或 PUSH_REGISTER_FAILED。\n * @example\n * ```ts\n * import { capacitor } from '@h-ai/capacitor'\n *\n * const initialized = await capacitor.init()\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await capacitor.push.register({ signal: new AbortController().signal })\n * if (!result.success) throw new Error(result.error.message)\n * void result.data\n * } finally {\n * const closed = await capacitor.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport async function registerPush(options?: PushOptions): Promise<HaiResult<PushRegistration>> {\n const scope = createPushScope(options)\n try {\n const token = await scope.wait(async () => {\n const push = await loadPushPlugin()\n scope.assertOpen()\n const permission = await push.requestPermissions()\n scope.assertOpen()\n if (permission.receive !== 'granted')\n throw new Error(capacitorM('capacitor_pushRegisterFailed'))\n let resolveToken!: (value: string) => void\n let rejectToken!: (reason: Error) => void\n const tokenResult = new Promise<string>((resolve, reject) => {\n resolveToken = resolve\n rejectToken = reject\n })\n // 在触发 register 之前就订阅结果,避免同步插件回调丢失或未处理拒绝。\n const setup = async () => {\n await scope.attach(() => push.addListener('registration', (result) => {\n if (!scope.signal.aborted)\n resolveToken(result.value)\n }))\n await scope.attach(() => push.addListener('registrationError', () => {\n if (!scope.signal.aborted)\n rejectToken(new Error(capacitorM('capacitor_pushRegisterFailed')))\n }))\n scope.assertOpen()\n await push.register()\n }\n return (await Promise.all([setup(), tokenResult]))[1]\n })\n await scope.dispose()\n return ok({ token })\n }\n catch (cause) {\n await scope.dispose().catch(() => logger.warn('Push registration cleanup failed; retry capacitor.close'))\n return err(HaiCapacitorError.PUSH_REGISTER_FAILED, capacitorM('capacitor_pushRegisterFailed'), cause)\n }\n}\n\n/**\n * 监听原生推送事件;设置阶段限时 30 秒,之后由 cleanup、信号或模块关闭结束。\n * @param callbacks - 收到消息与点击消息的回调。\n * @param options - 可选取消信号,监听建立后仍有效。\n * @returns 幂等清理函数;清理失败可见且可重试,关闭后立即停止业务回调。\n * @example\n * ```ts\n * import { capacitor } from '@h-ai/capacitor'\n *\n * const initialized = await capacitor.init()\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const listening = await capacitor.push.listen({ onReceived: notice => { document.title = notice.title ?? 'Notice' } })\n * if (!listening.success) throw new Error(listening.error.message)\n * const closed = await listening.data()\n * if (!closed.success) throw new Error(closed.error.message)\n * } finally {\n * const closed = await capacitor.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport async function listenPush(callbacks: PushNotificationCallbacks, options?: PushOptions): Promise<HaiResult<PushCleanup>> {\n const scope = createPushScope(options)\n try {\n await scope.wait(async () => {\n const push = await loadPushPlugin()\n scope.assertOpen()\n if (callbacks.onReceived) {\n await scope.attach(() => push.addListener('pushNotificationReceived', (notification) => {\n if (!scope.signal.aborted)\n callbacks.onReceived?.({ id: notification.id, title: notification.title, body: notification.body, data: notification.data as Record<string, unknown> })\n }))\n }\n if (callbacks.onActionPerformed) {\n await scope.attach(() => push.addListener('pushNotificationActionPerformed', (action) => {\n if (!scope.signal.aborted)\n callbacks.onActionPerformed?.({ id: action.notification.id, title: action.notification.title, body: action.notification.body, data: action.notification.data as Record<string, unknown> })\n }))\n }\n })\n scope.assertOpen()\n scope.finishSetup()\n return ok(async () => {\n try {\n await scope.dispose()\n return ok(undefined)\n }\n catch (cause) {\n return err(HaiCapacitorError.PUSH_CLEANUP_FAILED, capacitorM('capacitor_pushCleanupFailed'), cause)\n }\n })\n }\n catch (cause) {\n await scope.dispose().catch(() => logger.warn('Push setup cleanup failed; retry capacitor.close'))\n return err(HaiCapacitorError.PUSH_LISTEN_FAILED, capacitorM('capacitor_pushListenFailed'), cause)\n }\n}\n","/**\n * @h-ai/capacitor — 状态栏\n *\n * 封装 `@capacitor/status-bar` 插件,支持沉浸式状态栏配置。\n *\n * @module capacitor-status-bar\n */\n\nimport type { HaiResult } from '@h-ai/core'\nimport type { StatusBarConfig } from './capacitor-types.js'\nimport { err, ok } from '@h-ai/core'\nimport { capacitorM } from './capacitor-i18n.js'\nimport { HaiCapacitorError } from './capacitor-types.js'\n\n/**\n * 配置状态栏\n *\n * 需要安装 `@capacitor/status-bar` 插件。\n *\n * @param config - 状态栏配置\n * @returns HaiResult\n * @example\n * ```ts\n * import { capacitor } from '@h-ai/capacitor'\n *\n * const initialized = await capacitor.init()\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await capacitor.statusBar.configure({ style: 'dark', overlay: false })\n * if (!result.success) throw new Error(result.error.message)\n * void result.data\n * } finally {\n * const closed = await capacitor.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport async function configureStatusBar(config: StatusBarConfig): Promise<HaiResult<void>> {\n try {\n const { StatusBar, Style } = await import('@capacitor/status-bar')\n\n // 设置样式\n if (config.style) {\n const styleMap: Record<string, typeof Style[keyof typeof Style]> = {\n dark: Style.Dark,\n light: Style.Light,\n default: Style.Default,\n }\n await StatusBar.setStyle({ style: styleMap[config.style] ?? Style.Default })\n }\n\n // 设置背景色\n if (config.backgroundColor) {\n await StatusBar.setBackgroundColor({ color: config.backgroundColor })\n }\n\n // 设置覆盖模式\n if (config.overlay !== undefined) {\n await StatusBar.setOverlaysWebView({ overlay: config.overlay })\n }\n\n return ok(undefined)\n }\n catch (cause) {\n return err(\n HaiCapacitorError.STATUS_BAR_FAILED,\n capacitorM('capacitor_statusBarFailed'),\n cause,\n )\n }\n}\n\n/**\n * 显示状态栏\n *\n * @returns HaiResult\n * @example\n * ```ts\n * import { capacitor } from '@h-ai/capacitor'\n *\n * const initialized = await capacitor.init()\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await capacitor.statusBar.show()\n * if (!result.success) throw new Error(result.error.message)\n * void result.data\n * } finally {\n * const closed = await capacitor.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport async function showStatusBar(): Promise<HaiResult<void>> {\n try {\n const { StatusBar } = await import('@capacitor/status-bar')\n await StatusBar.show()\n return ok(undefined)\n }\n catch (cause) {\n return err(\n HaiCapacitorError.STATUS_BAR_FAILED,\n capacitorM('capacitor_statusBarFailed'),\n cause,\n )\n }\n}\n\n/**\n * 隐藏状态栏\n *\n * @returns HaiResult\n * @example\n * ```ts\n * import { capacitor } from '@h-ai/capacitor'\n *\n * const initialized = await capacitor.init()\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await capacitor.statusBar.hide()\n * if (!result.success) throw new Error(result.error.message)\n * void result.data\n * } finally {\n * const closed = await capacitor.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport async function hideStatusBar(): Promise<HaiResult<void>> {\n try {\n const { StatusBar } = await import('@capacitor/status-bar')\n await StatusBar.hide()\n return ok(undefined)\n }\n catch (cause) {\n return err(\n HaiCapacitorError.STATUS_BAR_FAILED,\n capacitorM('capacitor_statusBarFailed'),\n cause,\n )\n }\n}\n","/**\n * @h-ai/capacitor — Token 存储\n *\n * 基于原生安全存储插件的 TokenStorage 实现。\n * 仅在 Capacitor 原生环境启用,拒绝回退到不安全的 Web 存储。\n *\n * @module capacitor-token-storage\n */\n\nimport type { SecureStoragePlugin } from '@aparajita/capacitor-secure-storage'\nimport type { TokenStorage } from '@h-ai/api-client'\nimport type { HaiResult } from '@h-ai/core'\nimport { Preferences } from '@capacitor/preferences'\nimport { err, ok } from '@h-ai/core'\nimport { capacitorM } from './capacitor-i18n.js'\nimport { HaiCapacitorError } from './capacitor-types.js'\n\n/** 原生安全存储 Key;普通 Preferences 不存放认证凭据。 */\nconst PREF_ACCESS_TOKEN = 'hai_access_token'\nconst PREF_REFRESH_TOKEN = 'hai_refresh_token'\n\n/** 动态 import 自带模块缓存;不永久缓存平台检查失败或缺失插件的结果。 */\nasync function loadSecureStoragePlugin(): Promise<SecureStoragePlugin> {\n const { Capacitor } = await import('@capacitor/core')\n if (!Capacitor.isNativePlatform())\n throw new Error(capacitorM('capacitor_notAvailable'))\n const { SecureStorage } = await import('@aparajita/capacitor-secure-storage')\n return SecureStorage\n}\n\n/**\n * 创建基于原生安全存储的 TokenStorage\n *\n * 在原生 Android/iOS 环境下使用 `@aparajita/capacitor-secure-storage`\n * 保存 access token / refresh token。所有方法的存储/平台错误均通过 Promise 拒绝传播,\n * Web 调用明确失败,不做存储回退或伪成功。\n * `clear()` 一开始即屏蔽本实例旧凭据,删除失败时仍保持屏蔽;相应 key 成功写入后恢复读取。\n * 任一凭据写入失败也会屏蔽整对凭据,避免 access 已保存、refresh 失败时暴露半完成登录。\n * 本实例屏蔽不代表原生持久数据已删除;跨重启撤销仍依赖服务端退出/撤销会话。\n *\n * @returns TokenStorage 实例\n * @throws 返回适配器的方法在插件不可用或原生存储失败时拒绝 Promise。\n * @example\n * ```ts\n * import { createCapacitorTokenStorage } from '@h-ai/capacitor'\n *\n * const storage = createCapacitorTokenStorage()\n * try {\n * const token = await storage.getAccessToken()\n * void token // 仅原生安全存储;平台或存储失败会拒绝 Promise。\n * } catch (cause) {\n * // 应阻止认证流程继续并呈现本地化错误;不回退到普通 Preferences。\n * void cause\n * }\n * ```\n */\nexport function createCapacitorTokenStorage(): TokenStorage {\n let credentialGeneration = 0\n let accessBlocked = false\n let refreshBlocked = false\n\n /** 使本代际的读取/写入资格失效;实例内屏蔽不声称原生数据已经删除。 */\n function blockCredentials(): void {\n credentialGeneration++\n accessBlocked = true\n refreshBlocked = true\n }\n\n /** 部分写入失败时撤销整对本地凭据,交由认证调用方显式重试持久清理。 */\n async function writeToken(key: typeof PREF_ACCESS_TOKEN | typeof PREF_REFRESH_TOKEN, token: string): Promise<void> {\n const writeGeneration = credentialGeneration\n try {\n const storage = await loadSecureStoragePlugin()\n await storage.setItem(key, token)\n if (writeGeneration === credentialGeneration) {\n if (key === PREF_ACCESS_TOKEN)\n accessBlocked = false\n else\n refreshBlocked = false\n }\n }\n catch (error) {\n if (writeGeneration === credentialGeneration)\n blockCredentials()\n throw error\n }\n }\n\n return {\n async getAccessToken(): Promise<string | null> {\n const readGeneration = credentialGeneration\n const storage = await loadSecureStoragePlugin()\n if (accessBlocked)\n return null\n const token = await storage.getItem(PREF_ACCESS_TOKEN)\n return accessBlocked || readGeneration !== credentialGeneration ? null : token\n },\n\n async getRefreshToken(): Promise<string | null> {\n const readGeneration = credentialGeneration\n const storage = await loadSecureStoragePlugin()\n if (refreshBlocked)\n return null\n const token = await storage.getItem(PREF_REFRESH_TOKEN)\n return refreshBlocked || readGeneration !== credentialGeneration ? null : token\n },\n\n async setAccessToken(token: string): Promise<void> {\n await writeToken(PREF_ACCESS_TOKEN, token)\n },\n\n async setRefreshToken(token: string): Promise<void> {\n await writeToken(PREF_REFRESH_TOKEN, token)\n },\n\n async clear(): Promise<void> {\n blockCredentials()\n const storage = await loadSecureStoragePlugin()\n // 等全部删除完成再报告失败,避免迟到的删除与下一次登录写入交错。\n const results = await Promise.allSettled([\n storage.removeItem(PREF_ACCESS_TOKEN),\n storage.removeItem(PREF_REFRESH_TOKEN),\n ])\n const errors = results.filter(result => result.status === 'rejected').map(result => result.reason)\n if (errors.length > 0)\n throw new AggregateError(errors, capacitorM('capacitor_secureStorageClearFailed'))\n },\n }\n}\n\n/**\n * 安全读取 Preference 值(返回 HaiResult)\n *\n * @param key - Preference Key\n * @returns HaiResult 包裹的值\n * @example\n * ```ts\n * import { capacitor } from '@h-ai/capacitor'\n *\n * const initialized = await capacitor.init()\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await capacitor.preferences.get('theme')\n * if (!result.success) throw new Error(result.error.message)\n * void result.data\n * } finally {\n * const closed = await capacitor.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport async function safeGetPreference(key: string): Promise<HaiResult<string | null>> {\n try {\n const { value } = await Preferences.get({ key })\n return ok(value)\n }\n catch (cause) {\n return err(\n HaiCapacitorError.PREFERENCES_GET_FAILED,\n capacitorM('capacitor_preferencesGetFailed'),\n cause,\n )\n }\n}\n\n/**\n * 安全写入 Preference 值\n *\n * @param key - Preference Key\n * @param value - 要写入的值\n * @returns HaiResult\n * @example\n * ```ts\n * import { capacitor } from '@h-ai/capacitor'\n *\n * const initialized = await capacitor.init()\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await capacitor.preferences.set('theme', 'dark')\n * if (!result.success) throw new Error(result.error.message)\n * void result.data\n * } finally {\n * const closed = await capacitor.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport async function safeSetPreference(key: string, value: string): Promise<HaiResult<void>> {\n try {\n await Preferences.set({ key, value })\n return ok(undefined)\n }\n catch (cause) {\n return err(\n HaiCapacitorError.PREFERENCES_SET_FAILED,\n capacitorM('capacitor_preferencesSetFailed'),\n cause,\n )\n }\n}\n\n/**\n * 安全删除 Preference 值\n *\n * @param key - Preference Key\n * @returns HaiResult\n * @example\n * ```ts\n * import { capacitor } from '@h-ai/capacitor'\n *\n * const initialized = await capacitor.init()\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await capacitor.preferences.remove('theme')\n * if (!result.success) throw new Error(result.error.message)\n * void result.data\n * } finally {\n * const closed = await capacitor.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport async function safeRemovePreference(key: string): Promise<HaiResult<void>> {\n try {\n await Preferences.remove({ key })\n return ok(undefined)\n }\n catch (cause) {\n return err(\n HaiCapacitorError.PREFERENCES_REMOVE_FAILED,\n capacitorM('capacitor_preferencesRemoveFailed'),\n cause,\n )\n }\n}\n","/**\n * @h-ai/capacitor — 模块入口(生命周期管理)\n *\n * 提供 Capacitor 原生桥接的初始化与环境检测。\n * `capacitor.init()` 应在应用启动时调用,用于检测 Capacitor 环境可用性。\n *\n * @module capacitor-main\n */\n\nimport type { HaiResult } from '@h-ai/core'\nimport type { CameraOperations, CapacitorFunctions, DeviceOperations, PreferencesOperations, PushOperations, StatusBarOperations } from './capacitor-types.js'\nimport { core, err, ok } from '@h-ai/core'\nimport { takePhoto } from './capacitor-camera.js'\nimport { getAppVersion, getDeviceInfo } from './capacitor-device.js'\nimport { capacitorM } from './capacitor-i18n.js'\nimport { closePushListeners, listenPush, registerPush } from './capacitor-push.js'\nimport { configureStatusBar, hideStatusBar, showStatusBar } from './capacitor-status-bar.js'\nimport { safeGetPreference, safeRemovePreference, safeSetPreference } from './capacitor-token-storage.js'\nimport { HaiCapacitorError } from './capacitor-types.js'\n\nconst logger = core.logger.child({ module: 'capacitor', scope: 'main' })\n\n/** 是否已初始化 */\nlet initialized = false\n\n/** 是否正在初始化中(并发防护) */\nlet initInProgress = false\n\n/** 缓存平台信息 */\nlet cachedPlatform: string | null = null\n\n// ─── 未初始化代理 ───\n\nconst notInitialized = core.module.createNotInitializedKit(\n HaiCapacitorError.NOT_INITIALIZED,\n () => capacitorM('capacitor_notInitialized'),\n)\n\nconst notInitializedDevice = notInitialized.proxy<DeviceOperations>()\nconst notInitializedCamera = notInitialized.proxy<CameraOperations>()\nconst notInitializedPush = notInitialized.proxy<PushOperations>()\nconst notInitializedStatusBar = notInitialized.proxy<StatusBarOperations>()\nconst notInitializedPreferences = notInitialized.proxy<PreferencesOperations>()\n\n// ─── 已初始化时的操作实现 ───\n\nconst deviceOps: DeviceOperations = {\n getInfo: getDeviceInfo,\n getAppVersion,\n}\n\nconst cameraOps: CameraOperations = {\n takePhoto,\n}\n\nconst pushOps: PushOperations = {\n register: registerPush,\n listen: listenPush,\n}\n\nconst statusBarOps: StatusBarOperations = {\n configure: configureStatusBar,\n show: showStatusBar,\n hide: hideStatusBar,\n}\n\nconst preferencesOps: PreferencesOperations = {\n get: safeGetPreference,\n set: safeSetPreference,\n remove: safeRemovePreference,\n}\n\n// ─── 检测 Capacitor 是否可用(模块内部使用) ───\n\nfunction isCapacitorAvailable(): boolean {\n try {\n // window 上由 Capacitor 运行时注入 Capacitor 全局对象,lib.dom 未声明该属性,故强转为可查库的 Record\n return typeof window !== 'undefined'\n && (window as unknown as Record<string, unknown>).Capacitor !== undefined\n }\n catch {\n return false\n }\n}\n\n// ─── 服务对象 ───\n\n/**\n * Capacitor 模块服务对象\n * @example\n * ```ts\n * import { capacitor } from '@h-ai/capacitor'\n *\n * const initialized = await capacitor.init()\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await capacitor.device.getInfo()\n * if (!result.success) throw new Error(result.error.message)\n * void result.data\n * } finally {\n * const closed = await capacitor.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport const capacitor: CapacitorFunctions = {\n async init(): Promise<HaiResult<void>> {\n // 并发初始化防护:避免多次 init 同时执行导致资源泄漏\n if (initInProgress) {\n logger.warn('Capacitor init already in progress, skipping concurrent call')\n return err(\n HaiCapacitorError.INIT_IN_PROGRESS,\n capacitorM('capacitor_initInProgress'),\n )\n }\n initInProgress = true\n\n try {\n if (initialized) {\n logger.warn('Capacitor module is already initialized, reinitializing')\n const closed = await capacitor.close()\n if (!closed.success)\n return closed\n }\n // 先重试上次关闭未释放的句柄,不把资源清理失败隐藏成新的初始化成功。\n const cleaned = await closePushListeners()\n if (!cleaned.success)\n return cleaned\n\n logger.info('Initializing capacitor module')\n\n if (!isCapacitorAvailable()) {\n logger.error('Capacitor is not available in current environment')\n return err(\n HaiCapacitorError.NOT_AVAILABLE,\n capacitorM('capacitor_notAvailable'),\n )\n }\n\n const { Capacitor } = await import('@capacitor/core')\n const platform = Capacitor.getPlatform()\n\n if (!platform) {\n logger.error('Capacitor initialization failed, platform not detected')\n return err(\n HaiCapacitorError.INIT_FAILED,\n capacitorM('capacitor_initFailed'),\n )\n }\n\n cachedPlatform = platform\n initialized = true\n logger.info('Capacitor module initialized', { platform })\n return ok(undefined)\n }\n catch (cause) {\n logger.error('Capacitor module initialization failed', { error: cause })\n return err(\n HaiCapacitorError.INIT_FAILED,\n capacitorM('capacitor_initFailed'),\n cause,\n )\n }\n finally {\n initInProgress = false\n }\n },\n\n async close() {\n logger.info('Closing capacitor module')\n initialized = false\n cachedPlatform = null\n const cleaned = await closePushListeners()\n if (!cleaned.success)\n return cleaned\n logger.info('Capacitor module closed')\n return ok(undefined)\n },\n\n getPlatform(): string {\n if (cachedPlatform) {\n return cachedPlatform\n }\n try {\n // 同 isCapacitorAvailable:Capacitor 全局仅在运行时由 native bridge 注入,需强转后读取\n const cap = (window as unknown as Record<string, unknown>).Capacitor as { getPlatform?: () => string } | undefined\n return cap?.getPlatform?.() ?? 'web'\n }\n catch {\n return 'web'\n }\n },\n\n isNative(): boolean {\n try {\n const cap = (window as unknown as Record<string, unknown>).Capacitor as { isNativePlatform?: () => boolean } | undefined\n return cap?.isNativePlatform?.() ?? false\n }\n catch {\n return false\n }\n },\n\n get isInitialized(): boolean {\n return initialized\n },\n\n get device(): DeviceOperations {\n return initialized ? deviceOps : notInitializedDevice\n },\n\n get camera(): CameraOperations {\n return initialized ? cameraOps : notInitializedCamera\n },\n\n get push(): PushOperations {\n return initialized ? pushOps : notInitializedPush\n },\n\n get statusBar(): StatusBarOperations {\n return initialized ? statusBarOps : notInitializedStatusBar\n },\n\n get preferences(): PreferencesOperations {\n return initialized ? preferencesOps : notInitializedPreferences\n },\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../messages/en-US.json","../messages/ja-JP.json","../messages/zh-CN.json","../src/capacitor-i18n.ts","../src/capacitor-types.ts","../src/capacitor-camera.ts","../src/capacitor-device.ts","../src/capacitor-push.ts","../src/capacitor-status-bar.ts","../src/capacitor-token-storage.ts","../src/capacitor-main.ts"],"names":["core","ok","err","logger"],"mappings":"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","file":"index.js","sourcesContent":["{\n \"capacitor_initFailed\": \"Capacitor initialization failed\",\n \"capacitor_initInProgress\": \"Capacitor initialization is already in progress\",\n \"capacitor_notAvailable\": \"Capacitor is not available in this environment\",\n \"capacitor_secureStorageClearFailed\": \"Could not fully clear sign-in credentials from secure storage; retry signing out\",\n \"capacitor_preferencesGetFailed\": \"Failed to read from Capacitor Preferences\",\n \"capacitor_preferencesSetFailed\": \"Failed to write to Capacitor Preferences\",\n \"capacitor_preferencesRemoveFailed\": \"Failed to remove from Capacitor Preferences\",\n \"capacitor_deviceInfoFailed\": \"Failed to get device info\",\n \"capacitor_appVersionFailed\": \"Failed to get app version\",\n \"capacitor_pushRegisterFailed\": \"Failed to register push notifications\",\n \"capacitor_pushListenFailed\": \"Failed to listen for push notifications\",\n \"capacitor_cameraFailed\": \"Failed to access camera\",\n \"capacitor_statusBarFailed\": \"Failed to configure status bar\",\n \"capacitor_notInitialized\": \"Capacitor module is not initialized\",\n \"capacitor_pushCancelled\": \"The push operation was cancelled, timed out or the module was closed\",\n \"capacitor_pushCleanupFailed\": \"Push listeners could not be fully removed; retry cleanup or close the module\"\n}\n","{\n \"capacitor_initFailed\": \"Capacitorの初期化に失敗しました\",\n \"capacitor_initInProgress\": \"Capacitorの初期化は既に実行中です\",\n \"capacitor_notAvailable\": \"この環境ではCapacitorを利用できません\",\n \"capacitor_secureStorageClearFailed\": \"安全なストレージのログイン認証情報を完全に削除できませんでした。ログアウトを再試行してください\",\n \"capacitor_preferencesGetFailed\": \"Capacitor Preferencesからの読み取りに失敗しました\",\n \"capacitor_preferencesSetFailed\": \"Capacitor Preferencesへの書き込みに失敗しました\",\n \"capacitor_preferencesRemoveFailed\": \"Capacitor Preferencesからの削除に失敗しました\",\n \"capacitor_deviceInfoFailed\": \"デバイス情報の取得に失敗しました\",\n \"capacitor_appVersionFailed\": \"アプリのバージョンの取得に失敗しました\",\n \"capacitor_pushRegisterFailed\": \"プッシュ通知の登録に失敗しました\",\n \"capacitor_pushListenFailed\": \"プッシュ通知の受信設定に失敗しました\",\n \"capacitor_cameraFailed\": \"カメラへのアクセスに失敗しました\",\n \"capacitor_statusBarFailed\": \"ステータスバーの設定に失敗しました\",\n \"capacitor_notInitialized\": \"Capacitorモジュールが初期化されていません\",\n \"capacitor_pushCancelled\": \"プッシュ操作がキャンセルされたか、タイムアウトしたか、モジュールが終了しました\",\n \"capacitor_pushCleanupFailed\": \"プッシュリスナーを完全に削除できませんでした。クリーンアップを再試行するか、モジュールを終了してください\"\n}\n","{\n \"capacitor_initFailed\": \"Capacitor 初始化失败\",\n \"capacitor_initInProgress\": \"Capacitor 正在初始化中\",\n \"capacitor_notAvailable\": \"当前环境不支持 Capacitor\",\n \"capacitor_secureStorageClearFailed\": \"未能完全清除安全存储中的登录凭据,请重试退出\",\n \"capacitor_preferencesGetFailed\": \"读取 Capacitor Preferences 失败\",\n \"capacitor_preferencesSetFailed\": \"写入 Capacitor Preferences 失败\",\n \"capacitor_preferencesRemoveFailed\": \"删除 Capacitor Preferences 失败\",\n \"capacitor_deviceInfoFailed\": \"获取设备信息失败\",\n \"capacitor_appVersionFailed\": \"获取应用版本失败\",\n \"capacitor_pushRegisterFailed\": \"注册推送通知失败\",\n \"capacitor_pushListenFailed\": \"监听推送通知失败\",\n \"capacitor_cameraFailed\": \"访问相机失败\",\n \"capacitor_statusBarFailed\": \"配置状态栏失败\",\n \"capacitor_notInitialized\": \"Capacitor 模块未初始化\",\n \"capacitor_pushCancelled\": \"推送操作已取消、超时或模块已关闭\",\n \"capacitor_pushCleanupFailed\": \"推送监听器未完全移除,请重试清理或关闭模块\"\n}\n","/**\n * @h-ai/capacitor — i18n\n *\n * 模块 i18n 文案访问入口。\n * @module capacitor-i18n\n */\n\nimport { core } from '@h-ai/core'\nimport messagesEnUS from '../messages/en-US.json'\nimport messagesJaJP from '../messages/ja-JP.json'\nimport messagesZhCN from '../messages/zh-CN.json'\n\n/**\n * CapacitorMessageKey 的数据约束与操作契约。\n * @example 模块内部上下文\n * ```ts\n * const key: CapacitorMessageKey = 'capacitor_notAvailable'\n * const text = capacitorM(key)\n * void text\n * ```\n */\ntype CapacitorMessageKey = keyof typeof messagesZhCN\n\n/**\n * capacitor 模块 i18n 文案获取器\n *\n * @param key - 文案 Key\n * @returns 本地化后的文案字符串\n * @example 模块内部上下文\n * ```ts\n * const text = capacitorM('capacitor_notAvailable', { locale: 'en-US' })\n * void text\n * ```\n */\nexport const capacitorM = core.i18n.createMessageGetter<CapacitorMessageKey>({\n 'zh-CN': messagesZhCN,\n 'en-US': messagesEnUS,\n 'ja-JP': messagesJaJP,\n})\n","/**\n * @h-ai/capacitor — 类型定义\n *\n * Capacitor 原生桥接模块的公共类型。\n * @module capacitor-types\n */\n\nimport type { ErrorInfo, HaiResult } from '@h-ai/core'\nimport { core } from '@h-ai/core'\n\nconst CapacitorErrorInfo = {\n INIT_FAILED: '001:500',\n NOT_AVAILABLE: '002:400',\n INIT_IN_PROGRESS: '003:409',\n NOT_INITIALIZED: '010:500',\n PREFERENCES_GET_FAILED: '011:500',\n PREFERENCES_SET_FAILED: '012:500',\n PREFERENCES_REMOVE_FAILED: '013:500',\n DEVICE_INFO_FAILED: '020:500',\n APP_VERSION_FAILED: '021:500',\n PUSH_REGISTER_FAILED: '030:500',\n PUSH_LISTEN_FAILED: '031:500',\n PUSH_CLEANUP_FAILED: '032:500',\n CAMERA_FAILED: '040:500',\n STATUS_BAR_FAILED: '050:500',\n} as const satisfies ErrorInfo\n\n/**\n * Capacitor 模块的公共错误定义;业务操作通过 HaiResult 返回。\n * @example\n * ```ts\n * import { HaiCapacitorError } from '@h-ai/capacitor'\n * import { err } from '@h-ai/core'\n *\n * const result = err(HaiCapacitorError.NOT_AVAILABLE, 'Native bridge required')\n * if (!result.success) void result.error.code\n * ```\n */\nexport const HaiCapacitorError = core.error.buildHaiErrorsDef('capacitor', CapacitorErrorInfo)\n\n// ─── 子操作接口 ───\n\n/**\n * 设备信息操作\n * @example\n * ```ts\n * import type { DeviceOperations } from '@h-ai/capacitor'\n *\n * async function use(operations: DeviceOperations) {\n * const result = await operations.getInfo()\n * if (!result.success) return result.error.message\n * return result.data\n * }\n * // 由已初始化的 capacitor.device 提供这些操作。\n * ```\n */\nexport interface DeviceOperations {\n /** 获取设备信息(平台、型号、系统版本等) */\n getInfo: () => Promise<HaiResult<DeviceInfo>>\n /** 获取应用版本信息 */\n getAppVersion: () => Promise<HaiResult<{ version: string, build: string }>>\n}\n\n/**\n * 相机操作\n * @example\n * ```ts\n * import type { CameraOperations } from '@h-ai/capacitor'\n *\n * async function use(operations: CameraOperations) {\n * const result = await operations.takePhoto({ source: 'photos' })\n * if (!result.success) return result.error.message\n * return result.data\n * }\n * // 由已初始化的 capacitor.camera 提供这些操作。\n * ```\n */\nexport interface CameraOperations {\n /** 拍照或选取相册图片 */\n takePhoto: (options?: PhotoOptions) => Promise<HaiResult<PhotoResult>>\n}\n\n/**\n * 推送通知操作\n * @example\n * ```ts\n * import type { PushOperations } from '@h-ai/capacitor'\n *\n * async function use(operations: PushOperations) {\n * const result = await operations.register()\n * if (!result.success) return result.error.message\n * return result.data\n * }\n * // 由已初始化的 capacitor.push 提供这些操作。\n * ```\n */\nexport interface PushOperations {\n /** 注册原生 Token;30 秒总超时,可取消,结束时回收监听器。 */\n register: (options?: PushOptions) => Promise<HaiResult<PushRegistration>>\n /** 监听原生事件;设置阶段限时 30 秒,成功后由 cleanup/信号/模块关闭释放。 */\n listen: (callbacks: PushNotificationCallbacks, options?: PushOptions) => Promise<HaiResult<PushCleanup>>\n}\n\n/**\n * 状态栏操作\n * @example\n * ```ts\n * import type { StatusBarOperations } from '@h-ai/capacitor'\n *\n * async function use(operations: StatusBarOperations) {\n * const result = await operations.configure({ style: 'dark' })\n * if (!result.success) return result.error.message\n * return result.data\n * }\n * // 由已初始化的 capacitor.statusBar 提供这些操作。\n * ```\n */\nexport interface StatusBarOperations {\n /** 配置状态栏(样式、背景色、沉浸式) */\n configure: (config: StatusBarConfig) => Promise<HaiResult<void>>\n /** 显示状态栏 */\n show: () => Promise<HaiResult<void>>\n /** 隐藏状态栏 */\n hide: () => Promise<HaiResult<void>>\n}\n\n/**\n * Preferences 操作(安全读写,返回 HaiResult)\n * @example\n * ```ts\n * import type { PreferencesOperations } from '@h-ai/capacitor'\n *\n * async function use(operations: PreferencesOperations) {\n * const result = await operations.get('theme')\n * if (!result.success) return result.error.message\n * return result.data\n * }\n * // 由已初始化的 capacitor.preferences 提供这些操作。\n * ```\n */\nexport interface PreferencesOperations {\n /** 安全读取 Preference 值 */\n get: (key: string) => Promise<HaiResult<string | null>>\n /** 安全写入 Preference 值 */\n set: (key: string, value: string) => Promise<HaiResult<void>>\n /** 安全删除 Preference 值 */\n remove: (key: string) => Promise<HaiResult<void>>\n}\n\n// ─── 函数接口 ───\n\n/**\n * Capacitor 模块服务对象接口\n * @example\n * ```ts\n * import { capacitor } from '@h-ai/capacitor'\n *\n * const initialized = await capacitor.init()\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await capacitor.device.getInfo()\n * if (!result.success) throw new Error(result.error.message)\n * void result.data\n * } finally {\n * const closed = await capacitor.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport interface CapacitorFunctions {\n /** 初始化 Capacitor 模块 */\n init: () => Promise<HaiResult<void>>\n /** 关闭模块并回收推送监听;失败时保持不可用,保留句柄供再次 close 重试。 */\n close: () => Promise<HaiResult<void>>\n /** 获取当前平台 */\n getPlatform: () => string\n /** 是否运行在原生 App 中 */\n isNative: () => boolean\n /** 是否已初始化 */\n readonly isInitialized: boolean\n\n /** 设备信息操作 */\n readonly device: DeviceOperations\n /** 相机操作 */\n readonly camera: CameraOperations\n /** 推送通知操作 */\n readonly push: PushOperations\n /** 状态栏操作 */\n readonly statusBar: StatusBarOperations\n /** Preferences 安全读写操作 */\n readonly preferences: PreferencesOperations\n}\n\n// ─── 设备信息 ───\n\n/**\n * 设备平台\n * @example\n * ```ts\n * import type { DevicePlatform } from '@h-ai/capacitor'\n *\n * const value: DevicePlatform = 'android'\n * void value\n * ```\n */\nexport type DevicePlatform = 'ios' | 'android' | 'web'\n\n/**\n * 设备信息\n * @example\n * ```ts\n * import type { DeviceInfo } from '@h-ai/capacitor'\n *\n * const value: DeviceInfo = { platform: 'android', osVersion: '15', model: 'Example device', manufacturer: 'Example', isVirtual: false }\n * void value\n * ```\n */\nexport interface DeviceInfo {\n /** 平台(ios / android / web) */\n platform: DevicePlatform\n /** 操作系统版本(如 '14.5') */\n osVersion: string\n /** 设备型号(如 'iPhone 14 Pro') */\n model: string\n /** 设备制造商 */\n manufacturer: string\n /** 是否为虚拟设备(模拟器) */\n isVirtual: boolean\n /** 应用版本 */\n appVersion?: string\n /** 应用 Build 号 */\n appBuild?: string\n}\n\n// ─── 推送通知 ───\n\n/**\n * 推送流程的取消选项;listen 成功后信号仍可终止监听。\n * @example\n * ```ts\n * import type { PushOptions } from '@h-ai/capacitor'\n *\n * const value: PushOptions = { signal: new AbortController().signal }\n * void value\n * ```\n */\nexport interface PushOptions {\n /** 取消当前注册或监听,不影响其他消费者拥有的句柄。 */\n signal?: AbortSignal\n}\n\n/**\n * 幂等推送清理函数;原生 remove 失败返回错误,调用同一函数可重试。\n * @example\n * ```ts\n * import type { PushCleanup } from '@h-ai/capacitor'\n *\n * async function release(cleanup: PushCleanup) {\n * const result = await cleanup()\n * if (!result.success) return cleanup() // 同一所有权句柄允许重试清理。\n * return result\n * }\n * ```\n */\nexport type PushCleanup = () => Promise<HaiResult<void>>\n\n/**\n * 推送通知数据\n * @example\n * ```ts\n * import type { PushNotification } from '@h-ai/capacitor'\n *\n * const value: PushNotification = { id: 'notice_1', title: 'Order ready', data: { orderId: 'order_1' } }\n * void value\n * ```\n */\nexport interface PushNotification {\n /** 通知 ID */\n id: string\n /** 标题 */\n title?: string\n /** 正文 */\n body?: string\n /** 附加数据 */\n data?: Record<string, unknown>\n}\n\n/**\n * 推送注册结果\n * @example\n * ```ts\n * import type { PushRegistration } from '@h-ai/capacitor'\n *\n * const value: PushRegistration = { token: 'example-only-native-token' }\n * void value\n * ```\n */\nexport interface PushRegistration {\n /** 设备推送 Token */\n token: string\n}\n\n/**\n * 推送通知回调\n * @example\n * ```ts\n * import type { PushNotificationCallbacks } from '@h-ai/capacitor'\n *\n * const value: PushNotificationCallbacks = { onReceived: notification => { document.title = notification.title ?? 'Notice' } }\n * void value\n * ```\n */\nexport interface PushNotificationCallbacks {\n /** 收到推送时调用 */\n onReceived?: (notification: PushNotification) => void\n /** 用户点击推送时调用 */\n onActionPerformed?: (notification: PushNotification) => void\n}\n\n// ─── 相机 ───\n\n/**\n * 照片来源\n * @example\n * ```ts\n * import type { PhotoSource } from '@h-ai/capacitor'\n *\n * const value: PhotoSource = 'photos'\n * void value\n * ```\n */\nexport type PhotoSource = 'camera' | 'photos' | 'prompt'\n\n/**\n * 照片选项\n * @example\n * ```ts\n * import type { PhotoOptions } from '@h-ai/capacitor'\n *\n * const value: PhotoOptions = { source: 'photos', resultType: 'uri', quality: 90, width: 1200 }\n * void value\n * ```\n */\nexport interface PhotoOptions {\n /** 来源(默认 'prompt') */\n source?: PhotoSource\n /** 图片质量 0-100(默认 90) */\n quality?: number\n /** 最大宽度(像素) */\n width?: number\n /** 最大高度(像素) */\n height?: number\n /** 返回格式 */\n resultType?: 'uri' | 'base64' | 'dataUrl'\n}\n\n/**\n * 照片结果\n * @example\n * ```ts\n * import type { PhotoResult } from '@h-ai/capacitor'\n *\n * const value: PhotoResult = { data: 'file:///example/photo.jpeg', format: 'jpeg' }\n * void value\n * ```\n */\nexport interface PhotoResult {\n /** 图片路径或 base64 数据 */\n data: string\n /** 原生插件返回的格式扩展名(如 jpeg),不是 MIME 类型 */\n format: string\n}\n\n// ─── 状态栏 ───\n\n/**\n * 状态栏样式\n * @example\n * ```ts\n * import type { StatusBarStyle } from '@h-ai/capacitor'\n *\n * const value: StatusBarStyle = 'dark'\n * void value\n * ```\n */\nexport type StatusBarStyle = 'dark' | 'light' | 'default'\n\n/**\n * 状态栏配置\n * @example\n * ```ts\n * import type { StatusBarConfig } from '@h-ai/capacitor'\n *\n * const value: StatusBarConfig = { style: 'dark', overlay: false }\n * void value\n * ```\n */\nexport interface StatusBarConfig {\n /** 状态栏文字样式 */\n style?: StatusBarStyle\n /** 背景颜色(十六进制) */\n backgroundColor?: string\n /** 是否覆盖在内容上(沉浸式) */\n overlay?: boolean\n}\n","/**\n * @h-ai/capacitor — 相机 / 相册\n *\n * 封装 `@capacitor/camera` 插件,提供拍照与相册选择能力。\n *\n * @module capacitor-camera\n */\n\nimport type { HaiResult } from '@h-ai/core'\nimport type { PhotoOptions, PhotoResult } from './capacitor-types.js'\nimport { err, ok } from '@h-ai/core'\nimport { capacitorM } from './capacitor-i18n.js'\nimport { HaiCapacitorError } from './capacitor-types.js'\n\n/** 照片来源映射(内部使用) */\nconst SOURCE_MAP = {\n camera: 'Camera',\n photos: 'Photos',\n prompt: 'Prompt',\n} as const\n\n/** 结果类型映射(内部使用) */\nconst RESULT_TYPE_MAP = {\n uri: 'Uri',\n base64: 'Base64',\n dataUrl: 'DataUrl',\n} as const\n\n/**\n * 拍照或选取相册图片\n *\n * 需要安装 `@capacitor/camera` 插件。\n *\n * @param options - 照片选项\n * @returns 照片结果\n * @example\n * ```ts\n * import { capacitor } from '@h-ai/capacitor'\n *\n * const initialized = await capacitor.init()\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await capacitor.camera.takePhoto({ source: 'photos', width: 1200 })\n * if (!result.success) throw new Error(result.error.message)\n * void result.data\n * } finally {\n * const closed = await capacitor.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport async function takePhoto(options?: PhotoOptions): Promise<HaiResult<PhotoResult>> {\n try {\n const { Camera, CameraSource, CameraResultType } = await import('@capacitor/camera')\n\n const source = options?.source ?? 'prompt'\n const resultType = options?.resultType ?? 'uri'\n\n const photo = await Camera.getPhoto({\n quality: options?.quality ?? 90,\n source: CameraSource[SOURCE_MAP[source] as keyof typeof CameraSource],\n resultType: CameraResultType[RESULT_TYPE_MAP[resultType] as keyof typeof CameraResultType],\n width: options?.width,\n height: options?.height,\n })\n\n // 根据 resultType 返回对应数据\n let data: string\n if (resultType === 'base64') {\n data = photo.base64String ?? ''\n }\n else if (resultType === 'dataUrl') {\n data = photo.dataUrl ?? ''\n }\n else {\n data = photo.webPath ?? ''\n }\n\n return ok({\n data,\n format: photo.format,\n })\n }\n catch (cause) {\n return err(\n HaiCapacitorError.CAMERA_FAILED,\n capacitorM('capacitor_cameraFailed'),\n cause,\n )\n }\n}\n","/**\n * @h-ai/capacitor — 设备信息\n *\n * 封装 `@capacitor/device` 插件,提供统一的设备信息读取接口。\n *\n * @module capacitor-device\n */\n\nimport type { HaiResult } from '@h-ai/core'\nimport type { DeviceInfo } from './capacitor-types.js'\nimport { err, ok } from '@h-ai/core'\nimport { capacitorM } from './capacitor-i18n.js'\nimport { HaiCapacitorError } from './capacitor-types.js'\n\n/**\n * 获取设备信息\n *\n * 需要安装 `@capacitor/device` 插件。\n *\n * @returns 设备信息(平台、系统版本、型号等)\n * @example\n * ```ts\n * import { capacitor } from '@h-ai/capacitor'\n *\n * const initialized = await capacitor.init()\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await capacitor.device.getInfo()\n * if (!result.success) throw new Error(result.error.message)\n * void result.data\n * } finally {\n * const closed = await capacitor.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport async function getDeviceInfo(): Promise<HaiResult<DeviceInfo>> {\n try {\n const { Device } = await import('@capacitor/device')\n const info = await Device.getInfo()\n\n return ok({\n platform: info.platform as DeviceInfo['platform'],\n osVersion: info.osVersion,\n model: info.model,\n manufacturer: info.manufacturer,\n isVirtual: info.isVirtual,\n })\n }\n catch (cause) {\n return err(\n HaiCapacitorError.DEVICE_INFO_FAILED,\n capacitorM('capacitor_deviceInfoFailed'),\n cause,\n )\n }\n}\n\n/**\n * 获取应用版本信息\n *\n * @returns appVersion 与 appBuild\n * @example\n * ```ts\n * import { capacitor } from '@h-ai/capacitor'\n *\n * const initialized = await capacitor.init()\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await capacitor.device.getAppVersion()\n * if (!result.success) throw new Error(result.error.message)\n * void result.data\n * } finally {\n * const closed = await capacitor.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport async function getAppVersion(): Promise<HaiResult<{ version: string, build: string }>> {\n try {\n const { App } = await import('@capacitor/app')\n const info = await App.getInfo()\n\n return ok({\n version: info.version,\n build: info.build,\n })\n }\n catch (cause) {\n return err(\n HaiCapacitorError.APP_VERSION_FAILED,\n capacitorM('capacitor_appVersionFailed'),\n cause,\n )\n }\n}\n","/**\n * @h-ai/capacitor — 推送通知及其受管监听生命周期。\n * @module capacitor-push\n */\nimport type { PluginListenerHandle } from '@capacitor/core'\nimport type { HaiResult } from '@h-ai/core'\nimport type { PushCleanup, PushNotificationCallbacks, PushOptions, PushRegistration } from './capacitor-types.js'\nimport { core, err, ok } from '@h-ai/core'\nimport { capacitorM } from './capacitor-i18n.js'\nimport { HaiCapacitorError } from './capacitor-types.js'\n\nconst logger = core.logger.child({ module: 'capacitor', scope: 'push' })\n\n/**\n * 两类推送流程共用的资源所有权;关闭立即停止回调,迟到 handle 仍必须回收。\n * @example 模块内部上下文\n * ```ts\n * const scope: PushScope = createPushScope({ signal: new AbortController().signal })\n * try {\n * const state = await scope.wait(async () => 'ready')\n * scope.assertOpen()\n * void state\n * } finally {\n * await scope.dispose()\n * }\n * ```\n */\ninterface PushScope {\n readonly signal: AbortSignal\n assertOpen: () => void\n attach: (add: () => Promise<PluginListenerHandle>) => Promise<void>\n wait: <T>(operation: () => Promise<T>) => Promise<T>\n finishSetup: () => void\n dispose: () => Promise<void>\n}\n\nconst activeScopes = new Set<PushScope>()\n\n/** 为一次注册/监听建立所有权,不使用 removeAllListeners 影响其他消费者。 */\nfunction createPushScope(options?: PushOptions): PushScope {\n let scope: PushScope\n const controller = new AbortController()\n const handles = new Set<PluginListenerHandle>()\n const removals = new Map<PluginListenerHandle, Promise<void>>()\n let pending = 0\n let closing: Promise<void> | undefined\n let timer: ReturnType<typeof setTimeout> | undefined\n const cancelled = () => new Error(capacitorM('capacitor_pushCancelled'))\n\n function releaseIfComplete(): void {\n if (controller.signal.aborted && pending === 0 && handles.size === 0)\n activeScopes.delete(scope)\n }\n\n function remove(handle: PluginListenerHandle): Promise<void> {\n const current = removals.get(handle)\n if (current)\n return current\n const removal = Promise.resolve().then(() => handle.remove()).then(() => {\n handles.delete(handle)\n }).finally(() => {\n removals.delete(handle)\n releaseIfComplete()\n })\n removals.set(handle, removal)\n return removal\n }\n\n function finishSetup(): void {\n if (timer !== undefined) {\n clearTimeout(timer)\n timer = undefined\n }\n }\n\n function dispose(): Promise<void> {\n controller.abort()\n finishSetup()\n options?.signal?.removeEventListener('abort', onAbort)\n if (closing)\n return closing\n closing = Promise.allSettled([...handles].map(remove)).then((results) => {\n releaseIfComplete()\n if (results.some(result => result.status === 'rejected'))\n throw new Error(capacitorM('capacitor_pushCleanupFailed'))\n }).finally(() => { closing = undefined })\n return closing\n }\n\n function onAbort(): void {\n void dispose().catch(() => logger.warn('Push listener cleanup failed; retry cleanup or capacitor.close'))\n }\n\n scope = {\n signal: controller.signal,\n assertOpen() {\n if (controller.signal.aborted)\n throw cancelled()\n },\n async attach(add) {\n scope.assertOpen()\n pending++\n try {\n const handle = await add()\n handles.add(handle)\n if (controller.signal.aborted) {\n await remove(handle)\n throw cancelled()\n }\n }\n finally {\n pending--\n releaseIfComplete()\n }\n },\n async wait(operation) {\n scope.assertOpen()\n let interrupt: (() => void) | undefined\n const aborted = new Promise<never>((_, reject) => {\n interrupt = () => reject(cancelled())\n controller.signal.addEventListener('abort', interrupt, { once: true })\n })\n try {\n return await Promise.race([\n Promise.resolve().then(operation),\n aborted,\n ])\n }\n finally {\n if (interrupt)\n controller.signal.removeEventListener('abort', interrupt)\n }\n },\n finishSetup,\n dispose,\n }\n activeScopes.add(scope)\n timer = setTimeout(onAbort, 30_000)\n options?.signal?.addEventListener('abort', onAbort, { once: true })\n if (options?.signal?.aborted)\n onAbort()\n return scope\n}\n\n/** 原生能力必须实际存在;Web 插件不可作为 FCM/APNs 注册证明。 */\nasync function loadPushPlugin() {\n const { Capacitor } = await import('@capacitor/core')\n if (!Capacitor.isNativePlatform())\n throw new Error(capacitorM('capacitor_notAvailable'))\n return (await import('@capacitor/push-notifications')).PushNotifications\n}\n\n/**\n * 关闭模块所拥有的全部推送监听器;失败句柄保留以便再次 close 重试。\n * @returns 全部移除成功,或 PUSH_CLEANUP_FAILED;迟到句柄到达时立即移除。\n * @example\n * ```ts\n * import { capacitor } from '@h-ai/capacitor'\n *\n * // 关闭由本模块拥有的监听器,失败句柄可再次 close 重试。\n * const closed = await capacitor.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * ```\n */\nexport async function closePushListeners(): Promise<HaiResult<void>> {\n const results = await Promise.allSettled([...activeScopes].map(scope => scope.dispose()))\n return results.some(result => result.status === 'rejected')\n ? err(HaiCapacitorError.PUSH_CLEANUP_FAILED, capacitorM('capacitor_pushCleanupFailed'))\n : ok(undefined)\n}\n\n/**\n * 注册原生推送 Token;30 秒包含权限、挂载监听器与原生注册等待。\n * 部分失败、超时、AbortSignal 和模块关闭都会回收已知及迟到句柄。\n * @param options - 可选取消信号;取消返回失败结果,不注册后续原生操作。\n * @returns 真实原生插件返回的 token 或 PUSH_REGISTER_FAILED。\n * @example\n * ```ts\n * import { capacitor } from '@h-ai/capacitor'\n *\n * const initialized = await capacitor.init()\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await capacitor.push.register({ signal: new AbortController().signal })\n * if (!result.success) throw new Error(result.error.message)\n * void result.data\n * } finally {\n * const closed = await capacitor.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport async function registerPush(options?: PushOptions): Promise<HaiResult<PushRegistration>> {\n const scope = createPushScope(options)\n try {\n const token = await scope.wait(async () => {\n const push = await loadPushPlugin()\n scope.assertOpen()\n const permission = await push.requestPermissions()\n scope.assertOpen()\n if (permission.receive !== 'granted')\n throw new Error(capacitorM('capacitor_pushRegisterFailed'))\n let resolveToken!: (value: string) => void\n let rejectToken!: (reason: Error) => void\n const tokenResult = new Promise<string>((resolve, reject) => {\n resolveToken = resolve\n rejectToken = reject\n })\n // 在触发 register 之前就订阅结果,避免同步插件回调丢失或未处理拒绝。\n const setup = async () => {\n await scope.attach(() => push.addListener('registration', (result) => {\n if (!scope.signal.aborted)\n resolveToken(result.value)\n }))\n await scope.attach(() => push.addListener('registrationError', () => {\n if (!scope.signal.aborted)\n rejectToken(new Error(capacitorM('capacitor_pushRegisterFailed')))\n }))\n scope.assertOpen()\n await push.register()\n }\n return (await Promise.all([\n setup(),\n tokenResult,\n ]))[1]\n })\n await scope.dispose()\n return ok({ token })\n }\n catch (cause) {\n await scope.dispose().catch(() => logger.warn('Push registration cleanup failed; retry capacitor.close'))\n return err(HaiCapacitorError.PUSH_REGISTER_FAILED, capacitorM('capacitor_pushRegisterFailed'), cause)\n }\n}\n\n/**\n * 监听原生推送事件;设置阶段限时 30 秒,之后由 cleanup、信号或模块关闭结束。\n * @param callbacks - 收到消息与点击消息的回调。\n * @param options - 可选取消信号,监听建立后仍有效。\n * @returns 幂等清理函数;清理失败可见且可重试,关闭后立即停止业务回调。\n * @example\n * ```ts\n * import { capacitor } from '@h-ai/capacitor'\n *\n * const initialized = await capacitor.init()\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const listening = await capacitor.push.listen({ onReceived: notice => { document.title = notice.title ?? 'Notice' } })\n * if (!listening.success) throw new Error(listening.error.message)\n * const closed = await listening.data()\n * if (!closed.success) throw new Error(closed.error.message)\n * } finally {\n * const closed = await capacitor.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport async function listenPush(callbacks: PushNotificationCallbacks, options?: PushOptions): Promise<HaiResult<PushCleanup>> {\n const scope = createPushScope(options)\n try {\n await scope.wait(async () => {\n const push = await loadPushPlugin()\n scope.assertOpen()\n if (callbacks.onReceived) {\n await scope.attach(() => push.addListener('pushNotificationReceived', (notification) => {\n if (!scope.signal.aborted) {\n callbacks.onReceived?.({\n id: notification.id,\n title: notification.title,\n body: notification.body,\n data: notification.data as Record<string, unknown>,\n })\n }\n }))\n }\n if (callbacks.onActionPerformed) {\n await scope.attach(() => push.addListener('pushNotificationActionPerformed', (action) => {\n if (!scope.signal.aborted) {\n callbacks.onActionPerformed?.({\n id: action.notification.id,\n title: action.notification.title,\n body: action.notification.body,\n data: action.notification.data as Record<string, unknown>,\n })\n }\n }))\n }\n })\n scope.assertOpen()\n scope.finishSetup()\n return ok(async () => {\n try {\n await scope.dispose()\n return ok(undefined)\n }\n catch (cause) {\n return err(HaiCapacitorError.PUSH_CLEANUP_FAILED, capacitorM('capacitor_pushCleanupFailed'), cause)\n }\n })\n }\n catch (cause) {\n await scope.dispose().catch(() => logger.warn('Push setup cleanup failed; retry capacitor.close'))\n return err(HaiCapacitorError.PUSH_LISTEN_FAILED, capacitorM('capacitor_pushListenFailed'), cause)\n }\n}\n","/**\n * @h-ai/capacitor — 状态栏\n *\n * 封装 `@capacitor/status-bar` 插件,支持沉浸式状态栏配置。\n *\n * @module capacitor-status-bar\n */\n\nimport type { HaiResult } from '@h-ai/core'\nimport type { StatusBarConfig } from './capacitor-types.js'\nimport { err, ok } from '@h-ai/core'\nimport { capacitorM } from './capacitor-i18n.js'\nimport { HaiCapacitorError } from './capacitor-types.js'\n\n/**\n * 配置状态栏\n *\n * 需要安装 `@capacitor/status-bar` 插件。\n *\n * @param config - 状态栏配置\n * @returns HaiResult\n * @example\n * ```ts\n * import { capacitor } from '@h-ai/capacitor'\n *\n * const initialized = await capacitor.init()\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await capacitor.statusBar.configure({ style: 'dark', overlay: false })\n * if (!result.success) throw new Error(result.error.message)\n * void result.data\n * } finally {\n * const closed = await capacitor.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport async function configureStatusBar(config: StatusBarConfig): Promise<HaiResult<void>> {\n try {\n const { StatusBar, Style } = await import('@capacitor/status-bar')\n\n // 设置样式\n if (config.style) {\n const styleMap: Record<string, typeof Style[keyof typeof Style]> = {\n dark: Style.Dark,\n light: Style.Light,\n default: Style.Default,\n }\n await StatusBar.setStyle({ style: styleMap[config.style] ?? Style.Default })\n }\n\n // 设置背景色\n if (config.backgroundColor) {\n await StatusBar.setBackgroundColor({ color: config.backgroundColor })\n }\n\n // 设置覆盖模式\n if (config.overlay !== undefined) {\n await StatusBar.setOverlaysWebView({ overlay: config.overlay })\n }\n\n return ok(undefined)\n }\n catch (cause) {\n return err(\n HaiCapacitorError.STATUS_BAR_FAILED,\n capacitorM('capacitor_statusBarFailed'),\n cause,\n )\n }\n}\n\n/**\n * 显示状态栏\n *\n * @returns HaiResult\n * @example\n * ```ts\n * import { capacitor } from '@h-ai/capacitor'\n *\n * const initialized = await capacitor.init()\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await capacitor.statusBar.show()\n * if (!result.success) throw new Error(result.error.message)\n * void result.data\n * } finally {\n * const closed = await capacitor.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport async function showStatusBar(): Promise<HaiResult<void>> {\n try {\n const { StatusBar } = await import('@capacitor/status-bar')\n await StatusBar.show()\n return ok(undefined)\n }\n catch (cause) {\n return err(\n HaiCapacitorError.STATUS_BAR_FAILED,\n capacitorM('capacitor_statusBarFailed'),\n cause,\n )\n }\n}\n\n/**\n * 隐藏状态栏\n *\n * @returns HaiResult\n * @example\n * ```ts\n * import { capacitor } from '@h-ai/capacitor'\n *\n * const initialized = await capacitor.init()\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await capacitor.statusBar.hide()\n * if (!result.success) throw new Error(result.error.message)\n * void result.data\n * } finally {\n * const closed = await capacitor.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport async function hideStatusBar(): Promise<HaiResult<void>> {\n try {\n const { StatusBar } = await import('@capacitor/status-bar')\n await StatusBar.hide()\n return ok(undefined)\n }\n catch (cause) {\n return err(\n HaiCapacitorError.STATUS_BAR_FAILED,\n capacitorM('capacitor_statusBarFailed'),\n cause,\n )\n }\n}\n","/**\n * @h-ai/capacitor — Token 存储\n *\n * 基于原生安全存储插件的 TokenStorage 实现。\n * 仅在 Capacitor 原生环境启用,拒绝回退到不安全的 Web 存储。\n *\n * @module capacitor-token-storage\n */\n\nimport type { SecureStoragePlugin } from '@aparajita/capacitor-secure-storage'\nimport type { TokenStorage } from '@h-ai/api-client'\nimport type { HaiResult } from '@h-ai/core'\nimport { Preferences } from '@capacitor/preferences'\nimport { err, ok } from '@h-ai/core'\nimport { capacitorM } from './capacitor-i18n.js'\nimport { HaiCapacitorError } from './capacitor-types.js'\n\n/** 原生安全存储 Key;普通 Preferences 不存放认证凭据。 */\nconst PREF_ACCESS_TOKEN = 'hai_access_token'\nconst PREF_REFRESH_TOKEN = 'hai_refresh_token'\n\n// 所有适配器使用相同原生键;串行完成写入/删除,防止旧写在 clear 返回后恢复持久凭据。\nlet credentialMutationTail: Promise<void> = Promise.resolve()\n\nfunction mutateCredentials(operation: () => Promise<void>): Promise<void> {\n const pending = credentialMutationTail.then(operation)\n credentialMutationTail = pending.catch(() => {})\n return pending\n}\n\n/** 动态 import 自带模块缓存;不永久缓存平台检查失败或缺失插件的结果。 */\nasync function loadSecureStoragePlugin(): Promise<SecureStoragePlugin> {\n const { Capacitor } = await import('@capacitor/core')\n if (!Capacitor.isNativePlatform())\n throw new Error(capacitorM('capacitor_notAvailable'))\n const { SecureStorage } = await import('@aparajita/capacitor-secure-storage')\n return SecureStorage\n}\n\n/**\n * 创建基于原生安全存储的 TokenStorage\n *\n * 在原生 Android/iOS 环境下使用 `@aparajita/capacitor-secure-storage`\n * 保存 access token / refresh token。所有方法的存储/平台错误均通过 Promise 拒绝传播,\n * Web 调用明确失败,不做存储回退或伪成功。\n * `clear()` 一开始即屏蔽本实例旧凭据,删除失败时仍保持屏蔽;相应 key 成功写入后恢复读取。\n * 任一凭据写入失败也会屏蔽整对凭据,避免 access 已保存、refresh 失败时暴露半完成登录。\n * 本实例屏蔽不代表原生持久数据已删除;跨重启撤销仍依赖服务端退出/撤销会话。\n * 同模块内全部适配器的原生变更按调用顺序完成;clear 等待更早写入,之后的显式写入可建立新凭据。\n *\n * @returns TokenStorage 实例\n * @throws 返回适配器的方法在插件不可用或原生存储失败时拒绝 Promise。\n * @example\n * ```ts\n * import { createCapacitorTokenStorage } from '@h-ai/capacitor'\n *\n * const storage = createCapacitorTokenStorage()\n * try {\n * const token = await storage.getAccessToken()\n * void token // 仅原生安全存储;平台或存储失败会拒绝 Promise。\n * } catch (cause) {\n * // 应阻止认证流程继续并呈现本地化错误;不回退到普通 Preferences。\n * void cause\n * }\n * ```\n */\nexport function createCapacitorTokenStorage(): TokenStorage {\n let credentialGeneration = 0\n let accessBlocked = false\n let refreshBlocked = false\n\n /** 使本代际的读取/写入资格失效;实例内屏蔽不声称原生数据已经删除。 */\n function blockCredentials(): void {\n credentialGeneration++\n accessBlocked = true\n refreshBlocked = true\n }\n\n /** 部分写入失败时撤销整对本地凭据,交由认证调用方显式重试持久清理。 */\n async function writeToken(key: typeof PREF_ACCESS_TOKEN | typeof PREF_REFRESH_TOKEN, token: string): Promise<void> {\n const writeGeneration = credentialGeneration\n await mutateCredentials(async () => {\n try {\n const storage = await loadSecureStoragePlugin()\n await storage.setItem(key, token)\n if (writeGeneration === credentialGeneration) {\n if (key === PREF_ACCESS_TOKEN)\n accessBlocked = false\n else\n refreshBlocked = false\n }\n }\n catch (error) {\n if (writeGeneration === credentialGeneration)\n blockCredentials()\n throw error\n }\n })\n }\n\n return {\n async getAccessToken(): Promise<string | null> {\n const readGeneration = credentialGeneration\n await credentialMutationTail\n const storage = await loadSecureStoragePlugin()\n if (accessBlocked)\n return null\n const token = await storage.getItem(PREF_ACCESS_TOKEN)\n return accessBlocked || readGeneration !== credentialGeneration\n ? null\n : token\n },\n\n async getRefreshToken(): Promise<string | null> {\n const readGeneration = credentialGeneration\n await credentialMutationTail\n const storage = await loadSecureStoragePlugin()\n if (refreshBlocked)\n return null\n const token = await storage.getItem(PREF_REFRESH_TOKEN)\n return refreshBlocked || readGeneration !== credentialGeneration\n ? null\n : token\n },\n\n async setAccessToken(token: string): Promise<void> {\n await writeToken(PREF_ACCESS_TOKEN, token)\n },\n\n async setRefreshToken(token: string): Promise<void> {\n await writeToken(PREF_REFRESH_TOKEN, token)\n },\n\n async clear(): Promise<void> {\n blockCredentials()\n await mutateCredentials(async () => {\n const storage = await loadSecureStoragePlugin()\n // 两个删除全部完成才释放变更队列,失败也不能留下迟到的删除影响后续登录。\n const results = await Promise.allSettled([\n storage.removeItem(PREF_ACCESS_TOKEN),\n storage.removeItem(PREF_REFRESH_TOKEN),\n ])\n const errors = results.filter(result => result.status === 'rejected').map(result => result.reason)\n if (errors.length > 0)\n throw new AggregateError(errors, capacitorM('capacitor_secureStorageClearFailed'))\n })\n },\n }\n}\n\n/**\n * 安全读取 Preference 值(返回 HaiResult)\n *\n * @param key - Preference Key\n * @returns HaiResult 包裹的值\n * @example\n * ```ts\n * import { capacitor } from '@h-ai/capacitor'\n *\n * const initialized = await capacitor.init()\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await capacitor.preferences.get('theme')\n * if (!result.success) throw new Error(result.error.message)\n * void result.data\n * } finally {\n * const closed = await capacitor.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport async function safeGetPreference(key: string): Promise<HaiResult<string | null>> {\n try {\n const { value } = await Preferences.get({ key })\n return ok(value)\n }\n catch (cause) {\n return err(\n HaiCapacitorError.PREFERENCES_GET_FAILED,\n capacitorM('capacitor_preferencesGetFailed'),\n cause,\n )\n }\n}\n\n/**\n * 安全写入 Preference 值\n *\n * @param key - Preference Key\n * @param value - 要写入的值\n * @returns HaiResult\n * @example\n * ```ts\n * import { capacitor } from '@h-ai/capacitor'\n *\n * const initialized = await capacitor.init()\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await capacitor.preferences.set('theme', 'dark')\n * if (!result.success) throw new Error(result.error.message)\n * void result.data\n * } finally {\n * const closed = await capacitor.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport async function safeSetPreference(key: string, value: string): Promise<HaiResult<void>> {\n try {\n await Preferences.set({ key, value })\n return ok(undefined)\n }\n catch (cause) {\n return err(\n HaiCapacitorError.PREFERENCES_SET_FAILED,\n capacitorM('capacitor_preferencesSetFailed'),\n cause,\n )\n }\n}\n\n/**\n * 安全删除 Preference 值\n *\n * @param key - Preference Key\n * @returns HaiResult\n * @example\n * ```ts\n * import { capacitor } from '@h-ai/capacitor'\n *\n * const initialized = await capacitor.init()\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await capacitor.preferences.remove('theme')\n * if (!result.success) throw new Error(result.error.message)\n * void result.data\n * } finally {\n * const closed = await capacitor.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport async function safeRemovePreference(key: string): Promise<HaiResult<void>> {\n try {\n await Preferences.remove({ key })\n return ok(undefined)\n }\n catch (cause) {\n return err(\n HaiCapacitorError.PREFERENCES_REMOVE_FAILED,\n capacitorM('capacitor_preferencesRemoveFailed'),\n cause,\n )\n }\n}\n","/**\n * @h-ai/capacitor — 模块入口(生命周期管理)\n *\n * 提供 Capacitor 原生桥接的初始化与环境检测。\n * `capacitor.init()` 应在应用启动时调用,用于检测 Capacitor 环境可用性。\n *\n * @module capacitor-main\n */\n\nimport type { HaiResult } from '@h-ai/core'\nimport type { CameraOperations, CapacitorFunctions, DeviceOperations, PreferencesOperations, PushOperations, StatusBarOperations } from './capacitor-types.js'\nimport { core, err, ok } from '@h-ai/core'\nimport { takePhoto } from './capacitor-camera.js'\nimport { getAppVersion, getDeviceInfo } from './capacitor-device.js'\nimport { capacitorM } from './capacitor-i18n.js'\nimport { closePushListeners, listenPush, registerPush } from './capacitor-push.js'\nimport { configureStatusBar, hideStatusBar, showStatusBar } from './capacitor-status-bar.js'\nimport { safeGetPreference, safeRemovePreference, safeSetPreference } from './capacitor-token-storage.js'\nimport { HaiCapacitorError } from './capacitor-types.js'\n\nconst logger = core.logger.child({ module: 'capacitor', scope: 'main' })\n\n/** 是否已初始化 */\nlet initialized = false\n\n/** 是否正在初始化中(并发防护) */\nlet initInProgress = false\n\n/** 缓存平台信息 */\nlet cachedPlatform: string | null = null\n\n// ─── 未初始化代理 ───\n\nconst notInitialized = core.module.createNotInitializedKit(\n HaiCapacitorError.NOT_INITIALIZED,\n () => capacitorM('capacitor_notInitialized'),\n)\n\nconst notInitializedDevice = notInitialized.proxy<DeviceOperations>()\nconst notInitializedCamera = notInitialized.proxy<CameraOperations>()\nconst notInitializedPush = notInitialized.proxy<PushOperations>()\nconst notInitializedStatusBar = notInitialized.proxy<StatusBarOperations>()\nconst notInitializedPreferences = notInitialized.proxy<PreferencesOperations>()\n\n// ─── 已初始化时的操作实现 ───\n\nconst deviceOps: DeviceOperations = {\n getInfo: getDeviceInfo,\n getAppVersion,\n}\n\nconst cameraOps: CameraOperations = {\n takePhoto,\n}\n\nconst pushOps: PushOperations = {\n register: registerPush,\n listen: listenPush,\n}\n\nconst statusBarOps: StatusBarOperations = {\n configure: configureStatusBar,\n show: showStatusBar,\n hide: hideStatusBar,\n}\n\nconst preferencesOps: PreferencesOperations = {\n get: safeGetPreference,\n set: safeSetPreference,\n remove: safeRemovePreference,\n}\n\n// ─── 检测 Capacitor 是否可用(模块内部使用) ───\n\nfunction isCapacitorAvailable(): boolean {\n try {\n // window 上由 Capacitor 运行时注入 Capacitor 全局对象,lib.dom 未声明该属性,故强转为可查库的 Record\n return typeof window !== 'undefined'\n && (window as unknown as Record<string, unknown>).Capacitor !== undefined\n }\n catch {\n return false\n }\n}\n\n// ─── 服务对象 ───\n\n/**\n * Capacitor 模块服务对象\n * @example\n * ```ts\n * import { capacitor } from '@h-ai/capacitor'\n *\n * const initialized = await capacitor.init()\n * if (!initialized.success) throw new Error(initialized.error.message)\n * try {\n * const result = await capacitor.device.getInfo()\n * if (!result.success) throw new Error(result.error.message)\n * void result.data\n * } finally {\n * const closed = await capacitor.close()\n * if (!closed.success) throw new Error(closed.error.message)\n * }\n * ```\n */\nexport const capacitor: CapacitorFunctions = {\n async init(): Promise<HaiResult<void>> {\n // 并发初始化防护:避免多次 init 同时执行导致资源泄漏\n if (initInProgress) {\n logger.warn('Capacitor init already in progress, skipping concurrent call')\n return err(\n HaiCapacitorError.INIT_IN_PROGRESS,\n capacitorM('capacitor_initInProgress'),\n )\n }\n initInProgress = true\n\n try {\n if (initialized) {\n logger.warn('Capacitor module is already initialized, reinitializing')\n const closed = await capacitor.close()\n if (!closed.success)\n return closed\n }\n // 先重试上次关闭未释放的句柄,不把资源清理失败隐藏成新的初始化成功。\n const cleaned = await closePushListeners()\n if (!cleaned.success)\n return cleaned\n\n logger.info('Initializing capacitor module')\n\n if (!isCapacitorAvailable()) {\n logger.error('Capacitor is not available in current environment')\n return err(\n HaiCapacitorError.NOT_AVAILABLE,\n capacitorM('capacitor_notAvailable'),\n )\n }\n\n const { Capacitor } = await import('@capacitor/core')\n const platform = Capacitor.getPlatform()\n\n if (!platform) {\n logger.error('Capacitor initialization failed, platform not detected')\n return err(\n HaiCapacitorError.INIT_FAILED,\n capacitorM('capacitor_initFailed'),\n )\n }\n\n cachedPlatform = platform\n initialized = true\n logger.info('Capacitor module initialized', { platform })\n return ok(undefined)\n }\n catch (cause) {\n logger.error('Capacitor module initialization failed', { error: cause })\n return err(\n HaiCapacitorError.INIT_FAILED,\n capacitorM('capacitor_initFailed'),\n cause,\n )\n }\n finally {\n initInProgress = false\n }\n },\n\n async close() {\n logger.info('Closing capacitor module')\n initialized = false\n cachedPlatform = null\n const cleaned = await closePushListeners()\n if (!cleaned.success)\n return cleaned\n logger.info('Capacitor module closed')\n return ok(undefined)\n },\n\n getPlatform(): string {\n if (cachedPlatform) {\n return cachedPlatform\n }\n try {\n // 同 isCapacitorAvailable:Capacitor 全局仅在运行时由 native bridge 注入,需强转后读取\n const cap = (window as unknown as Record<string, unknown>).Capacitor as { getPlatform?: () => string } | undefined\n return cap?.getPlatform?.() ?? 'web'\n }\n catch {\n return 'web'\n }\n },\n\n isNative(): boolean {\n try {\n const cap = (window as unknown as Record<string, unknown>).Capacitor as { isNativePlatform?: () => boolean } | undefined\n return cap?.isNativePlatform?.() ?? false\n }\n catch {\n return false\n }\n },\n\n get isInitialized(): boolean {\n return initialized\n },\n\n get device(): DeviceOperations {\n return initialized ? deviceOps : notInitializedDevice\n },\n\n get camera(): CameraOperations {\n return initialized ? cameraOps : notInitializedCamera\n },\n\n get push(): PushOperations {\n return initialized ? pushOps : notInitializedPush\n },\n\n get statusBar(): StatusBarOperations {\n return initialized ? statusBarOps : notInitializedStatusBar\n },\n\n get preferences(): PreferencesOperations {\n return initialized ? preferencesOps : notInitializedPreferences\n },\n}\n"]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# JDK 21、Android SDK 36 和 Build Tools 36.0.0 由 Cirrus Labs 基础镜像提供。
|
|
2
|
+
ARG HAI_ANDROID_IMAGE=ghcr.io/cirruslabs/android-sdk:36@sha256:d9c965f2373f9c8cc023b207cdcc7d21508f98e6a45ab66462fe41aa9f866dea
|
|
3
|
+
FROM ${HAI_ANDROID_IMAGE}
|
|
4
|
+
|
|
5
|
+
ARG HAI_NPM_REGISTRY=https://registry.npmjs.org
|
|
6
|
+
ENV JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64
|
|
7
|
+
|
|
8
|
+
# 构建机使用 linux/amd64,APK 内的目标 ABI 由应用的 Gradle 配置决定。
|
|
9
|
+
RUN test "$(uname -m)" = x86_64 \
|
|
10
|
+
&& curl --fail --silent --show-error --location --retry 3 --max-time 120 \
|
|
11
|
+
https://nodejs.org/dist/v24.22.1/node-v24.22.1-linux-x64.tar.gz -o /tmp/node.tar.gz \
|
|
12
|
+
&& echo 'eb6728e77c576a35162c122c45ccadf224d89ff00abc8dae46cc2897c6002d7c /tmp/node.tar.gz' | sha256sum --check \
|
|
13
|
+
&& tar -xzf /tmp/node.tar.gz --strip-components=1 -C /usr/local \
|
|
14
|
+
&& rm /tmp/node.tar.gz \
|
|
15
|
+
&& npm install --global --registry "$HAI_NPM_REGISTRY" --no-audit --no-fund --include=optional pnpm@12.11.2 \
|
|
16
|
+
&& test "$(pnpm --version)" = 12.11.2 \
|
|
17
|
+
&& npm cache clean --force
|
|
18
|
+
|
|
19
|
+
# Capacitor 插件可能由 AGP 选择默认的 Build Tools 35.0.0。
|
|
20
|
+
ARG HAI_ANDROID_BUILD_TOOLS_35_URL=https://dl.google.com/android/repository/build-tools_r35_linux.zip
|
|
21
|
+
RUN mkdir /tmp/android-tools35 \
|
|
22
|
+
&& curl --fail --silent --show-error --location --retry 3 --retry-all-errors --connect-timeout 20 \
|
|
23
|
+
"$HAI_ANDROID_BUILD_TOOLS_35_URL" -o /tmp/android-tools35/tools.zip \
|
|
24
|
+
&& echo 'bd3a4966912eb8b30ed0d00b0cda6b6543b949d5ffe00bea54c04c81e1561d88 /tmp/android-tools35/tools.zip' | sha256sum --check \
|
|
25
|
+
&& unzip -q /tmp/android-tools35/tools.zip -d /tmp/android-tools35 \
|
|
26
|
+
&& mv /tmp/android-tools35/android-15 "$ANDROID_HOME/build-tools/35.0.0" \
|
|
27
|
+
&& test -x "$ANDROID_HOME/build-tools/35.0.0/aapt2" \
|
|
28
|
+
&& rm /tmp/android-tools35/tools.zip \
|
|
29
|
+
&& rmdir /tmp/android-tools35
|
|
30
|
+
|
|
31
|
+
WORKDIR /workspace
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@h-ai/capacitor",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.0-beta.
|
|
4
|
+
"version": "1.0.0-beta.21",
|
|
5
5
|
"description": "Hai Framework Capacitor bridge module for native mobile capabilities.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"exports": {
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
"main": "./dist/index.js",
|
|
14
14
|
"types": "./dist/index.d.ts",
|
|
15
15
|
"files": [
|
|
16
|
-
"dist"
|
|
16
|
+
"dist",
|
|
17
|
+
"images/android.Dockerfile"
|
|
17
18
|
],
|
|
18
19
|
"publishConfig": {
|
|
19
20
|
"access": "public"
|
|
@@ -23,7 +24,7 @@
|
|
|
23
24
|
},
|
|
24
25
|
"peerDependencies": {
|
|
25
26
|
"@aparajita/capacitor-secure-storage": "^8.0.1",
|
|
26
|
-
"@capacitor/core": "^8.
|
|
27
|
+
"@capacitor/core": "^8.6.0",
|
|
27
28
|
"@capacitor/preferences": "^8.0.1"
|
|
28
29
|
},
|
|
29
30
|
"peerDependenciesMeta": {
|
|
@@ -47,21 +48,21 @@
|
|
|
47
48
|
}
|
|
48
49
|
},
|
|
49
50
|
"dependencies": {
|
|
50
|
-
"@h-ai/api-client": "1.0.0-beta.
|
|
51
|
-
"@h-ai/core": "1.0.0-beta.
|
|
51
|
+
"@h-ai/api-client": "1.0.0-beta.21",
|
|
52
|
+
"@h-ai/core": "1.0.0-beta.21"
|
|
52
53
|
},
|
|
53
54
|
"devDependencies": {
|
|
54
55
|
"@aparajita/capacitor-secure-storage": "^8.0.1",
|
|
55
56
|
"@capacitor/app": "^8.1.2",
|
|
56
|
-
"@capacitor/camera": "^8.2.
|
|
57
|
+
"@capacitor/camera": "^8.2.6",
|
|
57
58
|
"@capacitor/device": "^8.0.3",
|
|
58
59
|
"@capacitor/push-notifications": "^8.1.3",
|
|
59
60
|
"@capacitor/status-bar": "^8.0.4",
|
|
60
|
-
"@types/node": "^26.6.
|
|
61
|
-
"rimraf": "^6.1.
|
|
62
|
-
"tsup": "^
|
|
61
|
+
"@types/node": "^26.6.6",
|
|
62
|
+
"rimraf": "^6.1.4",
|
|
63
|
+
"tsup": "^9.0.0",
|
|
63
64
|
"typescript": "^6.0.3",
|
|
64
|
-
"vitest": "^
|
|
65
|
+
"vitest": "^5.1.0"
|
|
65
66
|
},
|
|
66
67
|
"scripts": {
|
|
67
68
|
"build": "tsup",
|