@expo-harmony/cli 55.0.26-harmony.1 → 55.0.26-harmony.11

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.
Files changed (185) hide show
  1. package/README.md +87 -38
  2. package/build/args.d.ts +19 -0
  3. package/build/args.js +27 -0
  4. package/build/bin/expo-harmony.d.ts +2 -0
  5. package/build/bin/expo-harmony.js +1 -1
  6. package/build/buildHap/build.d.ts +18 -0
  7. package/build/buildHap/build.js +38 -0
  8. package/build/buildHap/index.d.ts +2 -0
  9. package/build/buildHap/index.js +19 -0
  10. package/build/buildHap/options.d.ts +7 -0
  11. package/build/buildHap/options.js +30 -0
  12. package/build/cli.d.ts +3 -0
  13. package/build/cli.js +40 -0
  14. package/build/command.d.ts +9 -0
  15. package/build/command.js +20 -0
  16. package/build/development/manifest.d.ts +1 -0
  17. package/build/development/manifest.js +121 -0
  18. package/build/development/protocol.d.ts +3 -0
  19. package/build/development/protocol.js +33 -0
  20. package/build/development/session.d.ts +5 -0
  21. package/build/development/session.js +66 -0
  22. package/build/doctor/doctor.d.ts +20 -0
  23. package/build/doctor/doctor.js +226 -0
  24. package/build/doctor/index.d.ts +2 -0
  25. package/build/doctor/index.js +16 -0
  26. package/build/doctor/options.d.ts +5 -0
  27. package/build/doctor/options.js +15 -0
  28. package/build/entry.d.ts +2 -0
  29. package/build/entry.js +47 -0
  30. package/build/errors.d.ts +12 -0
  31. package/build/errors.js +16 -0
  32. package/build/expo.d.ts +19 -0
  33. package/build/expo.js +53 -0
  34. package/build/exportEmbed/export.d.ts +18 -0
  35. package/build/exportEmbed/export.js +124 -0
  36. package/build/exportEmbed/index.d.ts +2 -0
  37. package/build/exportEmbed/index.js +19 -0
  38. package/build/exportEmbed/manifest.d.ts +44 -0
  39. package/build/exportEmbed/manifest.js +171 -0
  40. package/build/exportEmbed/options.d.ts +7 -0
  41. package/build/exportEmbed/options.js +22 -0
  42. package/build/file.d.ts +10 -0
  43. package/build/file.js +110 -0
  44. package/build/help.d.ts +1 -0
  45. package/build/help.js +44 -0
  46. package/build/index.d.ts +14 -0
  47. package/build/index.js +6 -5
  48. package/build/internal/development.d.ts +1 -0
  49. package/build/internal/development.js +8 -0
  50. package/build/internal/export.d.ts +1 -0
  51. package/build/internal/export.js +80 -0
  52. package/build/internal/prebuild.d.ts +3 -0
  53. package/build/internal/prebuild.js +9 -0
  54. package/build/log.d.ts +4 -0
  55. package/build/log.js +6 -0
  56. package/build/modules/index.d.ts +2 -0
  57. package/build/modules/index.js +16 -0
  58. package/build/modules/modules.d.ts +42 -0
  59. package/build/modules/modules.js +84 -0
  60. package/build/modules/options.d.ts +10 -0
  61. package/build/modules/options.js +45 -0
  62. package/build/native/bare.d.ts +4 -0
  63. package/build/native/bare.js +130 -0
  64. package/build/native/build.d.ts +15 -0
  65. package/build/native/build.js +69 -0
  66. package/build/native/cache.d.ts +11 -0
  67. package/build/native/cache.js +73 -0
  68. package/build/native/hvigor.d.ts +3 -0
  69. package/build/native/hvigor.js +83 -0
  70. package/build/native/install.d.ts +5 -0
  71. package/build/native/install.js +13 -0
  72. package/build/native/prepareProject.d.ts +11 -0
  73. package/build/native/prepareProject.js +68 -0
  74. package/build/native/project.d.ts +8 -0
  75. package/build/native/project.js +58 -0
  76. package/build/native/toolchain.d.ts +16 -0
  77. package/build/native/toolchain.js +172 -0
  78. package/build/native/types.d.ts +47 -0
  79. package/build/native/types.js +2 -0
  80. package/build/path.d.ts +4 -0
  81. package/build/path.js +27 -0
  82. package/build/prebuild/check.d.ts +10 -0
  83. package/build/prebuild/check.js +151 -0
  84. package/build/prebuild/clean.d.ts +2 -0
  85. package/build/prebuild/clean.js +50 -0
  86. package/build/prebuild/index.d.ts +2 -0
  87. package/build/prebuild/index.js +51 -0
  88. package/build/prebuild/options.d.ts +10 -0
  89. package/build/prebuild/options.js +50 -0
  90. package/build/prebuild/prebuild.d.ts +6 -0
  91. package/build/prebuild/prebuild.js +70 -0
  92. package/build/prebuild/template.d.ts +8 -0
  93. package/build/prebuild/template.js +86 -0
  94. package/build/process.d.ts +41 -0
  95. package/build/process.js +324 -0
  96. package/build/profile.d.ts +1 -0
  97. package/build/profile.js +12 -0
  98. package/build/project.d.ts +2 -0
  99. package/build/project.js +23 -0
  100. package/build/projectLock.d.ts +2 -0
  101. package/build/projectLock.js +185 -0
  102. package/build/run/devices.d.ts +29 -0
  103. package/build/run/devices.js +223 -0
  104. package/build/run/emulators.d.ts +13 -0
  105. package/build/run/emulators.js +82 -0
  106. package/build/run/index.d.ts +2 -0
  107. package/build/run/index.js +25 -0
  108. package/build/run/metro.d.ts +21 -0
  109. package/build/run/metro.js +162 -0
  110. package/build/run/options.d.ts +14 -0
  111. package/build/run/options.js +62 -0
  112. package/build/run/run.d.ts +46 -0
  113. package/build/run/run.js +135 -0
  114. package/build/runtime/config.d.ts +37 -0
  115. package/build/runtime/config.js +169 -0
  116. package/build/runtime/contract.d.ts +5 -0
  117. package/build/runtime/contract.js +109 -0
  118. package/build/runtime/fingerprint.d.ts +1 -0
  119. package/build/runtime/fingerprint.js +49 -0
  120. package/build/runtime/index.d.ts +2 -0
  121. package/build/runtime/index.js +50 -0
  122. package/build/start/index.d.ts +2 -0
  123. package/build/start/index.js +38 -0
  124. package/build/start/options.d.ts +9 -0
  125. package/build/start/options.js +38 -0
  126. package/build/updates/export.d.ts +14 -0
  127. package/build/updates/export.js +46 -0
  128. package/build/updates/index.d.ts +2 -0
  129. package/build/updates/index.js +96 -0
  130. package/build/upstream.d.ts +9 -0
  131. package/build/upstream.js +17 -0
  132. package/package.json +21 -5
  133. package/src/bin/expo-harmony.ts +2 -2
  134. package/src/buildHap/build.ts +13 -105
  135. package/src/buildHap/index.ts +19 -0
  136. package/src/cli.ts +27 -225
  137. package/src/command.ts +24 -0
  138. package/src/development/manifest.ts +127 -0
  139. package/src/development/protocol.ts +42 -0
  140. package/src/development/session.ts +83 -0
  141. package/src/doctor/doctor.ts +67 -62
  142. package/src/doctor/index.ts +16 -0
  143. package/src/exportEmbed/export.ts +73 -72
  144. package/src/exportEmbed/index.ts +17 -0
  145. package/src/file.ts +20 -11
  146. package/src/help.ts +41 -0
  147. package/src/index.ts +5 -3
  148. package/src/internal/development.ts +8 -0
  149. package/src/internal/export.ts +82 -0
  150. package/src/internal/prebuild.ts +3 -0
  151. package/src/log.ts +5 -0
  152. package/src/modules/index.ts +16 -0
  153. package/src/native/bare.ts +145 -0
  154. package/src/native/build.ts +83 -0
  155. package/src/{run → native}/cache.ts +10 -9
  156. package/src/native/hvigor.ts +88 -0
  157. package/src/native/install.ts +17 -0
  158. package/src/native/prepareProject.ts +99 -0
  159. package/src/native/project.ts +88 -0
  160. package/src/{tools.ts → native/toolchain.ts} +38 -140
  161. package/src/native/types.ts +44 -0
  162. package/src/prebuild/check.ts +56 -31
  163. package/src/prebuild/clean.ts +3 -3
  164. package/src/prebuild/index.ts +56 -0
  165. package/src/prebuild/prebuild.ts +11 -1
  166. package/src/prebuild/template.ts +2 -2
  167. package/src/process.ts +175 -64
  168. package/src/profile.ts +13 -0
  169. package/src/projectLock.ts +9 -1
  170. package/src/run/devices.ts +24 -11
  171. package/src/run/emulators.ts +4 -6
  172. package/src/run/index.ts +25 -0
  173. package/src/run/metro.ts +10 -1
  174. package/src/run/options.ts +2 -0
  175. package/src/run/run.ts +63 -129
  176. package/src/runtime/config.ts +207 -0
  177. package/src/runtime/contract.ts +167 -0
  178. package/src/runtime/fingerprint.ts +49 -0
  179. package/src/runtime/index.ts +54 -0
  180. package/src/start/index.ts +37 -0
  181. package/src/start/options.ts +12 -0
  182. package/src/updates/export.ts +60 -0
  183. package/src/updates/index.ts +89 -0
  184. package/src/buildHap/common.ts +0 -145
  185. package/src/run/install.ts +0 -41
@@ -0,0 +1,135 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.runHarmonyAsync = runHarmonyAsync;
7
+ exports.runHarmonySessionAsync = runHarmonySessionAsync;
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const config_1 = require("@expo/config");
10
+ const devices_1 = require("./devices");
11
+ const errors_1 = require("../errors");
12
+ const toolchain_1 = require("../native/toolchain");
13
+ const metro_1 = require("./metro");
14
+ const projectLock_1 = require("../projectLock");
15
+ const build_1 = require("../native/build");
16
+ const prepareProject_1 = require("../native/prepareProject");
17
+ const log_1 = require("../log");
18
+ const profile_1 = require("../profile");
19
+ const BundleName = /^[A-Za-z][A-Za-z0-9_]*(\.[A-Za-z][A-Za-z0-9_]*){2,}$/u;
20
+ function resolveRunIdentity(plan, options) {
21
+ if (options.appId && !BundleName.test(options.appId)) {
22
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_CONFIG_INVALID', '--app-id must contain at least three valid dot-separated segments.', { operation: 'resolve-run' });
23
+ }
24
+ if (options.appId && !options.noInstall && options.appId !== plan.bundleName) {
25
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_APP_ID_MISMATCH', `--app-id can differ from the generated bundle name only with --no-install; expected ${plan.bundleName}.`, { operation: 'resolve-run' });
26
+ }
27
+ return {
28
+ abilityName: plan.abilityName,
29
+ bundleName: options.appId || plan.bundleName,
30
+ };
31
+ }
32
+ async function runHarmonyUnlockedAsync(root, options = {}) {
33
+ const settings = {
34
+ appId: options.appId,
35
+ device: options.device,
36
+ interactiveBundler: Boolean(options.interactiveBundler),
37
+ io: options.io || console,
38
+ noBundler: Boolean(options.noBundler),
39
+ noInstall: Boolean(options.noInstall),
40
+ port: options.port || 8081,
41
+ privateKeyPath: options.privateKeyPath,
42
+ resetCache: Boolean(options.resetCache),
43
+ sync: Boolean(options.sync),
44
+ variant: options.variant || 'debug',
45
+ };
46
+ const steps = {};
47
+ const plan = await (0, prepareProject_1.prepareNativeProjectAsync)(root, {
48
+ ...settings,
49
+ check: false,
50
+ }, steps);
51
+ const identity = resolveRunIdentity(plan, settings);
52
+ const tools = (0, toolchain_1.resolveHarmonyToolchain)();
53
+ (0, log_1.progress)(settings, 'Selecting a Harmony device or emulator');
54
+ const device = await (0, profile_1.timedAsync)(steps, 'device', () => (0, devices_1.selectDeviceAsync)(tools.hdc, settings.device, {
55
+ cwd: plan.harmonyRoot,
56
+ emulator: (0, toolchain_1.resolveHarmonyEmulator)(tools),
57
+ emulatorLogFile: node_path_1.default.join(root, '.expo', 'harmony', 'emulator.log'),
58
+ onProgress: message => (0, log_1.progress)(settings, message),
59
+ }));
60
+ const built = await (0, build_1.buildNativeAsync)(root, plan, tools, settings, steps);
61
+ let metro = {
62
+ owner: 'disabled',
63
+ port: settings.port,
64
+ stop: async () => { },
65
+ waitAsync: async () => { },
66
+ };
67
+ try {
68
+ if (settings.variant === 'debug' && (0, config_1.getConfig)(root).exp.updates?.useNativeDebug !== true) {
69
+ (0, log_1.progress)(settings, settings.noBundler
70
+ ? 'Connecting to the existing Expo Metro server'
71
+ : 'Starting Expo Metro');
72
+ metro = await (0, profile_1.timedAsync)(steps, 'metro', () => settings.noBundler
73
+ ? (0, metro_1.requireExistingMetroAsync)(settings.port)
74
+ : (0, metro_1.startExpoMetroAsync)(root, {
75
+ interactive: settings.interactiveBundler,
76
+ port: settings.port,
77
+ privateKeyPath: settings.privateKeyPath,
78
+ resetCache: settings.resetCache,
79
+ }));
80
+ await (0, profile_1.timedAsync)(steps, 'metroPort', () => (0, devices_1.configureMetroPortAsync)(tools.hdc, device, settings.port, { cwd: plan.harmonyRoot }));
81
+ }
82
+ else {
83
+ steps.metro = 0;
84
+ steps.metroPort = 0;
85
+ }
86
+ if (settings.noInstall) {
87
+ steps.install = 0;
88
+ }
89
+ else {
90
+ (0, log_1.progress)(settings, `Installing the HAP on ${device.id}`);
91
+ await (0, profile_1.timedAsync)(steps, 'install', () => (0, devices_1.installHapAsync)(tools.hdc, device, plan.expectedHap, { cwd: plan.harmonyRoot }));
92
+ }
93
+ (0, log_1.progress)(settings, `Launching ${identity.bundleName}`);
94
+ await (0, profile_1.timedAsync)(steps, 'launch', () => (0, devices_1.launchAppAsync)(tools.hdc, device, identity.bundleName, identity.abilityName, { cwd: plan.harmonyRoot }));
95
+ const result = {
96
+ bundleName: identity.bundleName,
97
+ device: {
98
+ id: device.id,
99
+ transport: device.transport,
100
+ },
101
+ ...built,
102
+ installed: !settings.noInstall,
103
+ launched: true,
104
+ metro: {
105
+ owner: metro.owner,
106
+ port: metro.port,
107
+ },
108
+ ok: true,
109
+ schemaVersion: 1,
110
+ steps,
111
+ variant: settings.variant,
112
+ };
113
+ return { metro, result };
114
+ }
115
+ catch (error) {
116
+ await metro.stop();
117
+ throw new errors_1.HarmonyCliError(error?.code || 'ERR_HARMONY_UNKNOWN', error?.message || 'Harmony run failed.', {
118
+ cause: error,
119
+ exitCode: error?.exitCode,
120
+ operation: error?.operation,
121
+ });
122
+ }
123
+ }
124
+ async function runHarmonySessionAsync(root, options = {}) {
125
+ return (0, projectLock_1.withHarmonyProjectLockAsync)(root, 'run', () => runHarmonyUnlockedAsync(root, options));
126
+ }
127
+ async function runHarmonyAsync(root, options = {}) {
128
+ const session = await runHarmonySessionAsync(root, options);
129
+ try {
130
+ return session.result;
131
+ }
132
+ finally {
133
+ await session.metro.stop();
134
+ }
135
+ }
@@ -0,0 +1,37 @@
1
+ import { type ExpoConfigWithHarmony } from '@expo-harmony/config-plugins';
2
+ import type { HarmonyBuildPlan } from '../native/types';
3
+ export interface HarmonyMetroRuntime {
4
+ reactPackage: string;
5
+ harmonyPackage: string;
6
+ fixedRuntime: boolean;
7
+ }
8
+ export declare function resolveRuntimeVersionAsync(root: string, config: ExpoConfigWithHarmony, app: any): Promise<string>;
9
+ export declare function readMetroRuntimeAsync(root: string): Promise<HarmonyMetroRuntime | undefined>;
10
+ export declare function readNativeRuntime(plan: HarmonyBuildPlan): {
11
+ app: any;
12
+ config: {
13
+ nativeCompiler: string;
14
+ targetApiVersion: number | string;
15
+ compatibleApiVersion: number | string;
16
+ nativeLibFilterHash?: string;
17
+ permissions: string[];
18
+ querySchemes: string[];
19
+ backgroundModes: string[];
20
+ abiFilters: string[];
21
+ };
22
+ };
23
+ export declare function readProjectRuntimeAsync(root: string): Promise<{
24
+ config: ExpoConfigWithHarmony;
25
+ bundleName: string;
26
+ native: {
27
+ nativeCompiler: string;
28
+ targetApiVersion: number | string;
29
+ compatibleApiVersion: number | string;
30
+ nativeLibFilterHash?: string;
31
+ permissions: string[];
32
+ querySchemes: string[];
33
+ backgroundModes: string[];
34
+ abiFilters: string[];
35
+ };
36
+ runtimeVersion: string;
37
+ }>;
@@ -0,0 +1,169 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.resolveRuntimeVersionAsync = resolveRuntimeVersionAsync;
7
+ exports.readMetroRuntimeAsync = readMetroRuntimeAsync;
8
+ exports.readNativeRuntime = readNativeRuntime;
9
+ exports.readProjectRuntimeAsync = readProjectRuntimeAsync;
10
+ const node_fs_1 = __importDefault(require("node:fs"));
11
+ const node_crypto_1 = require("node:crypto");
12
+ const node_module_1 = require("node:module");
13
+ const node_path_1 = __importDefault(require("node:path"));
14
+ const config_1 = require("@expo/config");
15
+ const config_plugins_1 = require("@expo-harmony/config-plugins");
16
+ const prebuild_config_1 = require("@expo-harmony/prebuild-config");
17
+ const json5_1 = __importDefault(require("json5"));
18
+ const fingerprint_1 = require("./fingerprint");
19
+ const errors_1 = require("../errors");
20
+ const project_1 = require("../native/project");
21
+ async function resolveRuntimeVersionAsync(root, config, app) {
22
+ if (typeof config.runtimeVersion === 'object' && config.runtimeVersion?.policy === 'nativeVersion') {
23
+ if (typeof app?.versionName !== 'string' || !app.versionName
24
+ || !Number.isSafeInteger(app.versionCode) || app.versionCode < 1) {
25
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_RUNTIME_CONFIG', 'The nativeVersion policy requires a Harmony versionName and a positive versionCode.', { operation: 'runtime-contract' });
26
+ }
27
+ return `${app.versionName}(${app.versionCode})`;
28
+ }
29
+ if (typeof config.runtimeVersion === 'object' && config.runtimeVersion?.policy === 'fingerprint') {
30
+ return (await (0, fingerprint_1.fingerprintHarmonyAsync)(root)).hash;
31
+ }
32
+ const require = (0, node_module_1.createRequire)(node_path_1.default.join(root, 'package.json'));
33
+ const expo = (0, node_module_1.createRequire)(require.resolve('expo/package.json'));
34
+ const { getRuntimeVersionAsync } = expo('@expo/config-plugins/build/utils/Updates');
35
+ const version = await getRuntimeVersionAsync(root, {
36
+ ...config,
37
+ runtimeVersion: config.runtimeVersion ?? { policy: 'sdkVersion' },
38
+ }, 'harmony');
39
+ if (typeof version !== 'string' || !version) {
40
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_RUNTIME_CONFIG', 'Cannot resolve expo.runtimeVersion for Harmony.', { operation: 'runtime-contract' });
41
+ }
42
+ return version;
43
+ }
44
+ async function readMetroRuntimeAsync(root) {
45
+ const require = (0, node_module_1.createRequire)(node_path_1.default.join(root, 'package.json'));
46
+ const previous = process.env.EXPO_METRO_TARGET;
47
+ let config;
48
+ try {
49
+ process.env.EXPO_METRO_TARGET = 'harmony';
50
+ config = (await require('metro-config').resolveConfig(undefined, root)).config;
51
+ }
52
+ finally {
53
+ if (previous === undefined)
54
+ delete process.env.EXPO_METRO_TARGET;
55
+ else
56
+ process.env.EXPO_METRO_TARGET = previous;
57
+ }
58
+ return config?.resolver?.resolveRequest?.harmonyRuntime;
59
+ }
60
+ function onlyKeys(value, keys) {
61
+ return value !== null && typeof value === 'object' && !Array.isArray(value)
62
+ && Object.keys(value).every(key => keys.includes(key));
63
+ }
64
+ function optionalStrings(value) {
65
+ return value === undefined || (Array.isArray(value) && value.every(item => typeof item === 'string' && item.length > 0));
66
+ }
67
+ function supportedReleaseOptions(modes) {
68
+ if (modes === undefined)
69
+ return true;
70
+ if (!Array.isArray(modes) || modes.length > 1)
71
+ return false;
72
+ return modes.every((mode) => {
73
+ if (!onlyKeys(mode, ['name', 'arkOptions', 'nativeLib']) || mode.name !== 'release')
74
+ return false;
75
+ if (mode.nativeLib !== undefined) {
76
+ const symbol = mode.nativeLib?.debugSymbol;
77
+ if (!onlyKeys(mode.nativeLib, ['debugSymbol']) || !onlyKeys(symbol, ['strip', 'exclude'])
78
+ || typeof symbol.strip !== 'boolean' || !optionalStrings(symbol.exclude))
79
+ return false;
80
+ }
81
+ if (mode.arkOptions !== undefined) {
82
+ const obfuscation = mode.arkOptions?.obfuscation;
83
+ const rules = obfuscation?.ruleOptions;
84
+ if (!onlyKeys(mode.arkOptions, ['obfuscation']) || !onlyKeys(obfuscation, ['ruleOptions'])
85
+ || !onlyKeys(rules, ['enable', 'files']) || typeof rules.enable !== 'boolean'
86
+ || !optionalStrings(rules.files))
87
+ return false;
88
+ }
89
+ return true;
90
+ });
91
+ }
92
+ function nativeLibFilterHash(filter) {
93
+ if (filter === undefined)
94
+ return undefined;
95
+ if (!onlyKeys(filter, ['excludes', 'pickFirsts', 'pickLasts', 'enableOverride'])
96
+ || !['excludes', 'pickFirsts', 'pickLasts'].every(key => optionalStrings(filter[key]))
97
+ || (filter.enableOverride !== undefined && typeof filter.enableOverride !== 'boolean')) {
98
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_RUNTIME_CONFIG', 'Unsupported nativeLib.filter configuration.', { operation: 'runtime-contract' });
99
+ }
100
+ const values = {};
101
+ for (const key of ['excludes', 'pickFirsts', 'pickLasts']) {
102
+ if (filter[key]?.length)
103
+ values[key] = [...new Set(filter[key])].sort();
104
+ }
105
+ if (filter.enableOverride)
106
+ values['enableOverride'] = true;
107
+ return Object.keys(values).length ? (0, node_crypto_1.createHash)('sha256').update(JSON.stringify(values)).digest('hex') : undefined;
108
+ }
109
+ function nativeConfig(profile, module, build, product, ability) {
110
+ const selected = profile?.app?.products?.find(item => item.name === product);
111
+ const entry = module?.abilities?.find(item => item.name === ability);
112
+ if (!selected || !entry || !build
113
+ || /(?:USE_HERMES|HERMES_V1_ENABLED)(?::BOOL)?=(?:OFF|FALSE|0)/i.test(build.buildOption?.externalNativeOptions?.arguments || '')
114
+ || Object.keys(selected.buildOption || {}).some(key => key !== 'nativeCompiler')
115
+ || profile.app.buildModeSet?.some(mode => mode.buildOption)
116
+ || !supportedReleaseOptions(build.buildOptionSet) || build.targets?.some(target => target.buildOption)) {
117
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_RUNTIME_CONFIG', 'Runtime contracts require an existing Harmony product and ability, Hermes v1, and no per-target or per-mode overrides except release debugSymbol and ArkTS obfuscation rules.', { operation: 'runtime-contract' });
118
+ }
119
+ const hash = nativeLibFilterHash(build.buildOption?.nativeLib?.filter);
120
+ return {
121
+ nativeCompiler: selected.buildOption?.nativeCompiler,
122
+ targetApiVersion: (0, config_plugins_1.parseHarmonySdkVersion)(selected.targetSdkVersion, 'targetSdkVersion').api,
123
+ compatibleApiVersion: (0, config_plugins_1.parseHarmonySdkVersion)(selected.compatibleSdkVersion, 'compatibleSdkVersion').api,
124
+ ...(hash ? { nativeLibFilterHash: hash } : {}),
125
+ permissions: [...new Set((module.requestPermissions || []).map(item => item.name))].sort(),
126
+ querySchemes: [...new Set(module.querySchemes || [])].sort(),
127
+ backgroundModes: [...new Set(entry.backgroundModes || [])].sort(),
128
+ abiFilters: [...(build.buildOption?.externalNativeOptions?.abiFilters || [])].sort(),
129
+ };
130
+ }
131
+ function readNativeRuntime(plan) {
132
+ const read = (file) => json5_1.default.parse(node_fs_1.default.readFileSync(file, 'utf8'));
133
+ const app = read(node_path_1.default.join(plan.harmonyRoot, 'AppScope/app.json5')).app;
134
+ const profile = read(plan.projectFiles.projectBuildProfile);
135
+ const module = read(plan.projectFiles.moduleJson).module;
136
+ const build = read(node_path_1.default.join(plan.moduleRoot, 'build-profile.json5'));
137
+ return { app, config: nativeConfig(profile, module, build, plan.productName, plan.abilityName) };
138
+ }
139
+ async function readProjectRuntimeAsync(root) {
140
+ const { exp } = (0, config_1.getConfig)(root, { skipSDKVersionRequirement: true, isModdedConfig: true });
141
+ const config = exp;
142
+ let native;
143
+ let bundle;
144
+ if (config.harmony) {
145
+ const settings = (0, config_plugins_1.normalizeHarmonyConfig)(config);
146
+ const evaluated = await (0, config_plugins_1.compileHarmonyModsAsync)((0, prebuild_config_1.withHarmonyPrebuildConfig)(config), {
147
+ projectRoot: root,
148
+ introspect: true,
149
+ ignoreExistingNativeFiles: true,
150
+ });
151
+ const results = evaluated._internal?.modResults?.harmony;
152
+ native = {
153
+ app: results?.appJson?.app,
154
+ config: nativeConfig(results?.projectBuildProfile, results?.moduleJson?.module, results?.entryBuildProfile, settings.productName, settings.abilityName),
155
+ };
156
+ bundle = settings.bundleName;
157
+ }
158
+ else {
159
+ const plan = await (0, project_1.resolveHarmonyBuildPlanAsync)(root);
160
+ native = readNativeRuntime(plan);
161
+ bundle = plan.bundleName;
162
+ }
163
+ return {
164
+ config,
165
+ bundleName: bundle,
166
+ native: native.config,
167
+ runtimeVersion: await resolveRuntimeVersionAsync(root, config, native.app),
168
+ };
169
+ }
@@ -0,0 +1,5 @@
1
+ import { type HarmonyRuntimeContract } from '@expo-harmony/expo-modules-autolinking/runtime';
2
+ import type { HarmonyBuildPlan } from '../native/types';
3
+ import { type HarmonyMetroRuntime } from './config';
4
+ export declare function resolveRuntimeRequirementsAsync(root: string, metro?: HarmonyMetroRuntime): Promise<HarmonyRuntimeContract>;
5
+ export declare function publishRuntimeContractAsync(root: string, plan: HarmonyBuildPlan): Promise<HarmonyRuntimeContract>;
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.resolveRuntimeRequirementsAsync = resolveRuntimeRequirementsAsync;
7
+ exports.publishRuntimeContractAsync = publishRuntimeContractAsync;
8
+ const node_fs_1 = __importDefault(require("node:fs"));
9
+ const node_module_1 = require("node:module");
10
+ const node_path_1 = __importDefault(require("node:path"));
11
+ const expo_modules_autolinking_1 = require("@expo-harmony/expo-modules-autolinking");
12
+ const runtime_1 = require("@expo-harmony/expo-modules-autolinking/runtime");
13
+ const json5_1 = __importDefault(require("json5"));
14
+ const export_1 = require("../updates/export");
15
+ const errors_1 = require("../errors");
16
+ const file_1 = require("../file");
17
+ const config_1 = require("./config");
18
+ async function createRuntimeContractAsync(root, project, modules, metro) {
19
+ const resolution = metro ?? await (0, config_1.readMetroRuntimeAsync)(root);
20
+ if (!resolution?.fixedRuntime || resolution.harmonyPackage !== '@react-native-oh/react-native-harmony'
21
+ || typeof resolution.reactPackage !== 'string' || !resolution.reactPackage) {
22
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_RUNTIME_CONFIG', 'Runtime contracts require withHarmonyConfig with the standard RNOH package and supported React resolution.', { operation: 'runtime-contract' });
23
+ }
24
+ const require = (0, node_module_1.createRequire)(node_path_1.default.join(root, 'package.json'));
25
+ const version = (name) => require(`${name}/package.json`).version;
26
+ const native = (0, node_module_1.createRequire)(require.resolve(resolution.harmonyPackage + '/package.json'));
27
+ const contract = {
28
+ schemaVersion: (0, runtime_1.harmonyRuntimeSchemaVersion)(project.native),
29
+ platform: 'harmony',
30
+ runtimeVersion: project.runtimeVersion,
31
+ development: true,
32
+ expo: version('expo'),
33
+ expoModulesCore: version('expo-modules-core'),
34
+ rnoh: native('./package.json').version,
35
+ react: version(resolution.reactPackage),
36
+ hermes: native('hermes-compiler/package.json').version,
37
+ engine: 'hermes-v1',
38
+ bundleFormat: 'js-source',
39
+ renderer: 'rnoh',
40
+ modules: (0, expo_modules_autolinking_1.createNativeModuleContracts)(modules),
41
+ config: project.native,
42
+ };
43
+ if (contract.rnoh !== '0.84.1' || contract.react !== '19.2.3' || contract.hermes !== '250829098.0.9'
44
+ || !contract.expo.startsWith('55.') || !modules.some(module => module.packageName === '@expo-harmony/expo-modules-core')) {
45
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_RUNTIME_UNSUPPORTED', 'The fixed development runtime requires Expo SDK 55, Harmony Expo Modules Core, RNOH 0.84.1, React 19.2.3, and Hermes 250829098.0.9. Check the Metro React alias.', { operation: 'runtime-contract' });
46
+ }
47
+ const issues = (0, runtime_1.validateHarmonyRuntime)(contract);
48
+ if (issues.length > 0) {
49
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_RUNTIME_CONFIG', issues.map(issue => issue.message).join('\n'), { operation: 'runtime-contract' });
50
+ }
51
+ return contract;
52
+ }
53
+ async function resolveRuntimeRequirementsAsync(root, metro) {
54
+ const result = await (0, expo_modules_autolinking_1.verifyModulesAsync)({ projectRoot: root });
55
+ if (!result.valid) {
56
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_RUNTIME_MODULES', result.diagnostics.filter(item => item.severity === 'error').map(item => item.message).join('\n'), { operation: 'runtime-contract' });
57
+ }
58
+ const project = await (0, config_1.readProjectRuntimeAsync)(root);
59
+ return createRuntimeContractAsync(root, project, result.modules, metro);
60
+ }
61
+ async function publishRuntimeContractAsync(root, plan) {
62
+ const manifest = JSON.parse(await node_fs_1.default.promises.readFile(node_path_1.default.join(root, '.expo/harmony/autolinking.json'), 'utf8'));
63
+ if (manifest.schemaVersion !== 4 || manifest.platform !== 'harmony' || manifest.buildType !== plan.buildMode) {
64
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_RUNTIME_BUILD', 'Runtime publication requires autolinking from this native build.', { operation: 'runtime-contract' });
65
+ }
66
+ const project = await (0, config_1.readProjectRuntimeAsync)(root);
67
+ const requirements = await createRuntimeContractAsync(root, project, manifest.modules);
68
+ if (!manifest.runtimeModules || JSON.stringify(requirements.modules) !== JSON.stringify(manifest.runtimeModules)) {
69
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_RUNTIME_BUILD', 'Native inputs changed or lack a runtime snapshot. Run autolinking/prebuild again before publishing capabilities.', { operation: 'runtime-contract' });
70
+ }
71
+ const read = (file) => json5_1.default.parse(node_fs_1.default.readFileSync(file, 'utf8'));
72
+ const dependencies = read(plan.nativeInputs.manifest);
73
+ const declared = { ...dependencies.dependencies, ...dependencies.overrides };
74
+ const linked = (0, expo_modules_autolinking_1.ohpmDependenciesFromManifest)(manifest, { harmonyProjectPath: plan.harmonyRoot });
75
+ for (const [name, expected] of Object.entries(linked)) {
76
+ const actual = declared[name];
77
+ const target = node_fs_1.default.realpathSync(node_path_1.default.resolve(plan.harmonyRoot, expected));
78
+ if (typeof actual !== 'string' || !actual.startsWith('.')
79
+ || node_fs_1.default.realpathSync(node_path_1.default.resolve(plan.harmonyRoot, actual)) !== target) {
80
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_RUNTIME_BUILD', `Native dependency ${name} does not use the HAR recorded by autolinking.`, { operation: 'runtime-contract' });
81
+ }
82
+ }
83
+ const directory = node_path_1.default.join(plan.harmonyRoot, 'oh_modules/@rnoh/react-native-openharmony');
84
+ const rnoh = read(node_path_1.default.join(directory, 'oh-package.json5'));
85
+ const hermes = read(node_path_1.default.join(directory, 'src/main/cpp/third-party/hermes/npm/hermes-compiler/package.json'));
86
+ if (rnoh.version !== requirements.rnoh || hermes.version !== requirements.hermes
87
+ || declared['@rnoh/react-native-openharmony'] !== requirements.rnoh
88
+ || process.env.HERMES_V1_ENABLED === 'false') {
89
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_RUNTIME_BUILD', 'Runtime publication requires matching installed RNOH and Hermes v1.', { operation: 'runtime-contract' });
90
+ }
91
+ const native = (0, config_1.readNativeRuntime)(plan);
92
+ requirements.development = plan.buildMode === 'debug';
93
+ const contract = {
94
+ ...requirements,
95
+ schemaVersion: (0, runtime_1.harmonyRuntimeSchemaVersion)(native.config),
96
+ runtimeVersion: await (0, config_1.resolveRuntimeVersionAsync)(root, project.config, native.app),
97
+ config: native.config,
98
+ };
99
+ (0, runtime_1.assertHarmonyCompatibility)(contract, requirements);
100
+ const require = (0, node_module_1.createRequire)(node_path_1.default.join(root, 'package.json'));
101
+ const file = node_path_1.default.join(plan.exportPaths.rawfileRoot, 'expo-harmony-runtime.json');
102
+ const source = require.resolve('@expo-harmony/expo-modules-autolinking/runtime-source');
103
+ const target = node_path_1.default.join(plan.moduleRoot, 'src/main/ets/generated/HarmonyRuntime.ts');
104
+ await (0, file_1.atomicCopy)(source, target, root);
105
+ await (0, file_1.atomicWriteJson)(contract, file, root);
106
+ await (0, file_1.atomicWriteJson)(contract, node_path_1.default.join(root, '.expo/harmony/runtime.json'), root);
107
+ await (0, export_1.publishUpdatesConfigurationAsync)(root, plan.exportPaths.rawfileRoot, project.config, contract.runtimeVersion);
108
+ return contract;
109
+ }
@@ -0,0 +1 @@
1
+ export declare function fingerprintHarmonyAsync(root: string): Promise<import("@expo/fingerprint").Fingerprint>;
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.fingerprintHarmonyAsync = fingerprintHarmonyAsync;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const node_module_1 = require("node:module");
10
+ const config_1 = require("@expo/config");
11
+ const fingerprint_1 = require("@expo/fingerprint");
12
+ const expo_modules_autolinking_1 = require("@expo-harmony/expo-modules-autolinking");
13
+ const errors_1 = require("../errors");
14
+ async function fingerprintHarmonyAsync(root) {
15
+ const result = await (0, expo_modules_autolinking_1.verifyModulesAsync)({ projectRoot: root });
16
+ if (!result.valid)
17
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_FINGERPRINT', 'Build and verify Harmony native modules before calculating the runtime fingerprint.', { operation: 'fingerprint' });
18
+ const require = (0, node_module_1.createRequire)(node_path_1.default.join(root, 'package.json'));
19
+ const modules = (0, expo_modules_autolinking_1.createNativeModuleContracts)(result.modules);
20
+ const { exp } = (0, config_1.getConfig)(root, { skipSDKVersionRequirement: true, isModdedConfig: true });
21
+ const generated = [];
22
+ const previous = node_path_1.default.join(root, '.expo/harmony/export-manifest.json');
23
+ if (node_fs_1.default.existsSync(previous)) {
24
+ const manifest = JSON.parse(node_fs_1.default.readFileSync(previous, 'utf8'));
25
+ generated.push(...manifest.assets.map(asset => `**/harmony/**/src/main/resources/rawfile/${asset.path}`));
26
+ }
27
+ const sources = [{ type: 'contents', id: 'harmonyAutolinking', contents: JSON.stringify(modules), reasons: ['harmonyAutolinking'] }];
28
+ for (const name of ['@react-native-oh/react-native-harmony', '@expo-harmony/template', '@expo-harmony/config-plugins', '@expo-harmony/prebuild-config', '@expo-harmony/expo-build-properties']) {
29
+ const file = require.resolve(name + '/package.json');
30
+ sources.push({ type: 'dir', filePath: node_path_1.default.relative(root, node_path_1.default.dirname(file)), reasons: ['harmonyNativeToolchain'] });
31
+ }
32
+ for (const file of exp._internal?.harmonyBuildProperties?.release?.obfuscation?.files ?? []) {
33
+ sources.push({ type: 'file', filePath: file, reasons: ['harmonyObfuscationRules'] });
34
+ }
35
+ if (node_fs_1.default.existsSync(node_path_1.default.join(root, 'harmony')))
36
+ sources.push({ type: 'dir', filePath: 'harmony', reasons: ['harmonyNativeProject'] });
37
+ return (0, fingerprint_1.createFingerprintAsync)(root, {
38
+ platforms: [], extraSources: sources,
39
+ ignorePaths: [
40
+ ...generated,
41
+ '**/harmony/**/build/**', '**/harmony/**/.hvigor/**', '**/harmony/**/.cxx/**', '**/harmony/**/oh_modules/**',
42
+ '**/harmony/**/oh-package-lock.json5', '**/harmony/**/local.properties', '**/harmony/**/native-inputs.cmake',
43
+ '**/harmony/**/HarmonyRuntime.ts', '**/harmony/**/expo-harmony-runtime.json', '**/harmony/**/expo-updates/**',
44
+ '**/harmony/**/hermes_bundle.hbc', '**/harmony/**/hermes_bundle.hbc.*', '**/harmony/**/.expo-harmony/**',
45
+ '**/harmony/**/autolinking.cmake', '**/harmony/**/autolinking.cpp',
46
+ '**/harmony/**/generated/**', '**/*.har', '**/*.hap', '**/.git/**', '**/node_modules/**/node_modules/**',
47
+ ],
48
+ });
49
+ }
@@ -0,0 +1,2 @@
1
+ import { type Command } from '../command';
2
+ export declare const command: Command;
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.command = void 0;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const runtime_1 = require("@expo-harmony/expo-modules-autolinking/runtime");
10
+ const args_1 = require("../args");
11
+ const command_1 = require("../command");
12
+ const errors_1 = require("../errors");
13
+ const project_1 = require("../native/project");
14
+ const contract_1 = require("./contract");
15
+ const command = async (argv, io) => {
16
+ const args = (0, command_1.parseCommandArgs)((argv) => {
17
+ const { values, positionals } = (0, args_1.parseArgs)({
18
+ ...args_1.CommonOptions,
19
+ publish: { type: 'boolean' },
20
+ client: { type: 'string' },
21
+ variant: { type: 'string' },
22
+ }, argv);
23
+ if (positionals.length > 1 || (values.variant && !['debug', 'release'].includes(values.variant))
24
+ || (values.publish && values.client)) {
25
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_CONFIG_INVALID', 'Use runtime [project] [--client file | --publish] [--variant debug|release].', { operation: 'parse-arguments' });
26
+ }
27
+ return { help: Boolean(values.help), project: positionals[0], ...values };
28
+ }, argv, io);
29
+ if (!args)
30
+ return 0;
31
+ const { projectRoot: root, options } = args;
32
+ if (options.publish) {
33
+ const plan = await (0, project_1.resolveHarmonyBuildPlanAsync)(root, { buildMode: options.variant });
34
+ await (0, contract_1.publishRuntimeContractAsync)(root, plan);
35
+ io.log('Published Harmony runtime capabilities and host validator.');
36
+ }
37
+ else {
38
+ const contract = await (0, contract_1.resolveRuntimeRequirementsAsync)(root);
39
+ if (options.client) {
40
+ const client = JSON.parse(await node_fs_1.default.promises.readFile(node_path_1.default.resolve(options.client), 'utf8'));
41
+ (0, runtime_1.assertHarmonyCompatibility)(client, contract);
42
+ io.log('Project is compatible with the fixed Harmony client.');
43
+ }
44
+ else {
45
+ io.log(JSON.stringify(contract, null, 2));
46
+ }
47
+ }
48
+ return 0;
49
+ };
50
+ exports.command = command;
@@ -0,0 +1,2 @@
1
+ import { type Command } from '../command';
2
+ export declare const command: Command;
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.command = void 0;
4
+ const command_1 = require("../command");
5
+ const options_1 = require("./options");
6
+ const command = async (argv, io) => {
7
+ const args = (0, command_1.parseCommandArgs)(options_1.parseStartArgs, argv, io);
8
+ if (!args)
9
+ return 0;
10
+ const { projectRoot: root, options } = args;
11
+ const { startExpoMetroAsync } = await import('../run/metro.js');
12
+ const metro = await startExpoMetroAsync(root, {
13
+ ...options,
14
+ interactive: true,
15
+ });
16
+ try {
17
+ if (metro.development) {
18
+ io.log(`Harmony manifest: ${metro.development.manifestUrl}`);
19
+ io.log(`Harmony JS bundle: ${metro.development.bundleUrl}`);
20
+ io.log(`Harmony launch link: ${metro.development.launchLink}`);
21
+ }
22
+ if (metro.owner === 'existing') {
23
+ io.log(`Expo Metro is already running on port ${metro.port}.`);
24
+ if (options.resetCache) {
25
+ io.warn('Stop the existing Metro server and run this command again to reset its cache.');
26
+ }
27
+ }
28
+ else {
29
+ io.log('\n› Logs for your project will appear below. Press Ctrl+C to exit.');
30
+ await metro.waitAsync();
31
+ }
32
+ return 0;
33
+ }
34
+ finally {
35
+ await metro.stop();
36
+ }
37
+ };
38
+ exports.command = command;
@@ -0,0 +1,9 @@
1
+ declare function parseStartArgs(argv: string[]): {
2
+ help: boolean;
3
+ host: string;
4
+ port: number;
5
+ privateKeyPath: string;
6
+ project: string;
7
+ resetCache: boolean;
8
+ };
9
+ export { parseStartArgs };