expo-harmony-toolkit 1.8.3 → 1.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,225 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderExpoSecureStorePreviewShim = renderExpoSecureStorePreviewShim;
4
+ exports.renderExpoAssetPreviewShim = renderExpoAssetPreviewShim;
5
+ exports.renderExpoDevicePreviewShim = renderExpoDevicePreviewShim;
6
+ exports.renderExpoClipboardPreviewShim = renderExpoClipboardPreviewShim;
7
+ exports.renderExpoHapticsPreviewShim = renderExpoHapticsPreviewShim;
8
+ function renderExpoSecureStorePreviewShim() {
9
+ return `'use strict';
10
+
11
+ const secureStore = new Map();
12
+
13
+ async function isAvailableAsync() {
14
+ return true;
15
+ }
16
+
17
+ async function setItemAsync(key, value) {
18
+ secureStore.set(String(key), String(value));
19
+ }
20
+
21
+ async function getItemAsync(key) {
22
+ const normalizedKey = String(key);
23
+ return secureStore.has(normalizedKey) ? secureStore.get(normalizedKey) : null;
24
+ }
25
+
26
+ async function deleteItemAsync(key) {
27
+ secureStore.delete(String(key));
28
+ }
29
+
30
+ module.exports = {
31
+ AFTER_FIRST_UNLOCK: 'AFTER_FIRST_UNLOCK',
32
+ AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY: 'AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY',
33
+ ALWAYS: 'ALWAYS',
34
+ ALWAYS_THIS_DEVICE_ONLY: 'ALWAYS_THIS_DEVICE_ONLY',
35
+ WHEN_PASSCODE_SET_THIS_DEVICE_ONLY: 'WHEN_PASSCODE_SET_THIS_DEVICE_ONLY',
36
+ WHEN_UNLOCKED: 'WHEN_UNLOCKED',
37
+ WHEN_UNLOCKED_THIS_DEVICE_ONLY: 'WHEN_UNLOCKED_THIS_DEVICE_ONLY',
38
+ isAvailableAsync,
39
+ setItemAsync,
40
+ getItemAsync,
41
+ deleteItemAsync,
42
+ };
43
+ `;
44
+ }
45
+ function renderExpoAssetPreviewShim() {
46
+ return `'use strict';
47
+
48
+ class Asset {
49
+ constructor(input) {
50
+ this.name = input.name ?? null;
51
+ this.type = input.type ?? null;
52
+ this.hash = input.hash ?? null;
53
+ this.uri = input.uri;
54
+ this.localUri = input.localUri ?? input.uri;
55
+ this.width = input.width ?? null;
56
+ this.height = input.height ?? null;
57
+ this.downloaded = Boolean(input.downloaded);
58
+ }
59
+
60
+ static fromURI(uri) {
61
+ return new Asset({
62
+ name: String(uri).split('/').pop() || 'remote-asset',
63
+ uri: String(uri),
64
+ downloaded: true,
65
+ });
66
+ }
67
+
68
+ static fromModule(moduleId) {
69
+ if (typeof moduleId === 'string') {
70
+ return Asset.fromURI(moduleId);
71
+ }
72
+
73
+ return new Asset({
74
+ name: 'expo-harmony-module-asset',
75
+ uri: 'asset://' + String(moduleId),
76
+ localUri: 'asset://' + String(moduleId),
77
+ });
78
+ }
79
+
80
+ static async loadAsync(moduleIds) {
81
+ return loadAsync(moduleIds);
82
+ }
83
+
84
+ async downloadAsync() {
85
+ this.downloaded = true;
86
+ this.localUri = this.localUri ?? this.uri;
87
+ return this;
88
+ }
89
+ }
90
+
91
+ async function loadAsync(moduleIds) {
92
+ const input = Array.isArray(moduleIds) ? moduleIds : [moduleIds];
93
+ return Promise.all(input.map((moduleId) => Asset.fromModule(moduleId).downloadAsync()));
94
+ }
95
+
96
+ function useAssets() {
97
+ return [null, null];
98
+ }
99
+
100
+ module.exports = {
101
+ Asset,
102
+ loadAsync,
103
+ useAssets,
104
+ };
105
+ `;
106
+ }
107
+ function renderExpoDevicePreviewShim() {
108
+ return `'use strict';
109
+
110
+ const DeviceType = {
111
+ UNKNOWN: 0,
112
+ PHONE: 1,
113
+ TABLET: 2,
114
+ DESKTOP: 3,
115
+ TV: 4,
116
+ };
117
+
118
+ async function getDeviceTypeAsync() {
119
+ return DeviceType.UNKNOWN;
120
+ }
121
+
122
+ module.exports = {
123
+ DeviceType,
124
+ brand: 'OpenHarmony',
125
+ manufacturer: 'OpenHarmony',
126
+ modelName: 'Harmony preview device',
127
+ modelId: null,
128
+ designName: null,
129
+ productName: 'expo-harmony-preview',
130
+ deviceYearClass: null,
131
+ totalMemory: null,
132
+ supportedCpuArchitectures: [],
133
+ osName: 'HarmonyOS',
134
+ osVersion: 'preview',
135
+ osBuildId: null,
136
+ osInternalBuildId: null,
137
+ osBuildFingerprint: null,
138
+ platformApiLevel: null,
139
+ deviceName: 'Harmony preview device',
140
+ isDevice: true,
141
+ getDeviceTypeAsync,
142
+ };
143
+ `;
144
+ }
145
+ function renderExpoClipboardPreviewShim() {
146
+ return `'use strict';
147
+
148
+ let clipboardString = '';
149
+ let clipboardUrl = null;
150
+ let clipboardImage = null;
151
+
152
+ async function setStringAsync(value) {
153
+ clipboardString = String(value);
154
+ clipboardUrl = null;
155
+ return true;
156
+ }
157
+
158
+ async function getStringAsync() {
159
+ return clipboardString;
160
+ }
161
+
162
+ async function hasStringAsync() {
163
+ return clipboardString.length > 0;
164
+ }
165
+
166
+ async function setUrlAsync(value) {
167
+ clipboardUrl = String(value);
168
+ clipboardString = clipboardUrl;
169
+ }
170
+
171
+ async function getUrlAsync() {
172
+ return clipboardUrl;
173
+ }
174
+
175
+ async function setImageAsync(value) {
176
+ clipboardImage = String(value);
177
+ }
178
+
179
+ async function getImageAsync() {
180
+ return clipboardImage;
181
+ }
182
+
183
+ module.exports = {
184
+ setStringAsync,
185
+ getStringAsync,
186
+ hasStringAsync,
187
+ setUrlAsync,
188
+ getUrlAsync,
189
+ setImageAsync,
190
+ getImageAsync,
191
+ };
192
+ `;
193
+ }
194
+ function renderExpoHapticsPreviewShim() {
195
+ return `'use strict';
196
+
197
+ const ImpactFeedbackStyle = {
198
+ Light: 'light',
199
+ Medium: 'medium',
200
+ Heavy: 'heavy',
201
+ Rigid: 'rigid',
202
+ Soft: 'soft',
203
+ };
204
+
205
+ const NotificationFeedbackType = {
206
+ Success: 'success',
207
+ Warning: 'warning',
208
+ Error: 'error',
209
+ };
210
+
211
+ async function selectionAsync() {}
212
+
213
+ async function impactAsync() {}
214
+
215
+ async function notificationAsync() {}
216
+
217
+ module.exports = {
218
+ ImpactFeedbackStyle,
219
+ NotificationFeedbackType,
220
+ selectionAsync,
221
+ impactAsync,
222
+ notificationAsync,
223
+ };
224
+ `;
225
+ }
@@ -30,6 +30,18 @@ const EXPLORATORY_EVIDENCE_SOURCE = {
30
30
  device: 'none',
31
31
  release: 'none',
32
32
  };
33
+ const APP_FOUNDATION_BASELINE_EVIDENCE = {
34
+ bundle: true,
35
+ debugBuild: true,
36
+ device: false,
37
+ release: false,
38
+ };
39
+ const APP_FOUNDATION_BASELINE_EVIDENCE_SOURCE = {
40
+ bundle: 'automated',
41
+ debugBuild: 'automated',
42
+ device: 'none',
43
+ release: 'none',
44
+ };
33
45
  exports.CAPABILITY_DEFINITIONS = [
34
46
  {
35
47
  id: 'expo-file-system',
@@ -116,6 +128,101 @@ exports.CAPABILITY_DEFINITIONS = [
116
128
  'Exercise preview pause/resume plus denied or canceled outcomes without crashing the Harmony runtime.',
117
129
  ],
118
130
  },
131
+ {
132
+ id: 'expo-secure-store',
133
+ packageName: 'expo-secure-store',
134
+ status: 'manual',
135
+ supportTier: 'preview',
136
+ runtimeMode: 'shim',
137
+ evidence: APP_FOUNDATION_BASELINE_EVIDENCE,
138
+ evidenceSource: APP_FOUNDATION_BASELINE_EVIDENCE_SOURCE,
139
+ note: 'v1.9.0 app-foundation baseline provides a toolkit-managed session shim for secure-store API shape, keeping real encrypted Harmony storage on the promotion path before verified release parity.',
140
+ docsUrl: 'https://docs.expo.dev/versions/latest/sdk/securestore/',
141
+ nativePackageNames: [],
142
+ harmonyPermissions: [],
143
+ sampleRoute: '/secure-store',
144
+ acceptanceChecklist: [
145
+ 'Write a sample key with setItemAsync and read it back in the same JS session.',
146
+ 'Delete the sample key and verify getItemAsync returns null.',
147
+ 'Keep device persistence and encrypted backend behavior out of verified until native evidence is recorded.',
148
+ ],
149
+ },
150
+ {
151
+ id: 'expo-asset',
152
+ packageName: 'expo-asset',
153
+ status: 'manual',
154
+ supportTier: 'preview',
155
+ runtimeMode: 'shim',
156
+ evidence: APP_FOUNDATION_BASELINE_EVIDENCE,
157
+ evidenceSource: APP_FOUNDATION_BASELINE_EVIDENCE_SOURCE,
158
+ note: 'v1.9.0 app-foundation baseline keeps the Asset API bundle-safe on Harmony while native asset resolution and cache semantics remain promotion evidence.',
159
+ docsUrl: 'https://docs.expo.dev/versions/latest/sdk/asset/',
160
+ nativePackageNames: [],
161
+ harmonyPermissions: [],
162
+ sampleRoute: '/asset',
163
+ acceptanceChecklist: [
164
+ 'Create an Asset from a URI and expose deterministic metadata.',
165
+ 'Resolve loadAsync for one or more module identifiers without crashing the Harmony bundle.',
166
+ 'Keep native resource resolution and cache parity pending device and release evidence.',
167
+ ],
168
+ },
169
+ {
170
+ id: 'expo-device',
171
+ packageName: 'expo-device',
172
+ status: 'manual',
173
+ supportTier: 'preview',
174
+ runtimeMode: 'shim',
175
+ evidence: APP_FOUNDATION_BASELINE_EVIDENCE,
176
+ evidenceSource: APP_FOUNDATION_BASELINE_EVIDENCE_SOURCE,
177
+ note: 'v1.9.0 app-foundation baseline exposes stable Harmony placeholder device metadata for JS startup paths; real hardware metadata remains outside verified.',
178
+ docsUrl: 'https://docs.expo.dev/versions/latest/sdk/device/',
179
+ nativePackageNames: [],
180
+ harmonyPermissions: [],
181
+ sampleRoute: '/device',
182
+ acceptanceChecklist: [
183
+ 'Read stable device metadata constants without native module crashes.',
184
+ 'Resolve getDeviceTypeAsync with an explicit preview value.',
185
+ 'Record real hardware metadata behavior separately before promotion.',
186
+ ],
187
+ },
188
+ {
189
+ id: 'expo-clipboard',
190
+ packageName: 'expo-clipboard',
191
+ status: 'manual',
192
+ supportTier: 'preview',
193
+ runtimeMode: 'shim',
194
+ evidence: APP_FOUNDATION_BASELINE_EVIDENCE,
195
+ evidenceSource: APP_FOUNDATION_BASELINE_EVIDENCE_SOURCE,
196
+ note: 'v1.9.0 app-foundation baseline keeps clipboard reads and writes bundle-safe through a session shim while the Harmony clipboard adapter remains the native promotion path.',
197
+ docsUrl: 'https://docs.expo.dev/versions/latest/sdk/clipboard/',
198
+ nativePackageNames: ['@react-native-oh-tpl/clipboard'],
199
+ harmonyPermissions: [],
200
+ sampleRoute: '/clipboard',
201
+ acceptanceChecklist: [
202
+ 'Write a string through setStringAsync and read it back with getStringAsync.',
203
+ 'Expose hasStringAsync and URL helpers without crashing app-shell startup.',
204
+ 'Validate real system pasteboard behavior on device before verified promotion.',
205
+ ],
206
+ },
207
+ {
208
+ id: 'expo-haptics',
209
+ packageName: 'expo-haptics',
210
+ status: 'manual',
211
+ supportTier: 'preview',
212
+ runtimeMode: 'shim',
213
+ evidence: APP_FOUNDATION_BASELINE_EVIDENCE,
214
+ evidenceSource: APP_FOUNDATION_BASELINE_EVIDENCE_SOURCE,
215
+ note: 'v1.9.0 app-foundation baseline makes haptics calls no-op safely on Harmony until a real device vibration path is validated.',
216
+ docsUrl: 'https://docs.expo.dev/versions/latest/sdk/haptics/',
217
+ nativePackageNames: [],
218
+ harmonyPermissions: [],
219
+ sampleRoute: '/haptics',
220
+ acceptanceChecklist: [
221
+ 'Resolve selectionAsync, impactAsync, and notificationAsync without throwing.',
222
+ 'Expose Expo haptics enum values used by common app startup paths.',
223
+ 'Keep physical haptic feedback pending device evidence.',
224
+ ],
225
+ },
119
226
  {
120
227
  id: 'expo-notifications',
121
228
  packageName: 'expo-notifications',
@@ -135,6 +242,25 @@ exports.CAPABILITY_DEFINITIONS = [
135
242
  'Validate open-from-notification lifecycle.',
136
243
  ],
137
244
  },
245
+ {
246
+ id: 'react-native-gesture-handler',
247
+ packageName: 'react-native-gesture-handler',
248
+ status: 'manual',
249
+ supportTier: 'experimental',
250
+ runtimeMode: 'adapter',
251
+ evidence: APP_FOUNDATION_BASELINE_EVIDENCE,
252
+ evidenceSource: APP_FOUNDATION_BASELINE_EVIDENCE_SOURCE,
253
+ note: 'v1.9.0 formal acceptance slice tracks Gesture Handler through its Harmony adapter, but it remains experimental until device and release runtime evidence are closed.',
254
+ docsUrl: 'https://github.com/react-native-oh-library/react-native-harmony-gesture-handler',
255
+ nativePackageNames: ['@react-native-oh-tpl/react-native-gesture-handler'],
256
+ harmonyPermissions: [],
257
+ sampleRoute: '/gesture-handler',
258
+ acceptanceChecklist: [
259
+ 'Install the canonical package with the matching Harmony adapter.',
260
+ 'Bundle a minimal GestureHandlerRootView and tap handler surface.',
261
+ 'Record debug HAP evidence before any promotion decision; device and release remain separate gates.',
262
+ ],
263
+ },
138
264
  ];
139
265
  exports.CAPABILITY_BY_PACKAGE = Object.fromEntries(exports.CAPABILITY_DEFINITIONS.map((definition) => [definition.packageName, definition]));
140
266
  const SUPPORT_TIER_ORDER = {
@@ -112,8 +112,8 @@ exports.DEPENDENCY_CATALOG = {
112
112
  },
113
113
  'expo-asset': {
114
114
  status: 'manual',
115
- supportTier: 'experimental',
116
- note: 'Asset handling often needs Harmony-specific verification after bundling.',
115
+ supportTier: 'preview',
116
+ note: 'v1.9.0 app-foundation baseline keeps Asset API imports and load paths bundle-safe through the toolkit shim; native asset cache parity still needs device and release evidence.',
117
117
  },
118
118
  'expo-build-properties': {
119
119
  status: 'manual',
@@ -242,10 +242,15 @@ exports.DEPENDENCY_CATALOG = {
242
242
  },
243
243
  'expo-clipboard': {
244
244
  status: 'manual',
245
- supportTier: 'experimental',
246
- note: 'Clipboard support should be routed through the Harmony clipboard adapter where the app needs real pasteboard access.',
245
+ supportTier: 'preview',
246
+ note: 'v1.9.0 app-foundation baseline provides a session clipboard shim and keeps the Harmony clipboard adapter as the native promotion path.',
247
247
  replacement: '@react-native-oh-tpl/clipboard',
248
248
  },
249
+ 'expo-device': {
250
+ status: 'manual',
251
+ supportTier: 'preview',
252
+ note: 'v1.9.0 app-foundation baseline exposes stable Harmony placeholder device metadata while real hardware metadata remains promotion evidence.',
253
+ },
249
254
  'expo-dev-client': {
250
255
  status: 'manual',
251
256
  supportTier: 'experimental',
@@ -258,8 +263,8 @@ exports.DEPENDENCY_CATALOG = {
258
263
  },
259
264
  'expo-haptics': {
260
265
  status: 'manual',
261
- supportTier: 'experimental',
262
- note: 'Haptics require a Harmony runtime implementation before the app can claim device parity.',
266
+ supportTier: 'preview',
267
+ note: 'v1.9.0 app-foundation baseline turns haptics calls into safe no-ops until a real Harmony device feedback path is validated.',
263
268
  },
264
269
  'expo-image': {
265
270
  status: 'manual',
@@ -289,8 +294,8 @@ exports.DEPENDENCY_CATALOG = {
289
294
  },
290
295
  'expo-secure-store': {
291
296
  status: 'manual',
292
- supportTier: 'experimental',
293
- note: 'Secure storage must use a real Harmony secure backend or equivalent encrypted wrapper before release parity is claimed.',
297
+ supportTier: 'preview',
298
+ note: 'v1.9.0 app-foundation baseline provides a session secure-store shim; encrypted persistence still needs native device and release evidence.',
294
299
  },
295
300
  'expo-splash-screen': {
296
301
  status: 'manual',
@@ -7,5 +7,5 @@ export declare const PREVIEW_SAMPLE_PATH = "examples/official-native-capabilitie
7
7
  export declare const SUPPORTING_SAMPLE_PATHS: readonly ["examples/official-app-shell-sample", "examples/official-minimal-sample"];
8
8
  export declare const VERIFIED_JS_UI_CAPABILITY_NAMES: readonly ["expo-router", "expo-linking", "expo-constants", ...("react-native-reanimated" | "react-native-svg")[]];
9
9
  export declare const PREVIEW_CAPABILITY_DEFINITIONS: import("..").CapabilityDefinition[];
10
- export declare const EXPERIMENTAL_CAPABILITY_NAMES: readonly [...string[], "react-native-gesture-handler"];
11
- export declare const PUBLIC_CURRENT_VERSION = "1.8.3";
10
+ export declare const EXPERIMENTAL_CAPABILITY_NAMES: readonly string[];
11
+ export declare const PUBLIC_CURRENT_VERSION = "1.9.1";
@@ -22,7 +22,6 @@ exports.VERIFIED_JS_UI_CAPABILITY_NAMES = [
22
22
  ];
23
23
  exports.PREVIEW_CAPABILITY_DEFINITIONS = capabilities_1.CAPABILITY_DEFINITIONS.filter((definition) => definition.supportTier === 'preview');
24
24
  exports.EXPERIMENTAL_CAPABILITY_NAMES = [
25
- ...capabilities_1.CAPABILITY_DEFINITIONS.filter((definition) => definition.supportTier === 'experimental').map((definition) => definition.packageName),
26
- 'react-native-gesture-handler',
25
+ ...new Set(capabilities_1.CAPABILITY_DEFINITIONS.filter((definition) => definition.supportTier === 'experimental').map((definition) => definition.packageName)),
27
26
  ];
28
27
  exports.PUBLIC_CURRENT_VERSION = constants_1.TOOLKIT_VERSION;
@@ -32,7 +32,9 @@ function renderReadmeCurrentStatus(locale) {
32
32
  const releaseTracksValue = locale === 'zh'
33
33
  ? `\`latest\` = ${publicDocs_1.PUBLIC_RELEASE_TRACKS.latest};\`next\` = ${publicDocs_1.PUBLIC_RELEASE_TRACKS.next}`
34
34
  : `\`latest\` = fully accepted \`verified\` only; \`next\` = ${publicDocs_1.PUBLIC_RELEASE_TRACKS.next}`;
35
- const inputValue = locale === 'zh' ? 'Managed/CNG Expo 项目' : 'Managed/CNG Expo projects';
35
+ const inputValue = locale === 'zh'
36
+ ? 'Managed/CNG Expo 项目;bare workflow intake baseline'
37
+ : 'Managed/CNG Expo projects; bare workflow intake baseline';
36
38
  const listJoiner = locale === 'zh' ? '、' : ', ';
37
39
  return [
38
40
  `| ${headers[0]} | ${headers[1]} |`,
package/docs/cli-build.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # CLI 构建指南
2
2
 
3
- `v1.8.0` 延续 `verified + preview + experimental` 支持分层,并把四项 preview capability 的现有 `🟠` 缺口收口到明确 `🟡` 子集,其中 `expo55-rnoh082-ui-stack` 仍是唯一 verified 矩阵。
3
+ `v1.9.1` 延续 `verified + preview + experimental` 支持分层,把 bare workflow 放进 intake baseline,并补上 `build-hap` 的本地 HAR normalize opt-out;`expo55-rnoh082-ui-stack` 仍是唯一 verified 矩阵。
4
4
 
5
5
  CLI 命令集合不变:
6
6
 
@@ -39,6 +39,15 @@ expo-harmony build-hap --mode debug
39
39
  - `expo-image-picker`
40
40
  - `expo-location`
41
41
  - `expo-camera`
42
+ - `expo-secure-store`
43
+ - `expo-asset`
44
+ - `expo-device`
45
+ - `expo-clipboard`
46
+ - `expo-haptics`
47
+
48
+ 当前 experimental formal slice:
49
+
50
+ - `react-native-gesture-handler`
42
51
 
43
52
  ## UI stack 依赖安装注意事项
44
53
 
@@ -81,8 +90,9 @@ pnpm install --ignore-scripts
81
90
 
82
91
  额外说明:
83
92
 
84
- - `react-native-gesture-handler` 当前仍保留为矩阵外的手动探索项
85
- - `doctor --strict` 不再把它视为公开承诺的一部分
93
+ - `react-native-gesture-handler` 已进入 formal experimental slice,但仍不属于 verified 公开承诺
94
+ - bare workflow 当前只有 intake / debug baseline,不代表 release-ready
95
+ - `doctor --strict` 仍只代表完整验收的 verified 能力
86
96
 
87
97
  toolkit 不会改动用户业务路由、动画逻辑或页面源码;它只负责受管 sidecar、构建链和 metadata。
88
98
 
@@ -95,6 +105,27 @@ expo-harmony env --strict
95
105
  expo-harmony build-hap --mode debug
96
106
  ```
97
107
 
108
+ ### HAR normalize opt-out
109
+
110
+ 默认情况下,`build-hap` 会把 root / entry `oh-package.json5` 里的 `file:*.har` 本地依赖解压到 `harmony/expo-harmony-local-deps`,再让 `ohpm` 消费解压后的目录。这条路径仍是默认 verified build path。
111
+
112
+ 如果项目已经确认当前 DevEco / ohpm 能直接消费纯 HAR,可以显式跳过这一步:
113
+
114
+ ```bash
115
+ EXPO_HARMONY_SKIP_HAR_NORMALIZE=1 expo-harmony build-hap --mode debug
116
+ expo-harmony build-hap --mode debug --no-har-normalize
117
+ ```
118
+
119
+ 开启后:
120
+
121
+ - `file:../node_modules/.../*.har` specifier 会保持原样
122
+ - 不生成 `expo-harmony-local-deps`
123
+ - 不临时注册 normalized local HAR modules 到 `build-profile.json5`
124
+ - 不执行依赖 normalized local RNOH 目录的 codegen / path 兜底
125
+ - `ohpm install --all` 与 `hvigor assembleHap` 继续执行
126
+
127
+ 该开关是 escape hatch,不代表 opt-out 路径和默认路径具备完全相同的兼容兜底。
128
+
98
129
  ### Release
99
130
 
100
131
  ```bash
@@ -2,7 +2,7 @@
2
2
 
3
3
  路径:`examples/official-native-capabilities-sample`
4
4
 
5
- 这个 sample `v1.8.0` 的官方 preview native-capability walkthrough。它的目标不是把 preview 能力包装成 `verified`,而是把四项能力当前真实可承诺的 `🟡` 子集集中演示出来,并把 preview 边界收敛到真机 / release 证据,而不是接口缺口。
5
+ 这个 sample 是官方 preview native-capability 与 `v1.9.0` app-foundation walkthrough。它的目标不是把 preview 能力包装成 `verified`,而是把当前真实可承诺的 `🟡` 子集和 foundation shim baseline 集中演示出来,并把 preview 边界收敛到真机 / release 证据,而不是接口缺口。
6
6
 
7
7
  从 `v1.8.x` 开始,这个 sample 的角色会固定成两层:
8
8
 
@@ -19,6 +19,11 @@
19
19
  - `expo-image-picker`
20
20
  - `expo-location`
21
21
  - `expo-camera`
22
+ - `expo-secure-store`
23
+ - `expo-asset`
24
+ - `expo-device`
25
+ - `expo-clipboard`
26
+ - `expo-haptics`
22
27
 
23
28
  当前 route:
24
29
 
@@ -26,6 +31,11 @@
26
31
  - `/image-picker`
27
32
  - `/location`
28
33
  - `/camera`
34
+ - `/secure-store`
35
+ - `/asset`
36
+ - `/device`
37
+ - `/clipboard`
38
+ - `/haptics`
29
39
 
30
40
  对应的逐 capability 追踪板见:
31
41
 
@@ -88,6 +98,51 @@
88
98
  - microphone permission snapshot
89
99
  - denied / canceled / successful result 展示
90
100
 
101
+ ### `/secure-store`
102
+
103
+ `🟡 v1.9 app-foundation baseline`:
104
+
105
+ - `isAvailableAsync`
106
+ - session-scoped `setItemAsync`
107
+ - session-scoped `getItemAsync`
108
+ - `deleteItemAsync`
109
+ - encrypted persistence、keychain / keystore 等真实 native 后端仍待 device / release evidence
110
+
111
+ ### `/asset`
112
+
113
+ `🟡 v1.9 app-foundation baseline`:
114
+
115
+ - `Asset.fromURI`
116
+ - `Asset.loadAsync`
117
+ - deterministic metadata display
118
+ - native resource resolution、asset cache parity 仍待 device / release evidence
119
+
120
+ ### `/device`
121
+
122
+ `🟡 v1.9 app-foundation baseline`:
123
+
124
+ - stable Harmony placeholder metadata
125
+ - `getDeviceTypeAsync`
126
+ - real device model/build/hardware metadata 仍待 device / release evidence
127
+
128
+ ### `/clipboard`
129
+
130
+ `🟡 v1.9 app-foundation baseline`:
131
+
132
+ - session-scoped string write/read
133
+ - `hasStringAsync`
134
+ - URL helper roundtrip
135
+ - real system pasteboard behavior 仍待 Harmony adapter evidence
136
+
137
+ ### `/haptics`
138
+
139
+ `🟡 v1.9 app-foundation baseline`:
140
+
141
+ - `selectionAsync`
142
+ - `impactAsync`
143
+ - `notificationAsync`
144
+ - 当前为 no-op-safe shim;真实物理反馈仍待 device evidence
145
+
91
146
  ## 推荐命令
92
147
 
93
148
  ```bash
@@ -134,5 +189,5 @@ pnpm run harmony:build:release
134
189
 
135
190
  - 这个 sample 属于 `preview` 主线,不等于已经进入 `verified`
136
191
  - `doctor-report.json` 与 `toolkit-config.json` 中的 `runtimeMode` / `evidence.*` 会如实反映这些能力距离 verified 还缺哪些证据
137
- - 当前重点是“模拟器下真正可用、核心流清楚、文档一致”
192
+ - 当前重点是“bundle/debug baseline 可重复、核心流清楚、文档一致”
138
193
  - 等未来补齐真机 gate 后,才讨论 capability promotion