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

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 +100 -0
  18. package/build/development/protocol.d.ts +2 -0
  19. package/build/development/protocol.js +26 -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 +216 -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 +35 -0
  115. package/build/runtime/config.js +118 -0
  116. package/build/runtime/contract.d.ts +5 -0
  117. package/build/runtime/contract.js +108 -0
  118. package/build/runtime/fingerprint.d.ts +1 -0
  119. package/build/runtime/fingerprint.js +44 -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 +106 -0
  139. package/src/development/protocol.ts +37 -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 +16 -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 +150 -0
  177. package/src/runtime/contract.ts +165 -0
  178. package/src/runtime/fingerprint.ts +39 -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,162 @@
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.requireExistingMetroAsync = requireExistingMetroAsync;
7
+ exports.startExpoMetroAsync = startExpoMetroAsync;
8
+ const node_http_1 = __importDefault(require("node:http"));
9
+ const session_1 = require("../development/session");
10
+ const errors_1 = require("../errors");
11
+ const process_1 = require("../process");
12
+ const expo_1 = require("../expo");
13
+ function delay(milliseconds) {
14
+ return new Promise(resolve => setTimeout(resolve, milliseconds));
15
+ }
16
+ function probeMetroAsync(port, options = {}) {
17
+ const timeoutMs = options.timeoutMs || 750;
18
+ return new Promise((resolve) => {
19
+ let settled = false;
20
+ const finish = (value) => {
21
+ if (settled)
22
+ return;
23
+ settled = true;
24
+ resolve(value);
25
+ };
26
+ const request = node_http_1.default.get({
27
+ headers: { Accept: 'text/plain' },
28
+ host: options.host || '127.0.0.1',
29
+ path: '/status',
30
+ port,
31
+ }, (response) => {
32
+ let body = '';
33
+ response.setEncoding('utf8');
34
+ response.on('data', (chunk) => {
35
+ if (body.length < 1_024)
36
+ body += chunk;
37
+ });
38
+ response.on('end', () => finish(response.statusCode === 200 && body.trim() === 'packager-status:running'
39
+ ? 'metro'
40
+ : 'occupied'));
41
+ });
42
+ request.setTimeout(timeoutMs, () => {
43
+ request.destroy();
44
+ finish('occupied');
45
+ });
46
+ request.on('error', (error) => finish(error.code === 'ECONNREFUSED' || error.code === 'EHOSTUNREACH'
47
+ ? 'free'
48
+ : 'occupied'));
49
+ });
50
+ }
51
+ async function requireExistingMetroAsync(port) {
52
+ const status = await probeMetroAsync(port);
53
+ if (status === 'metro') {
54
+ return {
55
+ owner: 'existing',
56
+ port,
57
+ stop: async () => { },
58
+ waitAsync: async () => { },
59
+ };
60
+ }
61
+ const code = status === 'free' ? 'ERR_HARMONY_METRO_UNAVAILABLE' : 'ERR_HARMONY_METRO_PORT_IN_USE';
62
+ const message = status === 'free'
63
+ ? `No Metro server is running on port ${port}; start Expo Metro before using --no-bundler.`
64
+ : `Port ${port} is occupied by a process that is not a compatible Metro server.`;
65
+ throw new errors_1.HarmonyCliError(code, message, { operation: 'metro-probe' });
66
+ }
67
+ async function startExpoMetroAsync(projectRoot, options) {
68
+ const before = await probeMetroAsync(options.port);
69
+ if (before === 'metro') {
70
+ return {
71
+ development: await (0, session_1.readDevelopmentSessionAsync)(projectRoot, options.port, options.host),
72
+ owner: 'existing',
73
+ port: options.port,
74
+ stop: async () => { },
75
+ waitAsync: async () => { },
76
+ };
77
+ }
78
+ if (before === 'occupied') {
79
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_METRO_PORT_IN_USE', `Port ${options.port} is occupied by a process that is not a compatible Metro server.`, { operation: 'metro-probe' });
80
+ }
81
+ const expo = (0, expo_1.resolveExpoCli)(projectRoot);
82
+ const managed = (0, process_1.startManagedProcess)(process.execPath, [
83
+ require.resolve('../internal/development'),
84
+ expo.cliPath,
85
+ 'start',
86
+ projectRoot,
87
+ '--dev-client',
88
+ '--port', String(options.port),
89
+ ...(options.privateKeyPath ? ['--private-key-path', options.privateKeyPath] : []),
90
+ ...(options.resetCache ? ['--clear'] : []),
91
+ ], {
92
+ cwd: projectRoot,
93
+ env: {
94
+ ...process.env,
95
+ EXPO_METRO_TARGET: 'harmony',
96
+ ...(options.host ? { REACT_NATIVE_PACKAGER_HOSTNAME: options.host } : {}),
97
+ },
98
+ operation: 'expo-metro',
99
+ outputLimit: 1024 * 1024,
100
+ stdio: options.interactive ? 'inherit' : 'pipe',
101
+ });
102
+ let exitResult = null;
103
+ let exitError = null;
104
+ managed.completion.then((result) => {
105
+ exitResult = result;
106
+ }, (error) => {
107
+ exitError = error;
108
+ });
109
+ const startedAt = Date.now();
110
+ const timeoutMs = options.readyTimeoutMs || 1800000;
111
+ try {
112
+ while (Date.now() - startedAt < timeoutMs) {
113
+ if (exitError instanceof errors_1.HarmonyCliError) {
114
+ throw new errors_1.HarmonyCliError(exitError.code, exitError.message, {
115
+ cause: exitError,
116
+ exitCode: exitError.exitCode,
117
+ operation: exitError.operation,
118
+ });
119
+ }
120
+ if (exitError) {
121
+ const failure = exitError;
122
+ throw new errors_1.HarmonyCliError(failure.code || 'ERR_HARMONY_METRO_EXITED', failure.message || 'Expo Metro failed before becoming ready.', { cause: exitError, exitCode: failure.exitCode, operation: failure.operation });
123
+ }
124
+ if (exitResult) {
125
+ const diagnostics = (0, process_1.formatDiagnostics)(exitResult);
126
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_METRO_EXITED', `Expo Metro exited before becoming ready with code ${exitResult.code}.${diagnostics ? `\n${diagnostics}` : ''}`, { exitCode: exitResult.code || 1, operation: 'expo-metro' });
127
+ }
128
+ if (await probeMetroAsync(options.port) === 'metro') {
129
+ return {
130
+ development: await (0, session_1.readDevelopmentSessionAsync)(projectRoot, options.port, options.host),
131
+ owner: 'started',
132
+ port: options.port,
133
+ process: managed,
134
+ stop: () => managed.stop(),
135
+ async waitAsync() {
136
+ const result = await managed.completion;
137
+ if (managed.wasStopped())
138
+ return;
139
+ const interrupted = result.signal === 'SIGINT' || result.signal === 'SIGTERM';
140
+ if (result.code === 0 || interrupted)
141
+ return;
142
+ const diagnostics = (0, process_1.formatDiagnostics)(result);
143
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_METRO_EXITED', `Expo Metro exited with code ${result.code}.${diagnostics ? `\n${diagnostics}` : ''}`, { exitCode: result.code || 1, operation: 'expo-metro' });
144
+ },
145
+ };
146
+ }
147
+ await delay(150);
148
+ }
149
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_METRO_TIMEOUT', `Expo Metro did not become ready on port ${options.port} within ${timeoutMs}ms.`, { operation: 'expo-metro' });
150
+ }
151
+ catch (error) {
152
+ await managed.stop();
153
+ if (error instanceof errors_1.HarmonyCliError) {
154
+ throw new errors_1.HarmonyCliError(error.code, error.message, {
155
+ cause: error,
156
+ exitCode: error.exitCode,
157
+ operation: error.operation,
158
+ });
159
+ }
160
+ throw new errors_1.HarmonyCliError(error.code || 'ERR_HARMONY_METRO_EXITED', error.message || 'Expo Metro failed while waiting for the server to become ready.', { cause: error, exitCode: error.exitCode, operation: error.operation });
161
+ }
162
+ }
@@ -0,0 +1,14 @@
1
+ declare function parseRunArgs(argv: string[]): {
2
+ appId: string;
3
+ device: string;
4
+ help: boolean;
5
+ noBundler: boolean;
6
+ noInstall: boolean;
7
+ port: number;
8
+ privateKeyPath: string;
9
+ project: string;
10
+ resetCache: boolean;
11
+ sync: boolean;
12
+ variant: "debug" | "release";
13
+ };
14
+ export { parseRunArgs };
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseRunArgs = parseRunArgs;
4
+ const errors_1 = require("../errors");
5
+ const args_1 = require("../args");
6
+ const RunOptions = {
7
+ ...args_1.CommonOptions,
8
+ 'app-id': { type: 'string' },
9
+ 'device': { type: 'string' },
10
+ 'no-bundler': { type: 'boolean' },
11
+ 'no-install': { type: 'boolean' },
12
+ 'port': { type: 'string' },
13
+ 'private-key-path': { type: 'string' },
14
+ 'reset-cache': { type: 'boolean' },
15
+ 'sync': { type: 'boolean' },
16
+ 'variant': { type: 'string' },
17
+ };
18
+ function parseRunArgs(argv) {
19
+ const { positionals, values } = (0, args_1.parseArgs)(RunOptions, argv);
20
+ if (positionals.length > 1) {
21
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_CONFIG_INVALID', `Unexpected run positional argument: ${positionals[1]}`, {
22
+ operation: 'parse-arguments',
23
+ });
24
+ }
25
+ const variant = values.variant || 'debug';
26
+ if (variant !== 'debug' && variant !== 'release') {
27
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_CONFIG_INVALID', '--variant must be debug or release.', {
28
+ operation: 'parse-arguments',
29
+ });
30
+ }
31
+ const port = values.port === undefined ? 8081 : Number(values.port);
32
+ if (!Number.isInteger(port) || port < 1 || port > 65_535) {
33
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_CONFIG_INVALID', '--port must be an integer between 1 and 65535.', {
34
+ operation: 'parse-arguments',
35
+ });
36
+ }
37
+ const appId = values['app-id'];
38
+ if (appId !== undefined && (!appId || appId.trim() !== appId || appId.includes('\0'))) {
39
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_CONFIG_INVALID', '--app-id must be a non-empty Harmony bundle name.', {
40
+ operation: 'parse-arguments',
41
+ });
42
+ }
43
+ const device = values.device;
44
+ if (device !== undefined && (!device || device.trim() !== device || /[\0\r\n]/u.test(device))) {
45
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_CONFIG_INVALID', '--device must be a non-empty HDC id or exact emulator name.', {
46
+ operation: 'parse-arguments',
47
+ });
48
+ }
49
+ return {
50
+ appId,
51
+ device,
52
+ help: Boolean(values.help),
53
+ noBundler: Boolean(values['no-bundler']),
54
+ noInstall: Boolean(values['no-install']),
55
+ port,
56
+ privateKeyPath: values['private-key-path'],
57
+ project: positionals[0],
58
+ resetCache: Boolean(values['reset-cache']),
59
+ sync: Boolean(values.sync),
60
+ variant: variant,
61
+ };
62
+ }
@@ -0,0 +1,46 @@
1
+ import { type MetroSession } from './metro';
2
+ import { type NativeBuildResult } from '../native/build';
3
+ export interface HarmonyRunOptions {
4
+ appId?: string;
5
+ device?: string;
6
+ io?: Pick<Console, 'error' | 'log' | 'warn'>;
7
+ noBundler?: boolean;
8
+ noInstall?: boolean;
9
+ port?: number;
10
+ privateKeyPath?: string;
11
+ resetCache?: boolean;
12
+ sync?: boolean;
13
+ variant?: 'debug' | 'release';
14
+ }
15
+ export interface HarmonyRunResult extends NativeBuildResult {
16
+ bundleName: string;
17
+ device: {
18
+ id: string;
19
+ transport: string;
20
+ };
21
+ installed: boolean;
22
+ launched: true;
23
+ metro: {
24
+ owner: 'disabled' | 'existing' | 'started';
25
+ port: number;
26
+ };
27
+ ok: true;
28
+ schemaVersion: 1;
29
+ steps: Record<string, number>;
30
+ variant: 'debug' | 'release';
31
+ }
32
+ interface HarmonyRunSessionOptions extends HarmonyRunOptions {
33
+ interactiveBundler?: boolean;
34
+ }
35
+ interface HarmonyRunSession {
36
+ metro: MetroSession | {
37
+ owner: 'disabled';
38
+ port: number;
39
+ stop(): Promise<void>;
40
+ waitAsync(): Promise<void>;
41
+ };
42
+ result: HarmonyRunResult;
43
+ }
44
+ declare function runHarmonySessionAsync(root: string, options?: HarmonyRunSessionOptions): Promise<HarmonyRunSession>;
45
+ declare function runHarmonyAsync(root: string, options?: HarmonyRunOptions): Promise<HarmonyRunResult>;
46
+ export { runHarmonyAsync, runHarmonySessionAsync };
@@ -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,35 @@
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;
15
+ compatibleApiVersion: number;
16
+ permissions: string[];
17
+ querySchemes: string[];
18
+ backgroundModes: string[];
19
+ abiFilters: string[];
20
+ };
21
+ };
22
+ export declare function readProjectRuntimeAsync(root: string): Promise<{
23
+ config: ExpoConfigWithHarmony;
24
+ bundleName: string;
25
+ native: {
26
+ nativeCompiler: string;
27
+ targetApiVersion: number;
28
+ compatibleApiVersion: number;
29
+ permissions: string[];
30
+ querySchemes: string[];
31
+ backgroundModes: string[];
32
+ abiFilters: string[];
33
+ };
34
+ runtimeVersion: string;
35
+ }>;
@@ -0,0 +1,118 @@
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_module_1 = require("node:module");
12
+ const node_path_1 = __importDefault(require("node:path"));
13
+ const config_1 = require("@expo/config");
14
+ const config_plugins_1 = require("@expo-harmony/config-plugins");
15
+ const prebuild_config_1 = require("@expo-harmony/prebuild-config");
16
+ const json5_1 = __importDefault(require("json5"));
17
+ const fingerprint_1 = require("./fingerprint");
18
+ const errors_1 = require("../errors");
19
+ const project_1 = require("../native/project");
20
+ async function resolveRuntimeVersionAsync(root, config, app) {
21
+ if (typeof config.runtimeVersion === 'object' && config.runtimeVersion?.policy === 'nativeVersion') {
22
+ if (typeof app?.versionName !== 'string' || !app.versionName
23
+ || !Number.isSafeInteger(app.versionCode) || app.versionCode < 1) {
24
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_RUNTIME_CONFIG', 'The nativeVersion policy requires a Harmony versionName and a positive versionCode.', { operation: 'runtime-contract' });
25
+ }
26
+ return `${app.versionName}(${app.versionCode})`;
27
+ }
28
+ if (typeof config.runtimeVersion === 'object' && config.runtimeVersion?.policy === 'fingerprint') {
29
+ return (await (0, fingerprint_1.fingerprintHarmonyAsync)(root)).hash;
30
+ }
31
+ const require = (0, node_module_1.createRequire)(node_path_1.default.join(root, 'package.json'));
32
+ const expo = (0, node_module_1.createRequire)(require.resolve('expo/package.json'));
33
+ const { getRuntimeVersionAsync } = expo('@expo/config-plugins/build/utils/Updates');
34
+ const version = await getRuntimeVersionAsync(root, {
35
+ ...config,
36
+ runtimeVersion: config.runtimeVersion ?? { policy: 'sdkVersion' },
37
+ }, 'harmony');
38
+ if (typeof version !== 'string' || !version) {
39
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_RUNTIME_CONFIG', 'Cannot resolve expo.runtimeVersion for Harmony.', { operation: 'runtime-contract' });
40
+ }
41
+ return version;
42
+ }
43
+ async function readMetroRuntimeAsync(root) {
44
+ const require = (0, node_module_1.createRequire)(node_path_1.default.join(root, 'package.json'));
45
+ const previous = process.env.EXPO_METRO_TARGET;
46
+ let config;
47
+ try {
48
+ process.env.EXPO_METRO_TARGET = 'harmony';
49
+ config = (await require('metro-config').resolveConfig(undefined, root)).config;
50
+ }
51
+ finally {
52
+ if (previous === undefined)
53
+ delete process.env.EXPO_METRO_TARGET;
54
+ else
55
+ process.env.EXPO_METRO_TARGET = previous;
56
+ }
57
+ return config?.resolver?.resolveRequest?.harmonyRuntime;
58
+ }
59
+ function nativeConfig(profile, module, build, product, ability) {
60
+ const selected = profile?.app?.products?.find(item => item.name === product);
61
+ const entry = module?.abilities?.find(item => item.name === ability);
62
+ if (!selected || !entry || !build
63
+ || /(?:USE_HERMES|HERMES_V1_ENABLED)(?::BOOL)?=(?:OFF|FALSE|0)/i.test(build.buildOption?.externalNativeOptions?.arguments || '')
64
+ || Object.keys(selected.buildOption || {}).some(key => key !== 'nativeCompiler')
65
+ || profile.app.buildModeSet?.some(mode => mode.buildOption)
66
+ || build.buildOptionSet?.length || build.targets?.some(target => target.buildOption)) {
67
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_RUNTIME_CONFIG', 'Runtime contracts require an existing Harmony product and ability, Hermes v1, and native options without per-target or per-mode overrides.', { operation: 'runtime-contract' });
68
+ }
69
+ const api = (value) => typeof value === 'number' ? value : Number(/\((\d+)\)$/.exec(String(value))?.[1] ?? value);
70
+ return {
71
+ nativeCompiler: selected.buildOption?.nativeCompiler,
72
+ targetApiVersion: api(selected.targetSdkVersion),
73
+ compatibleApiVersion: api(selected.compatibleSdkVersion),
74
+ permissions: [...new Set((module.requestPermissions || []).map(item => item.name))].sort(),
75
+ querySchemes: [...new Set(module.querySchemes || [])].sort(),
76
+ backgroundModes: [...new Set(entry.backgroundModes || [])].sort(),
77
+ abiFilters: [...(build.buildOption?.externalNativeOptions?.abiFilters || [])].sort(),
78
+ };
79
+ }
80
+ function readNativeRuntime(plan) {
81
+ const read = (file) => json5_1.default.parse(node_fs_1.default.readFileSync(file, 'utf8'));
82
+ const app = read(node_path_1.default.join(plan.harmonyRoot, 'AppScope/app.json5')).app;
83
+ const profile = read(plan.projectFiles.projectBuildProfile);
84
+ const module = read(plan.projectFiles.moduleJson).module;
85
+ const build = read(node_path_1.default.join(plan.moduleRoot, 'build-profile.json5'));
86
+ return { app, config: nativeConfig(profile, module, build, plan.productName, plan.abilityName) };
87
+ }
88
+ async function readProjectRuntimeAsync(root) {
89
+ const { exp } = (0, config_1.getConfig)(root, { skipSDKVersionRequirement: true, isModdedConfig: true });
90
+ const config = exp;
91
+ let native;
92
+ let bundle;
93
+ if (config.harmony) {
94
+ const settings = (0, config_plugins_1.normalizeHarmonyConfig)(config);
95
+ const evaluated = await (0, config_plugins_1.compileHarmonyModsAsync)((0, prebuild_config_1.withHarmonyPrebuildConfig)(config), {
96
+ projectRoot: root,
97
+ introspect: true,
98
+ ignoreExistingNativeFiles: true,
99
+ });
100
+ const results = evaluated._internal?.modResults?.harmony;
101
+ native = {
102
+ app: results?.appJson?.app,
103
+ config: nativeConfig(results?.projectBuildProfile, results?.moduleJson?.module, results?.entryBuildProfile, settings.productName, settings.abilityName),
104
+ };
105
+ bundle = settings.bundleName;
106
+ }
107
+ else {
108
+ const plan = await (0, project_1.resolveHarmonyBuildPlanAsync)(root);
109
+ native = readNativeRuntime(plan);
110
+ bundle = plan.bundleName;
111
+ }
112
+ return {
113
+ config,
114
+ bundleName: bundle,
115
+ native: native.config,
116
+ runtimeVersion: await resolveRuntimeVersionAsync(root, config, native.app),
117
+ };
118
+ }
@@ -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>;