expo-harmony-toolkit 1.9.1 → 1.9.3
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 +7 -5
- package/README.md +7 -5
- package/build/core/build/localHar.js +63 -3
- package/build/core/build.js +23 -0
- package/build/core/constants.d.ts +1 -1
- package/build/core/constants.js +1 -1
- package/build/core/report.js +55 -1
- package/build/core/signing.d.ts +4 -1
- package/build/core/signing.js +19 -4
- package/build/core/template/capabilityRegistry.js +5 -0
- package/build/core/template/metro.js +4 -2
- package/build/data/capabilities.js +179 -15
- package/build/data/dependencyCatalog.js +9 -9
- package/build/data/publicDocs.d.ts +1 -1
- package/build/docs/render.d.ts +1 -0
- package/build/docs/render.js +28 -0
- package/docs/cli-build.md +10 -2
- package/docs/official-minimal-sample.md +1 -1
- package/docs/official-native-capabilities-sample.md +1 -1
- package/docs/roadmap.md +19 -2
- package/docs/signing-and-release.md +3 -1
- package/docs/support-matrix.md +15 -3
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
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.9.
|
|
12
|
+
<a href="https://github.com/BlackishGreen33/Expo-Harmony-Toolkit/releases"><img alt="Version" src="https://img.shields.io/badge/version-v1.9.3-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
14
|
<img alt="Input" src="https://img.shields.io/badge/input-Managed%2FCNG%20%2B%20bare%20intake-059669?style=flat-square">
|
|
15
15
|
</p>
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
</div>
|
|
25
25
|
|
|
26
26
|
> [!IMPORTANT]
|
|
27
|
-
> `v1.9.
|
|
27
|
+
> `v1.9.3` keeps the `v1.9.2` Wave A baseline and brings ccnubox-first `react-native-webview`, JPush runtime, `expo-media-library`, `lottie-react-native`, and `@shopify/react-native-skia` into Third-party Native Wave B formal experimental onboarding. The public promise remains tighter: `latest` only carries fully accepted `verified` capabilities, and Wave B does not widen the current `verified` boundary.
|
|
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,13 +48,13 @@
|
|
|
48
48
|
<!-- GENERATED:readme-current-status:start -->
|
|
49
49
|
| Item | Status |
|
|
50
50
|
| --- | --- |
|
|
51
|
-
| Current version | `v1.9.
|
|
51
|
+
| Current version | `v1.9.3` |
|
|
52
52
|
| Support model | `verified + preview + experimental` |
|
|
53
53
|
| Public `verified` matrix | `expo55-rnoh082-ui-stack` |
|
|
54
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
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
|
-
| `experimental` capabilities | `expo-notifications`, `react-native-gesture-handler` |
|
|
57
|
+
| `experimental` capabilities | `expo-notifications`, `react-native-gesture-handler`, `@react-native-async-storage/async-storage`, `react-native-screens`, `react-native-safe-area-context`, `react-native-webview`, `jpush-react-native`, `expo-media-library`, `lottie-react-native`, `@shopify/react-native-skia` |
|
|
58
58
|
| Release tracks | `latest` = fully accepted `verified` only; `next` = preview fast track |
|
|
59
59
|
| Capability telemetry | `runtimeMode` + `evidence(...)` + `evidenceSource(...)` + `coverageProfile` + `nextActions` |
|
|
60
60
|
| Build path | `doctor -> init -> bundle -> build-hap` |
|
|
@@ -71,6 +71,8 @@
|
|
|
71
71
|
- `expo-secure-store`, `expo-asset`, `expo-device`, `expo-clipboard`, and `expo-haptics` remain the `preview` shim baseline
|
|
72
72
|
- `expo-notifications`
|
|
73
73
|
- `react-native-gesture-handler` remains a formal experimental slice
|
|
74
|
+
- `@react-native-async-storage/async-storage`, `react-native-screens`, and `react-native-safe-area-context` remain Wave A experimental onboarding
|
|
75
|
+
- `react-native-webview`, JPush runtime, `expo-media-library`, `lottie-react-native`, and `@shopify/react-native-skia` remain Wave B experimental onboarding
|
|
74
76
|
- multiple public matrices
|
|
75
77
|
|
|
76
78
|
</details>
|
|
@@ -176,7 +178,7 @@ Common decision points:
|
|
|
176
178
|
<!-- GENERATED:readme-support-matrix:start -->
|
|
177
179
|
- `verified`: the only public matrix remains `expo55-rnoh082-ui-stack`
|
|
178
180
|
- `preview`: `expo-file-system`, `expo-image-picker`, `expo-location`, `expo-camera`, `expo-secure-store`, `expo-asset`, `expo-device`, `expo-clipboard`, `expo-haptics`
|
|
179
|
-
- `experimental`: `expo-notifications`, `react-native-gesture-handler`
|
|
181
|
+
- `experimental`: `expo-notifications`, `react-native-gesture-handler`, `@react-native-async-storage/async-storage`, `react-native-screens`, `react-native-safe-area-context`, `react-native-webview`, `jpush-react-native`, `expo-media-library`, `lottie-react-native`, `@shopify/react-native-skia`
|
|
180
182
|
|
|
181
183
|
`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.
|
|
182
184
|
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
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.9.
|
|
12
|
+
<a href="https://github.com/BlackishGreen33/Expo-Harmony-Toolkit/releases"><img alt="Version" src="https://img.shields.io/badge/version-v1.9.3-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
14
|
<img alt="Input" src="https://img.shields.io/badge/input-Managed%2FCNG%20%2B%20bare%20intake-059669?style=flat-square">
|
|
15
15
|
</p>
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
</div>
|
|
25
25
|
|
|
26
26
|
> [!IMPORTANT]
|
|
27
|
-
> `v1.9.
|
|
27
|
+
> `v1.9.3` 延续 `v1.9.2` 的 Wave A 基线,并把 ccnubox 优先的 `react-native-webview`、JPush runtime、`expo-media-library`、`lottie-react-native` 与 `@shopify/react-native-skia` 纳入 Third-party Native Wave B formal experimental onboarding。当前公开承诺仍然收紧为:`latest` 只承诺完整验收的 `verified` 能力,Wave B 不放宽当前 `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,13 +48,13 @@
|
|
|
48
48
|
<!-- GENERATED:readme-current-status:start -->
|
|
49
49
|
| 项目 | 说明 |
|
|
50
50
|
| --- | --- |
|
|
51
|
-
| 当前版本 | `v1.9.
|
|
51
|
+
| 当前版本 | `v1.9.3` |
|
|
52
52
|
| 支持模型 | `verified + preview + experimental` |
|
|
53
53
|
| 唯一 `verified` 公开矩阵 | `expo55-rnoh082-ui-stack` |
|
|
54
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
56
|
| `preview` 原生能力 | `expo-file-system`、`expo-image-picker`、`expo-location`、`expo-camera`、`expo-secure-store`、`expo-asset`、`expo-device`、`expo-clipboard`、`expo-haptics` |
|
|
57
|
-
| `experimental` 能力 | `expo-notifications`、`react-native-gesture-handler` |
|
|
57
|
+
| `experimental` 能力 | `expo-notifications`、`react-native-gesture-handler`、`@react-native-async-storage/async-storage`、`react-native-screens`、`react-native-safe-area-context`、`react-native-webview`、`jpush-react-native`、`expo-media-library`、`lottie-react-native`、`@shopify/react-native-skia` |
|
|
58
58
|
| 发布轨 | `latest` = fully accepted verified only;`next` = preview fast track |
|
|
59
59
|
| capability 遥测 | `runtimeMode` + `evidence(...)` + `evidenceSource(...)` + `coverageProfile` + `nextActions` |
|
|
60
60
|
| 构建链 | `doctor -> init -> bundle -> build-hap` |
|
|
@@ -71,6 +71,8 @@
|
|
|
71
71
|
- `expo-secure-store`、`expo-asset`、`expo-device`、`expo-clipboard`、`expo-haptics` 仍只属于 `preview` shim baseline
|
|
72
72
|
- `expo-notifications`
|
|
73
73
|
- `react-native-gesture-handler` 仍只属于 formal experimental slice
|
|
74
|
+
- `@react-native-async-storage/async-storage`、`react-native-screens`、`react-native-safe-area-context` 仍只属于 Wave A experimental onboarding
|
|
75
|
+
- `react-native-webview`、JPush runtime、`expo-media-library`、`lottie-react-native`、`@shopify/react-native-skia` 仍只属于 Wave B experimental onboarding
|
|
74
76
|
- 多矩阵并行支持
|
|
75
77
|
|
|
76
78
|
</details>
|
|
@@ -176,7 +178,7 @@ pnpm exec expo-harmony build-hap --mode release
|
|
|
176
178
|
<!-- GENERATED:readme-support-matrix:start -->
|
|
177
179
|
- `verified`:唯一公开矩阵仍是 `expo55-rnoh082-ui-stack`
|
|
178
180
|
- `preview`:`expo-file-system`, `expo-image-picker`, `expo-location`, `expo-camera`, `expo-secure-store`, `expo-asset`, `expo-device`, `expo-clipboard`, `expo-haptics`
|
|
179
|
-
- `experimental`:`expo-notifications`, `react-native-gesture-handler`
|
|
181
|
+
- `experimental`:`expo-notifications`, `react-native-gesture-handler`, `@react-native-async-storage/async-storage`, `react-native-screens`, `react-native-safe-area-context`, `react-native-webview`, `jpush-react-native`, `expo-media-library`, `lottie-react-native`, `@shopify/react-native-skia`
|
|
180
182
|
|
|
181
183
|
`doctor --strict` 继续只代表 `verified`。`doctor --target-tier preview` 会在同一 runtime matrix 下额外放行 preview 能力,但这不等于它们已经进入正式承诺。
|
|
182
184
|
|
|
@@ -83,9 +83,10 @@ async function extractHarArchiveToDirectory(archivePath, extractionRoot, package
|
|
|
83
83
|
if (!(await fs_extra_1.default.pathExists(archivePath))) {
|
|
84
84
|
throw new Error(`Local Harmony archive not found: ${archivePath}`);
|
|
85
85
|
}
|
|
86
|
-
const destinationPath = path_1.default.join(extractionRoot, `${sanitizePackageName(packageName)}-${path_1.default.basename(archivePath, path_1.default.extname(archivePath))}`);
|
|
86
|
+
const destinationPath = path_1.default.join(extractionRoot, `${sanitizePackageName(packageName)}-${sanitizePathComponent(path_1.default.basename(archivePath, path_1.default.extname(archivePath)))}`);
|
|
87
87
|
const stagingRoot = await fs_extra_1.default.mkdtemp(path_1.default.join(os_1.default.tmpdir(), 'expo-harmony-har-'));
|
|
88
88
|
try {
|
|
89
|
+
await validateHarArchiveMemberNames(archivePath);
|
|
89
90
|
await execFileAsync('tar', ['-xzf', archivePath, '-C', stagingRoot], {
|
|
90
91
|
maxBuffer: 20 * 1024 * 1024,
|
|
91
92
|
});
|
|
@@ -93,6 +94,7 @@ async function extractHarArchiveToDirectory(archivePath, extractionRoot, package
|
|
|
93
94
|
const sourceRoot = (await fs_extra_1.default.pathExists(path_1.default.join(packagedRoot, 'oh-package.json5')))
|
|
94
95
|
? packagedRoot
|
|
95
96
|
: stagingRoot;
|
|
97
|
+
await validateExtractedHarTree(sourceRoot, stagingRoot);
|
|
96
98
|
if (!(await fs_extra_1.default.pathExists(path_1.default.join(sourceRoot, 'oh-package.json5')))) {
|
|
97
99
|
throw new Error(`oh-package.json5 not found after extracting ${archivePath}`);
|
|
98
100
|
}
|
|
@@ -115,7 +117,61 @@ async function extractHarArchiveToDirectory(archivePath, extractionRoot, package
|
|
|
115
117
|
}
|
|
116
118
|
}
|
|
117
119
|
function sanitizePackageName(packageName) {
|
|
118
|
-
return packageName
|
|
120
|
+
return sanitizePathComponent(packageName);
|
|
121
|
+
}
|
|
122
|
+
function sanitizePathComponent(value) {
|
|
123
|
+
return value.replace(/[^a-zA-Z0-9._-]+/g, '-').replace(/^-+|-+$/g, '') || 'archive';
|
|
124
|
+
}
|
|
125
|
+
async function validateHarArchiveMemberNames(archivePath) {
|
|
126
|
+
const { stdout } = await execFileAsync('tar', ['-tzf', archivePath], {
|
|
127
|
+
maxBuffer: 20 * 1024 * 1024,
|
|
128
|
+
});
|
|
129
|
+
for (const rawMemberName of stdout.split('\n')) {
|
|
130
|
+
const memberName = rawMemberName.trim();
|
|
131
|
+
if (!memberName) {
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
const segments = memberName.split('/').filter(Boolean);
|
|
135
|
+
if (path_1.default.isAbsolute(memberName) ||
|
|
136
|
+
memberName.includes('\\') ||
|
|
137
|
+
segments.some((segment) => segment === '..')) {
|
|
138
|
+
throw new Error(`Unsafe path in local Harmony archive: ${memberName}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
async function validateExtractedHarTree(rootPath, stagingRoot) {
|
|
143
|
+
const stagingRealPath = await fs_extra_1.default.realpath(stagingRoot);
|
|
144
|
+
const rootRealPath = await fs_extra_1.default.realpath(rootPath);
|
|
145
|
+
if (!isPathInside(rootRealPath, stagingRealPath)) {
|
|
146
|
+
throw new Error(`Extracted local Harmony archive root escaped the staging directory: ${rootPath}`);
|
|
147
|
+
}
|
|
148
|
+
await walkExtractedHarTree(rootPath, rootRealPath);
|
|
149
|
+
}
|
|
150
|
+
async function walkExtractedHarTree(currentPath, rootRealPath) {
|
|
151
|
+
const currentStats = await fs_extra_1.default.lstat(currentPath);
|
|
152
|
+
if (currentStats.isSymbolicLink()) {
|
|
153
|
+
throw new Error(`Symbolic links are not allowed in local Harmony archives: ${currentPath}`);
|
|
154
|
+
}
|
|
155
|
+
if (currentStats.isFile() && currentStats.nlink > 1) {
|
|
156
|
+
throw new Error(`Hard links are not allowed in local Harmony archives: ${currentPath}`);
|
|
157
|
+
}
|
|
158
|
+
if (!currentStats.isDirectory() && !currentStats.isFile()) {
|
|
159
|
+
throw new Error(`Unsupported file type in local Harmony archive: ${currentPath}`);
|
|
160
|
+
}
|
|
161
|
+
const currentRealPath = await fs_extra_1.default.realpath(currentPath);
|
|
162
|
+
if (!isPathInside(currentRealPath, rootRealPath)) {
|
|
163
|
+
throw new Error(`Extracted local Harmony archive entry escaped the package directory: ${currentPath}`);
|
|
164
|
+
}
|
|
165
|
+
if (!currentStats.isDirectory()) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
for (const entryName of await fs_extra_1.default.readdir(currentPath)) {
|
|
169
|
+
await walkExtractedHarTree(path_1.default.join(currentPath, entryName), rootRealPath);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
function isPathInside(candidatePath, parentPath) {
|
|
173
|
+
const relativePath = path_1.default.relative(parentPath, candidatePath);
|
|
174
|
+
return relativePath === '' || (!relativePath.startsWith('..') && !path_1.default.isAbsolute(relativePath));
|
|
119
175
|
}
|
|
120
176
|
async function readNormalizedLocalHarPackageMetadata(directoryPath, packageName) {
|
|
121
177
|
await ensureNormalizedLocalHarModuleJson5(directoryPath);
|
|
@@ -245,6 +301,10 @@ async function ensureHarmonyBuildProfileSupportsNormalizedLocalDeps(harmonyProje
|
|
|
245
301
|
moduleEntry?.name === localHarPackage.moduleName);
|
|
246
302
|
});
|
|
247
303
|
if (existingModule) {
|
|
304
|
+
if (existingModule.name !== localHarPackage.moduleName) {
|
|
305
|
+
existingModule.name = localHarPackage.moduleName;
|
|
306
|
+
didChange = true;
|
|
307
|
+
}
|
|
248
308
|
if (existingModule.srcPath !== relativeDirectory) {
|
|
249
309
|
existingModule.srcPath = relativeDirectory;
|
|
250
310
|
didChange = true;
|
|
@@ -331,7 +391,7 @@ async function alignRnohCodegenWithNormalizedLocalPackage(harmonyProjectRoot, lo
|
|
|
331
391
|
if (!relativeRnohModulePath.startsWith('.')) {
|
|
332
392
|
relativeRnohModulePath = `./${relativeRnohModulePath}`;
|
|
333
393
|
}
|
|
334
|
-
const updatedHvigorFile = originalHvigorFile.replace(/rnohModulePath:\s*['"][^'"]+['"]/, `rnohModulePath:
|
|
394
|
+
const updatedHvigorFile = originalHvigorFile.replace(/rnohModulePath:\s*['"][^'"]+['"]/, `rnohModulePath: ${JSON.stringify(relativeRnohModulePath)}`);
|
|
335
395
|
if (updatedHvigorFile !== originalHvigorFile) {
|
|
336
396
|
await fs_extra_1.default.writeFile(hvigorFilePath, updatedHvigorFile);
|
|
337
397
|
}
|
package/build/core/build.js
CHANGED
|
@@ -18,6 +18,7 @@ const localHar_1 = require("./build/localHar");
|
|
|
18
18
|
const commands_1 = require("./build/commands");
|
|
19
19
|
const reporting_1 = require("./build/reporting");
|
|
20
20
|
const rnohCompatibility_1 = require("./build/rnohCompatibility");
|
|
21
|
+
const signing_1 = require("./signing");
|
|
21
22
|
var reporting_2 = require("./build/reporting");
|
|
22
23
|
Object.defineProperty(exports, "renderBuildReport", { enumerable: true, get: function () { return reporting_2.renderBuildReport; } });
|
|
23
24
|
exports.SKIP_HAR_NORMALIZE_WARNING = 'Local HAR normalization is disabled; ohpm will consume file:*.har dependencies directly and normalized build-profile/RNOH compatibility steps will be skipped.';
|
|
@@ -291,6 +292,7 @@ async function buildHapProject(projectRoot, options) {
|
|
|
291
292
|
let restoreNormalizedRnohCliAutolinkingTemplates = async () => { };
|
|
292
293
|
let restoreNormalizedRnohCodegenAlignment = async () => { };
|
|
293
294
|
let restoreNormalizedHarmonyPackageJsons = async () => { };
|
|
295
|
+
let restoreLocalSigningBuildProfile = async () => { };
|
|
294
296
|
if (!skipHarNormalize) {
|
|
295
297
|
try {
|
|
296
298
|
const normalizedLocalHarDependencies = await (0, localHar_1.normalizeLocalHarDependencies)(harmonyProjectRoot);
|
|
@@ -363,6 +365,7 @@ async function buildHapProject(projectRoot, options) {
|
|
|
363
365
|
await (0, localHar_1.patchRnohGeneratedCodegenForNormalizedLocalPackage)(harmonyProjectRoot);
|
|
364
366
|
}
|
|
365
367
|
await (0, localHar_1.patchKnownHarmonyAdapterCodegenOutputs)(harmonyProjectRoot);
|
|
368
|
+
restoreLocalSigningBuildProfile = await temporarilyMergeLocalSigningIntoBuildProfile(loadedProject.projectRoot, harmonyProjectRoot);
|
|
366
369
|
const hvigorCommand = (0, commands_1.buildInvocation)(envReport.hvigorPath, [
|
|
367
370
|
'assembleHap',
|
|
368
371
|
'--no-daemon',
|
|
@@ -416,6 +419,7 @@ async function buildHapProject(projectRoot, options) {
|
|
|
416
419
|
});
|
|
417
420
|
}
|
|
418
421
|
finally {
|
|
422
|
+
await restoreLocalSigningBuildProfile();
|
|
419
423
|
await restoreNormalizedHarmonyPackageJsons();
|
|
420
424
|
await restoreNormalizedRnohCodegenAlignment();
|
|
421
425
|
await restoreNormalizedRnohCliAutolinkingTemplates();
|
|
@@ -423,6 +427,25 @@ async function buildHapProject(projectRoot, options) {
|
|
|
423
427
|
await restoreNormalizedDependencies();
|
|
424
428
|
}
|
|
425
429
|
}
|
|
430
|
+
async function temporarilyMergeLocalSigningIntoBuildProfile(projectRoot, harmonyProjectRoot) {
|
|
431
|
+
const signingLocalConfig = await (0, signing_1.readSigningLocalConfig)(projectRoot);
|
|
432
|
+
if (!signingLocalConfig) {
|
|
433
|
+
return async () => { };
|
|
434
|
+
}
|
|
435
|
+
const buildProfilePath = path_1.default.join(harmonyProjectRoot, 'build-profile.json5');
|
|
436
|
+
if (!(await fs_extra_1.default.pathExists(buildProfilePath))) {
|
|
437
|
+
return async () => { };
|
|
438
|
+
}
|
|
439
|
+
const originalContents = await fs_extra_1.default.readFile(buildProfilePath, 'utf8');
|
|
440
|
+
const nextContents = (0, signing_1.mergeSigningLocalConfigIntoBuildProfile)(originalContents, signingLocalConfig, { includeSecrets: true });
|
|
441
|
+
if (nextContents === originalContents) {
|
|
442
|
+
return async () => { };
|
|
443
|
+
}
|
|
444
|
+
await fs_extra_1.default.writeFile(buildProfilePath, nextContents);
|
|
445
|
+
return async () => {
|
|
446
|
+
await fs_extra_1.default.writeFile(buildProfilePath, originalContents);
|
|
447
|
+
};
|
|
448
|
+
}
|
|
426
449
|
function getDesiredHarmonyScripts(projectRoot) {
|
|
427
450
|
return (0, project_1.loadProject)(projectRoot).then((loadedProject) => (0, template_1.buildDesiredPackageScripts)(loadedProject.packageJson));
|
|
428
451
|
}
|
|
@@ -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.9.
|
|
3
|
+
export declare const TOOLKIT_VERSION = "1.9.3";
|
|
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";
|
package/build/core/constants.js
CHANGED
|
@@ -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.9.
|
|
6
|
+
exports.TOOLKIT_VERSION = '1.9.3';
|
|
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';
|
package/build/core/report.js
CHANGED
|
@@ -26,11 +26,56 @@ const DEFAULT_RECORD = {
|
|
|
26
26
|
const BARE_WORKFLOW_DIRECTORY_NAMES = ['android', 'ios'];
|
|
27
27
|
const BARE_WORKFLOW_DEPENDENCIES = new Set(['expo-build-properties', 'expo-dev-client']);
|
|
28
28
|
const FORMAL_EXPERIMENTAL_ADAPTER_PAIRS = [
|
|
29
|
+
{
|
|
30
|
+
canonicalPackageName: '@react-native-async-storage/async-storage',
|
|
31
|
+
adapterPackageName: '@react-native-oh-tpl/async-storage',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
canonicalPackageName: 'react-native-webview',
|
|
35
|
+
adapterPackageName: '@react-native-oh-tpl/react-native-webview',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
canonicalPackageName: 'react-native-screens',
|
|
39
|
+
adapterPackageName: '@react-native-oh-tpl/react-native-screens',
|
|
40
|
+
},
|
|
29
41
|
{
|
|
30
42
|
canonicalPackageName: 'react-native-gesture-handler',
|
|
31
43
|
adapterPackageName: '@react-native-oh-tpl/react-native-gesture-handler',
|
|
32
44
|
},
|
|
45
|
+
{
|
|
46
|
+
canonicalPackageName: 'lottie-react-native',
|
|
47
|
+
adapterPackageName: '@react-native-oh-tpl/lottie-react-native',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
canonicalPackageName: 'expo-media-library',
|
|
51
|
+
adapterPackageName: '@react-native-oh-tpl/camera-roll',
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
canonicalPackageName: '@shopify/react-native-skia',
|
|
55
|
+
adapterPackageName: '@react-native-oh-tpl/react-native-skia',
|
|
56
|
+
},
|
|
33
57
|
];
|
|
58
|
+
const THIRD_PARTY_WAVE_A_PACKAGE_NAMES = new Set([
|
|
59
|
+
'@react-native-async-storage/async-storage',
|
|
60
|
+
'@react-native-oh-tpl/async-storage',
|
|
61
|
+
'react-native-screens',
|
|
62
|
+
'@react-native-oh-tpl/react-native-screens',
|
|
63
|
+
'react-native-safe-area-context',
|
|
64
|
+
]);
|
|
65
|
+
const THIRD_PARTY_WAVE_B_PACKAGE_NAMES = new Set([
|
|
66
|
+
'react-native-webview',
|
|
67
|
+
'@react-native-oh-tpl/react-native-webview',
|
|
68
|
+
'jpush-react-native',
|
|
69
|
+
'jcore-react-native',
|
|
70
|
+
'mx-jpush-expo',
|
|
71
|
+
'expo-notifications',
|
|
72
|
+
'expo-media-library',
|
|
73
|
+
'@react-native-oh-tpl/camera-roll',
|
|
74
|
+
'lottie-react-native',
|
|
75
|
+
'@react-native-oh-tpl/lottie-react-native',
|
|
76
|
+
'@shopify/react-native-skia',
|
|
77
|
+
'@react-native-oh-tpl/react-native-skia',
|
|
78
|
+
]);
|
|
34
79
|
async function buildDoctorReport(projectRoot, options = {}) {
|
|
35
80
|
const loadedProject = await (0, project_1.loadProject)(projectRoot);
|
|
36
81
|
const doctorConfig = (0, project_1.resolveExpoHarmonyDoctorConfig)(loadedProject.expoConfig);
|
|
@@ -477,7 +522,16 @@ function buildNextActions(input) {
|
|
|
477
522
|
actions.push('Keep this project on the bare workflow track for now: preserve the native directories, use `expo-harmony doctor --project-root .` for classification, and only claim verified support after bare workflow support lands in the mainline capability catalog.');
|
|
478
523
|
break;
|
|
479
524
|
case 'third-party-native-heavy':
|
|
480
|
-
|
|
525
|
+
if (dependencies.some((dependency) => THIRD_PARTY_WAVE_A_PACKAGE_NAMES.has(dependency.name))) {
|
|
526
|
+
actions.push('Keep Third-party Native Wave A on `doctor --target-tier experimental`: pair async-storage and screens with their Harmony adapters, keep safe-area on the toolkit shim, and close device/release evidence before any promotion.');
|
|
527
|
+
}
|
|
528
|
+
if (dependencies.some((dependency) => THIRD_PARTY_WAVE_B_PACKAGE_NAMES.has(dependency.name))) {
|
|
529
|
+
actions.push('Keep Third-party Native Wave B on `doctor --target-tier experimental`: pair ccnubox WebView, media-library, Lottie, and Skia surfaces with their Harmony adapters, keep JPush runtime evidence separate, and require debug/release simulator delivery gates before shipping.');
|
|
530
|
+
}
|
|
531
|
+
if (!dependencies.some((dependency) => THIRD_PARTY_WAVE_A_PACKAGE_NAMES.has(dependency.name)) &&
|
|
532
|
+
!dependencies.some((dependency) => THIRD_PARTY_WAVE_B_PACKAGE_NAMES.has(dependency.name))) {
|
|
533
|
+
actions.push('Isolate third-party native packages and onboard them through the mainline capability catalog one by one; start with `react-native-gesture-handler` if it is present, and treat unknown native surfaces as explicit unblockers rather than matrix drift.');
|
|
534
|
+
}
|
|
481
535
|
break;
|
|
482
536
|
}
|
|
483
537
|
if (hasRouterBlockingIssues) {
|
package/build/core/signing.d.ts
CHANGED
|
@@ -3,10 +3,13 @@ type SigningLocalAppFragment = {
|
|
|
3
3
|
signingConfigs?: JsonRecord[];
|
|
4
4
|
products?: JsonRecord[];
|
|
5
5
|
};
|
|
6
|
+
interface MergeSigningOptions {
|
|
7
|
+
includeSecrets?: boolean;
|
|
8
|
+
}
|
|
6
9
|
export declare function getSigningLocalPath(projectRoot: string): string;
|
|
7
10
|
export declare function getSigningLocalExamplePath(projectRoot: string): string;
|
|
8
11
|
export declare function readSigningLocalConfig(projectRoot: string): Promise<SigningLocalAppFragment | null>;
|
|
9
|
-
export declare function mergeSigningLocalConfigIntoBuildProfile(rawBuildProfileContents: string, signingLocalConfig: SigningLocalAppFragment | null): string;
|
|
12
|
+
export declare function mergeSigningLocalConfigIntoBuildProfile(rawBuildProfileContents: string, signingLocalConfig: SigningLocalAppFragment | null, options?: MergeSigningOptions): string;
|
|
10
13
|
export declare function hasSigningConfiguration(rawBuildProfileContents: string | null, signingLocalConfig?: SigningLocalAppFragment | null): boolean;
|
|
11
14
|
export declare function hasSigningLocalConfiguration(signingLocalConfig: SigningLocalAppFragment | null): boolean;
|
|
12
15
|
export declare function renderSigningLocalExampleConfig(): string;
|
package/build/core/signing.js
CHANGED
|
@@ -28,12 +28,12 @@ async function readSigningLocalConfig(projectRoot) {
|
|
|
28
28
|
const rawConfig = (await fs_extra_1.default.readJson(signingLocalPath));
|
|
29
29
|
return normalizeSigningLocalConfig(rawConfig);
|
|
30
30
|
}
|
|
31
|
-
function mergeSigningLocalConfigIntoBuildProfile(rawBuildProfileContents, signingLocalConfig) {
|
|
31
|
+
function mergeSigningLocalConfigIntoBuildProfile(rawBuildProfileContents, signingLocalConfig, options = {}) {
|
|
32
32
|
if (!signingLocalConfig) {
|
|
33
33
|
return rawBuildProfileContents;
|
|
34
34
|
}
|
|
35
35
|
const parsed = json5_1.default.parse(rawBuildProfileContents);
|
|
36
|
-
const merged = mergeSigningLocalConfig(parsed, signingLocalConfig);
|
|
36
|
+
const merged = mergeSigningLocalConfig(parsed, signingLocalConfig, options);
|
|
37
37
|
return JSON.stringify(merged, null, 2) + '\n';
|
|
38
38
|
}
|
|
39
39
|
function hasSigningConfiguration(rawBuildProfileContents, signingLocalConfig = null) {
|
|
@@ -99,7 +99,7 @@ function normalizeSigningLocalConfig(rawConfig) {
|
|
|
99
99
|
...(products ? { products } : {}),
|
|
100
100
|
};
|
|
101
101
|
}
|
|
102
|
-
function mergeSigningLocalConfig(buildProfile, signingLocalConfig) {
|
|
102
|
+
function mergeSigningLocalConfig(buildProfile, signingLocalConfig, options) {
|
|
103
103
|
const nextBuildProfile = {
|
|
104
104
|
...buildProfile,
|
|
105
105
|
app: isJsonRecord(buildProfile.app)
|
|
@@ -109,7 +109,7 @@ function mergeSigningLocalConfig(buildProfile, signingLocalConfig) {
|
|
|
109
109
|
: {},
|
|
110
110
|
};
|
|
111
111
|
if (Array.isArray(signingLocalConfig.signingConfigs)) {
|
|
112
|
-
nextBuildProfile.app.signingConfigs = signingLocalConfig.signingConfigs.map((entry) => (
|
|
112
|
+
nextBuildProfile.app.signingConfigs = signingLocalConfig.signingConfigs.map((entry) => cloneSigningConfigForBuildProfile(entry, options));
|
|
113
113
|
}
|
|
114
114
|
if (Array.isArray(signingLocalConfig.products)) {
|
|
115
115
|
const currentProducts = Array.isArray(nextBuildProfile.app.products)
|
|
@@ -135,6 +135,21 @@ function mergeSigningLocalConfig(buildProfile, signingLocalConfig) {
|
|
|
135
135
|
}
|
|
136
136
|
return nextBuildProfile;
|
|
137
137
|
}
|
|
138
|
+
function cloneSigningConfigForBuildProfile(entry, options) {
|
|
139
|
+
const nextEntry = { ...entry };
|
|
140
|
+
if (options.includeSecrets) {
|
|
141
|
+
return nextEntry;
|
|
142
|
+
}
|
|
143
|
+
delete nextEntry.storePassword;
|
|
144
|
+
delete nextEntry.keyPassword;
|
|
145
|
+
if (isJsonRecord(nextEntry.material)) {
|
|
146
|
+
const material = { ...nextEntry.material };
|
|
147
|
+
delete material.storePassword;
|
|
148
|
+
delete material.keyPassword;
|
|
149
|
+
nextEntry.material = material;
|
|
150
|
+
}
|
|
151
|
+
return nextEntry;
|
|
152
|
+
}
|
|
138
153
|
function hasNonEmptySigningConfigList(signingConfigs) {
|
|
139
154
|
return Array.isArray(signingConfigs) && signingConfigs.some(isJsonRecord);
|
|
140
155
|
}
|
|
@@ -8,6 +8,7 @@ const camera_1 = require("./renderers/camera");
|
|
|
8
8
|
const fileSystem_1 = require("./renderers/fileSystem");
|
|
9
9
|
const imagePicker_1 = require("./renderers/imagePicker");
|
|
10
10
|
const location_1 = require("./renderers/location");
|
|
11
|
+
const runtimeShims_1 = require("./runtimeShims");
|
|
11
12
|
const CAPABILITY_SHIM_RENDERERS = {
|
|
12
13
|
'expo-file-system': {
|
|
13
14
|
adapter: fileSystem_1.renderExpoFileSystemHarmonyAdapterShim,
|
|
@@ -45,6 +46,10 @@ const CAPABILITY_SHIM_RENDERERS = {
|
|
|
45
46
|
adapter: appFoundation_1.renderExpoHapticsPreviewShim,
|
|
46
47
|
preview: appFoundation_1.renderExpoHapticsPreviewShim,
|
|
47
48
|
},
|
|
49
|
+
'react-native-safe-area-context': {
|
|
50
|
+
adapter: runtimeShims_1.renderReactNativeSafeAreaContextHarmonyShim,
|
|
51
|
+
preview: runtimeShims_1.renderReactNativeSafeAreaContextHarmonyShim,
|
|
52
|
+
},
|
|
48
53
|
};
|
|
49
54
|
exports.MANAGED_EXPO_HARMONY_MODULE_RENDERERS = [
|
|
50
55
|
{
|
|
@@ -3,8 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.renderMetroConfig = renderMetroConfig;
|
|
4
4
|
function renderMetroConfig(enabledCapabilities) {
|
|
5
5
|
const previewCapabilityAliases = enabledCapabilities
|
|
6
|
-
.filter((capability) => capability.
|
|
7
|
-
capability.
|
|
6
|
+
.filter((capability) => capability.packageName !== 'react-native-safe-area-context' &&
|
|
7
|
+
(capability.packageName.startsWith('expo-') || capability.runtimeMode === 'shim') &&
|
|
8
|
+
(capability.supportTier === 'preview' ||
|
|
9
|
+
capability.supportTier === 'experimental'))
|
|
8
10
|
.map((capability) => ` '${capability.packageName}': path.resolve(__dirname, '.expo-harmony/shims/${capability.packageName}'),`)
|
|
9
11
|
.join('\n');
|
|
10
12
|
return `const fs = require('fs');
|
|
@@ -18,25 +18,37 @@ const PREVIEW_BASELINE_EVIDENCE_SOURCE = {
|
|
|
18
18
|
device: 'manual-doc',
|
|
19
19
|
release: 'none',
|
|
20
20
|
};
|
|
21
|
-
const
|
|
22
|
-
bundle:
|
|
23
|
-
debugBuild:
|
|
21
|
+
const APP_FOUNDATION_BASELINE_EVIDENCE = {
|
|
22
|
+
bundle: true,
|
|
23
|
+
debugBuild: true,
|
|
24
24
|
device: false,
|
|
25
25
|
release: false,
|
|
26
26
|
};
|
|
27
|
-
const
|
|
28
|
-
bundle: '
|
|
29
|
-
debugBuild: '
|
|
27
|
+
const APP_FOUNDATION_BASELINE_EVIDENCE_SOURCE = {
|
|
28
|
+
bundle: 'automated',
|
|
29
|
+
debugBuild: 'automated',
|
|
30
30
|
device: 'none',
|
|
31
31
|
release: 'none',
|
|
32
32
|
};
|
|
33
|
-
const
|
|
33
|
+
const THIRD_PARTY_WAVE_A_BASELINE_EVIDENCE = {
|
|
34
34
|
bundle: true,
|
|
35
35
|
debugBuild: true,
|
|
36
36
|
device: false,
|
|
37
37
|
release: false,
|
|
38
38
|
};
|
|
39
|
-
const
|
|
39
|
+
const THIRD_PARTY_WAVE_A_BASELINE_EVIDENCE_SOURCE = {
|
|
40
|
+
bundle: 'automated',
|
|
41
|
+
debugBuild: 'automated',
|
|
42
|
+
device: 'none',
|
|
43
|
+
release: 'none',
|
|
44
|
+
};
|
|
45
|
+
const THIRD_PARTY_WAVE_B_BASELINE_EVIDENCE = {
|
|
46
|
+
bundle: true,
|
|
47
|
+
debugBuild: true,
|
|
48
|
+
device: false,
|
|
49
|
+
release: false,
|
|
50
|
+
};
|
|
51
|
+
const THIRD_PARTY_WAVE_B_BASELINE_EVIDENCE_SOURCE = {
|
|
40
52
|
bundle: 'automated',
|
|
41
53
|
debugBuild: 'automated',
|
|
42
54
|
device: 'none',
|
|
@@ -229,17 +241,17 @@ exports.CAPABILITY_DEFINITIONS = [
|
|
|
229
241
|
status: 'manual',
|
|
230
242
|
supportTier: 'experimental',
|
|
231
243
|
runtimeMode: 'shim',
|
|
232
|
-
evidence:
|
|
233
|
-
evidenceSource:
|
|
234
|
-
note: '
|
|
244
|
+
evidence: THIRD_PARTY_WAVE_B_BASELINE_EVIDENCE,
|
|
245
|
+
evidenceSource: THIRD_PARTY_WAVE_B_BASELINE_EVIDENCE_SOURCE,
|
|
246
|
+
note: 'v1.9.3 Wave B keeps Expo Notifications on a safe experimental shim for ccnubox push flows while JPush delivery and signed-runtime behavior remain separate evidence gates.',
|
|
235
247
|
docsUrl: 'https://github.com/react-native-oh-library',
|
|
236
248
|
nativePackageNames: [],
|
|
237
249
|
harmonyPermissions: ['ohos.permission.NOTIFICATION_CONTROLLER'],
|
|
238
|
-
sampleRoute: '/notifications',
|
|
250
|
+
sampleRoute: '/third-party-wave-b/notifications',
|
|
239
251
|
acceptanceChecklist: [
|
|
240
|
-
'
|
|
241
|
-
'
|
|
242
|
-
'
|
|
252
|
+
'Bundle notification permission and channel setup paths without crashing.',
|
|
253
|
+
'Keep foreground receipt, click, and cold-start delivery pending signed simulator and device evidence.',
|
|
254
|
+
'Do not treat simulator app-shell launch as notification delivery acceptance.',
|
|
243
255
|
],
|
|
244
256
|
},
|
|
245
257
|
{
|
|
@@ -261,6 +273,158 @@ exports.CAPABILITY_DEFINITIONS = [
|
|
|
261
273
|
'Record debug HAP evidence before any promotion decision; device and release remain separate gates.',
|
|
262
274
|
],
|
|
263
275
|
},
|
|
276
|
+
{
|
|
277
|
+
id: 'async-storage',
|
|
278
|
+
packageName: '@react-native-async-storage/async-storage',
|
|
279
|
+
status: 'manual',
|
|
280
|
+
supportTier: 'experimental',
|
|
281
|
+
runtimeMode: 'adapter',
|
|
282
|
+
evidence: THIRD_PARTY_WAVE_A_BASELINE_EVIDENCE,
|
|
283
|
+
evidenceSource: THIRD_PARTY_WAVE_A_BASELINE_EVIDENCE_SOURCE,
|
|
284
|
+
note: 'v1.9.2 Wave A tracks Async Storage through its Harmony adapter, but persistence behavior remains experimental until device and release evidence are closed.',
|
|
285
|
+
docsUrl: 'https://github.com/react-native-oh-library/usage-docs',
|
|
286
|
+
nativePackageNames: ['@react-native-oh-tpl/async-storage'],
|
|
287
|
+
harmonyPermissions: [],
|
|
288
|
+
sampleRoute: '/third-party-wave-a/async-storage',
|
|
289
|
+
acceptanceChecklist: [
|
|
290
|
+
'Install the canonical package with the matching Harmony adapter.',
|
|
291
|
+
'Bundle a minimal Async Storage read/write/remove surface.',
|
|
292
|
+
'Keep real persistence, migration, and release HAP runtime behavior pending device evidence.',
|
|
293
|
+
],
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
id: 'react-native-screens',
|
|
297
|
+
packageName: 'react-native-screens',
|
|
298
|
+
status: 'manual',
|
|
299
|
+
supportTier: 'experimental',
|
|
300
|
+
runtimeMode: 'adapter',
|
|
301
|
+
evidence: THIRD_PARTY_WAVE_A_BASELINE_EVIDENCE,
|
|
302
|
+
evidenceSource: THIRD_PARTY_WAVE_A_BASELINE_EVIDENCE_SOURCE,
|
|
303
|
+
note: 'v1.9.2 Wave A tracks Screens through its Harmony adapter metadata, while managed autolinking and navigation-stack runtime behavior remain promotion gaps.',
|
|
304
|
+
docsUrl: 'https://github.com/react-native-oh-library',
|
|
305
|
+
nativePackageNames: ['@react-native-oh-tpl/react-native-screens'],
|
|
306
|
+
harmonyPermissions: [],
|
|
307
|
+
sampleRoute: '/third-party-wave-a/screens',
|
|
308
|
+
acceptanceChecklist: [
|
|
309
|
+
'Install the canonical package with the matching Harmony adapter.',
|
|
310
|
+
'Bundle a minimal navigation stack that imports react-native-screens.',
|
|
311
|
+
'Do not claim verified support until adapter autolinking, device, and release evidence are recorded.',
|
|
312
|
+
],
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
id: 'react-native-safe-area-context',
|
|
316
|
+
packageName: 'react-native-safe-area-context',
|
|
317
|
+
status: 'manual',
|
|
318
|
+
supportTier: 'experimental',
|
|
319
|
+
runtimeMode: 'shim',
|
|
320
|
+
evidence: THIRD_PARTY_WAVE_A_BASELINE_EVIDENCE,
|
|
321
|
+
evidenceSource: THIRD_PARTY_WAVE_A_BASELINE_EVIDENCE_SOURCE,
|
|
322
|
+
note: 'v1.9.2 Wave A formalizes the existing toolkit safe-area shim so app-shell layouts keep bundling while native safe-area measurements stay outside verified.',
|
|
323
|
+
docsUrl: 'https://github.com/react-native-oh-library',
|
|
324
|
+
nativePackageNames: [],
|
|
325
|
+
harmonyPermissions: [],
|
|
326
|
+
sampleRoute: '/third-party-wave-a/safe-area',
|
|
327
|
+
acceptanceChecklist: [
|
|
328
|
+
'Render SafeAreaProvider, SafeAreaView, and safe-area hooks through the toolkit shim.',
|
|
329
|
+
'Keep the Metro alias on .expo-harmony/shims/react-native-safe-area-context.',
|
|
330
|
+
'Validate real native inset measurement separately before any adapter promotion.',
|
|
331
|
+
],
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
id: 'react-native-webview',
|
|
335
|
+
packageName: 'react-native-webview',
|
|
336
|
+
status: 'manual',
|
|
337
|
+
supportTier: 'experimental',
|
|
338
|
+
runtimeMode: 'adapter',
|
|
339
|
+
evidence: THIRD_PARTY_WAVE_B_BASELINE_EVIDENCE,
|
|
340
|
+
evidenceSource: THIRD_PARTY_WAVE_B_BASELINE_EVIDENCE_SOURCE,
|
|
341
|
+
note: 'v1.9.3 Wave B tracks ccnubox WebView surfaces through the Harmony adapter, but navigation, injected scripts, and release runtime stability remain promotion evidence.',
|
|
342
|
+
docsUrl: 'https://github.com/react-native-oh-library/usage-docs/blob/master/en/react-native-webview.md',
|
|
343
|
+
nativePackageNames: ['@react-native-oh-tpl/react-native-webview'],
|
|
344
|
+
harmonyPermissions: [],
|
|
345
|
+
sampleRoute: '/third-party-wave-b/webview',
|
|
346
|
+
acceptanceChecklist: [
|
|
347
|
+
'Install react-native-webview with the matching Harmony adapter.',
|
|
348
|
+
'Bundle a minimal WebView surface with message passing and navigation callbacks.',
|
|
349
|
+
'Validate signed simulator startup and real WebView runtime behavior before any promotion.',
|
|
350
|
+
],
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
id: 'jpush-react-native',
|
|
354
|
+
packageName: 'jpush-react-native',
|
|
355
|
+
status: 'manual',
|
|
356
|
+
supportTier: 'experimental',
|
|
357
|
+
runtimeMode: 'adapter',
|
|
358
|
+
evidence: THIRD_PARTY_WAVE_B_BASELINE_EVIDENCE,
|
|
359
|
+
evidenceSource: THIRD_PARTY_WAVE_B_BASELINE_EVIDENCE_SOURCE,
|
|
360
|
+
note: 'v1.9.3 Wave B keeps ccnubox JPush runtime dependencies in formal telemetry; registrationId, arrival, click, and cold-start payloads still require signed runtime evidence.',
|
|
361
|
+
docsUrl: 'https://docs.jiguang.cn/jpush/client/HarmonyOS/hmos_guide',
|
|
362
|
+
nativePackageNames: ['jcore-react-native'],
|
|
363
|
+
harmonyPermissions: ['ohos.permission.NOTIFICATION_CONTROLLER'],
|
|
364
|
+
sampleRoute: '/third-party-wave-b/jpush',
|
|
365
|
+
acceptanceChecklist: [
|
|
366
|
+
'Keep jpush-react-native, jcore-react-native, and mx-jpush-expo visible in doctor reports.',
|
|
367
|
+
'Bundle guarded JPush init and registrationId lookup paths without native-module crashes.',
|
|
368
|
+
'Record signed simulator and device push delivery evidence before any promotion.',
|
|
369
|
+
],
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
id: 'expo-media-library',
|
|
373
|
+
packageName: 'expo-media-library',
|
|
374
|
+
status: 'manual',
|
|
375
|
+
supportTier: 'experimental',
|
|
376
|
+
runtimeMode: 'adapter',
|
|
377
|
+
evidence: THIRD_PARTY_WAVE_B_BASELINE_EVIDENCE,
|
|
378
|
+
evidenceSource: THIRD_PARTY_WAVE_B_BASELINE_EVIDENCE_SOURCE,
|
|
379
|
+
note: 'v1.9.3 Wave B tracks ccnubox media-library save flows through the camera-roll Harmony adapter while gallery write/read behavior remains runtime evidence.',
|
|
380
|
+
docsUrl: 'https://github.com/react-native-oh-library',
|
|
381
|
+
nativePackageNames: ['@react-native-oh-tpl/camera-roll'],
|
|
382
|
+
harmonyPermissions: ['ohos.permission.READ_IMAGEVIDEO', 'ohos.permission.WRITE_IMAGEVIDEO'],
|
|
383
|
+
sampleRoute: '/third-party-wave-b/media-library',
|
|
384
|
+
acceptanceChecklist: [
|
|
385
|
+
'Install expo-media-library with the camera-roll Harmony adapter available.',
|
|
386
|
+
'Bundle permission and create-asset code paths used by ccnubox screenshots.',
|
|
387
|
+
'Validate gallery write/read behavior on signed simulator and device before promotion.',
|
|
388
|
+
],
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
id: 'lottie-react-native',
|
|
392
|
+
packageName: 'lottie-react-native',
|
|
393
|
+
status: 'manual',
|
|
394
|
+
supportTier: 'experimental',
|
|
395
|
+
runtimeMode: 'adapter',
|
|
396
|
+
evidence: THIRD_PARTY_WAVE_B_BASELINE_EVIDENCE,
|
|
397
|
+
evidenceSource: THIRD_PARTY_WAVE_B_BASELINE_EVIDENCE_SOURCE,
|
|
398
|
+
note: 'v1.9.3 Wave B tracks ccnubox Lottie animation surfaces through the Harmony adapter, but animation playback remains simulator and device smoke evidence.',
|
|
399
|
+
docsUrl: 'https://github.com/react-native-oh-library',
|
|
400
|
+
nativePackageNames: ['@react-native-oh-tpl/lottie-react-native'],
|
|
401
|
+
harmonyPermissions: [],
|
|
402
|
+
sampleRoute: '/third-party-wave-b/lottie',
|
|
403
|
+
acceptanceChecklist: [
|
|
404
|
+
'Install lottie-react-native with the matching Harmony adapter.',
|
|
405
|
+
'Bundle a minimal LottieView surface and local animation asset path.',
|
|
406
|
+
'Validate playback on signed simulator and device before promotion.',
|
|
407
|
+
],
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
id: 'react-native-skia',
|
|
411
|
+
packageName: '@shopify/react-native-skia',
|
|
412
|
+
status: 'manual',
|
|
413
|
+
supportTier: 'experimental',
|
|
414
|
+
runtimeMode: 'adapter',
|
|
415
|
+
evidence: THIRD_PARTY_WAVE_B_BASELINE_EVIDENCE,
|
|
416
|
+
evidenceSource: THIRD_PARTY_WAVE_B_BASELINE_EVIDENCE_SOURCE,
|
|
417
|
+
note: 'v1.9.3 Wave B tracks ccnubox Skia timetable rendering through the Harmony adapter direction while canvas rendering remains runtime evidence.',
|
|
418
|
+
docsUrl: 'https://github.com/react-native-oh-library',
|
|
419
|
+
nativePackageNames: ['@react-native-oh-tpl/react-native-skia'],
|
|
420
|
+
harmonyPermissions: [],
|
|
421
|
+
sampleRoute: '/third-party-wave-b/skia',
|
|
422
|
+
acceptanceChecklist: [
|
|
423
|
+
'Install @shopify/react-native-skia with the Harmony Skia adapter available.',
|
|
424
|
+
'Bundle a minimal Canvas/Skia import surface used by timetable rendering.',
|
|
425
|
+
'Validate actual canvas rendering on signed simulator and device before promotion.',
|
|
426
|
+
],
|
|
427
|
+
},
|
|
264
428
|
];
|
|
265
429
|
exports.CAPABILITY_BY_PACKAGE = Object.fromEntries(exports.CAPABILITY_DEFINITIONS.map((definition) => [definition.packageName, definition]));
|
|
266
430
|
const SUPPORT_TIER_ORDER = {
|
|
@@ -138,25 +138,25 @@ exports.DEPENDENCY_CATALOG = {
|
|
|
138
138
|
'@react-native-async-storage/async-storage': {
|
|
139
139
|
status: 'manual',
|
|
140
140
|
supportTier: 'experimental',
|
|
141
|
-
note: '
|
|
141
|
+
note: 'v1.9.2 Wave A tracks Async Storage through its Harmony adapter, but persistence behavior still needs device and release evidence before verified promotion.',
|
|
142
142
|
docsUrl: 'https://github.com/react-native-oh-library',
|
|
143
143
|
},
|
|
144
144
|
'react-native-safe-area-context': {
|
|
145
|
-
status: '
|
|
145
|
+
status: 'manual',
|
|
146
146
|
supportTier: 'experimental',
|
|
147
|
-
note: '
|
|
147
|
+
note: 'v1.9.2 Wave A formalizes the toolkit-managed safe-area shim while real native inset measurement remains outside the verified matrix.',
|
|
148
148
|
docsUrl: 'https://github.com/react-native-oh-library',
|
|
149
149
|
},
|
|
150
150
|
'react-native-screens': {
|
|
151
151
|
status: 'manual',
|
|
152
152
|
supportTier: 'experimental',
|
|
153
|
-
note: '
|
|
153
|
+
note: 'v1.9.2 Wave A tracks Screens through its Harmony adapter metadata; navigation-stack runtime behavior still needs device and release evidence.',
|
|
154
154
|
docsUrl: 'https://github.com/react-native-oh-library',
|
|
155
155
|
},
|
|
156
156
|
'react-native-webview': {
|
|
157
157
|
status: 'manual',
|
|
158
158
|
supportTier: 'experimental',
|
|
159
|
-
note: '
|
|
159
|
+
note: 'v1.9.3 Wave B tracks ccnubox WebView usage through the Harmony adapter, but navigation, injected scripts, and release runtime behavior remain experimental evidence.',
|
|
160
160
|
docsUrl: 'https://github.com/react-native-oh-library',
|
|
161
161
|
},
|
|
162
162
|
...UI_STACK_COMPATIBILITY_RECORDS,
|
|
@@ -289,7 +289,7 @@ exports.DEPENDENCY_CATALOG = {
|
|
|
289
289
|
'expo-media-library': {
|
|
290
290
|
status: 'manual',
|
|
291
291
|
supportTier: 'experimental',
|
|
292
|
-
note: '
|
|
292
|
+
note: 'v1.9.3 Wave B tracks media-library screenshot save flows through the Harmony camera-roll adapter; gallery write/read behavior still needs signed runtime evidence.',
|
|
293
293
|
replacement: '@react-native-oh-tpl/camera-roll',
|
|
294
294
|
},
|
|
295
295
|
'expo-secure-store': {
|
|
@@ -325,19 +325,19 @@ exports.DEPENDENCY_CATALOG = {
|
|
|
325
325
|
'jcore-react-native': {
|
|
326
326
|
status: 'manual',
|
|
327
327
|
supportTier: 'experimental',
|
|
328
|
-
note: '
|
|
328
|
+
note: 'v1.9.3 Wave B keeps JCore visible as the JPush runtime foundation, but SDK registration and delivery still need signed runtime evidence.',
|
|
329
329
|
docsUrl: 'https://docs.jiguang.cn/jpush/client/HarmonyOS/hmos_guide',
|
|
330
330
|
},
|
|
331
331
|
'jpush-react-native': {
|
|
332
332
|
status: 'manual',
|
|
333
333
|
supportTier: 'experimental',
|
|
334
|
-
note: '
|
|
334
|
+
note: 'v1.9.3 Wave B keeps JPush in formal telemetry for ccnubox; registrationId, arrival, click, and cold-start payloads require signed runtime evidence.',
|
|
335
335
|
docsUrl: 'https://docs.jiguang.cn/jpush/client/HarmonyOS/hmos_guide',
|
|
336
336
|
},
|
|
337
337
|
'mx-jpush-expo': {
|
|
338
338
|
status: 'manual',
|
|
339
339
|
supportTier: 'experimental',
|
|
340
|
-
note: '
|
|
340
|
+
note: 'v1.9.3 Wave B treats this config plugin as JPush intake metadata; it may be excluded from config processing while runtime JPush dependencies stay visible in doctor reports.',
|
|
341
341
|
docsUrl: 'https://docs.jiguang.cn/jpush/client/HarmonyOS/hmos_guide',
|
|
342
342
|
},
|
|
343
343
|
'react-native-draggable-grid': {
|
|
@@ -8,4 +8,4 @@ export declare const SUPPORTING_SAMPLE_PATHS: readonly ["examples/official-app-s
|
|
|
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
10
|
export declare const EXPERIMENTAL_CAPABILITY_NAMES: readonly string[];
|
|
11
|
-
export declare const PUBLIC_CURRENT_VERSION = "1.9.
|
|
11
|
+
export declare const PUBLIC_CURRENT_VERSION = "1.9.3";
|
package/build/docs/render.d.ts
CHANGED
|
@@ -5,5 +5,6 @@ export declare function renderSupportMatrixVerifiedMatrix(locale: DocsLocale): s
|
|
|
5
5
|
export declare function renderSupportMatrixVerifiedAllowlist(): string;
|
|
6
6
|
export declare function renderSupportMatrixCapabilityTelemetry(locale: DocsLocale): string;
|
|
7
7
|
export declare function renderSupportMatrixPreviewCapabilities(locale: DocsLocale): string;
|
|
8
|
+
export declare function renderSupportMatrixExperimentalCapabilities(locale: DocsLocale): string;
|
|
8
9
|
export declare function renderSupportMatrixUiStackRules(locale: DocsLocale): string;
|
|
9
10
|
export declare function renderSupportMatrixReleaseTracks(locale: DocsLocale): string;
|
package/build/docs/render.js
CHANGED
|
@@ -6,9 +6,11 @@ exports.renderSupportMatrixVerifiedMatrix = renderSupportMatrixVerifiedMatrix;
|
|
|
6
6
|
exports.renderSupportMatrixVerifiedAllowlist = renderSupportMatrixVerifiedAllowlist;
|
|
7
7
|
exports.renderSupportMatrixCapabilityTelemetry = renderSupportMatrixCapabilityTelemetry;
|
|
8
8
|
exports.renderSupportMatrixPreviewCapabilities = renderSupportMatrixPreviewCapabilities;
|
|
9
|
+
exports.renderSupportMatrixExperimentalCapabilities = renderSupportMatrixExperimentalCapabilities;
|
|
9
10
|
exports.renderSupportMatrixUiStackRules = renderSupportMatrixUiStackRules;
|
|
10
11
|
exports.renderSupportMatrixReleaseTracks = renderSupportMatrixReleaseTracks;
|
|
11
12
|
const publicDocs_1 = require("../data/publicDocs");
|
|
13
|
+
const capabilities_1 = require("../data/capabilities");
|
|
12
14
|
const validatedMatrices_1 = require("../data/validatedMatrices");
|
|
13
15
|
const uiStack_1 = require("../data/uiStack");
|
|
14
16
|
const matrix = validatedMatrices_1.VALIDATED_RELEASE_MATRICES[validatedMatrices_1.DEFAULT_VALIDATED_MATRIX_ID];
|
|
@@ -183,6 +185,23 @@ function renderSupportMatrixPreviewCapabilities(locale) {
|
|
|
183
185
|
}).map((row) => `| ${row} |`),
|
|
184
186
|
].join('\n');
|
|
185
187
|
}
|
|
188
|
+
function renderSupportMatrixExperimentalCapabilities(locale) {
|
|
189
|
+
const headers = locale === 'zh'
|
|
190
|
+
? ['Expo 能力 / 依赖', '当前层级', 'runtimeMode', 'evidence', '说明']
|
|
191
|
+
: ['Expo capability / dependency', 'Support tier', 'runtimeMode', 'evidence', 'Notes'];
|
|
192
|
+
const experimentalCapabilities = capabilities_1.CAPABILITY_DEFINITIONS.filter((definition) => definition.supportTier === 'experimental');
|
|
193
|
+
return [
|
|
194
|
+
`| ${headers.join(' | ')} |`,
|
|
195
|
+
`| ${headers.map(() => '---').join(' | ')} |`,
|
|
196
|
+
...experimentalCapabilities.map((definition) => [
|
|
197
|
+
`\`${definition.packageName}\``,
|
|
198
|
+
`\`${definition.supportTier}\``,
|
|
199
|
+
`\`${definition.runtimeMode}\``,
|
|
200
|
+
`\`${renderEvidence(definition)}\``,
|
|
201
|
+
renderExperimentalCapabilityNote(definition, locale),
|
|
202
|
+
].join(' | ')).map((row) => `| ${row} |`),
|
|
203
|
+
].join('\n');
|
|
204
|
+
}
|
|
186
205
|
function renderSupportMatrixUiStackRules(locale) {
|
|
187
206
|
const intro = locale === 'zh'
|
|
188
207
|
? 'canonical 包与 Harmony adapter 包固定为双向强制:'
|
|
@@ -250,3 +269,12 @@ function renderEvidence(definition) {
|
|
|
250
269
|
`release=${definition.evidence.release ? 'yes' : 'no'}[${definition.evidenceSource.release}]`,
|
|
251
270
|
].join(', ');
|
|
252
271
|
}
|
|
272
|
+
function renderExperimentalCapabilityNote(definition, locale) {
|
|
273
|
+
const dependencyDirection = definition.nativePackageNames.length > 0
|
|
274
|
+
? definition.nativePackageNames.join(', ')
|
|
275
|
+
: 'toolkit-managed bridge';
|
|
276
|
+
if (locale === 'en') {
|
|
277
|
+
return `${definition.note} Direction: ${dependencyDirection}.`;
|
|
278
|
+
}
|
|
279
|
+
return `${definition.note} 依赖方向:${dependencyDirection}。`;
|
|
280
|
+
}
|
package/docs/cli-build.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# CLI 构建指南
|
|
2
2
|
|
|
3
|
-
`v1.9.
|
|
3
|
+
`v1.9.3` 延续 `verified + preview + experimental` 支持分层,把 ccnubox-first Third-party Native Wave B 纳入 formal experimental onboarding;`expo55-rnoh082-ui-stack` 仍是唯一 verified 矩阵。
|
|
4
4
|
|
|
5
5
|
CLI 命令集合不变:
|
|
6
6
|
|
|
@@ -48,6 +48,14 @@ expo-harmony build-hap --mode debug
|
|
|
48
48
|
当前 experimental formal slice:
|
|
49
49
|
|
|
50
50
|
- `react-native-gesture-handler`
|
|
51
|
+
- `@react-native-async-storage/async-storage`
|
|
52
|
+
- `react-native-screens`
|
|
53
|
+
- `react-native-safe-area-context`
|
|
54
|
+
- `react-native-webview`
|
|
55
|
+
- `jpush-react-native`
|
|
56
|
+
- `expo-media-library`
|
|
57
|
+
- `lottie-react-native`
|
|
58
|
+
- `@shopify/react-native-skia`
|
|
51
59
|
|
|
52
60
|
## UI stack 依赖安装注意事项
|
|
53
61
|
|
|
@@ -90,7 +98,7 @@ pnpm install --ignore-scripts
|
|
|
90
98
|
|
|
91
99
|
额外说明:
|
|
92
100
|
|
|
93
|
-
- `react-native-gesture-handler` 已进入 formal experimental
|
|
101
|
+
- `react-native-gesture-handler` 与 Wave A / Wave B 第三方 native packages 已进入 formal experimental onboarding,但仍不属于 verified 公开承诺
|
|
94
102
|
- bare workflow 当前只有 intake / debug baseline,不代表 release-ready
|
|
95
103
|
- `doctor --strict` 仍只代表完整验收的 verified 能力
|
|
96
104
|
|
|
@@ -45,7 +45,7 @@ pnpm run harmony:build:release
|
|
|
45
45
|
|
|
46
46
|
- `.expo-harmony/signing.local.example.json` 是可直接复制的本地 signing 範本
|
|
47
47
|
- `.expo-harmony/signing.local.json` 是本地 signing 入口
|
|
48
|
-
- toolkit
|
|
48
|
+
- toolkit 只会把非密钥 signing 引用写进 `harmony/build-profile.json5`;`storePassword` 与 `keyPassword` 会留在本地 signing 文件,并在 Hvigor build 期间临时注入后恢复
|
|
49
49
|
- 如果 signing 没配置,`harmony:env` 会明确报缺失;这属于预期阻断
|
|
50
50
|
|
|
51
51
|
## 不覆盖的内容
|
|
@@ -182,7 +182,7 @@ pnpm run harmony:build:release
|
|
|
182
182
|
|
|
183
183
|
- `.expo-harmony/signing.local.example.json` 是可直接复制的本地 signing 範本
|
|
184
184
|
- `.expo-harmony/signing.local.json` 是本地 signing 入口
|
|
185
|
-
- toolkit
|
|
185
|
+
- toolkit 只会把非密钥 signing 引用写进 `harmony/build-profile.json5`;`storePassword` 与 `keyPassword` 会留在本地 signing 文件,并在 Hvigor build 期间临时注入后恢复
|
|
186
186
|
- 即使 release 构建可走通,在没有真机前,这四项能力也仍保持 `preview`
|
|
187
187
|
|
|
188
188
|
## 当前结论
|
package/docs/roadmap.md
CHANGED
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
|
|
67
67
|
## 当前主线
|
|
68
68
|
|
|
69
|
-
`v1.8.x` 已完成 repo 内可收口项,当前主线切到 `v1.9.x`。`v1.9.0` 先落 bare workflow baseline、app foundation modules 与 `react-native-gesture-handler` formal slice;`v1.9.1` 作为 build-hap patch release,补上 HAR normalize opt-out
|
|
69
|
+
`v1.8.x` 已完成 repo 内可收口项,当前主线切到 `v1.9.x`。`v1.9.0` 先落 bare workflow baseline、app foundation modules 与 `react-native-gesture-handler` formal slice;`v1.9.1` 作为 build-hap patch release,补上 HAR normalize opt-out;`v1.9.2` 推进 Third-party Native Wave A,把 async-storage、screens 与 safe-area 拉进 formal experimental onboarding;`v1.9.3` 推进 ccnubox-first Third-party Native Wave B 与 regression farm。剩余需要真机或 release HAP 的证据继续保留在 v1.8.x capability board,不阻塞 v1.9.x 的 bare / foundation / third-party onboarding 工作。
|
|
70
70
|
|
|
71
71
|
### v1.8.0 Intake Hardening + Parallel Promotion
|
|
72
72
|
|
|
@@ -178,6 +178,8 @@
|
|
|
178
178
|
|
|
179
179
|
目标日期:`2026-09-30`
|
|
180
180
|
|
|
181
|
+
状态:implementation baseline complete;device / release evidence carryover。
|
|
182
|
+
|
|
181
183
|
目标:优先清掉最容易挡住“任何 Expo 项目”的高频第三方 native blocker。
|
|
182
184
|
|
|
183
185
|
- 第一批高频第三方 native blocker 进入正式 onboarding:
|
|
@@ -192,20 +194,35 @@
|
|
|
192
194
|
- sample / fixture
|
|
193
195
|
- debug + release evidence
|
|
194
196
|
- 任何进入 Wave A 的 blocker,都不允许继续停留在“矩阵外模糊探索”
|
|
197
|
+
- 本阶段发布为 `v1.9.2` 并进入 `latest`,但不改变 `verified` 边界:
|
|
198
|
+
- `@react-native-async-storage/async-storage` 以 `experimental` + `runtimeMode=adapter` 记录
|
|
199
|
+
- `react-native-screens` 以 `experimental` + `runtimeMode=adapter` 记录
|
|
200
|
+
- `react-native-safe-area-context` 以 `experimental` + `runtimeMode=shim` 记录
|
|
201
|
+
- `doctor --strict` 与 `doctor --target-tier preview` 仍阻断 Wave A
|
|
195
202
|
|
|
196
203
|
### v1.9.3 Third-party Native Wave B + Regression Farm
|
|
197
204
|
|
|
198
205
|
目标日期:`2026-10-31`
|
|
199
206
|
|
|
207
|
+
状态:implementation baseline complete;simulator delivery gate pending target;device / release evidence carryover。
|
|
208
|
+
|
|
200
209
|
目标:扩大第三方 native 覆盖,并把 `v1.9.0` 建好的最小 regression farm 扩容成持续回归面。
|
|
201
210
|
|
|
202
|
-
- 第二批高频第三方 native
|
|
211
|
+
- 第二批高频第三方 native 依赖进入主线:
|
|
212
|
+
- `react-native-webview`
|
|
213
|
+
- JPush runtime:`jpush-react-native`、`jcore-react-native`、`mx-jpush-expo`
|
|
214
|
+
- `expo-notifications`
|
|
215
|
+
- `expo-media-library`
|
|
216
|
+
- `lottie-react-native`
|
|
217
|
+
- `@shopify/react-native-skia`
|
|
203
218
|
- 扩容 project-shape regression farm:
|
|
204
219
|
- managed core
|
|
205
220
|
- managed native heavy
|
|
206
221
|
- bare
|
|
207
222
|
- third-party native heavy
|
|
208
223
|
- 至少维护一组“未知项目导入 -> doctor 分类 -> nextActions -> sample/build gate”的持续回归案例
|
|
224
|
+
- ccnubox-first Wave B 以 `experimental` + formal adapter/shim slice 记录,不改变 `verified` 边界
|
|
225
|
+
- debug / release HAP simulator delivery gate 必须单独记录;没有 `hdc` target 时不得把 build evidence 表述为 simulator pass
|
|
209
226
|
- 继续收口 preview capability 的 release evidence,尽量减少 `v2.0.0` 前的残留灰区
|
|
210
227
|
|
|
211
228
|
### v1.10.0 Any-project Intake Freeze
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
- DevEco Studio / Harmony SDK 已正确安装
|
|
10
10
|
- 项目已生成 `harmony/`
|
|
11
|
-
-
|
|
11
|
+
- `.expo-harmony/signing.local.json` 中存在可用的 `signingConfigs`
|
|
12
12
|
- AppGallery Connect / 本地签名材料与工程配置一致
|
|
13
13
|
|
|
14
14
|
`.expo-harmony/signing.local.json` 的最小结构与 `.expo-harmony/signing.local.example.json` 一致:
|
|
@@ -59,6 +59,8 @@ expo-harmony env
|
|
|
59
59
|
- `release` 构建不应视为 ready
|
|
60
60
|
- 可以直接从 `.expo-harmony/signing.local.example.json` 复制出 `.expo-harmony/signing.local.json` 再填入真实材料
|
|
61
61
|
|
|
62
|
+
`expo-harmony init` / `sync-template` 只会把非密钥 signing 引用写入可版本化的 `harmony/build-profile.json5`。`storePassword` 与 `keyPassword` 只应保留在 `.expo-harmony/signing.local.json`;`build-hap` 会在本地 Hvigor 构建期间临时注入这些 secret,并在 Hvigor 结束后恢复 `build-profile.json5`。没有本地 signing config 的 debug 构建仍不需要 release signing。
|
|
63
|
+
|
|
62
64
|
## 推荐流程
|
|
63
65
|
|
|
64
66
|
### Debug 验证
|
package/docs/support-matrix.md
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
- bare workflow 与第三方 native package 不再被描述成“收官后才考虑的另一条 extension 终局”
|
|
15
15
|
- `v2.0.0` 本身被定义成“任何 Expo 项目都能可靠打包成鸿蒙 App”的目标版本
|
|
16
16
|
- 但当前公开承诺依旧没有放宽;它们只是进入同一主线 backlog,而不是已经进入 `verified`
|
|
17
|
-
- `v1.9.
|
|
17
|
+
- `v1.9.3` 新增的是 ccnubox-first Third-party Native Wave B formal experimental onboarding,不是 verified 扩容
|
|
18
18
|
|
|
19
19
|
`doctor --strict` 继续只代表 `verified`。
|
|
20
20
|
|
|
@@ -134,10 +134,20 @@
|
|
|
134
134
|
|
|
135
135
|
## Experimental 能力
|
|
136
136
|
|
|
137
|
+
<!-- GENERATED:support-matrix-experimental-capabilities:start -->
|
|
137
138
|
| Expo 能力 / 依赖 | 当前层级 | runtimeMode | evidence | 说明 |
|
|
138
139
|
| --- | --- | --- | --- | --- |
|
|
139
|
-
| `expo-notifications` | `experimental` | `shim` | `bundle=
|
|
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
|
|
140
|
+
| `expo-notifications` | `experimental` | `shim` | `bundle=yes[automated], debugBuild=yes[automated], device=no[none], release=no[none]` | v1.9.3 Wave B keeps Expo Notifications on a safe experimental shim for ccnubox push flows while JPush delivery and signed-runtime behavior remain separate evidence gates. 依赖方向:toolkit-managed bridge。 |
|
|
141
|
+
| `react-native-gesture-handler` | `experimental` | `adapter` | `bundle=yes[automated], debugBuild=yes[automated], device=no[none], release=no[none]` | 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. 依赖方向:@react-native-oh-tpl/react-native-gesture-handler。 |
|
|
142
|
+
| `@react-native-async-storage/async-storage` | `experimental` | `adapter` | `bundle=yes[automated], debugBuild=yes[automated], device=no[none], release=no[none]` | v1.9.2 Wave A tracks Async Storage through its Harmony adapter, but persistence behavior remains experimental until device and release evidence are closed. 依赖方向:@react-native-oh-tpl/async-storage。 |
|
|
143
|
+
| `react-native-screens` | `experimental` | `adapter` | `bundle=yes[automated], debugBuild=yes[automated], device=no[none], release=no[none]` | v1.9.2 Wave A tracks Screens through its Harmony adapter metadata, while managed autolinking and navigation-stack runtime behavior remain promotion gaps. 依赖方向:@react-native-oh-tpl/react-native-screens。 |
|
|
144
|
+
| `react-native-safe-area-context` | `experimental` | `shim` | `bundle=yes[automated], debugBuild=yes[automated], device=no[none], release=no[none]` | v1.9.2 Wave A formalizes the existing toolkit safe-area shim so app-shell layouts keep bundling while native safe-area measurements stay outside verified. 依赖方向:toolkit-managed bridge。 |
|
|
145
|
+
| `react-native-webview` | `experimental` | `adapter` | `bundle=yes[automated], debugBuild=yes[automated], device=no[none], release=no[none]` | v1.9.3 Wave B tracks ccnubox WebView surfaces through the Harmony adapter, but navigation, injected scripts, and release runtime stability remain promotion evidence. 依赖方向:@react-native-oh-tpl/react-native-webview。 |
|
|
146
|
+
| `jpush-react-native` | `experimental` | `adapter` | `bundle=yes[automated], debugBuild=yes[automated], device=no[none], release=no[none]` | v1.9.3 Wave B keeps ccnubox JPush runtime dependencies in formal telemetry; registrationId, arrival, click, and cold-start payloads still require signed runtime evidence. 依赖方向:jcore-react-native。 |
|
|
147
|
+
| `expo-media-library` | `experimental` | `adapter` | `bundle=yes[automated], debugBuild=yes[automated], device=no[none], release=no[none]` | v1.9.3 Wave B tracks ccnubox media-library save flows through the camera-roll Harmony adapter while gallery write/read behavior remains runtime evidence. 依赖方向:@react-native-oh-tpl/camera-roll。 |
|
|
148
|
+
| `lottie-react-native` | `experimental` | `adapter` | `bundle=yes[automated], debugBuild=yes[automated], device=no[none], release=no[none]` | v1.9.3 Wave B tracks ccnubox Lottie animation surfaces through the Harmony adapter, but animation playback remains simulator and device smoke evidence. 依赖方向:@react-native-oh-tpl/lottie-react-native。 |
|
|
149
|
+
| `@shopify/react-native-skia` | `experimental` | `adapter` | `bundle=yes[automated], debugBuild=yes[automated], device=no[none], release=no[none]` | v1.9.3 Wave B tracks ccnubox Skia timetable rendering through the Harmony adapter direction while canvas rendering remains runtime evidence. 依赖方向:@react-native-oh-tpl/react-native-skia。 |
|
|
150
|
+
<!-- GENERATED:support-matrix-experimental-capabilities:end -->
|
|
141
151
|
|
|
142
152
|
## UI Stack 配对规则
|
|
143
153
|
|
|
@@ -269,6 +279,8 @@ toolkit 受管的核心产物仍包括:
|
|
|
269
279
|
|
|
270
280
|
- bare Expo;当前只进入 intake / debug baseline
|
|
271
281
|
- `expo-secure-store`、`expo-asset`、`expo-device`、`expo-clipboard`、`expo-haptics` 当前仍只是 preview shim baseline
|
|
282
|
+
- `@react-native-async-storage/async-storage`、`react-native-screens`、`react-native-safe-area-context` 当前仍只是 Wave A experimental onboarding
|
|
283
|
+
- `react-native-webview`、JPush runtime、`expo-media-library`、`lottie-react-native`、`@shopify/react-native-skia` 当前仍只是 Wave B experimental onboarding
|
|
272
284
|
- 多 Expo / RNOH 并行 verified 矩阵
|
|
273
285
|
- 任意第三方 native package 的即时正式承诺
|
|
274
286
|
|