peachsim 0.1.663 → 0.1.665

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 (83) hide show
  1. package/cli/app-fonts.ts +6 -3
  2. package/cli/commands/control.ts +27 -19
  3. package/cli/commands/inspect/core.ts +58 -32
  4. package/cli/commands/inspect/settling.ts +27 -21
  5. package/dist-lib/agent-daemon-client.cjs +1 -1
  6. package/dist-lib/agent-events.cjs +1 -1
  7. package/dist-lib/agent-identity.cjs +1 -1
  8. package/dist-lib/agent-sessions.cjs +1 -1
  9. package/dist-lib/attached-projects.cjs +1 -1
  10. package/dist-lib/auth/shared-session.cjs +1 -1
  11. package/dist-lib/backend-origin.cjs +1 -1
  12. package/dist-lib/beta.cjs +1 -1
  13. package/dist-lib/beta.mjs +1 -1
  14. package/dist-lib/bridge-constants.cjs +1 -1
  15. package/dist-lib/bridge-contract-input.cjs +2 -6
  16. package/dist-lib/bridge-contract-input.mjs +2 -6
  17. package/dist-lib/bridge-contract.cjs +6 -1
  18. package/dist-lib/bridge-contract.mjs +5 -1
  19. package/dist-lib/capture-contract.cjs +1 -1
  20. package/dist-lib/capture-contract.mjs +1 -1
  21. package/dist-lib/cli-constants.cjs +1 -1
  22. package/dist-lib/cloud-contract.cjs +1 -1
  23. package/dist-lib/cloud-contract.mjs +1 -1
  24. package/dist-lib/cloud-test-args.cjs +1 -1
  25. package/dist-lib/cloud-test-args.mjs +1 -1
  26. package/dist-lib/cloud.cjs +1 -1
  27. package/dist-lib/cloud.mjs +1 -1
  28. package/dist-lib/config.cjs +1 -1
  29. package/dist-lib/detox/index.cjs +1 -1
  30. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  31. package/dist-lib/home-paths.cjs +1 -1
  32. package/dist-lib/host/bridge-host.cjs +2191 -335
  33. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  34. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  35. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  36. package/dist-lib/host/replacement-module-handler.cjs +7 -7
  37. package/dist-lib/host/websocket-proxy.cjs +1 -1
  38. package/dist-lib/index.cjs +23 -30
  39. package/dist-lib/jump-to-source-babel.cjs +1 -1
  40. package/dist-lib/jump-to-source-native.cjs +1 -1
  41. package/dist-lib/kotlin-host.cjs +1 -1
  42. package/dist-lib/menu.cjs +1 -1
  43. package/dist-lib/menu.mjs +1 -1
  44. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  45. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  46. package/dist-lib/metro-production-bundle.cjs +1 -1
  47. package/dist-lib/metro-production-bundle.mjs +1 -1
  48. package/dist-lib/metro.cjs +7 -7
  49. package/dist-lib/native-app-identity.cjs +1 -1
  50. package/dist-lib/native-app-identity.mjs +1 -1
  51. package/dist-lib/plist.cjs +1 -1
  52. package/dist-lib/plist.mjs +1 -1
  53. package/dist-lib/profiles.cjs +1 -1
  54. package/dist-lib/public-brand.cjs +1 -1
  55. package/dist-lib/react-native-host-modules.cjs +1 -1
  56. package/dist-lib/react-native-host-modules.mjs +1 -1
  57. package/dist-lib/render-mode.cjs +1 -1
  58. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  59. package/dist-lib/sdk.cjs +56 -39
  60. package/dist-lib/sdk.mjs +54 -39
  61. package/dist-lib/skills.cjs +2803 -974
  62. package/dist-lib/swift-compile.cjs +1 -1
  63. package/dist-lib/swift-compile.mjs +1 -1
  64. package/dist-lib/swift-package.cjs +1 -1
  65. package/dist-lib/swift-package.mjs +1 -1
  66. package/dist-lib/swift-submit.cjs +1 -1
  67. package/dist-lib/swift-submit.mjs +1 -1
  68. package/dist-lib/swift.cjs +1 -1
  69. package/dist-lib/vite-swift.cjs +1 -1
  70. package/dist-lib/vite.cjs +7 -7
  71. package/kotlin/ComposeSources/PeachComposeHost.kt +6 -3
  72. package/kotlin/fixtures/kotlin-app-variants/Overlays.kt +56 -0
  73. package/kotlin/fixtures/kotlin-app-variants/Scroll.kt +63 -0
  74. package/kotlin/fixtures/kotlin-app-variants/Sheet.kt +43 -0
  75. package/kotlin/fixtures/kotlin-app-variants/Tabs.kt +47 -0
  76. package/kotlin/fixtures/kotlin-app-variants/Widgets.kt +67 -0
  77. package/package.json +2 -2
  78. package/skills/peach-setup/SKILL.md +1 -1
  79. package/src/bridge-contract-input.ts +1 -5
  80. package/src/bridge-contract.ts +16 -4
  81. package/src/expo-swift-host.ts +248 -0
  82. package/src/host/bridge-host.ts +19 -0
  83. package/src/vite-plugin.ts +3 -3
package/cli/app-fonts.ts CHANGED
@@ -261,9 +261,9 @@ export function discoverNativeLinkedAppFonts(opts: {
261
261
  return metroFontSpecsFromPaths(fontPaths, metroOrigin)
262
262
  }
263
263
 
264
- // resolve a metro dev server's project root. expo serves a manifest at the
265
- // server origin carrying `extra.expoClient._internal.projectRoot` — the absolute
266
- // dir of the app being served. the local `peach open <port>` path runs from the
264
+ // resolve a metro dev server's project root. Expo's middleware advertises it
265
+ // in X-React-Native-Project-Root, encoded with encodeURI. Older servers name it
266
+ // in extra.expoClient._internal.projectRoot. The local peach open path runs from the
267
267
  // CLI's cwd, which almost never owns the bundle being opened (e.g.
268
268
  // `peach open 8081` run from ~/contrast against a 3pc metro), so scanning cwd for
269
269
  // font declarations finds nothing.
@@ -281,6 +281,9 @@ export async function resolveMetroProjectRoot(
281
281
  const timer = setTimeout(() => controller.abort(), opts?.timeoutMs ?? 1500)
282
282
  let servedManifest = false
283
283
  try {
284
+ const status = await fetch(`${origin}/status`, { signal: controller.signal })
285
+ const advertisedRoot = status.headers.get('X-React-Native-Project-Root')
286
+ if (status.ok && advertisedRoot) return decodeURI(advertisedRoot)
284
287
  const res = await fetch(`${origin}/`, {
285
288
  headers: { 'expo-platform': 'ios', Accept: 'application/json' },
286
289
  signal: controller.signal,
@@ -90,7 +90,7 @@ import {
90
90
  worldForBridgePort,
91
91
  type BridgeSimInfo,
92
92
  } from '../ws-bridge'
93
- import { inspectDescribe, inspectWaitReady } from './inspect/core'
93
+ import { inspectDescribe, inspectReadySnapshot, waitReadyReason } from './inspect/core'
94
94
  import { isAgentEnv } from './inspect/env'
95
95
  import type { DriverId, DriverLaunchResult } from '../drivers/types'
96
96
 
@@ -917,6 +917,7 @@ export async function buildShellUrl(
917
917
  // fallback typeface, which then reads as a conformance diff.
918
918
  const project =
919
919
  opts.project ?? (await resolveAppProjectForBundleUrl(bundleUrlOf(resolved)))
920
+ const expoRoot = project?.appDir ?? null
920
921
  const stamped = stampNativeFirebaseConfig(
921
922
  stampAppCatalog(
922
923
  await stampAppSplash(await stampAppFonts(resolved, project), project),
@@ -924,6 +925,20 @@ export async function buildShellUrl(
924
925
  ),
925
926
  project,
926
927
  )
928
+ const expoStamped = new URL(stamped)
929
+ if (expoRoot) {
930
+ const world = resolveBridgeWorld()
931
+ const port = resolveBridgePortForPin({
932
+ baseUrl,
933
+ wsPort: world.bridgePort,
934
+ explicitPort: false,
935
+ })
936
+ if (port.error) throw new Error(port.error)
937
+ expoStamped.searchParams.set(
938
+ 'expoSwift',
939
+ `http://localhost:${port.port}/__peach/expo-swift?root=${encodeURIComponent(expoRoot)}`,
940
+ )
941
+ }
927
942
  if (
928
943
  !opts.device &&
929
944
  !opts.proof &&
@@ -933,8 +948,8 @@ export async function buildShellUrl(
933
948
  !opts.env?.length &&
934
949
  !opts.dotEnv
935
950
  )
936
- return stamped
937
- const url = new URL(stamped)
951
+ return expoStamped.toString()
952
+ const url = expoStamped
938
953
  if (opts.dotEnv) url.searchParams.set('dotEnv', JSON.stringify(opts.dotEnv))
939
954
  if (opts.device) url.searchParams.set('device', opts.device)
940
955
  if (opts.posture) url.searchParams.set('posture', opts.posture)
@@ -970,6 +985,7 @@ function deriveCurrentSimBaseUrl(
970
985
  url.searchParams.delete('appSplash')
971
986
  url.searchParams.delete('appCatalog')
972
987
  url.searchParams.delete('nativeFirebase')
988
+ url.searchParams.delete('expoSwift')
973
989
  return url.toString()
974
990
  } catch {
975
991
  return fallback
@@ -1346,24 +1362,16 @@ async function maybeDescribeAfterOpen(wsPort: number, simId: string, args: strin
1346
1362
  })
1347
1363
  try {
1348
1364
  if (noDescribe) return
1349
- let readyProbe: Awaited<ReturnType<typeof inspectWaitReady>> | null = null
1365
+ // the tree just held still, so one probe says whether the app is up
1366
+ let readyProbe: Awaited<ReturnType<typeof inspectReadySnapshot>> | null = null
1350
1367
  try {
1351
- readyProbe = await inspectWaitReady(bridge, 1)
1368
+ readyProbe = await inspectReadySnapshot(bridge, 3000)
1352
1369
  } catch {}
1353
- if (readyProbe) {
1354
- if (!readyProbe.ready) {
1355
- const reason = readyProbe.loadingText
1356
- ? `still showing "${readyProbe.loadingText}"`
1357
- : readyProbe.flag !== true
1358
- ? 'guest ready event has not fired'
1359
- : readyProbe.targets <= 0
1360
- ? 'no visible app content is inspectable yet'
1361
- : 'app tree is still stabilizing'
1362
- console.log(
1363
- ` app still loading: ${reason} (nodes: ${readyProbe.nodes}, targets: ${readyProbe.targets})`,
1364
- )
1365
- console.log(' before interacting, run: peach wait ready --max-ms 120000')
1366
- }
1370
+ if (readyProbe && !readyProbe.ready) {
1371
+ console.log(
1372
+ ` app still loading: ${waitReadyReason(readyProbe)} (nodes: ${readyProbe.nodes}, targets: ${readyProbe.targets})`,
1373
+ )
1374
+ console.log(' before interacting, run: peach wait ready --max-ms 120000')
1367
1375
  }
1368
1376
  const result = await inspectDescribe(bridge, {
1369
1377
  describe: true,
@@ -14,12 +14,14 @@
14
14
  import {
15
15
  HEADLESS_TENANT_FETCH_HUNG_MARKER,
16
16
  PUBLIC_NO_NETWORK_MARKER,
17
+ readyProbeIsReady,
17
18
  type SemanticWaitResult,
18
19
  type WsBridgeCommand,
19
20
  } from '../../../src/bridge-contract'
20
21
  export {
21
22
  readyProbeHasContent,
22
23
  readyProbeHasTargetContent,
24
+ readyProbeIsReady,
23
25
  } from '../../../src/bridge-contract'
24
26
  import type {
25
27
  SimSemanticNode,
@@ -1177,8 +1179,6 @@ export interface WaitReadyOptions {
1177
1179
  progressIntervalMs?: number
1178
1180
  onProgress?: (status: WaitReadyStatus) => void
1179
1181
  simId?: string
1180
- // how long the node count must hold still; see SemanticWaitCondition.
1181
- stableMs?: number
1182
1182
  }
1183
1183
 
1184
1184
  export function waitReadyReason(
@@ -1239,22 +1239,60 @@ export async function inspectWaitReady(
1239
1239
  timeoutMs = 20_000,
1240
1240
  options: WaitReadyOptions = {},
1241
1241
  ): Promise<WaitReadyStatus> {
1242
- const start = Date.now()
1242
+ const result = await sendReadyWait(bridge, timeoutMs, timeoutMs + 1_000, options.simId)
1243
+ // a guest app that redboxes still paints: its ErrorBoundary is a real tree
1244
+ // with real nodes, so the engine's own readiness condition matches and every
1245
+ // caller reads exit 0 as "the app is up". an app that has errored is not
1246
+ // ready, so a guest error is disqualifying here rather than merely printed
1247
+ // on the failure path. `errors` (a console.error count) is deliberately not
1248
+ // used because apps can log errors benignly. `suppressedEntryError` remains
1249
+ // excluded because it never gates readiness by contract.
1250
+ const status = waitReadyStatus(result, result.matched && !result.probe?.externalError)
1251
+ if (!status.ready && options.onProgress) options.onProgress(status)
1252
+ return status
1253
+ }
1254
+
1255
+ // how long one snapshot request may take to cross the bridge and read once
1256
+ const READY_SNAPSHOT_SEND_TIMEOUT_MS = 2_000
1257
+
1258
+ // one readiness probe, judged on its own: no stability window, since the
1259
+ // caller has already let the app settle. any runtime answers it: a wait that
1260
+ // times out still returns its last probe. only a wait that read exactly once
1261
+ // proves anything. none says nothing (an older runtime whose deadline passed
1262
+ // before its first read), and a second read could report a tree that mounted
1263
+ // after the settle, so either asks again until budgetMs runs out; the first
1264
+ // request goes out even with no budget left.
1265
+ export async function inspectReadySnapshot(
1266
+ bridge: InspectBridge,
1267
+ budgetMs: number,
1268
+ simId?: string,
1269
+ ): Promise<WaitReadyStatus> {
1270
+ const deadline = Date.now() + budgetMs
1243
1271
  let result: SemanticWaitResult
1272
+ do {
1273
+ result = await sendReadyWait(bridge, 1, READY_SNAPSHOT_SEND_TIMEOUT_MS, simId)
1274
+ } while (result.polls !== 1 && !result.error && Date.now() < deadline)
1275
+ return waitReadyStatus(
1276
+ result,
1277
+ result.polls === 1 && result.probe ? readyProbeIsReady(result.probe) : false,
1278
+ )
1279
+ }
1280
+
1281
+ async function sendReadyWait(
1282
+ bridge: InspectBridge,
1283
+ timeoutMs: number,
1284
+ sendTimeoutMs: number,
1285
+ simId: string | undefined,
1286
+ ): Promise<SemanticWaitResult> {
1287
+ const start = Date.now()
1244
1288
  try {
1245
- result = await bridge.send(
1289
+ return await bridge.send(
1246
1290
  {
1247
1291
  type: 'waitFor',
1248
- ...(options.simId ? { simId: options.simId } : {}),
1249
- waitForOptions: {
1250
- condition: {
1251
- type: 'ready',
1252
- ...(options.stableMs === undefined ? {} : { stableMs: options.stableMs }),
1253
- },
1254
- timeoutMs,
1255
- },
1292
+ ...(simId ? { simId } : {}),
1293
+ waitForOptions: { condition: { type: 'ready' }, timeoutMs },
1256
1294
  },
1257
- { timeoutMs: timeoutMs + 1_000 },
1295
+ { timeoutMs: sendTimeoutMs },
1258
1296
  )
1259
1297
  } catch (error) {
1260
1298
  const bridgeError = error instanceof Error ? error.message : String(error)
@@ -1264,24 +1302,14 @@ export async function inspectWaitReady(
1264
1302
  ) {
1265
1303
  throw error
1266
1304
  }
1267
- result = {
1268
- matched: false,
1269
- elapsedMs: Date.now() - start,
1270
- polls: 0,
1271
- error: bridgeError,
1272
- }
1305
+ return { matched: false, elapsedMs: Date.now() - start, polls: 0, error: bridgeError }
1273
1306
  }
1307
+ }
1308
+
1309
+ function waitReadyStatus(result: SemanticWaitResult, ready: boolean): WaitReadyStatus {
1274
1310
  const probe = result.probe
1275
- // a guest app that redboxes still paints: its ErrorBoundary is a real tree
1276
- // with real nodes, so the engine's own readiness condition matches and every
1277
- // caller reads exit 0 as "the app is up". an app that has errored is not
1278
- // ready, so a guest error is disqualifying here rather than merely printed
1279
- // on the failure path. `errors` (a console.error count) is deliberately not
1280
- // used because apps can log errors benignly. `suppressedEntryError` remains
1281
- // excluded because it never gates readiness by contract.
1282
- const externalError = probe?.externalError ?? ''
1283
- const status: WaitReadyStatus = {
1284
- ready: result.matched && !externalError,
1311
+ return {
1312
+ ready,
1285
1313
  elapsedMs: result.elapsedMs,
1286
1314
  nodes: probe?.nodes ?? 0,
1287
1315
  targets: probe?.targets ?? 0,
@@ -1292,13 +1320,11 @@ export async function inspectWaitReady(
1292
1320
  loadingText: probe?.loadingText ?? '',
1293
1321
  externalReady: probe?.externalReady ?? null,
1294
1322
  externalStatus: probe?.externalStatus ?? '',
1295
- externalError,
1323
+ externalError: probe?.externalError ?? '',
1296
1324
  suppressedEntryError: probe?.suppressedEntryError ?? '',
1297
1325
  errors: probe?.errors ?? 0,
1298
1326
  bridgeError: result.error ?? '',
1299
1327
  }
1300
- if (!status.ready && options.onProgress) options.onProgress(status)
1301
- return status
1302
1328
  }
1303
1329
 
1304
1330
  // `wait selector` — block until a node with the given testID is present and
@@ -1,4 +1,5 @@
1
1
  import {
2
+ inspectReadySnapshot,
2
3
  inspectWaitReady,
3
4
  type InspectBridge,
4
5
  type WaitReadyOptions,
@@ -222,36 +223,41 @@ export type SettledWaitReady = {
222
223
  // the last readiness read: the settled tree once the app went idle,
223
224
  // otherwise the read that ended the wait.
224
225
  status: WaitReadyStatus
225
- // null when the app never became ready.
226
+ // the last settle; null when the app never became ready.
226
227
  idle: WaitForSootsimIdleResult | null
227
228
  elapsedMs: number
228
229
  }
229
230
 
230
- // `wait ready`, let the app settle, then read readiness again: the app keeps
231
- // mounting while it settles, so the first tree that counted as ready
232
- // undercounts what the caller is about to see. the settle already proved the
233
- // tree holds still, so the re-read asks for no stability window and answers
234
- // on its first poll unless the app stopped being ready.
231
+ // `wait ready`, let the app settle, then read the settled tree once: the app
232
+ // keeps mounting while it settles, so the first tree that counted as ready
233
+ // undercounts what the caller is about to see. a settled app that is no
234
+ // longer ready (it reloaded, or went back to loading) has not proven the tree
235
+ // it shows next, so that starts the wait over. one deadline bounds it all.
235
236
  export async function waitReadyAndSettle(
236
237
  bridge: InspectBridge,
237
238
  timeoutMs: number,
238
239
  options: WaitReadyOptions = {},
239
240
  ): Promise<SettledWaitReady> {
240
241
  const start = Date.now()
241
- const ready = await inspectWaitReady(bridge, timeoutMs, options)
242
- if (!ready.ready) return { status: ready, idle: null, elapsedMs: Date.now() - start }
243
- const idle = await waitForSootsimIdle({
244
- bridge,
245
- simId: options.simId,
246
- maxMs: Math.max(100, Math.min(10_000, timeoutMs - (Date.now() - start))),
247
- pollMs: 32,
248
- stablePolls: 2,
242
+ const remaining = () => Math.max(0, start + timeoutMs - Date.now())
243
+ const result = (status: WaitReadyStatus, idle: WaitForSootsimIdleResult | null) => ({
244
+ status,
245
+ idle,
246
+ elapsedMs: Date.now() - start,
249
247
  })
250
- if (!idle.settled) return { status: ready, idle, elapsedMs: Date.now() - start }
251
- const settled = await inspectWaitReady(
252
- bridge,
253
- Math.max(1_000, timeoutMs - (Date.now() - start)),
254
- { ...(options.simId ? { simId: options.simId } : {}), stableMs: 0 },
255
- )
256
- return { status: settled, idle, elapsedMs: Date.now() - start }
248
+ let idle: WaitForSootsimIdleResult | null = null
249
+ for (;;) {
250
+ const ready = await inspectWaitReady(bridge, remaining(), options)
251
+ if (!ready.ready) return result(ready, idle)
252
+ idle = await waitForSootsimIdle({
253
+ bridge,
254
+ simId: options.simId,
255
+ maxMs: Math.min(10_000, remaining()),
256
+ pollMs: 32,
257
+ stablePolls: 2,
258
+ })
259
+ if (!idle.settled) return result(ready, idle)
260
+ const settled = await inspectReadySnapshot(bridge, remaining(), options.simId)
261
+ if (settled.ready || remaining() === 0) return result(settled, idle)
262
+ }
257
263
  }
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.663 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.665 | (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.663 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.665 | (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.663 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.665 | (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.663 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.665 | (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.663 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.665 | (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.663 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.665 | (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.663 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.665 | (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/beta.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.663 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.665 | (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/beta.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.663 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.665 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/beta.ts
4
4
  var IS_BETA = true;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.663 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.665 | (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.663 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.665 | (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;
@@ -374,12 +374,8 @@ function parseSemanticWaitOptions(value) {
374
374
  const type = stringValue(condition.type, "waitFor.condition.type");
375
375
  const timeoutMs = semanticWaitTimeout(input, "waitFor");
376
376
  if (type === "ready") {
377
- const stableMs = optionalNumber(condition, "stableMs", "waitFor.condition");
378
- if (stableMs !== void 0 && stableMs < 0) {
379
- validationFailure("waitFor.condition.stableMs", "must be at least 0");
380
- }
381
377
  return {
382
- condition: { type, ...stableMs === void 0 ? {} : { stableMs } },
378
+ condition: { type },
383
379
  ...timeoutMs === void 0 ? {} : { timeoutMs }
384
380
  };
385
381
  }
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.663 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.665 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/bridge-contract.ts
4
4
  var SIM_LONG_PRESS_MAX_MS = 5e3;
@@ -338,12 +338,8 @@ function parseSemanticWaitOptions(value) {
338
338
  const type = stringValue(condition.type, "waitFor.condition.type");
339
339
  const timeoutMs = semanticWaitTimeout(input, "waitFor");
340
340
  if (type === "ready") {
341
- const stableMs = optionalNumber(condition, "stableMs", "waitFor.condition");
342
- if (stableMs !== void 0 && stableMs < 0) {
343
- validationFailure("waitFor.condition.stableMs", "must be at least 0");
344
- }
345
341
  return {
346
- condition: { type, ...stableMs === void 0 ? {} : { stableMs } },
342
+ condition: { type },
347
343
  ...timeoutMs === void 0 ? {} : { timeoutMs }
348
344
  };
349
345
  }
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.663 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.665 | (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;
@@ -33,6 +33,7 @@ __export(bridge_contract_exports, {
33
33
  performStepRefusal: () => performStepRefusal,
34
34
  readyProbeHasContent: () => readyProbeHasContent,
35
35
  readyProbeHasTargetContent: () => readyProbeHasTargetContent,
36
+ readyProbeIsReady: () => readyProbeIsReady,
36
37
  refusedPerformStepReason: () => refusedPerformStepReason
37
38
  });
38
39
  module.exports = __toCommonJS(bridge_contract_exports);
@@ -113,6 +114,9 @@ function readyProbeHasContent(probe) {
113
114
  function readyProbeHasTargetContent(probe) {
114
115
  return probe.liveFrameActive || probe.targets > 0;
115
116
  }
117
+ function readyProbeIsReady(probe) {
118
+ return (probe.flag === true ? readyProbeHasContent(probe) : readyProbeHasTargetContent(probe)) && probe.externalReady !== false && !probe.externalError && !probe.loadingText;
119
+ }
116
120
  // Annotate the CommonJS export names for ESM import in node:
117
121
  0 && (module.exports = {
118
122
  HEADLESS_TENANT_FETCH_HUNG_MARKER,
@@ -126,5 +130,6 @@ function readyProbeHasTargetContent(probe) {
126
130
  performStepRefusal,
127
131
  readyProbeHasContent,
128
132
  readyProbeHasTargetContent,
133
+ readyProbeIsReady,
129
134
  refusedPerformStepReason
130
135
  });
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.663 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.665 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/bridge-contract.ts
4
4
  var REFUSED_PERFORM_STEP_TYPES = [
@@ -78,6 +78,9 @@ function readyProbeHasContent(probe) {
78
78
  function readyProbeHasTargetContent(probe) {
79
79
  return probe.liveFrameActive || probe.targets > 0;
80
80
  }
81
+ function readyProbeIsReady(probe) {
82
+ return (probe.flag === true ? readyProbeHasContent(probe) : readyProbeHasTargetContent(probe)) && probe.externalReady !== false && !probe.externalError && !probe.loadingText;
83
+ }
81
84
  export {
82
85
  HEADLESS_TENANT_FETCH_HUNG_MARKER,
83
86
  PUBLIC_NO_NETWORK_MARKER,
@@ -90,5 +93,6 @@ export {
90
93
  performStepRefusal,
91
94
  readyProbeHasContent,
92
95
  readyProbeHasTargetContent,
96
+ readyProbeIsReady,
93
97
  refusedPerformStepReason
94
98
  };
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.663 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.665 | (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.663 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.665 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/capture-contract.ts
4
4
  var RNX_SCREEN_CAPTURE_VERSION = 1;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.663 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.665 | (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.663 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.665 | (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.663 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.665 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.663 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.665 | (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.663 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.665 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/cloud-test-args.ts
4
4
  var CLOUD_TEST_ARGS_MAX_COUNT = 32;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.663 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.665 | (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.663 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.665 | (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);
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.663 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.665 | (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.663 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.665 | (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.663 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.665 | (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.663 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.665 | (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;