rnxsim 0.1.419 → 0.1.421

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 (62) hide show
  1. package/cli/cloud-client.ts +164 -12
  2. package/cli/cloud-dispatch.ts +7 -2
  3. package/cli/commands/box/checkout-plane.ts +78 -40
  4. package/cli/commands/platform.ts +30 -9
  5. package/cli/outbound-endpoints.ts +18 -0
  6. package/cli/shell-init.ts +1 -1
  7. package/cli/ws-bridge.ts +7 -5
  8. package/dist-lib/agent-daemon-client.cjs +1 -1
  9. package/dist-lib/agent-events.cjs +1 -1
  10. package/dist-lib/agent-identity.cjs +1 -1
  11. package/dist-lib/agent-sessions.cjs +1 -1
  12. package/dist-lib/attached-projects.cjs +1 -1
  13. package/dist-lib/auth/shared-session.cjs +1 -1
  14. package/dist-lib/backend-origin.cjs +1 -1
  15. package/dist-lib/beta.cjs +1 -1
  16. package/dist-lib/beta.mjs +1 -1
  17. package/dist-lib/bridge-constants.cjs +1 -1
  18. package/dist-lib/bridge-contract-input.cjs +1 -1
  19. package/dist-lib/bridge-contract-input.mjs +1 -1
  20. package/dist-lib/bridge-contract.cjs +1 -1
  21. package/dist-lib/bridge-contract.mjs +1 -1
  22. package/dist-lib/capture-contract.cjs +1 -1
  23. package/dist-lib/capture-contract.mjs +1 -1
  24. package/dist-lib/cli-constants.cjs +1 -1
  25. package/dist-lib/cloud-contract.cjs +1 -1
  26. package/dist-lib/cloud-contract.mjs +1 -1
  27. package/dist-lib/cloud.cjs +447 -0
  28. package/dist-lib/cloud.mjs +419 -0
  29. package/dist-lib/config.cjs +1 -1
  30. package/dist-lib/detox/index.cjs +1 -1
  31. package/dist-lib/dev-bundle-resolution.cjs +6 -4
  32. package/dist-lib/home-paths.cjs +1 -1
  33. package/dist-lib/host/bridge-host.cjs +55 -28
  34. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  35. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  36. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  37. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  38. package/dist-lib/host/websocket-proxy.cjs +1 -1
  39. package/dist-lib/index.cjs +7 -5
  40. package/dist-lib/jump-to-source-babel.cjs +1 -1
  41. package/dist-lib/menu.cjs +1 -1
  42. package/dist-lib/menu.mjs +1 -1
  43. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  44. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  45. package/dist-lib/metro-production-bundle.cjs +1 -1
  46. package/dist-lib/metro-production-bundle.mjs +1 -1
  47. package/dist-lib/metro.cjs +4 -3
  48. package/dist-lib/profiles.cjs +1 -1
  49. package/dist-lib/public-brand.cjs +1 -1
  50. package/dist-lib/react-native-host-modules.cjs +1 -1
  51. package/dist-lib/react-native-host-modules.mjs +1 -1
  52. package/dist-lib/render-mode.cjs +1 -1
  53. package/dist-lib/scripts/dev-server-scanner.cjs +9 -6
  54. package/dist-lib/sdk.cjs +1 -1
  55. package/dist-lib/sdk.mjs +1 -1
  56. package/dist-lib/skills.cjs +64 -34
  57. package/dist-lib/vite.cjs +1 -1
  58. package/package.json +8 -1
  59. package/scripts/dev-server-scanner.ts +9 -5
  60. package/src/cloud.ts +499 -0
  61. package/src/dev-bundle-resolution.ts +6 -3
  62. package/src/dev-server-open.ts +3 -2
@@ -0,0 +1,419 @@
1
+ /*! rnx v0.1.421 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
+ var __defProp = Object.defineProperty;
3
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
5
+
6
+ // src/backend-origin.ts
7
+ function normalizeHostname(hostname) {
8
+ return hostname.replace(/^\[|\]$/g, "").toLowerCase();
9
+ }
10
+ function isLoopbackHost(hostname) {
11
+ const normalized = normalizeHostname(hostname);
12
+ return normalized === "localhost" || normalized.endsWith(".localhost") || normalized === "0.0.0.0" || normalized === "::1" || /^127(?:\.\d{1,3}){3}$/.test(normalized);
13
+ }
14
+
15
+ // src/cloud-contract.ts
16
+ var RNX_CLOUD_MAX_ARTIFACT_BYTES = 20 * 1024 * 1024;
17
+ var RNX_CLOUD_DESIGN_MAX_STORAGE_BYTES = 1024 * 1024;
18
+ var RNX_CLOUD_COMMAND_TYPES = Object.freeze([
19
+ "state",
20
+ "query",
21
+ "resolve",
22
+ "perform",
23
+ "waitFor",
24
+ "reset",
25
+ "screenshot",
26
+ "capture",
27
+ "captureRegions",
28
+ "settle",
29
+ "setAppearance",
30
+ "openUrl",
31
+ "tree",
32
+ "tap",
33
+ "longPress",
34
+ "memory",
35
+ "diagnostics"
36
+ ]);
37
+ var RNX_CLOUD_COMMAND_TYPE_SET = new Set(RNX_CLOUD_COMMAND_TYPES);
38
+ function isRnxCloudCommandType(value) {
39
+ return RNX_CLOUD_COMMAND_TYPE_SET.has(value);
40
+ }
41
+
42
+ // src/public-brand.ts
43
+ var name = "rnx";
44
+ var rnxPublicBrand = Object.freeze({
45
+ name,
46
+ packageName: "rnxsim",
47
+ commandName: "rnx",
48
+ domain: "rnxsim.com",
49
+ origin: "https://rnxsim.com"
50
+ });
51
+
52
+ // src/cloud.ts
53
+ var DEFAULT_DEVICE = "iphone-16";
54
+ var DEFAULT_WAIT_READY_MS = 2e4;
55
+ function isRecord(value) {
56
+ return value !== null && typeof value === "object" && !Array.isArray(value);
57
+ }
58
+ function normalizeCloudOrigin(source) {
59
+ let parsed;
60
+ try {
61
+ parsed = new URL(source);
62
+ } catch {
63
+ throw new Error(`invalid RNX Cloud origin: ${source}`);
64
+ }
65
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:" || parsed.username || parsed.password || parsed.pathname !== "/" || parsed.search || parsed.hash) {
66
+ throw new Error(`RNX Cloud origin must be an HTTP(S) origin: ${source}`);
67
+ }
68
+ if (parsed.protocol === "http:" && !isLoopbackHost(parsed.hostname)) {
69
+ throw new Error(`RNX Cloud origin requires HTTPS outside loopback: ${source}`);
70
+ }
71
+ return parsed.origin;
72
+ }
73
+ function resolveOrigin(value) {
74
+ const configured = value?.trim() || process.env.RNX_CLOUD_ORIGIN?.trim();
75
+ return normalizeCloudOrigin(configured || rnxPublicBrand.origin);
76
+ }
77
+ function responseError(status, text) {
78
+ if (text) {
79
+ try {
80
+ const parsed = JSON.parse(text);
81
+ const error = isRecord(parsed) ? parsed.error : null;
82
+ if (isRecord(error) && typeof error.message === "string") {
83
+ return new Error(`RNX Cloud request failed (${status}): ${error.message}`);
84
+ }
85
+ } catch {
86
+ }
87
+ }
88
+ return new Error(
89
+ `RNX Cloud request failed (${status})${text.trim() ? `: ${text.trim()}` : ""}`
90
+ );
91
+ }
92
+ function copyArrayBuffer(bytes) {
93
+ const copy = new ArrayBuffer(bytes.byteLength);
94
+ new Uint8Array(copy).set(bytes);
95
+ return copy;
96
+ }
97
+ async function sha256Hex(bytes) {
98
+ const digest = await globalThis.crypto.subtle.digest("SHA-256", copyArrayBuffer(bytes));
99
+ const view = new Uint8Array(digest);
100
+ let hex = "";
101
+ for (const byte of view) hex += byte.toString(16).padStart(2, "0");
102
+ return hex;
103
+ }
104
+ function readJsArtifact(bytes) {
105
+ let source;
106
+ try {
107
+ source = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
108
+ } catch {
109
+ throw new Error("rnx Cloud artifact must be valid UTF-8 JavaScript, not bytecode");
110
+ }
111
+ if (source.includes("\0") || !source.includes("__d(") || !source.includes("__r(")) {
112
+ throw new Error("rnx Cloud artifact must be a Metro JavaScript bundle");
113
+ }
114
+ return source;
115
+ }
116
+ function readClaim(value) {
117
+ if (!isRecord(value) || typeof value.id !== "string" || !value.id || typeof value.expiresAt !== "number") {
118
+ throw new Error("RNX Cloud returned an invalid claim");
119
+ }
120
+ return { id: value.id, expiresAt: value.expiresAt };
121
+ }
122
+ function artifactDigestId(sha256) {
123
+ return `sha256:${sha256}`;
124
+ }
125
+ function readArtifact(value, expectedId, expectedBytes) {
126
+ if (!isRecord(value) || value.id !== expectedId || typeof value.bytes !== "number" || !Number.isInteger(value.bytes) || value.bytes !== expectedBytes) {
127
+ throw new Error("RNX Cloud returned an invalid artifact receipt");
128
+ }
129
+ return { id: expectedId, bytes: expectedBytes };
130
+ }
131
+ function readSimulatorCreate(value, expectedId, expectedBytes) {
132
+ if (!isRecord(value)) throw new Error("RNX Cloud create returned an invalid simulator");
133
+ const claim = readClaim(value.claim);
134
+ if (typeof value.simId !== "string" || !value.simId || typeof value.token !== "string") {
135
+ throw new Error("RNX Cloud create returned an invalid simulator");
136
+ }
137
+ return {
138
+ simId: value.simId,
139
+ token: value.token,
140
+ claim,
141
+ artifact: readArtifact(value.artifact, expectedId, expectedBytes)
142
+ };
143
+ }
144
+ function readBoxCreate(value, expectedBytes, expectedSha256) {
145
+ const artifactId = artifactDigestId(expectedSha256);
146
+ if (!isRecord(value)) throw new Error("RNX Cloud create returned invalid JSON");
147
+ const simulator = readSimulatorCreate(value.simulator, artifactId, expectedBytes);
148
+ if (typeof value.boxId !== "string" || !value.boxId || value.size !== "nano" || typeof value.token !== "string" || !value.token) {
149
+ throw new Error("RNX Cloud create returned an invalid box receipt");
150
+ }
151
+ return {
152
+ receipt: {
153
+ boxId: value.boxId,
154
+ size: "nano",
155
+ artifact: readArtifact(value.artifact, artifactId, expectedBytes),
156
+ simulator: {
157
+ simId: simulator.simId,
158
+ artifact: simulator.artifact,
159
+ claim: simulator.claim
160
+ }
161
+ },
162
+ boxToken: value.token,
163
+ simulatorToken: simulator.token
164
+ };
165
+ }
166
+ function cloudCommand(command) {
167
+ const clean = {};
168
+ for (const [key, value] of Object.entries(command)) {
169
+ if (key !== "id" && key !== "simId") clean[key] = value;
170
+ }
171
+ if (!isRnxCloudCommandType(command.type)) {
172
+ throw new Error(`RNX Cloud does not support bridge command type ${command.type}`);
173
+ }
174
+ return { ...clean, type: command.type };
175
+ }
176
+ var NanoBox = class _NanoBox {
177
+ constructor(session) {
178
+ __publicField(this, "session");
179
+ this.session = session;
180
+ }
181
+ get boxId() {
182
+ return this.session.boxId;
183
+ }
184
+ get simId() {
185
+ return this.session.simId;
186
+ }
187
+ get claim() {
188
+ return this.session.claim;
189
+ }
190
+ get receipt() {
191
+ return {
192
+ boxId: this.session.boxId,
193
+ size: "nano",
194
+ artifact: this.session.artifact,
195
+ simulator: {
196
+ simId: this.session.simId,
197
+ artifact: this.session.artifact,
198
+ claim: this.session.claim
199
+ }
200
+ };
201
+ }
202
+ async command(command, options) {
203
+ const body = {
204
+ id: globalThis.crypto.randomUUID(),
205
+ claimId: this.session.claim.id,
206
+ command: cloudCommand(command)
207
+ };
208
+ let response;
209
+ try {
210
+ response = await fetch(
211
+ `${this.session.origin}/v1/sims/${encodeURIComponent(this.session.simId)}/commands`,
212
+ {
213
+ method: "POST",
214
+ headers: {
215
+ authorization: `Bearer ${this.session.simulatorToken}`,
216
+ "content-type": "application/json"
217
+ },
218
+ body: JSON.stringify(body),
219
+ ...options?.timeoutMs ? { signal: AbortSignal.timeout(options.timeoutMs) } : {}
220
+ }
221
+ );
222
+ } catch (error) {
223
+ throw new Error(
224
+ `could not reach RNX Cloud at ${this.session.origin}: ${error instanceof Error ? error.message : String(error)}`
225
+ );
226
+ }
227
+ const text = await response.text();
228
+ if (!response.ok) throw responseError(response.status, text);
229
+ let parsed;
230
+ try {
231
+ parsed = JSON.parse(text);
232
+ } catch {
233
+ throw new Error("RNX Cloud command returned unreadable JSON");
234
+ }
235
+ if (!isRecord(parsed) || parsed.id !== body.id) {
236
+ throw new Error("RNX Cloud command returned an invalid command receipt");
237
+ }
238
+ if (typeof parsed.error === "string" && parsed.error) throw new Error(parsed.error);
239
+ if (!Object.hasOwn(parsed, "result")) {
240
+ throw new Error("RNX Cloud command receipt has no result");
241
+ }
242
+ return parsed.result;
243
+ }
244
+ describe() {
245
+ return this.command({ type: "tree" });
246
+ }
247
+ do(command) {
248
+ return this.command(command);
249
+ }
250
+ wait(condition, options) {
251
+ const timeoutMs = options?.maxMs ?? DEFAULT_WAIT_READY_MS;
252
+ const waitCondition = condition === "ready" ? { type: "ready" } : condition;
253
+ return this.command(
254
+ {
255
+ type: "waitFor",
256
+ waitForOptions: { condition: waitCondition, timeoutMs }
257
+ },
258
+ { timeoutMs: timeoutMs + 1e3 }
259
+ );
260
+ }
261
+ screenshot(options) {
262
+ return this.command({
263
+ type: "screenshot",
264
+ layers: options?.layers ?? "tenant"
265
+ });
266
+ }
267
+ capture() {
268
+ return this.command({ type: "capture" });
269
+ }
270
+ reset() {
271
+ return this.command({ type: "reset" });
272
+ }
273
+ async confirm() {
274
+ const response = await fetch(
275
+ `${this.session.origin}/v1/sims/${encodeURIComponent(this.session.simId)}/claim`,
276
+ {
277
+ method: "POST",
278
+ headers: {
279
+ authorization: `Bearer ${this.session.simulatorToken}`,
280
+ "content-type": "application/json"
281
+ },
282
+ body: JSON.stringify({ claimId: this.session.claim.id })
283
+ }
284
+ );
285
+ const text = await response.text();
286
+ if (!response.ok) throw responseError(response.status, text);
287
+ let parsed;
288
+ try {
289
+ parsed = JSON.parse(text);
290
+ } catch {
291
+ throw new Error("RNX Cloud claim returned unreadable JSON");
292
+ }
293
+ const claim = readClaim(isRecord(parsed) ? parsed.claim : null);
294
+ if (claim.id !== this.session.claim.id) {
295
+ throw new Error("RNX Cloud confirmation replaced the initial claim");
296
+ }
297
+ this.session = { ...this.session, claim };
298
+ return claim;
299
+ }
300
+ async addSimulator(input = {}) {
301
+ const device = input.device?.trim() || DEFAULT_DEVICE;
302
+ const response = await fetch(
303
+ `${this.session.origin}/v1/boxes/${encodeURIComponent(this.session.boxId)}/simulators`,
304
+ {
305
+ method: "POST",
306
+ headers: {
307
+ authorization: `Bearer ${this.session.boxToken}`,
308
+ "x-rnx-account-authorization": this.session.authorization,
309
+ "content-type": "application/json"
310
+ },
311
+ body: JSON.stringify({
312
+ device,
313
+ ...input.storageFrom === void 0 ? {} : { storageFrom: input.storageFrom }
314
+ })
315
+ }
316
+ );
317
+ const text = await response.text();
318
+ if (!response.ok) throw responseError(response.status, text);
319
+ let parsed;
320
+ try {
321
+ parsed = JSON.parse(text);
322
+ } catch {
323
+ throw new Error("RNX Cloud simulator create returned unreadable JSON");
324
+ }
325
+ if (!isRecord(parsed) || parsed.boxId !== this.session.boxId) {
326
+ throw new Error("RNX Cloud simulator create returned an invalid box id");
327
+ }
328
+ const simulator = readSimulatorCreate(
329
+ parsed.simulator,
330
+ this.session.artifact.id,
331
+ this.session.artifact.bytes
332
+ );
333
+ const pooled = new _NanoBox({
334
+ ...this.session,
335
+ simId: simulator.simId,
336
+ simulatorToken: simulator.token,
337
+ claim: simulator.claim
338
+ });
339
+ await pooled.confirm();
340
+ return pooled;
341
+ }
342
+ async close() {
343
+ const response = await fetch(
344
+ `${this.session.origin}/v1/boxes/${encodeURIComponent(this.session.boxId)}`,
345
+ {
346
+ method: "DELETE",
347
+ headers: { authorization: `Bearer ${this.session.boxToken}` }
348
+ }
349
+ );
350
+ const text = await response.text();
351
+ if (!response.ok && response.status !== 404) {
352
+ throw responseError(response.status, text);
353
+ }
354
+ }
355
+ };
356
+ async function createNanoBox(input) {
357
+ if (input.artifact.byteLength > RNX_CLOUD_MAX_ARTIFACT_BYTES) {
358
+ throw new Error(
359
+ `rnx Cloud artifact is ${input.artifact.byteLength} bytes and exceeds the ${RNX_CLOUD_MAX_ARTIFACT_BYTES}-byte limit`
360
+ );
361
+ }
362
+ if (input.artifact.byteLength <= 0) {
363
+ throw new Error("rnx Cloud artifact is empty");
364
+ }
365
+ readJsArtifact(input.artifact);
366
+ const origin = resolveOrigin(input.origin);
367
+ const sha256 = await sha256Hex(input.artifact);
368
+ if (input.sha256 && input.sha256 !== sha256) {
369
+ throw new Error("rnx Cloud artifact sha256 does not match the provided digest");
370
+ }
371
+ const device = input.device?.trim() || DEFAULT_DEVICE;
372
+ let response;
373
+ try {
374
+ response = await fetch(`${origin}/v1/boxes`, {
375
+ method: "POST",
376
+ headers: {
377
+ authorization: input.authorization,
378
+ "content-type": "application/javascript",
379
+ "x-rnx-box-size": "nano",
380
+ "x-rnx-artifact-sha256": sha256,
381
+ "x-rnx-platform": "ios",
382
+ "x-rnx-device": device
383
+ },
384
+ body: copyArrayBuffer(input.artifact)
385
+ });
386
+ } catch (error) {
387
+ throw new Error(
388
+ `could not reach RNX Cloud at ${origin}: ${error instanceof Error ? error.message : String(error)}`
389
+ );
390
+ }
391
+ const responseText = await response.text();
392
+ if (!response.ok) throw responseError(response.status, responseText);
393
+ let parsed;
394
+ try {
395
+ parsed = JSON.parse(responseText);
396
+ } catch {
397
+ throw new Error("RNX Cloud create returned unreadable JSON");
398
+ }
399
+ const created = readBoxCreate(parsed, input.artifact.byteLength, sha256);
400
+ const box = new NanoBox({
401
+ origin,
402
+ authorization: input.authorization,
403
+ boxId: created.receipt.boxId,
404
+ boxToken: created.boxToken,
405
+ simId: created.receipt.simulator.simId,
406
+ simulatorToken: created.simulatorToken,
407
+ claim: created.receipt.simulator.claim,
408
+ artifact: created.receipt.artifact
409
+ });
410
+ await box.confirm();
411
+ return box;
412
+ }
413
+ export {
414
+ NanoBox,
415
+ RNX_CLOUD_COMMAND_TYPES,
416
+ RNX_CLOUD_MAX_ARTIFACT_BYTES,
417
+ createNanoBox,
418
+ isRnxCloudCommandType
419
+ };
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.419 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.421 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.419 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.421 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.419 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.421 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -169,12 +169,14 @@ async function probeExpoManifest(normalizedBaseUrl, port) {
169
169
  async (res) => res.ok ? { ok: true, body: await res.text() } : null,
170
170
  () => void 0
171
171
  );
172
+ let probeDeadline;
172
173
  const answered = await Promise.race([
173
174
  pending,
174
- new Promise(
175
- (resolve) => setTimeout(() => resolve("timeout"), MANIFEST_PROBE_TIMEOUT_MS).unref?.()
176
- )
175
+ new Promise((resolve) => {
176
+ probeDeadline = setTimeout(() => resolve("timeout"), MANIFEST_PROBE_TIMEOUT_MS);
177
+ })
177
178
  ]);
179
+ clearTimeout(probeDeadline);
178
180
  if (answered === "timeout" || answered === void 0) return { state: "no-answer" };
179
181
  if (answered === null) return { state: "no-manifest" };
180
182
  body = answered.body;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.419 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.421 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.419 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.421 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -539,7 +539,7 @@ async function readAppSettingsBundle(projectRoot) {
539
539
  // scripts/dev-server-scanner.ts
540
540
  init_optional_demo_registry();
541
541
  var EXEC_EXIT_GRACE_MS = 2e3;
542
- function execGuarded(command, timeout, extra = {}) {
542
+ async function execGuarded(command, timeout, extra = {}) {
543
543
  let child;
544
544
  const ran = new Promise((resolve2, reject) => {
545
545
  child = (0, import_child_process.exec)(command, { encoding: "utf8", timeout, ...extra }, (err, stdout) => {
@@ -547,16 +547,20 @@ function execGuarded(command, timeout, extra = {}) {
547
547
  else resolve2({ stdout });
548
548
  });
549
549
  });
550
+ let deadlineTimer;
550
551
  const deadline = new Promise((_resolve, reject) => {
551
- const timer = setTimeout(() => {
552
+ deadlineTimer = setTimeout(() => {
552
553
  child?.kill("SIGKILL");
553
554
  const err = new Error(`\`${command}\` did not exit within ${timeout}ms`);
554
555
  Object.assign(err, { killed: true });
555
556
  reject(err);
556
557
  }, timeout + EXEC_EXIT_GRACE_MS);
557
- timer.unref?.();
558
558
  });
559
- return Promise.race([ran, deadline]);
559
+ try {
560
+ return await Promise.race([ran, deadline]);
561
+ } finally {
562
+ clearTimeout(deadlineTimer);
563
+ }
560
564
  }
561
565
  var TIMEOUT_MS = 250;
562
566
  var MANIFEST_TIMEOUT_MS = 1500;
@@ -622,7 +626,6 @@ function httpGet(hostname, port, path12, method = "GET", timeout = TIMEOUT_MS, h
622
626
  req.socket?.unref();
623
627
  }, timeout);
624
628
  const abandon = setTimeout(() => req.destroy(), BODY_TIMEOUT_MS);
625
- abandon.unref?.();
626
629
  req.on("close", () => {
627
630
  clearTimeout(giveUp);
628
631
  clearTimeout(abandon);
@@ -3896,6 +3899,8 @@ var CheckoutRequiresGitError = class extends Error {
3896
3899
  };
3897
3900
  var BINARY_SNIFF_BYTES = 8192;
3898
3901
  var LIST_FILES_TIMEOUT_MS = 1e4;
3902
+ var LIST_FILES_ATTEMPTS = 3;
3903
+ var LIST_FILES_RETRY_MS = 25;
3899
3904
  function looksBinary(filePath, size) {
3900
3905
  if (size === 0) return false;
3901
3906
  let fd;
@@ -3927,33 +3932,55 @@ var CheckoutFilePlane = class {
3927
3932
  index() {
3928
3933
  return this.entries;
3929
3934
  }
3930
- async refresh() {
3931
- let stdout;
3932
- try {
3933
- const args = this.trackedOnly ? ["ls-files", "-z"] : ["ls-files", "-co", "--exclude-standard", "-z"];
3934
- stdout = (0, import_node_child_process4.execFileSync)("git", args, {
3935
- cwd: this.root,
3936
- encoding: "utf8",
3937
- maxBuffer: 64 * 1024 * 1024,
3938
- timeout: this.listTimeoutMs,
3939
- // stating the environment is not redundant here. bun 1.3.14 resolves
3940
- // the binary for the synchronous form against the PATH it captured at
3941
- // startup and ignores a later change to `process.env.PATH`, so without
3942
- // this it runs a different git than every other call in the process.
3943
- env: process.env
3944
- });
3945
- } catch (error) {
3946
- const message = error instanceof Error ? error.message : String(error);
3947
- const killed = error instanceof Error && "signal" in error && error.signal === "SIGTERM";
3948
- throw new CheckoutRequiresGitError(
3949
- killed ? `git did not list the project's files in ${this.root} within ${this.listTimeoutMs / 1e3}s, so the box has no file set to serve. the checkout may be on a stalled filesystem, or this machine may be too loaded to start a subprocess.` : `could not list the project's files with git in ${this.root}: ${message}`
3950
- );
3951
- }
3935
+ listFiles() {
3936
+ const args = this.trackedOnly ? ["ls-files", "-z"] : ["ls-files", "-co", "--exclude-standard", "-z"];
3937
+ const stdout = (0, import_node_child_process4.execFileSync)("git", args, {
3938
+ cwd: this.root,
3939
+ encoding: "utf8",
3940
+ maxBuffer: 64 * 1024 * 1024,
3941
+ timeout: this.listTimeoutMs,
3942
+ // stating the environment is not redundant here. bun 1.3.14 resolves
3943
+ // the binary for the synchronous form against the PATH it captured at
3944
+ // startup and ignores a later change to `process.env.PATH`, so without
3945
+ // this it runs a different git than every other call in the process.
3946
+ env: process.env
3947
+ });
3952
3948
  if (stdout.length === 0) {
3953
3949
  throw new CheckoutRequiresGitError(
3954
3950
  `git listed no ${this.trackedOnly ? "tracked " : ""}files in ${this.root}. either the checkout contains no eligible files, or this machine could not read the child process's output.`
3955
3951
  );
3956
3952
  }
3953
+ return stdout;
3954
+ }
3955
+ async refresh() {
3956
+ let stdout = "";
3957
+ let lastFailure;
3958
+ for (let attempt = 1; attempt <= LIST_FILES_ATTEMPTS; attempt++) {
3959
+ try {
3960
+ stdout = this.listFiles();
3961
+ lastFailure = void 0;
3962
+ break;
3963
+ } catch (error) {
3964
+ if (error instanceof CheckoutRequiresGitError) {
3965
+ lastFailure = error;
3966
+ } else {
3967
+ const message = error instanceof Error ? error.message : String(error);
3968
+ const killed = error instanceof Error && "signal" in error && error.signal === "SIGTERM";
3969
+ lastFailure = new CheckoutRequiresGitError(
3970
+ killed ? `git did not list the project's files in ${this.root} within ${this.listTimeoutMs / 1e3}s, so the box has no file set to serve. the checkout may be on a stalled filesystem, or this machine may be too loaded to start a subprocess.` : `could not list the project's files with git in ${this.root}: ${message}`
3971
+ );
3972
+ }
3973
+ if (attempt < LIST_FILES_ATTEMPTS) {
3974
+ await new Promise(
3975
+ (resolve2) => setTimeout(resolve2, LIST_FILES_RETRY_MS * attempt)
3976
+ );
3977
+ }
3978
+ }
3979
+ }
3980
+ if (lastFailure) {
3981
+ lastFailure.message = `${lastFailure.message} (${LIST_FILES_ATTEMPTS} attempts)`;
3982
+ throw lastFailure;
3983
+ }
3957
3984
  const next = /* @__PURE__ */ new Map();
3958
3985
  const nextSniffed = /* @__PURE__ */ new Map();
3959
3986
  for (const relative of stdout.split("\0")) {
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.419 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.421 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.419 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.421 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.419 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.421 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/host/fetch-proxy-overrides.ts
4
4
  var FETCH_PROXY_BROWSER_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36";
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.419 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.421 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.419 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.421 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.419 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.421 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -841,11 +841,12 @@ function createBridge(wsPort, opts = {}) {
841
841
  } catch {
842
842
  }
843
843
  ws.close();
844
- setTimeout(() => {
844
+ const terminateGrace = setTimeout(() => {
845
845
  if (ws.readyState !== import_ws.WebSocket.CLOSED) {
846
846
  ws.terminate();
847
847
  }
848
- }, 250).unref();
848
+ }, 250);
849
+ ws.once("close", () => clearTimeout(terminateGrace));
849
850
  }
850
851
  };
851
852
  }
@@ -2858,9 +2859,11 @@ function openRnxDesktopWhenMetroIsReady({
2858
2859
  const deadline = Date.now() + timeoutMs;
2859
2860
  return new Promise((resolve2) => {
2860
2861
  let finished = false;
2862
+ let retryTimer;
2861
2863
  const finish = (opened) => {
2862
2864
  if (finished) return;
2863
2865
  finished = true;
2866
+ clearTimeout(retryTimer);
2864
2867
  scheduledMetroOpens.delete(key);
2865
2868
  resolve2(opened);
2866
2869
  };
@@ -2873,8 +2876,7 @@ function openRnxDesktopWhenMetroIsReady({
2873
2876
  finish(false);
2874
2877
  return;
2875
2878
  }
2876
- const timer = setTimeout(check, intervalMs);
2877
- timer.unref();
2879
+ retryTimer = setTimeout(check, intervalMs);
2878
2880
  };
2879
2881
  const check = () => {
2880
2882
  const request = import_node_http.default.get(
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.419 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.421 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;