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.
- package/cli/cloud-client.ts +164 -12
- package/cli/cloud-dispatch.ts +7 -2
- package/cli/commands/box/checkout-plane.ts +78 -40
- package/cli/commands/platform.ts +30 -9
- package/cli/outbound-endpoints.ts +18 -0
- package/cli/shell-init.ts +1 -1
- package/cli/ws-bridge.ts +7 -5
- package/dist-lib/agent-daemon-client.cjs +1 -1
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/agent-identity.cjs +1 -1
- package/dist-lib/agent-sessions.cjs +1 -1
- package/dist-lib/attached-projects.cjs +1 -1
- package/dist-lib/auth/shared-session.cjs +1 -1
- package/dist-lib/backend-origin.cjs +1 -1
- package/dist-lib/beta.cjs +1 -1
- package/dist-lib/beta.mjs +1 -1
- package/dist-lib/bridge-constants.cjs +1 -1
- package/dist-lib/bridge-contract-input.cjs +1 -1
- package/dist-lib/bridge-contract-input.mjs +1 -1
- package/dist-lib/bridge-contract.cjs +1 -1
- package/dist-lib/bridge-contract.mjs +1 -1
- package/dist-lib/capture-contract.cjs +1 -1
- package/dist-lib/capture-contract.mjs +1 -1
- package/dist-lib/cli-constants.cjs +1 -1
- package/dist-lib/cloud-contract.cjs +1 -1
- package/dist-lib/cloud-contract.mjs +1 -1
- package/dist-lib/cloud.cjs +447 -0
- package/dist-lib/cloud.mjs +419 -0
- package/dist-lib/config.cjs +1 -1
- package/dist-lib/detox/index.cjs +1 -1
- package/dist-lib/dev-bundle-resolution.cjs +6 -4
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +55 -28
- package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
- package/dist-lib/host/replacement-module-handler.cjs +1 -1
- package/dist-lib/host/websocket-proxy.cjs +1 -1
- package/dist-lib/index.cjs +7 -5
- package/dist-lib/jump-to-source-babel.cjs +1 -1
- package/dist-lib/menu.cjs +1 -1
- package/dist-lib/menu.mjs +1 -1
- package/dist-lib/metro-fingerprint-registry.cjs +1 -1
- package/dist-lib/metro-fingerprint-registry.mjs +1 -1
- package/dist-lib/metro-production-bundle.cjs +1 -1
- package/dist-lib/metro-production-bundle.mjs +1 -1
- package/dist-lib/metro.cjs +4 -3
- package/dist-lib/profiles.cjs +1 -1
- package/dist-lib/public-brand.cjs +1 -1
- package/dist-lib/react-native-host-modules.cjs +1 -1
- package/dist-lib/react-native-host-modules.mjs +1 -1
- package/dist-lib/render-mode.cjs +1 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +9 -6
- package/dist-lib/sdk.cjs +1 -1
- package/dist-lib/sdk.mjs +1 -1
- package/dist-lib/skills.cjs +64 -34
- package/dist-lib/vite.cjs +1 -1
- package/package.json +8 -1
- package/scripts/dev-server-scanner.ts +9 -5
- package/src/cloud.ts +499 -0
- package/src/dev-bundle-resolution.ts +6 -3
- 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
|
+
};
|
package/dist-lib/config.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
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;
|
package/dist-lib/detox/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
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.
|
|
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
|
-
|
|
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;
|
package/dist-lib/home-paths.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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;
|
package/dist-lib/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
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)
|
|
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
|
-
|
|
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.
|
|
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;
|