@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,324 @@
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.formatDiagnostics = formatDiagnostics;
7
+ exports.runCheckedAsync = runCheckedAsync;
8
+ exports.spawnAsync = spawnAsync;
9
+ exports.startManagedProcess = startManagedProcess;
10
+ const cross_spawn_1 = __importDefault(require("cross-spawn"));
11
+ const tool_command_1 = require("@expo-harmony/expo-modules-autolinking/tool-command");
12
+ const errors_1 = require("./errors");
13
+ const DefaultOutputLimit = 1024 * 1024;
14
+ const DefaultStopGraceMs = 3_000;
15
+ class BoundedCapture {
16
+ limit;
17
+ buffers = [];
18
+ head = 0;
19
+ length = 0;
20
+ constructor(limit) {
21
+ this.limit = limit;
22
+ }
23
+ append(value) {
24
+ const buffer = typeof value === 'string' ? new TextEncoder().encode(value) : Uint8Array.from(value);
25
+ if (buffer.length >= this.limit) {
26
+ this.buffers = [buffer.subarray(buffer.length - this.limit)];
27
+ this.head = 0;
28
+ this.length = this.limit;
29
+ return;
30
+ }
31
+ while (this.length + buffer.length > this.limit && this.head < this.buffers.length) {
32
+ const first = this.buffers[this.head];
33
+ const excess = this.length + buffer.length - this.limit;
34
+ if (first.length > excess) {
35
+ this.buffers[this.head] = first.subarray(excess);
36
+ this.length -= excess;
37
+ break;
38
+ }
39
+ this.length -= first.length;
40
+ this.head += 1;
41
+ }
42
+ this.buffers.push(buffer);
43
+ this.length += buffer.length;
44
+ if (this.head > 128 && this.head * 2 > this.buffers.length) {
45
+ this.buffers = this.buffers.slice(this.head);
46
+ this.head = 0;
47
+ }
48
+ }
49
+ toString() {
50
+ return Buffer.concat(this.buffers.slice(this.head), this.length).toString('utf8');
51
+ }
52
+ }
53
+ function formatDiagnostics(result, limit = 4_000) {
54
+ return (result.stderr || result.stdout || '').slice(-limit).trim();
55
+ }
56
+ function spawnAsync(command, args, options = {}) {
57
+ return new Promise((resolve, reject) => {
58
+ if (options.signal?.aborted) {
59
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_PROCESS_FAILED', `Cannot run ${command}: operation aborted.`, {
60
+ cause: options.signal.reason,
61
+ operation: options.operation || 'spawn',
62
+ });
63
+ }
64
+ const piped = Boolean(options.capture || options.onStdout || options.onStderr);
65
+ const limit = options.outputLimit || DefaultOutputLimit;
66
+ const child = (0, cross_spawn_1.default)(command, args, {
67
+ cwd: options.cwd,
68
+ env: options.env || process.env,
69
+ shell: false,
70
+ stdio: piped ? ['ignore', 'pipe', 'pipe'] : 'inherit',
71
+ windowsHide: true,
72
+ });
73
+ const stdout = new BoundedCapture(limit);
74
+ const stderr = new BoundedCapture(limit);
75
+ let timedOut = false;
76
+ let settled = false;
77
+ let escalation = null;
78
+ let stopping;
79
+ if (piped) {
80
+ child.stdout.on('data', (chunk) => {
81
+ stdout.append(chunk);
82
+ options.onStdout?.(chunk);
83
+ });
84
+ child.stderr.on('data', (chunk) => {
85
+ stderr.append(chunk);
86
+ options.onStderr?.(chunk);
87
+ });
88
+ }
89
+ const stop = (signal = 'SIGTERM') => {
90
+ if (stopping || settled)
91
+ return;
92
+ stopping = Promise.resolve().then(() => (0, tool_command_1.terminateProcess)(child, signal));
93
+ if (process.platform === 'win32') {
94
+ void finish(null, signal);
95
+ }
96
+ else {
97
+ stopping.catch((cause) => {
98
+ void finish(null, null, cause);
99
+ });
100
+ escalation = setTimeout(() => {
101
+ stopping = (0, tool_command_1.terminateProcess)(child, 'SIGKILL');
102
+ stopping.catch((cause) => {
103
+ void finish(null, null, cause);
104
+ });
105
+ }, options.stopGraceMs || DefaultStopGraceMs);
106
+ escalation.unref?.();
107
+ }
108
+ };
109
+ const interrupt = () => stop('SIGINT');
110
+ const terminate = () => stop('SIGTERM');
111
+ process.once('SIGINT', interrupt);
112
+ process.once('SIGTERM', terminate);
113
+ const timeout = options.timeoutMs
114
+ ? setTimeout(() => {
115
+ timedOut = true;
116
+ stop('SIGTERM');
117
+ }, options.timeoutMs)
118
+ : null;
119
+ timeout?.unref?.();
120
+ const cleanup = () => {
121
+ if (timeout)
122
+ clearTimeout(timeout);
123
+ if (escalation)
124
+ clearTimeout(escalation);
125
+ process.removeListener('SIGINT', interrupt);
126
+ process.removeListener('SIGTERM', terminate);
127
+ options.signal?.removeEventListener('abort', abort);
128
+ };
129
+ const finish = async (code, signal, cause) => {
130
+ try {
131
+ await stopping;
132
+ }
133
+ catch (error) {
134
+ cause = error;
135
+ }
136
+ if (settled)
137
+ return;
138
+ settled = true;
139
+ cleanup();
140
+ if (cause)
141
+ reject(new errors_1.HarmonyCliError('ERR_HARMONY_PROCESS_FAILED', `Cannot run ${command}: ${cause.message}`, {
142
+ cause,
143
+ operation: options.operation || 'spawn',
144
+ }));
145
+ else
146
+ resolve({
147
+ code: code === null ? 1 : code,
148
+ signal,
149
+ stderr: stderr.toString(),
150
+ stdout: stdout.toString(),
151
+ timedOut,
152
+ });
153
+ };
154
+ child.once('error', (cause) => {
155
+ void finish(null, null, cause);
156
+ });
157
+ child.once('close', (code, signal) => {
158
+ void finish(code, signal);
159
+ });
160
+ const abort = () => stop('SIGTERM');
161
+ options.signal?.addEventListener('abort', abort, { once: true });
162
+ if (options.signal?.aborted)
163
+ abort();
164
+ });
165
+ }
166
+ function startManagedProcess(command, args, options = {}) {
167
+ if (options.signal?.aborted) {
168
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_PROCESS_FAILED', `Cannot launch ${command}: operation aborted.`, {
169
+ cause: options.signal.reason,
170
+ operation: options.operation || 'spawn',
171
+ });
172
+ }
173
+ const limit = options.outputLimit || DefaultOutputLimit;
174
+ const piped = options.stdio !== 'inherit';
175
+ const child = (0, cross_spawn_1.default)(command, args, {
176
+ cwd: options.cwd,
177
+ env: options.env || process.env,
178
+ shell: false,
179
+ stdio: piped ? ['ignore', 'pipe', 'pipe'] : 'inherit',
180
+ windowsHide: true,
181
+ });
182
+ const stdout = new BoundedCapture(limit);
183
+ const stderr = new BoundedCapture(limit);
184
+ let failure = null;
185
+ let closed = false;
186
+ let stopped = false;
187
+ let stopping;
188
+ let stopPromise;
189
+ let finish;
190
+ if (piped) {
191
+ child.stdout.on('data', (chunk) => {
192
+ stdout.append(chunk);
193
+ options.onStdout?.(chunk);
194
+ });
195
+ child.stderr.on('data', (chunk) => {
196
+ stderr.append(chunk);
197
+ options.onStderr?.(chunk);
198
+ });
199
+ }
200
+ const interrupt = () => {
201
+ void stop('SIGINT').catch(() => { });
202
+ };
203
+ const terminate = () => {
204
+ void stop('SIGTERM').catch(() => { });
205
+ };
206
+ const abort = () => {
207
+ void stop('SIGTERM').catch(() => { });
208
+ };
209
+ const cleanup = () => {
210
+ process.removeListener('SIGINT', interrupt);
211
+ process.removeListener('SIGTERM', terminate);
212
+ options.signal?.removeEventListener('abort', abort);
213
+ };
214
+ const completion = new Promise((resolve, reject) => {
215
+ child.once('error', (cause) => {
216
+ failure = new errors_1.HarmonyCliError('ERR_HARMONY_PROCESS_FAILED', `Cannot launch ${command}: ${cause.message}`, {
217
+ cause,
218
+ operation: options.operation || 'spawn',
219
+ });
220
+ });
221
+ finish = async (code, signal) => {
222
+ try {
223
+ await stopping;
224
+ }
225
+ catch (cause) {
226
+ failure = new errors_1.HarmonyCliError('ERR_HARMONY_PROCESS_FAILED', `Cannot stop ${command}: ${cause.message}`, {
227
+ cause, operation: options.operation || 'spawn',
228
+ });
229
+ }
230
+ if (closed)
231
+ return;
232
+ closed = true;
233
+ cleanup();
234
+ if (failure)
235
+ reject(failure);
236
+ else
237
+ resolve({
238
+ code: code === null ? 1 : code,
239
+ signal,
240
+ stderr: stderr.toString(),
241
+ stdout: stdout.toString(),
242
+ timedOut: false,
243
+ });
244
+ };
245
+ child.once('close', (code, signal) => {
246
+ void finish(code, signal);
247
+ });
248
+ });
249
+ // A readiness probe may be the first consumer. Keep early spawn failures from
250
+ // becoming unhandled rejections while the probe is still polling.
251
+ completion.catch(() => { });
252
+ async function stop(signal = 'SIGTERM', graceMs = DefaultStopGraceMs) {
253
+ if (stopPromise)
254
+ return stopPromise;
255
+ if (closed)
256
+ return completion;
257
+ stopped = true;
258
+ stopping = Promise.resolve().then(() => (0, tool_command_1.terminateProcess)(child, signal));
259
+ stopPromise = (async () => {
260
+ if (process.platform === 'win32') {
261
+ void finish(null, signal);
262
+ return completion;
263
+ }
264
+ try {
265
+ await stopping;
266
+ }
267
+ catch {
268
+ await finish(null, signal);
269
+ return completion;
270
+ }
271
+ let timer;
272
+ await Promise.race([
273
+ completion.catch(() => undefined),
274
+ new Promise((resolve) => {
275
+ timer = setTimeout(resolve, graceMs);
276
+ timer.unref?.();
277
+ }),
278
+ ]);
279
+ if (timer)
280
+ clearTimeout(timer);
281
+ if (!closed) {
282
+ stopping = (0, tool_command_1.terminateProcess)(child, 'SIGKILL');
283
+ try {
284
+ await stopping;
285
+ }
286
+ catch {
287
+ await finish(null, signal);
288
+ }
289
+ }
290
+ return completion.catch(() => undefined);
291
+ })();
292
+ stopPromise.catch(() => { });
293
+ return stopPromise;
294
+ }
295
+ process.once('SIGINT', interrupt);
296
+ process.once('SIGTERM', terminate);
297
+ if (options.signal?.aborted)
298
+ abort();
299
+ else
300
+ options.signal?.addEventListener('abort', abort, { once: true });
301
+ return {
302
+ child,
303
+ completion,
304
+ getStderr: () => stderr.toString(),
305
+ getStdout: () => stdout.toString(),
306
+ stop,
307
+ wasStopped: () => stopped,
308
+ };
309
+ }
310
+ async function runCheckedAsync(command, args, options) {
311
+ const result = await spawnAsync(command, args, {
312
+ capture: true,
313
+ cwd: options.cwd,
314
+ env: options.env,
315
+ operation: options.operation,
316
+ outputLimit: options.outputLimit || 2 * 1024 * 1024,
317
+ timeoutMs: options.timeoutMs,
318
+ });
319
+ if (result.code !== 0 || result.timedOut) {
320
+ const diagnostics = formatDiagnostics(result);
321
+ throw new errors_1.HarmonyCliError(options.code, `${options.message} exited with code ${result.code}${result.timedOut ? ' after timing out' : ''}.${diagnostics ? `\n${diagnostics}` : ''}`, { exitCode: result.code || 1, operation: options.operation });
322
+ }
323
+ return result;
324
+ }
@@ -0,0 +1 @@
1
+ export declare function timedAsync<T>(steps: Record<string, number>, name: string, operation: () => Promise<T> | T): Promise<T>;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.timedAsync = timedAsync;
4
+ async function timedAsync(steps, name, operation) {
5
+ const started = performance.now();
6
+ try {
7
+ return await operation();
8
+ }
9
+ finally {
10
+ steps[name] = Math.max(0, Math.round(performance.now() - started));
11
+ }
12
+ }
@@ -0,0 +1,2 @@
1
+ declare function resolveProject(start?: string): string;
2
+ export { resolveProject };
@@ -0,0 +1,23 @@
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.resolveProject = resolveProject;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const errors_1 = require("./errors");
10
+ function resolveProject(start = process.cwd()) {
11
+ let current = node_path_1.default.resolve(start);
12
+ if (node_fs_1.default.existsSync(current) && node_fs_1.default.statSync(current).isFile())
13
+ current = node_path_1.default.dirname(current);
14
+ while (true) {
15
+ if (node_fs_1.default.existsSync(node_path_1.default.join(current, 'package.json')))
16
+ return node_fs_1.default.realpathSync(current);
17
+ const parent = node_path_1.default.dirname(current);
18
+ if (parent === current)
19
+ break;
20
+ current = parent;
21
+ }
22
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_CONFIG_INVALID', `No package.json was found from ${start}.`, { operation: 'resolve-project' });
23
+ }
@@ -0,0 +1,2 @@
1
+ declare function withHarmonyProjectLockAsync<T>(projectRoot: string, operation: string, callback: () => Promise<T> | T): Promise<T>;
2
+ export { withHarmonyProjectLockAsync };
@@ -0,0 +1,185 @@
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.withHarmonyProjectLockAsync = withHarmonyProjectLockAsync;
7
+ const node_async_hooks_1 = require("node:async_hooks");
8
+ const node_crypto_1 = __importDefault(require("node:crypto"));
9
+ const node_fs_1 = __importDefault(require("node:fs"));
10
+ const node_path_1 = __importDefault(require("node:path"));
11
+ const errors_1 = require("./errors");
12
+ const DefaultMalformedLockGraceMs = 30_000;
13
+ const DefaultPollIntervalMs = 100;
14
+ const DefaultWaitTimeoutMs = 5_000;
15
+ const LockRelativePath = '.expo/harmony/native-operation.lock';
16
+ const ActiveLock = new node_async_hooks_1.AsyncLocalStorage();
17
+ async function canonicalProjectRootAsync(projectRoot) {
18
+ try {
19
+ return await node_fs_1.default.promises.realpath(node_path_1.default.resolve(projectRoot));
20
+ }
21
+ catch (cause) {
22
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_PROJECT_LOCK', 'Cannot resolve the application project before acquiring its Harmony native operation lock.', { cause, operation: 'project-lock' });
23
+ }
24
+ }
25
+ function projectLockPath(projectRoot) {
26
+ return node_path_1.default.join(projectRoot, ...LockRelativePath.split('/'));
27
+ }
28
+ async function processIsAliveAsync(pid) {
29
+ if (!Number.isInteger(pid) || Number(pid) <= 0)
30
+ return false;
31
+ try {
32
+ process.kill(Number(pid), 0);
33
+ return true;
34
+ }
35
+ catch (cause) {
36
+ return cause?.code !== 'ESRCH';
37
+ }
38
+ }
39
+ async function readLockOwnerAsync(lockPath) {
40
+ try {
41
+ const candidate = JSON.parse(await node_fs_1.default.promises.readFile(lockPath, 'utf8'));
42
+ if (!candidate
43
+ || typeof candidate !== 'object'
44
+ || !Number.isInteger(candidate.pid)
45
+ || candidate.pid <= 0
46
+ || typeof candidate.createdAt !== 'string'
47
+ || typeof candidate.operation !== 'string'
48
+ || typeof candidate.projectRoot !== 'string'
49
+ || typeof candidate.token !== 'string') {
50
+ return null;
51
+ }
52
+ return candidate;
53
+ }
54
+ catch (_cause) {
55
+ return null;
56
+ }
57
+ }
58
+ async function removeStaleProjectLockAsync(lockPath) {
59
+ let observed;
60
+ try {
61
+ observed = await node_fs_1.default.promises.lstat(lockPath);
62
+ }
63
+ catch (_cause) {
64
+ return false;
65
+ }
66
+ if (!observed.isFile() || observed.isSymbolicLink())
67
+ return false;
68
+ const owner = await readLockOwnerAsync(lockPath);
69
+ if (owner) {
70
+ if (await processIsAliveAsync(owner.pid))
71
+ return false;
72
+ }
73
+ else if (Date.now() - observed.mtimeMs < DefaultMalformedLockGraceMs) {
74
+ return false;
75
+ }
76
+ try {
77
+ const current = await node_fs_1.default.promises.lstat(lockPath);
78
+ if (!current.isFile() || current.isSymbolicLink()
79
+ || current.dev !== observed.dev
80
+ || current.ino !== observed.ino
81
+ || current.mtimeMs !== observed.mtimeMs
82
+ || current.size !== observed.size) {
83
+ return false;
84
+ }
85
+ await node_fs_1.default.promises.unlink(lockPath);
86
+ return true;
87
+ }
88
+ catch (_cause) {
89
+ return false;
90
+ }
91
+ }
92
+ async function releaseOwnedLockAsync(handle, lockPath, owned) {
93
+ await handle.close().catch(() => { });
94
+ if (!owned)
95
+ return false;
96
+ try {
97
+ const current = await node_fs_1.default.promises.lstat(lockPath);
98
+ if (!current.isFile() || current.isSymbolicLink()
99
+ || current.dev !== owned.dev || current.ino !== owned.ino) {
100
+ return false;
101
+ }
102
+ await node_fs_1.default.promises.unlink(lockPath);
103
+ return true;
104
+ }
105
+ catch (_cause) {
106
+ return false;
107
+ }
108
+ }
109
+ function delayAsync(milliseconds) {
110
+ return new Promise(resolve => setTimeout(resolve, milliseconds));
111
+ }
112
+ async function acquireHarmonyProjectLockAsync(projectRoot, operation) {
113
+ const canonicalRoot = await canonicalProjectRootAsync(projectRoot);
114
+ const lockPath = projectLockPath(canonicalRoot);
115
+ const deadline = Date.now() + DefaultWaitTimeoutMs;
116
+ await node_fs_1.default.promises.mkdir(node_path_1.default.dirname(lockPath), { recursive: true });
117
+ while (true) {
118
+ try {
119
+ const handle = await node_fs_1.default.promises.open(lockPath, 'wx', 0o600);
120
+ let owned;
121
+ const owner = {
122
+ createdAt: new Date().toISOString(),
123
+ operation,
124
+ pid: process.pid,
125
+ projectRoot: canonicalRoot,
126
+ token: node_crypto_1.default.randomUUID(),
127
+ };
128
+ try {
129
+ owned = await handle.stat();
130
+ await handle.writeFile(`${JSON.stringify(owner)}\n`);
131
+ await handle.sync();
132
+ }
133
+ catch (cause) {
134
+ await releaseOwnedLockAsync(handle, lockPath, owned);
135
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_PROJECT_LOCK', 'Cannot initialize the Harmony native operation lock.', { cause, operation: 'project-lock' });
136
+ }
137
+ let released = false;
138
+ return {
139
+ async releaseAsync() {
140
+ if (released)
141
+ return;
142
+ released = true;
143
+ if (await releaseOwnedLockAsync(handle, lockPath, owned))
144
+ return;
145
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_PROJECT_LOCK', 'The Harmony native operation lock changed before it could be released safely.', { operation: 'project-lock' });
146
+ },
147
+ };
148
+ }
149
+ catch (cause) {
150
+ if (cause instanceof errors_1.HarmonyCliError) {
151
+ throw new errors_1.HarmonyCliError(cause.code, cause.message, {
152
+ cause,
153
+ exitCode: cause.exitCode,
154
+ operation: cause.operation,
155
+ });
156
+ }
157
+ if (cause?.code !== 'EEXIST') {
158
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_PROJECT_LOCK', 'Cannot acquire the Harmony native operation lock.', { cause, operation: 'project-lock' });
159
+ }
160
+ if (await removeStaleProjectLockAsync(lockPath))
161
+ continue;
162
+ if (Date.now() >= deadline) {
163
+ const owner = await readLockOwnerAsync(lockPath);
164
+ const detail = owner?.operation ? ` (${owner.operation}, pid ${owner.pid})` : '';
165
+ throw new errors_1.HarmonyCliError('ERR_HARMONY_PROJECT_BUSY', `Another Harmony native operation is using this project${detail}. Retry after it finishes.`, { operation });
166
+ }
167
+ await delayAsync(Math.min(DefaultPollIntervalMs, Math.max(1, deadline - Date.now())));
168
+ }
169
+ }
170
+ }
171
+ async function withHarmonyProjectLockAsync(projectRoot, operation, callback) {
172
+ const canonicalRoot = await canonicalProjectRootAsync(projectRoot);
173
+ const active = ActiveLock.getStore();
174
+ if (active?.projectRoot === canonicalRoot && !active.released)
175
+ return await callback();
176
+ const lease = await acquireHarmonyProjectLockAsync(canonicalRoot, operation);
177
+ const context = { projectRoot: canonicalRoot, released: false };
178
+ try {
179
+ return await ActiveLock.run(context, callback);
180
+ }
181
+ finally {
182
+ context.released = true;
183
+ await lease.releaseAsync();
184
+ }
185
+ }
@@ -0,0 +1,29 @@
1
+ import { type HarmonyTool } from '../native/toolchain';
2
+ interface Device {
3
+ aliases: string[];
4
+ connectTool: string | null;
5
+ id: string;
6
+ location: string | null;
7
+ state: string;
8
+ transport: string;
9
+ }
10
+ interface HdcOptions {
11
+ allowFailure?: boolean;
12
+ code?: string;
13
+ cwd?: string;
14
+ devicePort?: number;
15
+ message?: string;
16
+ operation?: string;
17
+ outputLimit?: number;
18
+ timeoutMs?: number;
19
+ }
20
+ interface DeviceSelectionOptions extends HdcOptions {
21
+ emulator?: HarmonyTool;
22
+ emulatorLogFile?: string;
23
+ onProgress?: (message: string) => void;
24
+ }
25
+ declare function selectDeviceAsync(hdc: HarmonyTool, requested?: string, options?: DeviceSelectionOptions): Promise<Device>;
26
+ declare function installHapAsync(hdc: HarmonyTool, device: Device, hap: string, options?: HdcOptions): Promise<void>;
27
+ declare function configureMetroPortAsync(hdc: HarmonyTool, device: Device, port: number, options?: HdcOptions): Promise<void>;
28
+ declare function launchAppAsync(hdc: HarmonyTool, device: Device, bundleName: string, abilityName: string, options?: HdcOptions): Promise<void>;
29
+ export { configureMetroPortAsync, installHapAsync, launchAppAsync, selectDeviceAsync, };