expo-harmony-toolkit 1.8.3 → 1.9.0

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  <div align="center">
2
2
  <h1>Expo Harmony Toolkit</h1>
3
- <p><strong>A HarmonyOS migration, admission, and UI-stack build toolkit for Managed/CNG Expo projects.</strong></p>
3
+ <p><strong>A HarmonyOS migration, admission, and UI-stack build toolkit for Managed/CNG Expo projects, now with a bare intake baseline.</strong></p>
4
4
  <p>One verified UI-stack matrix, additive preview/experimental capability tiers, managed Harmony sidecar scaffolding, and a toolkit-driven <code>doctor → init → bundle → build-hap</code> path.</p>
5
5
  <p>
6
6
  <a href="./README.md">简体中文</a> ·
@@ -9,9 +9,9 @@
9
9
  <p>
10
10
  <a href="https://github.com/BlackishGreen33/Expo-Harmony-Toolkit/actions/workflows/ci.yml"><img alt="Checks" src="https://img.shields.io/badge/checks-passing-16a34a?style=flat-square&logo=githubactions&logoColor=white"></a>
11
11
  <a href="./LICENSE"><img alt="License" src="https://img.shields.io/badge/license-MIT-0f766e?style=flat-square"></a>
12
- <a href="https://github.com/BlackishGreen33/Expo-Harmony-Toolkit/releases"><img alt="Version" src="https://img.shields.io/badge/version-v1.8.3-111827?style=flat-square"></a>
12
+ <a href="https://github.com/BlackishGreen33/Expo-Harmony-Toolkit/releases"><img alt="Version" src="https://img.shields.io/badge/version-v1.9.0-111827?style=flat-square"></a>
13
13
  <a href="./docs/support-matrix.md"><img alt="Matrix" src="https://img.shields.io/badge/matrix-expo55--rnoh082--ui--stack-2563eb?style=flat-square"></a>
14
- <img alt="Input" src="https://img.shields.io/badge/input-Managed%2FCNG-059669?style=flat-square">
14
+ <img alt="Input" src="https://img.shields.io/badge/input-Managed%2FCNG%20%2B%20bare%20intake-059669?style=flat-square">
15
15
  </p>
16
16
  <p>
17
17
  <a href="./docs/support-matrix.md">Support Matrix</a> ·
@@ -24,7 +24,7 @@
24
24
  </div>
25
25
 
26
26
  > [!IMPORTANT]
27
- > `v1.8` keeps the `verified + preview + experimental` model and continues to keep `expo-location` and `expo-camera` in `preview`. The public promise remains tighter: `latest` only carries fully accepted `verified` capabilities, while `next` is reserved for preview fast-track work. The roadmap now defines `v2.0.0` itself as “reliable Harmony packaging for any Expo project”, so the intermediate versions will keep getting split smaller, but that still does not widen the current `verified` boundary yet.
27
+ > `v1.9.0` keeps the `verified + preview + experimental` model and brings bare workflow intake, five app foundation modules, and the `react-native-gesture-handler` formal slice onto the mainline tracker. The public promise remains tighter: `latest` only carries fully accepted `verified` capabilities, while `next` is reserved for preview fast-track work. The expanded goal still does not widen the current `verified` boundary yet.
28
28
 
29
29
  > [!TIP]
30
30
  > The two validated `@react-native-oh-tpl/*` adapters in the public matrix are currently consumed via exact Git URLs and commits. For repository development and the official UI-stack sample, prefer `pnpm install --ignore-scripts` so adapter prepare hooks do not fail on private upstream resources.
@@ -48,12 +48,12 @@
48
48
  <!-- GENERATED:readme-current-status:start -->
49
49
  | Item | Status |
50
50
  | --- | --- |
51
- | Current version | `v1.8.3` |
51
+ | Current version | `v1.9.0` |
52
52
  | Support model | `verified + preview + experimental` |
53
53
  | Public `verified` matrix | `expo55-rnoh082-ui-stack` |
54
- | Supported input | Managed/CNG Expo projects |
54
+ | Supported input | Managed/CNG Expo projects; bare workflow intake baseline |
55
55
  | `verified` JS/UI capabilities | `expo-router`, `expo-linking`, `expo-constants`, `react-native-reanimated`, `react-native-svg` |
56
- | `preview` native capabilities | `expo-file-system`, `expo-image-picker`, `expo-location`, `expo-camera` |
56
+ | `preview` native capabilities | `expo-file-system`, `expo-image-picker`, `expo-location`, `expo-camera`, `expo-secure-store`, `expo-asset`, `expo-device`, `expo-clipboard`, `expo-haptics` |
57
57
  | `experimental` capabilities | `expo-notifications`, `react-native-gesture-handler` |
58
58
  | Release tracks | `latest` = fully accepted `verified` only; `next` = preview fast track |
59
59
  | Capability telemetry | `runtimeMode` + `evidence(...)` + `evidenceSource(...)` + `coverageProfile` + `nextActions` |
@@ -66,10 +66,11 @@
66
66
  <details>
67
67
  <summary><strong>Still outside the verified public promise</strong></summary>
68
68
 
69
- - bare Expo
69
+ - bare Expo still only reaches the intake baseline
70
70
  - `expo-file-system`, `expo-image-picker`, `expo-location`, and `expo-camera` remain `preview`
71
+ - `expo-secure-store`, `expo-asset`, `expo-device`, `expo-clipboard`, and `expo-haptics` remain the `preview` shim baseline
71
72
  - `expo-notifications`
72
- - `react-native-gesture-handler`
73
+ - `react-native-gesture-handler` remains a formal experimental slice
73
74
  - multiple public matrices
74
75
 
75
76
  </details>
@@ -174,7 +175,7 @@ Common decision points:
174
175
 
175
176
  <!-- GENERATED:readme-support-matrix:start -->
176
177
  - `verified`: the only public matrix remains `expo55-rnoh082-ui-stack`
177
- - `preview`: `expo-file-system`, `expo-image-picker`, `expo-location`, `expo-camera`
178
+ - `preview`: `expo-file-system`, `expo-image-picker`, `expo-location`, `expo-camera`, `expo-secure-store`, `expo-asset`, `expo-device`, `expo-clipboard`, `expo-haptics`
178
179
  - `experimental`: `expo-notifications`, `react-native-gesture-handler`
179
180
 
180
181
  `doctor --strict` still means `verified` only. `doctor --target-tier preview` allows the same runtime matrix plus preview-tier capabilities, but that does not promote them into the formal public promise.
@@ -198,7 +199,7 @@ If you want the `v1.8.x` promotion board directly, use [acceptance/v1.8.x-capabi
198
199
  - `examples/official-ui-stack-sample`
199
200
  The current public main sample, covering router, linking, constants, SVG, reanimated, and the Harmony sidecar build flow.
200
201
  - `examples/official-native-capabilities-sample`
201
- The `v1.8.x` preview walkthrough sample, covering the supported core subsets for `expo-file-system`, `expo-image-picker`, `expo-location`, and `expo-camera` plus permission, bundle, debug-build validation, and per-capability acceptance tracking.
202
+ Preview walkthrough sample for `expo-file-system`, `expo-image-picker`, `expo-location`, `expo-camera`, and the v1.9 app foundation modules, keeping core subsets, permissions, bundle/debug evidence, and per-capability acceptance tracking explicit.
202
203
  - `examples/official-app-shell-sample`
203
204
  The minimal App Shell onboarding sample that demonstrates router, linking, constants, pathname, observed URL, and a generated deep-link flow.
204
205
  - `examples/official-minimal-sample`
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  <div align="center">
2
2
  <h1>Expo Harmony Toolkit</h1>
3
- <p><strong>面向 Managed/CNG Expo 项目的 HarmonyOS 迁移、准入检查与 UI-stack 构建工具链。</strong></p>
3
+ <p><strong>面向 Managed/CNG Expo 项目,并开始纳入 bare intake baseline 的 HarmonyOS 迁移、准入检查与 UI-stack 构建工具链。</strong></p>
4
4
  <p>One verified UI-stack matrix, additive preview/experimental capability tiers, managed Harmony sidecar scaffolding, and a toolkit-driven <code>doctor → init → bundle → build-hap</code> path.</p>
5
5
  <p>
6
6
  <a href="./README.md">简体中文</a> ·
@@ -9,9 +9,9 @@
9
9
  <p>
10
10
  <a href="https://github.com/BlackishGreen33/Expo-Harmony-Toolkit/actions/workflows/ci.yml"><img alt="Checks" src="https://img.shields.io/badge/checks-passing-16a34a?style=flat-square&logo=githubactions&logoColor=white"></a>
11
11
  <a href="./LICENSE"><img alt="License" src="https://img.shields.io/badge/license-MIT-0f766e?style=flat-square"></a>
12
- <a href="https://github.com/BlackishGreen33/Expo-Harmony-Toolkit/releases"><img alt="Version" src="https://img.shields.io/badge/version-v1.8.3-111827?style=flat-square"></a>
12
+ <a href="https://github.com/BlackishGreen33/Expo-Harmony-Toolkit/releases"><img alt="Version" src="https://img.shields.io/badge/version-v1.9.0-111827?style=flat-square"></a>
13
13
  <a href="./docs/support-matrix.md"><img alt="Matrix" src="https://img.shields.io/badge/matrix-expo55--rnoh082--ui--stack-2563eb?style=flat-square"></a>
14
- <img alt="Input" src="https://img.shields.io/badge/input-Managed%2FCNG-059669?style=flat-square">
14
+ <img alt="Input" src="https://img.shields.io/badge/input-Managed%2FCNG%20%2B%20bare%20intake-059669?style=flat-square">
15
15
  </p>
16
16
  <p>
17
17
  <a href="./docs/support-matrix.md">支持矩阵</a> ·
@@ -24,7 +24,7 @@
24
24
  </div>
25
25
 
26
26
  > [!IMPORTANT]
27
- > `v1.8` 延续 `verified + preview + experimental` 三层支持模型,并把 `expo-location`、`expo-camera` 继续保持在 `preview`。当前公开承诺仍然收紧为:`latest` 只承诺完整验收的 `verified` 能力,`next` 用于 preview fast track。最新 roadmap 已把 `v2.0.0` 直接定义成“任何 Expo 项目都能可靠打包成鸿蒙 App”,因此中间版本会继续拆小,但这不等于当前 `verified` 边界已经放宽。
27
+ > `v1.9.0` 延续 `verified + preview + experimental` 三层支持模型,并把 bare workflow intake、五个 app foundation modules 与 `react-native-gesture-handler` formal slice 纳入主线追踪。当前公开承诺仍然收紧为:`latest` 只承诺完整验收的 `verified` 能力,`next` 用于 preview fast track。目标扩大不等于当前 `verified` 边界已经放宽。
28
28
 
29
29
  > [!TIP]
30
30
  > 由于当前公开矩阵内的两套 `@react-native-oh-tpl/*` adapter 依赖以 Git URL + exact commit 形式接入,仓库开发和官方 UI-stack sample 推荐使用 `pnpm install --ignore-scripts`,避免 Git adapter 在 prepare 阶段拉取私有资源而中断安装。
@@ -48,12 +48,12 @@
48
48
  <!-- GENERATED:readme-current-status:start -->
49
49
  | 项目 | 说明 |
50
50
  | --- | --- |
51
- | 当前版本 | `v1.8.3` |
51
+ | 当前版本 | `v1.9.0` |
52
52
  | 支持模型 | `verified + preview + experimental` |
53
53
  | 唯一 `verified` 公开矩阵 | `expo55-rnoh082-ui-stack` |
54
- | 输入范围 | Managed/CNG Expo 项目 |
54
+ | 输入范围 | Managed/CNG Expo 项目;bare workflow intake baseline |
55
55
  | `verified` JS/UI 能力 | `expo-router`、`expo-linking`、`expo-constants`、`react-native-reanimated`、`react-native-svg` |
56
- | `preview` 原生能力 | `expo-file-system`、`expo-image-picker`、`expo-location`、`expo-camera` |
56
+ | `preview` 原生能力 | `expo-file-system`、`expo-image-picker`、`expo-location`、`expo-camera`、`expo-secure-store`、`expo-asset`、`expo-device`、`expo-clipboard`、`expo-haptics` |
57
57
  | `experimental` 能力 | `expo-notifications`、`react-native-gesture-handler` |
58
58
  | 发布轨 | `latest` = fully accepted verified only;`next` = preview fast track |
59
59
  | capability 遥测 | `runtimeMode` + `evidence(...)` + `evidenceSource(...)` + `coverageProfile` + `nextActions` |
@@ -66,10 +66,11 @@
66
66
  <details>
67
67
  <summary><strong>当前仍不在 verified 正式承诺范围</strong></summary>
68
68
 
69
- - bare Expo
69
+ - bare Expo 仍只到 intake baseline
70
70
  - `expo-file-system`、`expo-image-picker`、`expo-location`、`expo-camera` 仍只属于 `preview`
71
+ - `expo-secure-store`、`expo-asset`、`expo-device`、`expo-clipboard`、`expo-haptics` 仍只属于 `preview` shim baseline
71
72
  - `expo-notifications`
72
- - `react-native-gesture-handler`
73
+ - `react-native-gesture-handler` 仍只属于 formal experimental slice
73
74
  - 多矩阵并行支持
74
75
 
75
76
  </details>
@@ -174,7 +175,7 @@ pnpm exec expo-harmony build-hap --mode release
174
175
 
175
176
  <!-- GENERATED:readme-support-matrix:start -->
176
177
  - `verified`:唯一公开矩阵仍是 `expo55-rnoh082-ui-stack`
177
- - `preview`:`expo-file-system`, `expo-image-picker`, `expo-location`, `expo-camera`
178
+ - `preview`:`expo-file-system`, `expo-image-picker`, `expo-location`, `expo-camera`, `expo-secure-store`, `expo-asset`, `expo-device`, `expo-clipboard`, `expo-haptics`
178
179
  - `experimental`:`expo-notifications`, `react-native-gesture-handler`
179
180
 
180
181
  `doctor --strict` 继续只代表 `verified`。`doctor --target-tier preview` 会在同一 runtime matrix 下额外放行 preview 能力,但这不等于它们已经进入正式承诺。
@@ -204,7 +205,7 @@ pnpm exec expo-harmony build-hap --mode release
204
205
  - `examples/official-ui-stack-sample`
205
206
  当前唯一对外主 sample,同时覆盖 router、linking、constants、SVG、reanimated 和 Harmony sidecar 构建链。
206
207
  - `examples/official-native-capabilities-sample`
207
- `v1.8.x` 的 preview walkthrough sample,用来承接 `expo-file-system`、`expo-image-picker`、`expo-location`、`expo-camera` 的核心支持子集、permission、bundle、debug build 与逐 capability 验收追踪。
208
+ preview walkthrough sample,用来承接 `expo-file-system`、`expo-image-picker`、`expo-location`、`expo-camera` 与 v1.9 app foundation modules 的核心支持子集、permission、bundle、debug build 与逐 capability 验收追踪。
208
209
  - `examples/official-app-shell-sample`
209
210
  最小可理解的 App Shell onboarding sample,用来展示 router、linking、constants、pathname、observed URL 与 generated deep link。
210
211
  - `examples/official-minimal-sample`
@@ -1,6 +1,6 @@
1
1
  export declare const TOOLKIT_PACKAGE_NAME = "expo-harmony-toolkit";
2
2
  export declare const CLI_NAME = "expo-harmony";
3
- export declare const TOOLKIT_VERSION = "1.8.3";
3
+ export declare const TOOLKIT_VERSION = "1.9.0";
4
4
  export declare const TEMPLATE_VERSION = "rnoh-0.82.29";
5
5
  export declare const RNOH_VERSION = "0.82.29";
6
6
  export declare const RNOH_CLI_VERSION = "0.82.29";
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DESIRED_PACKAGE_SCRIPTS = exports.HARMONY_RUNTIME_PRELUDE_RELATIVE_PATH = exports.HARMONY_ROUTER_ENTRY_FILENAME = exports.STRICT_ENV_EXIT_CODE = exports.STRICT_DOCTOR_EXIT_CODE = exports.DEFAULT_HVIGOR_PLUGIN_FILENAME = exports.PREBUILD_METADATA_FILENAME = exports.TOOLKIT_CONFIG_FILENAME = exports.BUILD_REPORT_FILENAME = exports.ENV_REPORT_FILENAME = exports.DOCTOR_REPORT_FILENAME = exports.MANIFEST_FILENAME = exports.SIGNING_LOCAL_EXAMPLE_FILENAME = exports.SIGNING_LOCAL_FILENAME = exports.GENERATED_SHIMS_DIR = exports.GENERATED_DIR = exports.SUPPORTED_EXPO_SDKS = exports.RNOH_CLI_VERSION = exports.RNOH_VERSION = exports.TEMPLATE_VERSION = exports.TOOLKIT_VERSION = exports.CLI_NAME = exports.TOOLKIT_PACKAGE_NAME = void 0;
4
4
  exports.TOOLKIT_PACKAGE_NAME = 'expo-harmony-toolkit';
5
5
  exports.CLI_NAME = 'expo-harmony';
6
- exports.TOOLKIT_VERSION = '1.8.3';
6
+ exports.TOOLKIT_VERSION = '1.9.0';
7
7
  exports.TEMPLATE_VERSION = 'rnoh-0.82.29';
8
8
  exports.RNOH_VERSION = '0.82.29';
9
9
  exports.RNOH_CLI_VERSION = '0.82.29';
@@ -25,6 +25,12 @@ const DEFAULT_RECORD = {
25
25
  };
26
26
  const BARE_WORKFLOW_DIRECTORY_NAMES = ['android', 'ios'];
27
27
  const BARE_WORKFLOW_DEPENDENCIES = new Set(['expo-build-properties', 'expo-dev-client']);
28
+ const FORMAL_EXPERIMENTAL_ADAPTER_PAIRS = [
29
+ {
30
+ canonicalPackageName: 'react-native-gesture-handler',
31
+ adapterPackageName: '@react-native-oh-tpl/react-native-gesture-handler',
32
+ },
33
+ ];
28
34
  async function buildDoctorReport(projectRoot, options = {}) {
29
35
  const loadedProject = await (0, project_1.loadProject)(projectRoot);
30
36
  const doctorConfig = (0, project_1.resolveExpoHarmonyDoctorConfig)(loadedProject.expoConfig);
@@ -329,6 +335,24 @@ async function collectBlockingIssues(projectRoot, expoConfig, packageJson, expoP
329
335
  });
330
336
  }
331
337
  }
338
+ for (const pairing of FORMAL_EXPERIMENTAL_ADAPTER_PAIRS) {
339
+ const hasCanonical = dependencyMap.has(pairing.canonicalPackageName);
340
+ const hasAdapter = dependencyMap.has(pairing.adapterPackageName);
341
+ if (hasCanonical && !hasAdapter) {
342
+ issues.push({
343
+ code: 'dependency.required_missing',
344
+ message: `Using ${pairing.canonicalPackageName} in its formal experimental Harmony slice also requires ${pairing.adapterPackageName}.`,
345
+ subject: pairing.adapterPackageName,
346
+ });
347
+ }
348
+ if (hasAdapter && !hasCanonical) {
349
+ issues.push({
350
+ code: 'dependency.required_missing',
351
+ message: `Using ${pairing.adapterPackageName} in its formal experimental Harmony slice also requires ${pairing.canonicalPackageName}.`,
352
+ subject: pairing.canonicalPackageName,
353
+ });
354
+ }
355
+ }
332
356
  if (!expoConfig.android?.package && !expoConfig.ios?.bundleIdentifier) {
333
357
  issues.push({
334
358
  code: 'config.native_identifier.missing',
@@ -392,14 +416,14 @@ function resolveDependencyGapCategory(dependency, coverageProfile) {
392
416
  dependency.name === 'react-native')) {
393
417
  return 'bare-workflow-gap';
394
418
  }
419
+ if (isThirdPartyNativeGapDependency(dependency)) {
420
+ return 'third-party-native-gap';
421
+ }
395
422
  if (isOfficialExpoDependencyName(dependency.name) || capabilities_1.CAPABILITY_BY_PACKAGE[dependency.name]) {
396
423
  return dependency.supportTier === 'verified' && dependency.status === 'supported'
397
424
  ? 'matrix-drift'
398
425
  : 'official-module-gap';
399
426
  }
400
- if (isThirdPartyNativeGapDependency(dependency)) {
401
- return 'third-party-native-gap';
402
- }
403
427
  if (coverageProfile === 'bare') {
404
428
  return 'bare-workflow-gap';
405
429
  }
@@ -462,7 +486,7 @@ function buildNextActions(input) {
462
486
  if (blockingIssues.some((issue) => issue.code === 'dependency.not_allowed' &&
463
487
  (issue.subject === 'react-native-gesture-handler' ||
464
488
  issue.subject === '@react-native-oh-tpl/react-native-gesture-handler'))) {
465
- actions.push('Keep `react-native-gesture-handler` out of the verified lane until its Harmony adapter path has stable doctor, sample, and build coverage.');
489
+ actions.push('Keep `react-native-gesture-handler` on the formal experimental slice until its Harmony adapter path closes device and release evidence.');
466
490
  }
467
491
  if (hasBlockingIssueCode(blockingIssues, 'config.native_identifier.missing')) {
468
492
  actions.push('Set `android.package` or `ios.bundleIdentifier` in Expo config before expecting a strict Harmony build path.');
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MANAGED_EXPO_HARMONY_MODULE_RENDERERS = void 0;
4
4
  exports.renderCapabilityModuleShim = renderCapabilityModuleShim;
5
5
  exports.renderUnsupportedCapabilityShim = renderUnsupportedCapabilityShim;
6
+ const appFoundation_1 = require("./renderers/appFoundation");
6
7
  const camera_1 = require("./renderers/camera");
7
8
  const fileSystem_1 = require("./renderers/fileSystem");
8
9
  const imagePicker_1 = require("./renderers/imagePicker");
@@ -24,6 +25,26 @@ const CAPABILITY_SHIM_RENDERERS = {
24
25
  adapter: camera_1.renderExpoCameraHarmonyAdapterShim,
25
26
  preview: camera_1.renderExpoCameraPreviewShim,
26
27
  },
28
+ 'expo-secure-store': {
29
+ adapter: appFoundation_1.renderExpoSecureStorePreviewShim,
30
+ preview: appFoundation_1.renderExpoSecureStorePreviewShim,
31
+ },
32
+ 'expo-asset': {
33
+ adapter: appFoundation_1.renderExpoAssetPreviewShim,
34
+ preview: appFoundation_1.renderExpoAssetPreviewShim,
35
+ },
36
+ 'expo-device': {
37
+ adapter: appFoundation_1.renderExpoDevicePreviewShim,
38
+ preview: appFoundation_1.renderExpoDevicePreviewShim,
39
+ },
40
+ 'expo-clipboard': {
41
+ adapter: appFoundation_1.renderExpoClipboardPreviewShim,
42
+ preview: appFoundation_1.renderExpoClipboardPreviewShim,
43
+ },
44
+ 'expo-haptics': {
45
+ adapter: appFoundation_1.renderExpoHapticsPreviewShim,
46
+ preview: appFoundation_1.renderExpoHapticsPreviewShim,
47
+ },
27
48
  };
28
49
  exports.MANAGED_EXPO_HARMONY_MODULE_RENDERERS = [
29
50
  {
@@ -0,0 +1,5 @@
1
+ export declare function renderExpoSecureStorePreviewShim(): string;
2
+ export declare function renderExpoAssetPreviewShim(): string;
3
+ export declare function renderExpoDevicePreviewShim(): string;
4
+ export declare function renderExpoClipboardPreviewShim(): string;
5
+ export declare function renderExpoHapticsPreviewShim(): string;
@@ -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.0";
@@ -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.0` 延续 `verified + preview + experimental` 支持分层,把 bare workflow 放进 intake baseline,并把五个 app foundation modules 纳入 preview shim baseline;`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
 
@@ -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
package/docs/roadmap.md CHANGED
@@ -6,6 +6,7 @@
6
6
 
7
7
  - 所有 Expo project types 与 package categories,最终都进入同一条 `mainline capability catalog`
8
8
  - `v2.0.0` 本身就要接近最完美、最最终的交付形态,而不是“还差最后一段 extension”
9
+ - `v2.0.0` 的可靠打包承诺收敛为 catalog-covered Expo projects:只要依赖已经进入主线 capability catalog,就必须稳定闭环
9
10
  - 公开承诺仍然按 evidence gate 逐步放开,而不是因为目标扩大就提前放宽 `latest`
10
11
  - `latest` 继续只承接完整验收的 `verified`
11
12
  - `next` 继续承接 fast track,用来加速收敛,但不伪装成 `verified`
@@ -65,6 +66,8 @@
65
66
 
66
67
  ## 当前主线
67
68
 
69
+ `v1.8.x` 已完成 repo 内可收口项,当前主线切到 `v1.9.x`。`v1.9.0` 先落 bare workflow baseline、app foundation modules 与 `react-native-gesture-handler` formal slice;剩余需要真机或 release HAP 的证据继续保留在 v1.8.x capability board,不阻塞 v1.9.x 的 bare / foundation / third-party onboarding 工作。
70
+
68
71
  ### v1.8.0 Intake Hardening + Parallel Promotion
69
72
 
70
73
  目标日期:`2026-05-15`
@@ -98,7 +101,9 @@
98
101
 
99
102
  目标日期:`2026-06-15`
100
103
 
101
- 目标:把 v1.8.0 建好的 intake + promotion 架子收口到可持续执行的证据节奏。
104
+ 状态:repo-only closeout complete;device / release evidence carryover。
105
+
106
+ 目标:把 v1.8.0 建好的 intake + promotion 架子收口到可持续执行的证据节奏,并把外部验收项从 repo 内可完成项中拆开。
102
107
 
103
108
  - 每个 preview capability 必须单独维护:
104
109
  - bundle evidence
@@ -108,12 +113,18 @@
108
113
  - combined sample 只负责回归烟测,不再替代单 capability 证据
109
114
  - `toolkit-config.json`、`doctor-report.json`、roadmap、support matrix、acceptance board 保持同源同步
110
115
  - `latest` 仍不承接任何证据未闭环的 capability
116
+ - 不需要真机或 release HAP 的收尾项已经完成,可以进入 `v1.9.x`
117
+ - 仍然顺延的外部证据:
118
+ - 单 capability 真机 device acceptance record
119
+ - 单 capability release HAP runtime acceptance
111
120
 
112
121
  ### v1.9.0 Bare Workflow Baseline + App Foundation Modules
113
122
 
114
123
  目标日期:`2026-08-31`
115
124
 
116
- 目标:先把 bare workflow 拉进主线,并补齐最容易卡死真实项目的官方基础模块。
125
+ 状态:implementation baseline complete;device / release evidence carryover。
126
+
127
+ 目标:把 bare workflow 拉进主线,补齐最容易卡死真实项目的官方基础模块,并先建立第三方 blocker 的正式接入切片。
117
128
 
118
129
  - bare workflow 正式进入主线里程碑
119
130
  - 优先补齐:
@@ -123,6 +134,15 @@
123
134
  - `expo-clipboard`
124
135
  - `expo-haptics`
125
136
  - 高频第三方 native dependencies 开始进入同一 capability catalog
137
+ - `react-native-gesture-handler` 进入 formal acceptance slice:
138
+ - 保留 experimental / non-verified 边界
139
+ - 补齐 sample / fixture / doctor / debug build evidence
140
+ - device / release evidence 仍按 promotion gate 单独关闭
141
+ - project-shape regression farm 建立最小版本:
142
+ - managed core
143
+ - managed native heavy
144
+ - bare
145
+ - third-party native heavy
126
146
  - 对每个新能力继续坚持:
127
147
  - fixture
128
148
  - sample route
@@ -131,6 +151,7 @@
131
151
  - bundle
132
152
  - debug build
133
153
  - acceptance 记录
154
+ - 本阶段不改变 `verified` 边界;foundation modules 以 `preview` + `runtimeMode=shim` 记录,`react-native-gesture-handler` 以 `experimental` + formal adapter slice 记录
134
155
 
135
156
  ### v1.9.1 Third-party Native Wave A
136
157
 
@@ -139,10 +160,10 @@
139
160
  目标:优先清掉最容易挡住“任何 Expo 项目”的高频第三方 native blocker。
140
161
 
141
162
  - 第一批高频第三方 native blocker 进入正式 onboarding:
142
- - `react-native-gesture-handler`
143
163
  - `react-native-safe-area-context`
144
164
  - `react-native-screens`
145
165
  - `@react-native-async-storage/async-storage`
166
+ - `react-native-gesture-handler` 如果在 `v1.9.0` 已完成 formal acceptance slice,则本阶段只继续补 device / release evidence 与 promotion 决策
146
167
  - 建立第三方 native capability onboarding checklist:
147
168
  - catalog entry
148
169
  - shim / adapter strategy
@@ -155,10 +176,10 @@
155
176
 
156
177
  目标日期:`2026-10-31`
157
178
 
158
- 目标:扩大第三方 native 覆盖,并建立“任何 Expo 项目”目标需要的持续回归面。
179
+ 目标:扩大第三方 native 覆盖,并把 `v1.9.0` 建好的最小 regression farm 扩容成持续回归面。
159
180
 
160
181
  - 第二批高频第三方 native 依赖进入主线
161
- - 引入 project-shape regression farm:
182
+ - 扩容 project-shape regression farm:
162
183
  - managed core
163
184
  - managed native heavy
164
185
  - bare
@@ -193,13 +214,16 @@
193
214
  - 对仍无法在 `v2.0.0` 前关闭的项,必须提前明确降级策略与替代路径
194
215
  - `v2.0.0` 不接受“先宣布完成,再慢慢补长尾”
195
216
 
196
- ### v2.0.0 Any Expo Project Reliable Packaging
217
+ ### v2.0.0 Catalog-covered Expo Project Reliable Packaging
218
+
219
+ 原 `Any Expo Project Reliable Packaging` 叙事保留为 intake 目标,但可靠打包承诺明确收敛到 catalog-covered 项目。
197
220
 
198
221
  目标日期:`2026-12-31`
199
222
 
200
- 定义:任何 Expo 项目,只要依赖已经存在于主线 capability catalog 内,就能可靠打包成鸿蒙 App;`v2.0.0` 本身就是基本接近最完美、最最终的形态。
223
+ 定义:任何 Expo 项目,只要依赖已经存在于主线 capability catalog 内,就能可靠打包成鸿蒙 App;未进入 catalog 的依赖必须至少被 `doctor` 明确分类、指出 blocker,并给出下一步。`v2.0.0` 本身就是基本接近最完美、最最终的形态。
201
224
 
202
- - 不再只覆盖“主流项目”,而是把“任何 Expo 项目都能可靠打包成鸿蒙 App”作为正式版本定义
225
+ - 不再只覆盖“主流项目”,而是把 catalog-covered Expo projects 的可靠打包作为正式版本定义
226
+ - “任何 Expo 项目都能可靠打包成鸿蒙 App”不得被简化成无边界长尾承诺;真实承诺边界是 mainline capability catalog
203
227
  - `Managed/CNG` 与 bare workflow 都必须进入稳定交付路径
204
228
  - 高频官方 Expo 模块与高频第三方 native blockers 必须已经进入同一公开 capability catalog
205
229
  - `doctor -> sync-template -> bundle -> build-hap --mode debug|release` 必须对主线 catalog 内项目形成稳定闭环
@@ -208,7 +232,8 @@
208
232
  ## 长期方向
209
233
 
210
234
  - 所有输入最终都进入同一个 `mainline capability catalog`
211
- - `v2.0.0` 就是“任何 Expo 项目可靠打包成鸿蒙 App”的正式目标版本
235
+ - `v2.0.0` 就是 catalog-covered Expo projects 可靠打包成鸿蒙 App 的正式目标版本
236
+ - 对 catalog 外项目,正式目标是完整 intake:能分类、能说明 blocker、能给下一步,而不是伪装成已经可靠打包
212
237
  - `latest` 只由完整验收能力驱动
213
238
  - `next` 用来加速收敛主线 backlog,而不是制造伪 verified 叙事
214
239
  - roadmap 的关键问题不再是“要不要把某类项目算进主线”,而是“它何时进入主线、当前缺哪格 evidence、下一步怎么 unblock”
@@ -1,6 +1,6 @@
1
- # v1.8 支持矩阵
1
+ # v1.9 支持矩阵
2
2
 
3
- `v1.8` 继续采用 tiered support model,但从现在开始把“层级”与“晋升距离”拆开表达:
3
+ `v1.9` 继续采用 tiered support model,并把“层级”与“晋升距离”拆开表达:
4
4
 
5
5
  - `supportTier` 表示公开承诺层级
6
6
  - `runtimeMode` 表示当前运行时接入形态:`shim`、`adapter`、`verified`
@@ -9,11 +9,12 @@
9
9
  - `coverageProfile` 表示当前项目属于哪条主线输入形态
10
10
  - `nextActions` 表示 toolkit 为当前项目生成的有序下一步
11
11
 
12
- 同时,`v1.8.x` 的 roadmap 已经改成单一 `mainline capability catalog` 方向:
12
+ 同时,roadmap 已经改成单一 `mainline capability catalog` 方向:
13
13
 
14
14
  - bare workflow 与第三方 native package 不再被描述成“收官后才考虑的另一条 extension 终局”
15
15
  - `v2.0.0` 本身被定义成“任何 Expo 项目都能可靠打包成鸿蒙 App”的目标版本
16
16
  - 但当前公开承诺依旧没有放宽;它们只是进入同一主线 backlog,而不是已经进入 `verified`
17
+ - `v1.9.0` 新增的是 bare intake baseline、app foundation preview shim baseline 与 `react-native-gesture-handler` formal experimental slice,不是 verified 扩容
17
18
 
18
19
  `doctor --strict` 继续只代表 `verified`。
19
20
 
@@ -106,15 +107,22 @@
106
107
  | `expo-image-picker` | `preview` | `adapter` | `bundle=yes[automated], debugBuild=yes[automated], device=yes[manual-doc], release=no[none]` | react-native-image-picker, react-native-permissions | ohos.permission.CAMERA、ohos.permission.MICROPHONE、ohos.permission.READ_IMAGEVIDEO | `/image-picker` |
107
108
  | `expo-location` | `preview` | `adapter` | `bundle=yes[automated], debugBuild=yes[automated], device=yes[manual-doc], release=no[none]` | @react-native-community/geolocation, react-native-permissions | ohos.permission.LOCATION、ohos.permission.APPROXIMATELY_LOCATION、ohos.permission.LOCATION_IN_BACKGROUND、ohos.permission.ACCELEROMETER | `/location` |
108
109
  | `expo-camera` | `preview` | `adapter` | `bundle=yes[automated], debugBuild=yes[automated], device=yes[manual-doc], release=no[none]` | react-native-camera-kit, react-native-permissions | ohos.permission.CAMERA、ohos.permission.MICROPHONE | `/camera` |
110
+ | `expo-secure-store` | `preview` | `shim` | `bundle=yes[automated], debugBuild=yes[automated], device=no[none], release=no[none]` | toolkit-managed bridge | 无新增必需权限 | `/secure-store` |
111
+ | `expo-asset` | `preview` | `shim` | `bundle=yes[automated], debugBuild=yes[automated], device=no[none], release=no[none]` | toolkit-managed bridge | 无新增必需权限 | `/asset` |
112
+ | `expo-device` | `preview` | `shim` | `bundle=yes[automated], debugBuild=yes[automated], device=no[none], release=no[none]` | toolkit-managed bridge | 无新增必需权限 | `/device` |
113
+ | `expo-clipboard` | `preview` | `shim` | `bundle=yes[automated], debugBuild=yes[automated], device=no[none], release=no[none]` | @react-native-oh-tpl/clipboard | 无新增必需权限 | `/clipboard` |
114
+ | `expo-haptics` | `preview` | `shim` | `bundle=yes[automated], debugBuild=yes[automated], device=no[none], release=no[none]` | toolkit-managed bridge | 无新增必需权限 | `/haptics` |
109
115
  <!-- GENERATED:support-matrix-preview-capabilities:end -->
110
116
 
111
117
  说明:
112
118
 
113
119
  - 当前四项 preview capability 都已经完成 preview baseline 的 bundle / debug build / route walkthrough
114
120
  - 四项 preview capability 都已经进入 `adapter` 路径;`device=yes[manual-doc]` 只表示已有人工设备验收记录,仍不代表 verified 或 release-ready
115
- - 当前 preview baseline 的默认 evidenceSource 固定为:`bundle/debugBuild=automated`、`device=manual-doc`、`release=none`
121
+ - 当前原生 adapter preview baseline 的默认 evidenceSource 固定为:`bundle/debugBuild=automated`、`device=manual-doc`、`release=none`
122
+ - `v1.9.0` 新增的 app foundation modules 当前是 `runtimeMode=shim`,`bundle/debugBuild=automated`,`device/release=none`
116
123
  - `v1.8.2` 的 ccnubox release HAP 模拟器安装/启动记录只证明 app-shell release 构建链路,不改变上述 per-capability `release=no[none]`
117
124
  - `v1.8.x` 开始,combined sample smoke 只负责总回归;每项 capability 还必须单独维护 device / release acceptance 记录,见 [acceptance/v1.8.x-capability-board.md](../acceptance/v1.8.x-capability-board.md)
125
+ - `v1.8.x` repo 内可完成的 closeout 已完成;剩余 carryover 只包括单 capability 真机 device record 与 release HAP runtime acceptance
118
126
  - `expo-file-system`
119
127
  - `🟡` 当前主路径是 UTF-8/base64 sandbox I/O、append/partial read、`getInfoAsync({ md5: true })` 与 `downloadAsync`
120
128
  - `expo-image-picker`
@@ -129,7 +137,7 @@
129
137
  | Expo 能力 / 依赖 | 当前层级 | runtimeMode | evidence | 说明 |
130
138
  | --- | --- | --- | --- | --- |
131
139
  | `expo-notifications` | `experimental` | `shim` | `bundle=no, debugBuild=no, device=no, release=no` | 服务链路与交付故事未打通 |
132
- | `react-native-gesture-handler` | `experimental` | matrix 外本地探索 | matrix | 继续保留本地探索 shim,但不在公开 verified 矩阵内 |
140
+ | `react-native-gesture-handler` | `experimental` | `adapter` | `bundle=yes[automated], debugBuild=yes[automated], device=no[none], release=no[none]` | v1.9.0 formal acceptance slice;必须与 `@react-native-oh-tpl/react-native-gesture-handler` 成对接入,仍不在公开 verified 矩阵内 |
133
141
 
134
142
  ## UI Stack 配对规则
135
143
 
@@ -229,7 +237,7 @@ toolkit 受管的核心产物仍包括:
229
237
 
230
238
  ## Release Tracks
231
239
 
232
- 从 `v1.8` 开始,发布节奏按双轨设计:
240
+ 从 `v1.8` 开始,发布节奏按双轨设计;`v1.9.0` 继续沿用同一规则:
233
241
 
234
242
  <!-- GENERATED:support-matrix-release-tracks:start -->
235
243
  - `latest`
@@ -255,9 +263,12 @@ toolkit 受管的核心产物仍包括:
255
263
 
256
264
  `v1.8.2` 记录的 simulator release-HAP evidence 只覆盖 ccnubox app-shell 的 signed HAP 构建、安装和启动;单 capability 的 release acceptance 仍必须按各自记录单独关闭。
257
265
 
266
+ `v1.8.x` repo-only closeout 不改变 capability evidence source:`device=yes[manual-doc]` 仍只代表既有人工基线,`release=no[none]` 仍表示没有单 capability release HAP runtime evidence。
267
+
258
268
  ## 当前仍未进入正式公开承诺
259
269
 
260
- - bare Expo
270
+ - bare Expo;当前只进入 intake / debug baseline
271
+ - `expo-secure-store`、`expo-asset`、`expo-device`、`expo-clipboard`、`expo-haptics` 当前仍只是 preview shim baseline
261
272
  - 多 Expo / RNOH 并行 verified 矩阵
262
273
  - 任意第三方 native package 的即时正式承诺
263
274
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-harmony-toolkit",
3
- "version": "1.8.3",
3
+ "version": "1.9.0",
4
4
  "description": "面向 Managed/CNG Expo 项目的 HarmonyOS 迁移、准入检查与 UI-stack 构建工具链",
5
5
  "main": "app.plugin.js",
6
6
  "types": "build/index.d.ts",