peachsim 0.1.527 → 0.1.571

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 (169) hide show
  1. package/README.md +28 -28
  2. package/cli/app-fonts.ts +1 -1
  3. package/cli/app-splash.ts +1 -1
  4. package/cli/bin.ts +1 -1
  5. package/cli/bridge-diagnostics.ts +1 -1
  6. package/cli/bridge-flow-runner.ts +72 -12
  7. package/cli/cloud-client.ts +273 -6
  8. package/cli/cloud-dispatch.ts +23 -2
  9. package/cli/cloud-session.ts +60 -4
  10. package/cli/cloud-test-result-files.ts +97 -0
  11. package/cli/cloud-test-tunnel.ts +354 -0
  12. package/cli/commands/box/bundle-plane.ts +2 -2
  13. package/cli/commands/box/checkout-plane.ts +1 -1
  14. package/cli/commands/box/rnx-command.ts +2 -2
  15. package/cli/commands/box/search-command.ts +2 -2
  16. package/cli/commands/box.ts +8 -8
  17. package/cli/commands/compat.ts +1 -1
  18. package/cli/commands/control.ts +22 -3
  19. package/cli/commands/detox.ts +1 -1
  20. package/cli/commands/device.ts +1 -1
  21. package/cli/commands/diagnose.ts +1 -1
  22. package/cli/commands/film.ts +1 -1
  23. package/cli/commands/flow.ts +60 -3
  24. package/cli/commands/inspect/actions.ts +1 -1
  25. package/cli/commands/inspect/core.ts +6 -7
  26. package/cli/commands/inspect/find.ts +2 -1
  27. package/cli/commands/inspect/resolve-target.ts +5 -3
  28. package/cli/commands/inspect/shared.ts +1 -1
  29. package/cli/commands/inspect/wait-event.ts +1 -1
  30. package/cli/commands/inspect/wait-selector.ts +15 -4
  31. package/cli/commands/inspect.ts +19 -14
  32. package/cli/commands/maestro.ts +582 -69
  33. package/cli/commands/permissions.ts +1 -1
  34. package/cli/commands/platform.ts +205 -8
  35. package/cli/commands/preview.ts +2 -2
  36. package/cli/commands/record.ts +2 -2
  37. package/cli/commands/report-issue.ts +2 -2
  38. package/cli/commands/screenshot-layers.ts +1 -1
  39. package/cli/commands/screenshot.ts +1 -1
  40. package/cli/commands/setup.ts +2 -2
  41. package/cli/commands/test.ts +600 -117
  42. package/cli/commands/three-mode.ts +1 -1
  43. package/cli/commands/upload.ts +5 -5
  44. package/cli/commands/what-happened.ts +1 -1
  45. package/cli/desktop-companion.ts +3 -3
  46. package/cli/drivers/playwright-sim-host.ts +2 -2
  47. package/cli/flow-file.ts +7 -0
  48. package/cli/flow-live-status.ts +65 -8
  49. package/cli/help-core.ts +3 -3
  50. package/cli/hidden-runtime-alias.ts +1 -1
  51. package/cli/maestro-report.ts +180 -0
  52. package/cli/main.ts +1 -1
  53. package/cli/outbound-endpoints.ts +187 -136
  54. package/cli/parse-args.ts +1 -1
  55. package/cli/registry.ts +1 -1
  56. package/cli/resolve-assets.ts +1 -1
  57. package/cli/run-registry.ts +10 -1
  58. package/cli/send-to-box.ts +4 -4
  59. package/cli/ws-bridge.ts +3 -5
  60. package/detox/expectations.ts +1 -1
  61. package/detox/gestures.ts +2 -2
  62. package/detox/index.ts +71 -29
  63. package/detox/jest-preset.cjs +5 -5
  64. package/detox/jest-setup-after-env.cjs +1 -1
  65. package/dist-lib/agent-daemon-client.cjs +2 -2
  66. package/dist-lib/agent-events.cjs +1 -1
  67. package/dist-lib/agent-identity.cjs +1 -1
  68. package/dist-lib/agent-sessions.cjs +2 -2
  69. package/dist-lib/attached-projects.cjs +2 -2
  70. package/dist-lib/auth/shared-session.cjs +1 -1
  71. package/dist-lib/backend-origin.cjs +1 -1
  72. package/dist-lib/beta.cjs +1 -1
  73. package/dist-lib/beta.mjs +1 -1
  74. package/dist-lib/bridge-constants.cjs +1 -1
  75. package/dist-lib/bridge-contract-input.cjs +1 -1
  76. package/dist-lib/bridge-contract-input.mjs +1 -1
  77. package/dist-lib/bridge-contract.cjs +1 -1
  78. package/dist-lib/bridge-contract.mjs +1 -1
  79. package/dist-lib/capture-contract.cjs +94 -3
  80. package/dist-lib/capture-contract.mjs +93 -3
  81. package/dist-lib/cli-constants.cjs +1 -1
  82. package/dist-lib/cloud-contract.cjs +1 -1
  83. package/dist-lib/cloud-contract.mjs +1 -1
  84. package/dist-lib/cloud-test-args.cjs +116 -0
  85. package/dist-lib/cloud-test-args.mjs +86 -0
  86. package/dist-lib/cloud.cjs +2 -2
  87. package/dist-lib/cloud.mjs +2 -2
  88. package/dist-lib/config.cjs +1 -1
  89. package/dist-lib/detox/index.cjs +106 -25
  90. package/dist-lib/detox/jest-preset.cjs +5 -5
  91. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  92. package/dist-lib/home-paths.cjs +1 -1
  93. package/dist-lib/host/bridge-host.cjs +150 -65
  94. package/dist-lib/host/fetch-proxy-handler.cjs +28 -2
  95. package/dist-lib/host/fetch-proxy-overrides.cjs +33 -3
  96. package/dist-lib/host/fetch-proxy-overrides.mjs +30 -2
  97. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  98. package/dist-lib/host/websocket-proxy.cjs +1 -1
  99. package/dist-lib/index.cjs +224 -41
  100. package/dist-lib/jump-to-source-babel.cjs +1 -1
  101. package/dist-lib/jump-to-source-native.cjs +1 -1
  102. package/dist-lib/menu.cjs +2 -2
  103. package/dist-lib/menu.mjs +2 -2
  104. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  105. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  106. package/dist-lib/metro-production-bundle.cjs +3 -3
  107. package/dist-lib/metro-production-bundle.mjs +3 -3
  108. package/dist-lib/metro.cjs +8 -8
  109. package/dist-lib/profiles.cjs +1 -1
  110. package/dist-lib/public-brand.cjs +2 -2
  111. package/dist-lib/react-native-host-modules.cjs +1 -1
  112. package/dist-lib/react-native-host-modules.mjs +1 -1
  113. package/dist-lib/render-mode.cjs +1 -1
  114. package/dist-lib/scripts/dev-server-scanner.cjs +31 -15
  115. package/dist-lib/sdk.cjs +3 -2
  116. package/dist-lib/sdk.mjs +3 -2
  117. package/dist-lib/skills.cjs +478 -266
  118. package/dist-lib/swift-compile.cjs +372 -0
  119. package/dist-lib/swift-compile.mjs +339 -0
  120. package/dist-lib/swift-package.cjs +78 -0
  121. package/dist-lib/swift-package.mjs +52 -0
  122. package/dist-lib/swift-submit.cjs +1 -1
  123. package/dist-lib/swift-submit.mjs +1 -1
  124. package/dist-lib/swift.cjs +583 -16
  125. package/dist-lib/vite.cjs +483 -150
  126. package/package.json +30 -9
  127. package/scripts/dev-server-scanner.ts +41 -14
  128. package/scripts/optional-demo-registry.ts +3 -3
  129. package/skills/contrast/SKILL.md +9 -9
  130. package/skills/rnx-debug/SKILL.md +14 -4
  131. package/skills/rnx-perf/SKILL.md +5 -5
  132. package/skills/rnx-setup/SKILL.md +11 -11
  133. package/skills/rnx-test/SKILL.md +14 -14
  134. package/skills/rnx-visual/SKILL.md +6 -6
  135. package/src/agent-sessions.ts +3 -3
  136. package/src/attached-projects.ts +1 -1
  137. package/src/bridge-contract-input.ts +1 -1
  138. package/src/bridge-contract.ts +23 -5
  139. package/src/capture-contract.ts +145 -3
  140. package/src/cloud-test-args.ts +100 -0
  141. package/src/config.ts +2 -2
  142. package/src/connect.ts +2 -2
  143. package/src/home-paths.ts +3 -3
  144. package/src/host/bridge-host.ts +2 -2
  145. package/src/host/fetch-proxy-handler.ts +4 -3
  146. package/src/host/fetch-proxy-overrides.ts +41 -0
  147. package/src/host/plane-host.ts +2 -2
  148. package/src/index.ts +3 -0
  149. package/src/metro-plugin.ts +4 -4
  150. package/src/metro-production-bundle.ts +2 -2
  151. package/src/native-seam-manifest.ts +6 -6
  152. package/src/public-brand.ts +1 -1
  153. package/src/rolldown-plugin-swift.ts +16 -12
  154. package/src/runtime-assets.ts +8 -6
  155. package/src/screenshots/frame-compose.ts +24 -56
  156. package/src/screenshots/org-deck.ts +1 -1
  157. package/src/screenshots/registry.ts +4 -4
  158. package/src/screenshots/schema.ts +1 -1
  159. package/src/sdk.ts +2 -2
  160. package/src/settings-bundle.ts +1 -1
  161. package/src/sim-client.ts +2 -4
  162. package/src/skills/builtin/compat-check.ts +1 -1
  163. package/src/swift-compile.ts +359 -0
  164. package/src/swift-package.ts +69 -0
  165. package/src/swift-submit.ts +3 -2
  166. package/src/vite-plugin-one.ts +1 -1
  167. package/src/vite-plugin-swift.ts +114 -113
  168. package/src/vite-plugin.ts +30 -17
  169. package/src/worklets-babel.ts +2 -2
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.571 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/capture-contract.ts
4
4
  var RNX_SCREEN_CAPTURE_VERSION = 1;
@@ -10,7 +10,7 @@ function selectRnxRasterNodeIds(tree) {
10
10
  if (!visibleFrame || visibleFrame.width <= 0 || visibleFrame.height <= 0) continue;
11
11
  if (node.type === "masked-view" || node.type === "text" && typeof node.text === "string" && (/\p{Emoji_Presentation}/u.test(node.text) || /\uFE0F|\u200D|\u20E3|\p{Emoji_Modifier}|\p{Regional_Indicator}/u.test(
12
12
  node.text
13
- )) || node.style._liquidGlass !== void 0 || node.style._liquidGlassEdge !== void 0) {
13
+ )) || node.style._liquidGlass !== void 0 || node.style._liquidGlassEdge !== void 0 || node.style._liquidGlassMaterialTint !== void 0) {
14
14
  unsupported.add(node.nodeId);
15
15
  }
16
16
  }
@@ -109,6 +109,95 @@ function rnxCaptureRasterRegions(capture) {
109
109
  return frame ? [{ key: String(nodeId), nodeId, frame }] : [];
110
110
  });
111
111
  }
112
+ function packRnxCaptureRegions(regions, pixelRatio, surface) {
113
+ const physical = regions.map((region) => {
114
+ const left = Math.floor(region.frame.x * pixelRatio);
115
+ const top = Math.floor(region.frame.y * pixelRatio);
116
+ const right = Math.ceil((region.frame.x + region.frame.width) * pixelRatio);
117
+ const bottom = Math.ceil((region.frame.y + region.frame.height) * pixelRatio);
118
+ if (left < 0 || top < 0 || right > surface.pixelWidth || bottom > surface.pixelHeight || right <= left || bottom <= top) {
119
+ throw new Error(`capture region ${region.key} is outside the app surface`);
120
+ }
121
+ return {
122
+ key: region.key,
123
+ nodeId: region.nodeId,
124
+ frame: {
125
+ x: left / pixelRatio,
126
+ y: top / pixelRatio,
127
+ width: (right - left) / pixelRatio,
128
+ height: (bottom - top) / pixelRatio
129
+ },
130
+ sourceX: left,
131
+ sourceY: top,
132
+ pixelWidth: right - left,
133
+ pixelHeight: bottom - top
134
+ };
135
+ });
136
+ const largestWidth = Math.max(...physical.map((region) => region.pixelWidth));
137
+ const totalArea = physical.reduce(
138
+ (area, region) => area + region.pixelWidth * region.pixelHeight,
139
+ 0
140
+ );
141
+ if (totalArea > 4194304) {
142
+ throw new Error("capture region pixels exceed the atlas budget");
143
+ }
144
+ const candidates = [
145
+ largestWidth,
146
+ Math.max(largestWidth, Math.ceil(Math.sqrt(totalArea))),
147
+ Math.max(largestWidth, surface.pixelWidth),
148
+ 4096
149
+ ];
150
+ const ordered = [...physical].sort(
151
+ (left, right) => right.pixelHeight - left.pixelHeight || right.pixelWidth - left.pixelWidth || left.key.localeCompare(right.key)
152
+ );
153
+ let best = null;
154
+ for (const candidate of [...new Set(candidates)]) {
155
+ if (candidate > 4096) continue;
156
+ let x = 0;
157
+ let y = 0;
158
+ let rowHeight = 0;
159
+ let usedWidth = 0;
160
+ const packed = [];
161
+ for (const region of ordered) {
162
+ if (x > 0 && x + region.pixelWidth > candidate) {
163
+ y += rowHeight;
164
+ x = 0;
165
+ rowHeight = 0;
166
+ }
167
+ packed.push({ ...region, atlasX: x, atlasY: y });
168
+ x += region.pixelWidth;
169
+ rowHeight = Math.max(rowHeight, region.pixelHeight);
170
+ usedWidth = Math.max(usedWidth, x);
171
+ }
172
+ const height2 = y + rowHeight;
173
+ if (height2 > 4096 || usedWidth * height2 > 4194304) continue;
174
+ const result = { width: usedWidth, height: height2, regions: packed };
175
+ if (!best || result.width * result.height < best.width * best.height) best = result;
176
+ }
177
+ if (!best) throw new Error("capture regions cannot fit in one bounded atlas");
178
+ const width = best.width / pixelRatio;
179
+ const height = best.height / pixelRatio;
180
+ return {
181
+ width,
182
+ height,
183
+ pixelWidth: best.width,
184
+ pixelHeight: best.height,
185
+ regions: best.regions,
186
+ atlasRegions: best.regions.map((region) => ({
187
+ key: region.key,
188
+ nodeId: region.nodeId,
189
+ frame: region.frame,
190
+ // a region flush with the far edge is anchored to the logical edge so
191
+ // its source never runs past the atlas by a rounding hair
192
+ source: {
193
+ x: region.atlasX + region.pixelWidth === best.width ? width - region.frame.width : region.atlasX / pixelRatio,
194
+ y: region.atlasY + region.pixelHeight === best.height ? height - region.frame.height : region.atlasY / pixelRatio,
195
+ width: region.frame.width,
196
+ height: region.frame.height
197
+ }
198
+ }))
199
+ };
200
+ }
112
201
  function captureRecord(value) {
113
202
  return value !== null && typeof value === "object" && !Array.isArray(value);
114
203
  }
@@ -164,7 +253,7 @@ function isRnxScreenCapture(value) {
164
253
  return captureRecord(tree.viewport) && captureInteger(tree.viewport.width) && tree.viewport.width > 0 && captureInteger(tree.viewport.height) && tree.viewport.height > 0 && captureInteger(tree.rootNodeId) && (tree.commitRevision === void 0 || captureInteger(tree.commitRevision)) && Array.isArray(tree.nodes) && tree.nodes.every(captureNode) && (visual === void 0 || visual.kind === "png" && (visual.producer === "headless-cpu-app-surface" || visual.producer === "browser-compositor-app-surface") && typeof visual.dataUri === "string" && visual.dataUri.startsWith("data:image/png;base64,") && captureInteger(visual.pixelWidth) && visual.pixelWidth > 0 && captureInteger(visual.pixelHeight) && visual.pixelHeight > 0);
165
254
  }
166
255
  function isRnxCaptureRegionAtlas(value) {
167
- if (!captureRecord(value) || value.kind !== "png-atlas" || value.producer !== "headless-cpu-app-surface" || typeof value.dataUri !== "string" || !value.dataUri.startsWith("data:image/png;base64,") || !captureNumber(value.width) || value.width <= 0 || !captureNumber(value.height) || value.height <= 0 || !captureInteger(value.pixelWidth) || value.pixelWidth <= 0 || !captureInteger(value.pixelHeight) || value.pixelHeight <= 0 || !Array.isArray(value.regions) || value.regions.length === 0 || value.regions.length > 256) {
256
+ if (!captureRecord(value) || value.kind !== "png-atlas" || value.producer !== "headless-cpu-app-surface" && value.producer !== "browser-compositor-app-surface" || typeof value.dataUri !== "string" || !value.dataUri.startsWith("data:image/png;base64,") || !captureNumber(value.width) || value.width <= 0 || !captureNumber(value.height) || value.height <= 0 || !captureInteger(value.pixelWidth) || value.pixelWidth <= 0 || !captureInteger(value.pixelHeight) || value.pixelHeight <= 0 || !Array.isArray(value.regions) || value.regions.length === 0 || value.regions.length > 256) {
168
257
  return false;
169
258
  }
170
259
  const scale = value.pixelWidth / value.width;
@@ -184,6 +273,7 @@ export {
184
273
  RNX_SCREEN_CAPTURE_VERSION,
185
274
  isRnxCaptureRegionAtlas,
186
275
  isRnxScreenCapture,
276
+ packRnxCaptureRegions,
187
277
  rnxCaptureRasterRegions,
188
278
  rnxRasterFrames,
189
279
  selectRnxRasterNodeIds
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.571 | (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.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.571 | (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.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.571 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/cloud-contract.ts
4
4
  var RNX_CLOUD_MAX_ARTIFACT_BYTES = 20 * 1024 * 1024;
@@ -0,0 +1,116 @@
1
+ /*! rnx v0.1.571 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
+ let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
+ "use strict";
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
21
+
22
+ // src/cloud-test-args.ts
23
+ var cloud_test_args_exports = {};
24
+ __export(cloud_test_args_exports, {
25
+ CLOUD_TEST_ARGS_MAX_BYTES: () => CLOUD_TEST_ARGS_MAX_BYTES,
26
+ CLOUD_TEST_ARGS_MAX_COUNT: () => CLOUD_TEST_ARGS_MAX_COUNT,
27
+ CLOUD_TEST_ARG_MAX_LENGTH: () => CLOUD_TEST_ARG_MAX_LENGTH,
28
+ CLOUD_TEST_VALUE_FLAGS: () => CLOUD_TEST_VALUE_FLAGS,
29
+ isCloudTestValueFlag: () => isCloudTestValueFlag,
30
+ parseCloudTestArgs: () => parseCloudTestArgs
31
+ });
32
+ module.exports = __toCommonJS(cloud_test_args_exports);
33
+ var CLOUD_TEST_ARGS_MAX_COUNT = 32;
34
+ var CLOUD_TEST_ARG_MAX_LENGTH = 1e3;
35
+ var CLOUD_TEST_ARGS_MAX_BYTES = 16 * 1024;
36
+ var CLOUD_TEST_VALUE_FLAGS = [
37
+ "-t",
38
+ "--testNamePattern",
39
+ "--testPathPattern",
40
+ "--include-tags",
41
+ "--exclude-tags",
42
+ "--grep"
43
+ ];
44
+ var CLOUD_TEST_VALUE_FLAG_SET = new Set(CLOUD_TEST_VALUE_FLAGS);
45
+ function isCloudTestValueFlag(name) {
46
+ return CLOUD_TEST_VALUE_FLAG_SET.has(name);
47
+ }
48
+ function parseCloudTestArgs(value) {
49
+ if (value === void 0 || value === null) return { ok: true, args: [] };
50
+ if (!Array.isArray(value)) {
51
+ return { ok: false, message: "testArgs must be an array of strings" };
52
+ }
53
+ if (value.length > CLOUD_TEST_ARGS_MAX_COUNT) {
54
+ return {
55
+ ok: false,
56
+ message: `testArgs may contain at most ${CLOUD_TEST_ARGS_MAX_COUNT} entries`
57
+ };
58
+ }
59
+ const encoder = new TextEncoder();
60
+ let bytes = 0;
61
+ const entries = [];
62
+ for (const entry of value) {
63
+ if (typeof entry !== "string" || entry.length === 0) {
64
+ return { ok: false, message: "testArgs entries must be non-empty strings" };
65
+ }
66
+ if (entry.length > CLOUD_TEST_ARG_MAX_LENGTH || entry.includes("\0")) {
67
+ return { ok: false, message: `testArgs entry is too long or carries a NUL byte: ${entry.slice(0, 60)}` };
68
+ }
69
+ bytes += encoder.encode(entry).length;
70
+ if (bytes > CLOUD_TEST_ARGS_MAX_BYTES) {
71
+ return {
72
+ ok: false,
73
+ message: `testArgs may contain at most ${CLOUD_TEST_ARGS_MAX_BYTES} bytes`
74
+ };
75
+ }
76
+ entries.push(entry);
77
+ }
78
+ const args = [];
79
+ for (let i = 0; i < entries.length; i++) {
80
+ const entry = entries[i];
81
+ if (!entry.startsWith("-")) {
82
+ return {
83
+ ok: false,
84
+ message: `testArgs carries a positional argument (selection flags only): ${entry.slice(0, 60)}`
85
+ };
86
+ }
87
+ const eq = entry.indexOf("=");
88
+ const head = eq > 0 ? entry.slice(0, eq) : entry;
89
+ if (!isCloudTestValueFlag(head)) {
90
+ return { ok: false, message: `testArgs carries an unknown flag: ${head.slice(0, 60)}` };
91
+ }
92
+ if (eq > 0) {
93
+ if (!entry.slice(eq + 1)) {
94
+ return { ok: false, message: `${head} expects a value` };
95
+ }
96
+ args.push(entry);
97
+ continue;
98
+ }
99
+ const val = entries[i + 1];
100
+ if (val === void 0 || val.startsWith("-")) {
101
+ return { ok: false, message: `${head} expects a value` };
102
+ }
103
+ args.push(entry, val);
104
+ i += 1;
105
+ }
106
+ return { ok: true, args };
107
+ }
108
+ // Annotate the CommonJS export names for ESM import in node:
109
+ 0 && (module.exports = {
110
+ CLOUD_TEST_ARGS_MAX_BYTES,
111
+ CLOUD_TEST_ARGS_MAX_COUNT,
112
+ CLOUD_TEST_ARG_MAX_LENGTH,
113
+ CLOUD_TEST_VALUE_FLAGS,
114
+ isCloudTestValueFlag,
115
+ parseCloudTestArgs
116
+ });
@@ -0,0 +1,86 @@
1
+ /*! rnx v0.1.571 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
+
3
+ // src/cloud-test-args.ts
4
+ var CLOUD_TEST_ARGS_MAX_COUNT = 32;
5
+ var CLOUD_TEST_ARG_MAX_LENGTH = 1e3;
6
+ var CLOUD_TEST_ARGS_MAX_BYTES = 16 * 1024;
7
+ var CLOUD_TEST_VALUE_FLAGS = [
8
+ "-t",
9
+ "--testNamePattern",
10
+ "--testPathPattern",
11
+ "--include-tags",
12
+ "--exclude-tags",
13
+ "--grep"
14
+ ];
15
+ var CLOUD_TEST_VALUE_FLAG_SET = new Set(CLOUD_TEST_VALUE_FLAGS);
16
+ function isCloudTestValueFlag(name) {
17
+ return CLOUD_TEST_VALUE_FLAG_SET.has(name);
18
+ }
19
+ function parseCloudTestArgs(value) {
20
+ if (value === void 0 || value === null) return { ok: true, args: [] };
21
+ if (!Array.isArray(value)) {
22
+ return { ok: false, message: "testArgs must be an array of strings" };
23
+ }
24
+ if (value.length > CLOUD_TEST_ARGS_MAX_COUNT) {
25
+ return {
26
+ ok: false,
27
+ message: `testArgs may contain at most ${CLOUD_TEST_ARGS_MAX_COUNT} entries`
28
+ };
29
+ }
30
+ const encoder = new TextEncoder();
31
+ let bytes = 0;
32
+ const entries = [];
33
+ for (const entry of value) {
34
+ if (typeof entry !== "string" || entry.length === 0) {
35
+ return { ok: false, message: "testArgs entries must be non-empty strings" };
36
+ }
37
+ if (entry.length > CLOUD_TEST_ARG_MAX_LENGTH || entry.includes("\0")) {
38
+ return { ok: false, message: `testArgs entry is too long or carries a NUL byte: ${entry.slice(0, 60)}` };
39
+ }
40
+ bytes += encoder.encode(entry).length;
41
+ if (bytes > CLOUD_TEST_ARGS_MAX_BYTES) {
42
+ return {
43
+ ok: false,
44
+ message: `testArgs may contain at most ${CLOUD_TEST_ARGS_MAX_BYTES} bytes`
45
+ };
46
+ }
47
+ entries.push(entry);
48
+ }
49
+ const args = [];
50
+ for (let i = 0; i < entries.length; i++) {
51
+ const entry = entries[i];
52
+ if (!entry.startsWith("-")) {
53
+ return {
54
+ ok: false,
55
+ message: `testArgs carries a positional argument (selection flags only): ${entry.slice(0, 60)}`
56
+ };
57
+ }
58
+ const eq = entry.indexOf("=");
59
+ const head = eq > 0 ? entry.slice(0, eq) : entry;
60
+ if (!isCloudTestValueFlag(head)) {
61
+ return { ok: false, message: `testArgs carries an unknown flag: ${head.slice(0, 60)}` };
62
+ }
63
+ if (eq > 0) {
64
+ if (!entry.slice(eq + 1)) {
65
+ return { ok: false, message: `${head} expects a value` };
66
+ }
67
+ args.push(entry);
68
+ continue;
69
+ }
70
+ const val = entries[i + 1];
71
+ if (val === void 0 || val.startsWith("-")) {
72
+ return { ok: false, message: `${head} expects a value` };
73
+ }
74
+ args.push(entry, val);
75
+ i += 1;
76
+ }
77
+ return { ok: true, args };
78
+ }
79
+ export {
80
+ CLOUD_TEST_ARGS_MAX_BYTES,
81
+ CLOUD_TEST_ARGS_MAX_COUNT,
82
+ CLOUD_TEST_ARG_MAX_LENGTH,
83
+ CLOUD_TEST_VALUE_FLAGS,
84
+ isCloudTestValueFlag,
85
+ parseCloudTestArgs
86
+ };
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.571 | (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;
@@ -173,7 +173,7 @@ function rnxCloudScopesAllowCommand(scopes, type) {
173
173
  var name = "rnx";
174
174
  var rnxPublicBrand = Object.freeze({
175
175
  name,
176
- packageName: "rnxsim",
176
+ packageName: "peachsim",
177
177
  commandName: "rnx",
178
178
  domain: "rnxsim.com",
179
179
  origin: "https://rnxsim.com"
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.571 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  var __defProp = Object.defineProperty;
3
3
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
4
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
@@ -143,7 +143,7 @@ function rnxCloudScopesAllowCommand(scopes, type) {
143
143
  var name = "rnx";
144
144
  var rnxPublicBrand = Object.freeze({
145
145
  name,
146
- packageName: "rnxsim",
146
+ packageName: "peachsim",
147
147
  commandName: "rnx",
148
148
  domain: "rnxsim.com",
149
149
  origin: "https://rnxsim.com"
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.571 | (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.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.571 | (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;
@@ -233,7 +233,8 @@ __export(detox_exports, {
233
233
  element: () => element,
234
234
  expect: () => sootsimExpect,
235
235
  waitFor: () => sootsimWaitFor,
236
- waitForSootsimIdle: () => waitForSootsimIdle
236
+ waitForSootsimIdle: () => waitForSootsimIdle,
237
+ waitForSootsimShellReady: () => waitForSootsimShellReady
237
238
  });
238
239
  module.exports = __toCommonJS(detox_exports);
239
240
  var fs = __toESM(require("fs"), 1);
@@ -758,7 +759,7 @@ async function launchReapedChromeScoped(launch, opts = {}, rendererTarget, polic
758
759
  );
759
760
  }
760
761
 
761
- // ../sootsim-engine/src/capture/browser-composite.ts
762
+ // ../peach-engine/src/capture/browser-composite.ts
762
763
  function resolveBrowserCompositeCdpClip(request, metrics) {
763
764
  if (!metrics || typeof metrics !== "object") {
764
765
  throw new Error("browser composite capture requires CDP layout metrics");
@@ -824,6 +825,65 @@ function readBrowserCompositeCaptureRequest(value) {
824
825
  return request;
825
826
  }
826
827
 
828
+ // ../peach-engine/src/remote-stream/remoteEngineParam.ts
829
+ function readRemoteEngineParam(search, hash) {
830
+ const fromQuery = new URLSearchParams(search).get("remoteEngine");
831
+ if (fromQuery) return fromQuery;
832
+ const fromFragment = new URLSearchParams(hash.replace(/^#/, "")).get("remoteEngine");
833
+ return fromFragment || null;
834
+ }
835
+
836
+ // ../peach-engine/src/render-worker/app-load.ts
837
+ var TEST_FIXTURE_SHELL_APP_ID = "sootsim-test-fixture";
838
+ var KITCHEN_SINK_SHELL_APP_ID = "sootsim-kitchen-sink";
839
+ var REMOTE_DISPLAY_SHELL_APP_ID = "sootsim-remote-display";
840
+ function getVisibleHarnessAppForUrl(initUrl) {
841
+ if (!initUrl) return null;
842
+ try {
843
+ const parsed = new URL(initUrl);
844
+ const testName = parsed.searchParams.get("test");
845
+ if (testName) {
846
+ return {
847
+ id: TEST_FIXTURE_SHELL_APP_ID,
848
+ name: testName,
849
+ color: "#0f172a",
850
+ icon: "T",
851
+ hidden: true
852
+ };
853
+ }
854
+ if (parsed.searchParams.get("mode") === "kitchen-sink") {
855
+ return {
856
+ id: KITCHEN_SINK_SHELL_APP_ID,
857
+ name: "Kitchen Sink",
858
+ color: "#0f172a",
859
+ icon: "K",
860
+ hidden: true
861
+ };
862
+ }
863
+ if (readRemoteEngineParam(parsed.search, parsed.hash)) {
864
+ return {
865
+ id: REMOTE_DISPLAY_SHELL_APP_ID,
866
+ name: "Remote",
867
+ color: "#0f172a",
868
+ icon: "R",
869
+ hidden: true
870
+ };
871
+ }
872
+ } catch {
873
+ }
874
+ return null;
875
+ }
876
+ function isAppBootTargetUrl(initUrl) {
877
+ if (!initUrl) return false;
878
+ if (getVisibleHarnessAppForUrl(initUrl)) return true;
879
+ try {
880
+ const parsed = new URL(initUrl);
881
+ return parsed.searchParams.has("nativePreview") || parsed.searchParams.has("bundle") || parsed.searchParams.has("app") || /\/app\/[^/]+$/i.test(parsed.pathname) || /\/rn(?:\/[^/]+)?$/i.test(parsed.pathname) || /\/dock\/[^/]+$/i.test(parsed.pathname);
882
+ } catch {
883
+ return false;
884
+ }
885
+ }
886
+
827
887
  // src/poll-until.ts
828
888
  async function pollUntil(read, ready, options) {
829
889
  if (!Number.isFinite(options.timeoutMs) || options.timeoutMs <= 0) {
@@ -1969,8 +2029,9 @@ async function waitForSootsimSurfaceMetrics(page, timeout = 3e4) {
1969
2029
  );
1970
2030
  }
1971
2031
  async function waitForSootsimShellReady(page, timeout = 3e4) {
2032
+ const expectsInitialAppPaint = isAppBootTargetUrl(page.url());
1972
2033
  try {
1973
- await page.evaluate(async (timeoutMs) => {
2034
+ await page.evaluate(async ({ timeoutMs, expectsInitialAppPaint: expectsInitialAppPaint2 }) => {
1974
2035
  const start = Date.now();
1975
2036
  const deadline = start + timeoutMs;
1976
2037
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
@@ -1992,8 +2053,6 @@ async function waitForSootsimShellReady(page, timeout = 3e4) {
1992
2053
  }
1993
2054
  };
1994
2055
  const isTenantOnly = new URL(window.location.href).searchParams.get("renderMode") === "tenant-only";
1995
- const currentUrl = new URL(window.location.href);
1996
- const expectsInitialAppPaint = currentUrl.searchParams.has("test") || currentUrl.pathname.includes("/app/");
1997
2056
  let shellHost = null;
1998
2057
  while (Date.now() < deadline) {
1999
2058
  const candidate = window.SootSim?.bridges?.shellHost;
@@ -2010,26 +2069,47 @@ async function waitForSootsimShellReady(page, timeout = 3e4) {
2010
2069
  if (shellHost.ready && typeof shellHost.ready.then === "function") {
2011
2070
  await withTimeout("sootsim shell ready", shellHost.ready);
2012
2071
  }
2013
- if (!expectsInitialAppPaint && shellHost.firstContentPainted && typeof shellHost.firstContentPainted.then === "function") {
2014
- await withTimeout(
2015
- "sootsim shell first content paint",
2016
- shellHost.firstContentPainted
2017
- );
2018
- }
2019
- const waitForScreenTransitions = window.__sootsimTest?.waitForScreenTransitions;
2020
- if (typeof waitForScreenTransitions === "function") {
2021
- const transitionResult = await waitForScreenTransitions({
2022
- timeoutMs: Math.min(remaining(), 3e3),
2023
- settleMs: 64,
2024
- startWindowMs: 700
2025
- });
2026
- if (transitionResult?.timedOut === true) {
2027
- throw new Error(
2028
- `sootsim app launch transitions timed out: ${JSON.stringify(transitionResult)}`
2029
- );
2072
+ const isRenderHostDisposed = (error) => error instanceof Error && error.message.includes("render host disposed");
2073
+ for (; ; ) {
2074
+ try {
2075
+ if (expectsInitialAppPaint2) {
2076
+ while (Date.now() < deadline) {
2077
+ if (document.documentElement.dataset.sootsimBootStage === "painted") {
2078
+ break;
2079
+ }
2080
+ await sleep(50);
2081
+ }
2082
+ if (document.documentElement.dataset.sootsimBootStage !== "painted") {
2083
+ throw new Error(
2084
+ `sootsim app first content paint timed out after ${timeoutMs}ms`
2085
+ );
2086
+ }
2087
+ } else if (shellHost.firstContentPainted && typeof shellHost.firstContentPainted.then === "function") {
2088
+ await withTimeout(
2089
+ "sootsim shell first content paint",
2090
+ shellHost.firstContentPainted
2091
+ );
2092
+ }
2093
+ const waitForScreenTransitions = window.__sootsimTest?.waitForScreenTransitions;
2094
+ if (typeof waitForScreenTransitions === "function") {
2095
+ const transitionResult = await waitForScreenTransitions({
2096
+ timeoutMs: Math.min(remaining(), 3e3),
2097
+ settleMs: 64,
2098
+ startWindowMs: 700
2099
+ });
2100
+ if (transitionResult?.timedOut === true) {
2101
+ throw new Error(
2102
+ `sootsim app launch transitions timed out: ${JSON.stringify(transitionResult)}`
2103
+ );
2104
+ }
2105
+ }
2106
+ break;
2107
+ } catch (error) {
2108
+ if (!isRenderHostDisposed(error) || Date.now() >= deadline) throw error;
2109
+ await sleep(100);
2030
2110
  }
2031
2111
  }
2032
- }, timeout);
2112
+ }, { timeoutMs: timeout, expectsInitialAppPaint });
2033
2113
  } catch (error) {
2034
2114
  throw await describeSootsimBridgeFailure(page, error);
2035
2115
  }
@@ -3785,5 +3865,6 @@ async function cleanup() {
3785
3865
  element,
3786
3866
  expect,
3787
3867
  waitFor,
3788
- waitForSootsimIdle
3868
+ waitForSootsimIdle,
3869
+ waitForSootsimShellReady
3789
3870
  });
@@ -1,16 +1,16 @@
1
1
  // jest preset for running existing detox suites against sootsim.
2
2
  //
3
- // users drop `preset: 'rnxsim/detox'` into their jest config
3
+ // users drop `preset: 'peachsim/detox'` into their jest config
4
4
  // (or `rnx-detox.config.cjs`) and their `import { by, device, element,
5
5
  // expect, waitFor } from 'detox'` calls resolve to the sootsim driver.
6
6
 
7
7
  const path = require('path')
8
8
 
9
9
  // resolve the published driver entry so the mapping works from any cwd.
10
- // `require.resolve('sootsim/detox')` honours the subpath export in the
11
- // sootsim package.json.
12
- const DRIVER_ENTRY = require.resolve('rnxsim/detox')
13
- const PACKAGE_ROOT = path.dirname(require.resolve('rnxsim/package.json'))
10
+ // `require.resolve('peachsim/detox')` honours the subpath export in the
11
+ // peachsim package.json.
12
+ const DRIVER_ENTRY = require.resolve('peachsim/detox')
13
+ const PACKAGE_ROOT = path.dirname(require.resolve('peachsim/package.json'))
14
14
 
15
15
  /** @type {import('@jest/types').Config.InitialOptions} */
16
16
  module.exports = {
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.571 | (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.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.571 | (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;