peachsim 0.1.527 → 0.1.570

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 (158) hide show
  1. package/README.md +26 -26
  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 +57 -5
  7. package/cli/cloud-client.ts +273 -6
  8. package/cli/cloud-dispatch.ts +22 -2
  9. package/cli/cloud-session.ts +60 -4
  10. package/cli/cloud-test-tunnel.ts +354 -0
  11. package/cli/commands/box/bundle-plane.ts +2 -2
  12. package/cli/commands/box/checkout-plane.ts +1 -1
  13. package/cli/commands/box/rnx-command.ts +2 -2
  14. package/cli/commands/box/search-command.ts +2 -2
  15. package/cli/commands/box.ts +8 -8
  16. package/cli/commands/compat.ts +1 -1
  17. package/cli/commands/control.ts +22 -3
  18. package/cli/commands/detox.ts +1 -1
  19. package/cli/commands/device.ts +1 -1
  20. package/cli/commands/diagnose.ts +1 -1
  21. package/cli/commands/film.ts +1 -1
  22. package/cli/commands/flow.ts +60 -3
  23. package/cli/commands/inspect/actions.ts +1 -1
  24. package/cli/commands/inspect/core.ts +6 -7
  25. package/cli/commands/inspect/find.ts +2 -1
  26. package/cli/commands/inspect/resolve-target.ts +5 -3
  27. package/cli/commands/inspect/shared.ts +1 -1
  28. package/cli/commands/inspect/wait-event.ts +1 -1
  29. package/cli/commands/inspect/wait-selector.ts +15 -4
  30. package/cli/commands/inspect.ts +19 -14
  31. package/cli/commands/maestro.ts +464 -43
  32. package/cli/commands/permissions.ts +1 -1
  33. package/cli/commands/platform.ts +205 -8
  34. package/cli/commands/preview.ts +2 -2
  35. package/cli/commands/record.ts +2 -2
  36. package/cli/commands/report-issue.ts +2 -2
  37. package/cli/commands/screenshot-layers.ts +1 -1
  38. package/cli/commands/screenshot.ts +1 -1
  39. package/cli/commands/setup.ts +2 -2
  40. package/cli/commands/test.ts +440 -115
  41. package/cli/commands/three-mode.ts +1 -1
  42. package/cli/commands/upload.ts +5 -5
  43. package/cli/commands/what-happened.ts +1 -1
  44. package/cli/desktop-companion.ts +3 -3
  45. package/cli/drivers/playwright-sim-host.ts +2 -2
  46. package/cli/flow-file.ts +7 -0
  47. package/cli/flow-live-status.ts +65 -8
  48. package/cli/help-core.ts +3 -3
  49. package/cli/hidden-runtime-alias.ts +1 -1
  50. package/cli/maestro-report.ts +178 -0
  51. package/cli/main.ts +1 -1
  52. package/cli/outbound-endpoints.ts +185 -136
  53. package/cli/parse-args.ts +1 -1
  54. package/cli/registry.ts +1 -1
  55. package/cli/resolve-assets.ts +1 -1
  56. package/cli/run-registry.ts +8 -0
  57. package/cli/send-to-box.ts +4 -4
  58. package/cli/ws-bridge.ts +3 -5
  59. package/detox/expectations.ts +1 -1
  60. package/detox/gestures.ts +2 -2
  61. package/detox/index.ts +71 -29
  62. package/detox/jest-preset.cjs +3 -3
  63. package/detox/jest-setup-after-env.cjs +1 -1
  64. package/dist-lib/agent-daemon-client.cjs +2 -2
  65. package/dist-lib/agent-events.cjs +1 -1
  66. package/dist-lib/agent-identity.cjs +1 -1
  67. package/dist-lib/agent-sessions.cjs +2 -2
  68. package/dist-lib/attached-projects.cjs +2 -2
  69. package/dist-lib/auth/shared-session.cjs +1 -1
  70. package/dist-lib/backend-origin.cjs +1 -1
  71. package/dist-lib/beta.cjs +1 -1
  72. package/dist-lib/beta.mjs +1 -1
  73. package/dist-lib/bridge-constants.cjs +1 -1
  74. package/dist-lib/bridge-contract-input.cjs +1 -1
  75. package/dist-lib/bridge-contract-input.mjs +1 -1
  76. package/dist-lib/bridge-contract.cjs +1 -1
  77. package/dist-lib/bridge-contract.mjs +1 -1
  78. package/dist-lib/capture-contract.cjs +94 -3
  79. package/dist-lib/capture-contract.mjs +93 -3
  80. package/dist-lib/cli-constants.cjs +1 -1
  81. package/dist-lib/cloud-contract.cjs +1 -1
  82. package/dist-lib/cloud-contract.mjs +1 -1
  83. package/dist-lib/cloud.cjs +2 -2
  84. package/dist-lib/cloud.mjs +2 -2
  85. package/dist-lib/config.cjs +1 -1
  86. package/dist-lib/detox/index.cjs +106 -25
  87. package/dist-lib/detox/jest-preset.cjs +3 -3
  88. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  89. package/dist-lib/home-paths.cjs +1 -1
  90. package/dist-lib/host/bridge-host.cjs +137 -61
  91. package/dist-lib/host/fetch-proxy-handler.cjs +28 -2
  92. package/dist-lib/host/fetch-proxy-overrides.cjs +33 -3
  93. package/dist-lib/host/fetch-proxy-overrides.mjs +30 -2
  94. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  95. package/dist-lib/host/websocket-proxy.cjs +1 -1
  96. package/dist-lib/index.cjs +209 -16
  97. package/dist-lib/jump-to-source-babel.cjs +1 -1
  98. package/dist-lib/jump-to-source-native.cjs +1 -1
  99. package/dist-lib/menu.cjs +2 -2
  100. package/dist-lib/menu.mjs +2 -2
  101. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  102. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  103. package/dist-lib/metro-production-bundle.cjs +3 -3
  104. package/dist-lib/metro-production-bundle.mjs +3 -3
  105. package/dist-lib/metro.cjs +8 -8
  106. package/dist-lib/profiles.cjs +1 -1
  107. package/dist-lib/public-brand.cjs +2 -2
  108. package/dist-lib/react-native-host-modules.cjs +1 -1
  109. package/dist-lib/react-native-host-modules.mjs +1 -1
  110. package/dist-lib/render-mode.cjs +1 -1
  111. package/dist-lib/scripts/dev-server-scanner.cjs +31 -15
  112. package/dist-lib/sdk.cjs +3 -2
  113. package/dist-lib/sdk.mjs +3 -2
  114. package/dist-lib/skills.cjs +362 -163
  115. package/dist-lib/swift-submit.cjs +1 -1
  116. package/dist-lib/swift-submit.mjs +1 -1
  117. package/dist-lib/swift.cjs +583 -16
  118. package/dist-lib/vite.cjs +29 -14
  119. package/package.json +9 -9
  120. package/scripts/dev-server-scanner.ts +41 -14
  121. package/scripts/optional-demo-registry.ts +3 -3
  122. package/skills/contrast/SKILL.md +9 -9
  123. package/skills/rnx-debug/SKILL.md +14 -4
  124. package/skills/rnx-perf/SKILL.md +5 -5
  125. package/skills/rnx-setup/SKILL.md +11 -11
  126. package/skills/rnx-test/SKILL.md +11 -11
  127. package/skills/rnx-visual/SKILL.md +6 -6
  128. package/src/agent-sessions.ts +3 -3
  129. package/src/attached-projects.ts +1 -1
  130. package/src/bridge-contract-input.ts +1 -1
  131. package/src/bridge-contract.ts +23 -5
  132. package/src/capture-contract.ts +145 -3
  133. package/src/config.ts +2 -2
  134. package/src/connect.ts +2 -2
  135. package/src/home-paths.ts +3 -3
  136. package/src/host/bridge-host.ts +2 -2
  137. package/src/host/fetch-proxy-handler.ts +4 -3
  138. package/src/host/fetch-proxy-overrides.ts +41 -0
  139. package/src/host/plane-host.ts +2 -2
  140. package/src/index.ts +3 -0
  141. package/src/metro-plugin.ts +4 -4
  142. package/src/metro-production-bundle.ts +2 -2
  143. package/src/native-seam-manifest.ts +6 -6
  144. package/src/public-brand.ts +1 -1
  145. package/src/rolldown-plugin-swift.ts +8 -5
  146. package/src/runtime-assets.ts +8 -6
  147. package/src/screenshots/frame-compose.ts +24 -56
  148. package/src/screenshots/org-deck.ts +1 -1
  149. package/src/screenshots/registry.ts +4 -4
  150. package/src/screenshots/schema.ts +1 -1
  151. package/src/sdk.ts +2 -2
  152. package/src/settings-bundle.ts +1 -1
  153. package/src/sim-client.ts +2 -4
  154. package/src/skills/builtin/compat-check.ts +1 -1
  155. package/src/vite-plugin-one.ts +1 -1
  156. package/src/vite-plugin-swift.ts +11 -6
  157. package/src/vite-plugin.ts +30 -17
  158. package/src/worklets-babel.ts +2 -2
package/cli/parse-args.ts CHANGED
@@ -77,7 +77,7 @@ export const HYPHENATED_GROUPING_VERBS = new Set(['do', 'get', 'wait', 'shell',
77
77
 
78
78
  // known commands — if first positional arg matches, it's a command.
79
79
  // every entry here must have a `cliCommandMetas` entry in
80
- // `packages/rnx-skills/src/cli/meta.ts` (or sit on the internal
80
+ // `packages/peach-skills/src/cli/meta.ts` (or sit on the internal
81
81
  // allowlist in `test/sootsimCliRegistry.test.ts`), so help, website docs,
82
82
  // and agent skills can never drift from what the parser accepts.
83
83
  export const COMMANDS = new Set(RNX_NODE_COMMAND_NAMES)
package/cli/registry.ts CHANGED
@@ -1,2 +1,2 @@
1
- export * from '@rnx/skills/cli'
1
+ export * from '@peach/skills/cli'
2
2
  export * from './command-registry'
@@ -13,7 +13,7 @@ import { rnxPublicBrand } from '../src/public-brand'
13
13
  // when running from source, this file lives at cli/resolve-assets.ts
14
14
  const thisDir = dirname(fileURLToPath(import.meta.url))
15
15
 
16
- // walk up to find package root: look for package.json with the rnxsim package name
16
+ // walk up to find package root: look for package.json with the peachsim package name
17
17
  function findPackageRoot(): string {
18
18
  let dir = thisDir
19
19
  for (let i = 0; i < 5; i++) {
@@ -170,6 +170,14 @@ export async function registerRun(args: RegisterRunArgs): Promise<RegisteredRun
170
170
  const repo = args.repo ?? (auth.kind === 'github' ? auth.repo : null) ?? provenance.repo
171
171
 
172
172
  try {
173
+ // fault injection for the registration-failure control, shared by both
174
+ // drivers through this choke: fail exactly like a dead endpoint,
175
+ // without touching the network. downstream (warn shape, null return,
176
+ // require-registration exit) is byte-identical to a real failure.
177
+ if (process.env.RNX_INJECT_REGISTRATION_FAILURE === '1') {
178
+ console.warn(' warn: run registration failed: injected failure')
179
+ return null
180
+ }
173
181
  const res = await fetch(`${args.origin.replace(/\/$/, '')}/api/sootsim/test-runs`, {
174
182
  method: 'POST',
175
183
  headers: {
@@ -5,16 +5,16 @@
5
5
  // it directly; both hand it a way to run a bridge command against the sim that
6
6
  // is running the app, and both get the same simulator id and URL back.
7
7
 
8
- import { isStorageSnapshot } from '../../sootsim-engine/src/preview/storage-snapshot-contract.ts'
8
+ import { isStorageSnapshot } from '../../peach-engine/src/preview/storage-snapshot-contract.ts'
9
9
  import {
10
10
  DEFAULT_DEVICE_BY_PLATFORM,
11
11
  getRuntimePlatform,
12
12
  listDeviceModels,
13
- } from '../../sootsim-engine/src/settings/devices.ts'
13
+ } from '../../peach-engine/src/settings/devices.ts'
14
14
  import { toRNXProductionBundleUrl } from '../src/metro-production-bundle.ts'
15
15
  import { createCloudBox } from './cloud-client.ts'
16
- import type { StorageSnapshot } from '../../sootsim-engine/src/preview/storage-snapshot-contract.ts'
17
- import type { DeviceModel } from '../../sootsim-engine/src/settings/devices.ts'
16
+ import type { StorageSnapshot } from '../../peach-engine/src/preview/storage-snapshot-contract.ts'
17
+ import type { DeviceModel } from '../../peach-engine/src/settings/devices.ts'
18
18
  import type { BridgeCommandType } from '../src/bridge-contract.ts'
19
19
 
20
20
  export interface SendToBoxOptions {
package/cli/ws-bridge.ts CHANGED
@@ -30,6 +30,7 @@ import {
30
30
  readCurrentSimId,
31
31
  saveCurrentSimId,
32
32
  } from './current-sim'
33
+ import type { WsBridgeCommand } from '../src/bridge-contract'
33
34
 
34
35
  // --- bridge world resolution ---------------------------------------------
35
36
  //
@@ -319,10 +320,7 @@ export interface WsBridge {
319
320
  // `opts.timeoutMs` overrides the bridge-wide commandTimeoutMs for a single
320
321
  // call — used for fast readiness probes so a dead/unresponsive sim fails in
321
322
  // seconds instead of stalling the full command timeout.
322
- send(
323
- cmd: { type: string; [k: string]: any },
324
- opts?: { timeoutMs?: number },
325
- ): Promise<any>
323
+ send(cmd: WsBridgeCommand, opts?: { timeoutMs?: number }): Promise<any>
326
324
  listSims(): Promise<BridgeSimInfo[]>
327
325
  // a guest reload retires the socket and the same page reconnects under a new
328
326
  // or restored id. a caller that pins an id resolves that target here; null
@@ -993,7 +991,7 @@ export async function evalInBridge<T = unknown>(
993
991
  code: string,
994
992
  opts: { acquireLock?: boolean } = {},
995
993
  ): Promise<T> {
996
- const payload: { type: string; [k: string]: any } = { type: 'evaluate', code }
994
+ const payload: WsBridgeCommand = { type: 'evaluate', code }
997
995
  if (opts.acquireLock) payload.acquireLock = true
998
996
  return bridge.send(payload) as Promise<T>
999
997
  }
@@ -5,7 +5,7 @@
5
5
  import { dragScrollNode } from './gestures'
6
6
  import type { SootSimElement } from './element-types'
7
7
  import type { Matcher } from './matchers'
8
- import type { SootSimNodeInfo } from '@rnx/globals'
8
+ import type { SootSimNodeInfo } from '@peach/globals'
9
9
  import type { Page } from 'playwright'
10
10
 
11
11
  type FindFn = (matcher: Matcher) => Promise<any>
package/detox/gestures.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { pollUntil } from '../src/poll-until'
2
- // pull in @rnx/globals so window.__sootsimTest and
2
+ // pull in @peach/globals so window.__sootsimTest and
3
3
  // window.__sootsimFindInteractiveCanvas are typed inside page.evaluate
4
4
  // bodies. all sootsim-namespaced globals are declared canonically there —
5
5
  // add new entries to packages/rnx-globals/src/index.ts, not here.
6
- import type {} from '@rnx/globals'
6
+ import type {} from '@peach/globals'
7
7
  import type { Page } from 'playwright'
8
8
 
9
9
  // shape of the surface-metrics bridge response we care about. the bridge
package/detox/index.ts CHANGED
@@ -9,7 +9,8 @@ import { launchReapedChromeScoped } from '../../../scripts/lib/reap-browser'
9
9
  import {
10
10
  readBrowserCompositeCaptureRequest,
11
11
  resolveBrowserCompositeCdpClip,
12
- } from '../../sootsim-engine/src/capture/browser-composite'
12
+ } from '../../peach-engine/src/capture/browser-composite'
13
+ import { isAppBootTargetUrl } from '../../peach-engine/src/render-worker/app-load'
13
14
  import { pollUntil } from '../src/poll-until'
14
15
  import {
15
16
  createExpect,
@@ -177,9 +178,22 @@ async function waitForSootsimSurfaceMetrics(page: Page, timeout = 30000): Promis
177
178
  )
178
179
  }
179
180
 
180
- async function waitForSootsimShellReady(page: Page, timeout = 30000): Promise<void> {
181
+ export async function waitForSootsimShellReady(
182
+ page: Page,
183
+ timeout = 30000,
184
+ ): Promise<void> {
185
+ // home boots paint shell content through the shell worker; app boots
186
+ // (direct /rn bundle urls, harness fixtures, deep links) paint through
187
+ // the compositor and shellHost.firstContentPainted never settles. classify
188
+ // with the engine's own boot-target predicate — the same one the shell
189
+ // worker and SootSimCanvas agree on — and await the matching boundary:
190
+ // shell first paint for home boots, the latched boot-stage painted
191
+ // receipt (stamped once tenant, shell, and compositor all present) for
192
+ // app boots.
193
+ // exported for the focused regression test.
194
+ const expectsInitialAppPaint = isAppBootTargetUrl(page.url())
181
195
  try {
182
- await page.evaluate(async (timeoutMs) => {
196
+ await page.evaluate(async ({ timeoutMs, expectsInitialAppPaint }) => {
183
197
  type ShellHostReadyBridge = {
184
198
  ready?: Promise<unknown>
185
199
  firstContentPainted?: Promise<unknown>
@@ -208,9 +222,6 @@ async function waitForSootsimShellReady(page: Page, timeout = 30000): Promise<vo
208
222
 
209
223
  const isTenantOnly =
210
224
  new URL(window.location.href).searchParams.get('renderMode') === 'tenant-only'
211
- const currentUrl = new URL(window.location.href)
212
- const expectsInitialAppPaint =
213
- currentUrl.searchParams.has('test') || currentUrl.pathname.includes('/app/')
214
225
  let shellHost: ShellHostReadyBridge | null = null
215
226
  while (Date.now() < deadline) {
216
227
  const candidate = (window as any).SootSim?.bridges?.shellHost
@@ -229,32 +240,63 @@ async function waitForSootsimShellReady(page: Page, timeout = 30000): Promise<vo
229
240
  if (shellHost.ready && typeof shellHost.ready.then === 'function') {
230
241
  await withTimeout('sootsim shell ready', shellHost.ready)
231
242
  }
232
- if (
233
- !expectsInitialAppPaint &&
234
- shellHost.firstContentPainted &&
235
- typeof shellHost.firstContentPainted.then === 'function'
236
- ) {
237
- await withTimeout(
238
- 'sootsim shell first content paint',
239
- shellHost.firstContentPainted,
240
- )
241
- }
243
+ // the tenant restarts into its matching worker artifact when a bundle's
244
+ // actual mode differs from the booted one (production bundle, no url
245
+ // mode params), and any in-flight tenant call then rejects with
246
+ // 'render host disposed'. the receipt poll below is restart-immune
247
+ // (pure dom read), but the transitions drain is a tenant call, so run
248
+ // the paint boundary plus drain together and re-run them when a
249
+ // restart lands mid-sequence. paint timeouts and transition timeouts
250
+ // are genuine failures and are never retried.
251
+ const isRenderHostDisposed = (error: unknown): boolean =>
252
+ error instanceof Error && error.message.includes('render host disposed')
253
+ for (;;) {
254
+ try {
255
+ if (expectsInitialAppPaint) {
256
+ while (Date.now() < deadline) {
257
+ if (
258
+ document.documentElement.dataset.sootsimBootStage === 'painted'
259
+ ) {
260
+ break
261
+ }
262
+ await sleep(50)
263
+ }
264
+ if (document.documentElement.dataset.sootsimBootStage !== 'painted') {
265
+ throw new Error(
266
+ `sootsim app first content paint timed out after ${timeoutMs}ms`,
267
+ )
268
+ }
269
+ } else if (
270
+ shellHost.firstContentPainted &&
271
+ typeof shellHost.firstContentPainted.then === 'function'
272
+ ) {
273
+ await withTimeout(
274
+ 'sootsim shell first content paint',
275
+ shellHost.firstContentPainted,
276
+ )
277
+ }
242
278
 
243
- const waitForScreenTransitions = (window as any).__sootsimTest
244
- ?.waitForScreenTransitions
245
- if (typeof waitForScreenTransitions === 'function') {
246
- const transitionResult = await waitForScreenTransitions({
247
- timeoutMs: Math.min(remaining(), 3000),
248
- settleMs: 64,
249
- startWindowMs: 700,
250
- })
251
- if (transitionResult?.timedOut === true) {
252
- throw new Error(
253
- `sootsim app launch transitions timed out: ${JSON.stringify(transitionResult)}`,
254
- )
279
+ const waitForScreenTransitions = (window as any).__sootsimTest
280
+ ?.waitForScreenTransitions
281
+ if (typeof waitForScreenTransitions === 'function') {
282
+ const transitionResult = await waitForScreenTransitions({
283
+ timeoutMs: Math.min(remaining(), 3000),
284
+ settleMs: 64,
285
+ startWindowMs: 700,
286
+ })
287
+ if (transitionResult?.timedOut === true) {
288
+ throw new Error(
289
+ `sootsim app launch transitions timed out: ${JSON.stringify(transitionResult)}`,
290
+ )
291
+ }
292
+ }
293
+ break
294
+ } catch (error) {
295
+ if (!isRenderHostDisposed(error) || Date.now() >= deadline) throw error
296
+ await sleep(100)
255
297
  }
256
298
  }
257
- }, timeout)
299
+ }, { timeoutMs: timeout, expectsInitialAppPaint })
258
300
  } catch (error) {
259
301
  throw await describeSootsimBridgeFailure(page, error)
260
302
  }
@@ -1,6 +1,6 @@
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
 
@@ -9,8 +9,8 @@ const path = require('path')
9
9
  // resolve the published driver entry so the mapping works from any cwd.
10
10
  // `require.resolve('sootsim/detox')` honours the subpath export in the
11
11
  // sootsim package.json.
12
- const DRIVER_ENTRY = require.resolve('rnxsim/detox')
13
- const PACKAGE_ROOT = path.dirname(require.resolve('rnxsim/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 = {
@@ -15,7 +15,7 @@
15
15
  // setup failure from a behavior failure. no-op unless the run asks for it.
16
16
  require('./record-block-execution.cjs')
17
17
 
18
- const { cleanup } = require('rnxsim/detox')
18
+ const { cleanup } = require('peachsim/detox')
19
19
 
20
20
  afterAll(async () => {
21
21
  await cleanup()
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.570 | (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;
@@ -56,7 +56,7 @@ var import_node_url = require("node:url");
56
56
  var name = "rnx";
57
57
  var rnxPublicBrand = Object.freeze({
58
58
  name,
59
- packageName: "rnxsim",
59
+ packageName: "peachsim",
60
60
  commandName: "rnx",
61
61
  domain: "rnxsim.com",
62
62
  origin: "https://rnxsim.com"
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.570 | (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.570 | (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.570 | (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;
@@ -61,7 +61,7 @@ var import_node_url = require("node:url");
61
61
  var name = "rnx";
62
62
  var rnxPublicBrand = Object.freeze({
63
63
  name,
64
- packageName: "rnxsim",
64
+ packageName: "peachsim",
65
65
  commandName: "rnx",
66
66
  domain: "rnxsim.com",
67
67
  origin: "https://rnxsim.com"
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.570 | (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;
@@ -89,7 +89,7 @@ var init_optional_demo_registry = __esm({
89
89
  import_node_fs = require("node:fs");
90
90
  import_node_path2 = require("node:path");
91
91
  import_node_url = require("node:url");
92
- REGISTRY_REPO_PATH = "packages/sootsim/scripts/demo-app-registry.ts";
92
+ REGISTRY_REPO_PATH = "packages/peachsim/scripts/demo-app-registry.ts";
93
93
  MAX_ANCESTOR_STEPS = 8;
94
94
  cached = null;
95
95
  }
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.570 | (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.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.570 | (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.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.570 | (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.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.570 | (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.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.570 | (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.570 | (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.570 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/bridge-contract.ts
4
4
  var SIM_LONG_PRESS_MAX_MS = 5e3;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.570 | (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.570 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/bridge-contract.ts
4
4
  var REFUSED_PERFORM_STEP_TYPES = [
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.527 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.570 | (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;
@@ -25,6 +25,7 @@ __export(capture_contract_exports, {
25
25
  RNX_SCREEN_CAPTURE_VERSION: () => RNX_SCREEN_CAPTURE_VERSION,
26
26
  isRnxCaptureRegionAtlas: () => isRnxCaptureRegionAtlas,
27
27
  isRnxScreenCapture: () => isRnxScreenCapture,
28
+ packRnxCaptureRegions: () => packRnxCaptureRegions,
28
29
  rnxCaptureRasterRegions: () => rnxCaptureRasterRegions,
29
30
  rnxRasterFrames: () => rnxRasterFrames,
30
31
  selectRnxRasterNodeIds: () => selectRnxRasterNodeIds
@@ -39,7 +40,7 @@ function selectRnxRasterNodeIds(tree) {
39
40
  if (!visibleFrame || visibleFrame.width <= 0 || visibleFrame.height <= 0) continue;
40
41
  if (node.type === "masked-view" || node.type === "text" && typeof node.text === "string" && (new RegExp("\\p{Emoji_Presentation}", "u").test(node.text) || new RegExp("\\uFE0F|\\u200D|\\u20E3|\\p{Emoji_Modifier}|\\p{Regional_Indicator}", "u").test(
41
42
  node.text
42
- )) || node.style._liquidGlass !== void 0 || node.style._liquidGlassEdge !== void 0) {
43
+ )) || node.style._liquidGlass !== void 0 || node.style._liquidGlassEdge !== void 0 || node.style._liquidGlassMaterialTint !== void 0) {
43
44
  unsupported.add(node.nodeId);
44
45
  }
45
46
  }
@@ -138,6 +139,95 @@ function rnxCaptureRasterRegions(capture) {
138
139
  return frame ? [{ key: String(nodeId), nodeId, frame }] : [];
139
140
  });
140
141
  }
142
+ function packRnxCaptureRegions(regions, pixelRatio, surface) {
143
+ const physical = regions.map((region) => {
144
+ const left = Math.floor(region.frame.x * pixelRatio);
145
+ const top = Math.floor(region.frame.y * pixelRatio);
146
+ const right = Math.ceil((region.frame.x + region.frame.width) * pixelRatio);
147
+ const bottom = Math.ceil((region.frame.y + region.frame.height) * pixelRatio);
148
+ if (left < 0 || top < 0 || right > surface.pixelWidth || bottom > surface.pixelHeight || right <= left || bottom <= top) {
149
+ throw new Error(`capture region ${region.key} is outside the app surface`);
150
+ }
151
+ return {
152
+ key: region.key,
153
+ nodeId: region.nodeId,
154
+ frame: {
155
+ x: left / pixelRatio,
156
+ y: top / pixelRatio,
157
+ width: (right - left) / pixelRatio,
158
+ height: (bottom - top) / pixelRatio
159
+ },
160
+ sourceX: left,
161
+ sourceY: top,
162
+ pixelWidth: right - left,
163
+ pixelHeight: bottom - top
164
+ };
165
+ });
166
+ const largestWidth = Math.max(...physical.map((region) => region.pixelWidth));
167
+ const totalArea = physical.reduce(
168
+ (area, region) => area + region.pixelWidth * region.pixelHeight,
169
+ 0
170
+ );
171
+ if (totalArea > 4194304) {
172
+ throw new Error("capture region pixels exceed the atlas budget");
173
+ }
174
+ const candidates = [
175
+ largestWidth,
176
+ Math.max(largestWidth, Math.ceil(Math.sqrt(totalArea))),
177
+ Math.max(largestWidth, surface.pixelWidth),
178
+ 4096
179
+ ];
180
+ const ordered = [...physical].sort(
181
+ (left, right) => right.pixelHeight - left.pixelHeight || right.pixelWidth - left.pixelWidth || left.key.localeCompare(right.key)
182
+ );
183
+ let best = null;
184
+ for (const candidate of [...new Set(candidates)]) {
185
+ if (candidate > 4096) continue;
186
+ let x = 0;
187
+ let y = 0;
188
+ let rowHeight = 0;
189
+ let usedWidth = 0;
190
+ const packed = [];
191
+ for (const region of ordered) {
192
+ if (x > 0 && x + region.pixelWidth > candidate) {
193
+ y += rowHeight;
194
+ x = 0;
195
+ rowHeight = 0;
196
+ }
197
+ packed.push({ ...region, atlasX: x, atlasY: y });
198
+ x += region.pixelWidth;
199
+ rowHeight = Math.max(rowHeight, region.pixelHeight);
200
+ usedWidth = Math.max(usedWidth, x);
201
+ }
202
+ const height2 = y + rowHeight;
203
+ if (height2 > 4096 || usedWidth * height2 > 4194304) continue;
204
+ const result = { width: usedWidth, height: height2, regions: packed };
205
+ if (!best || result.width * result.height < best.width * best.height) best = result;
206
+ }
207
+ if (!best) throw new Error("capture regions cannot fit in one bounded atlas");
208
+ const width = best.width / pixelRatio;
209
+ const height = best.height / pixelRatio;
210
+ return {
211
+ width,
212
+ height,
213
+ pixelWidth: best.width,
214
+ pixelHeight: best.height,
215
+ regions: best.regions,
216
+ atlasRegions: best.regions.map((region) => ({
217
+ key: region.key,
218
+ nodeId: region.nodeId,
219
+ frame: region.frame,
220
+ // a region flush with the far edge is anchored to the logical edge so
221
+ // its source never runs past the atlas by a rounding hair
222
+ source: {
223
+ x: region.atlasX + region.pixelWidth === best.width ? width - region.frame.width : region.atlasX / pixelRatio,
224
+ y: region.atlasY + region.pixelHeight === best.height ? height - region.frame.height : region.atlasY / pixelRatio,
225
+ width: region.frame.width,
226
+ height: region.frame.height
227
+ }
228
+ }))
229
+ };
230
+ }
141
231
  function captureRecord(value) {
142
232
  return value !== null && typeof value === "object" && !Array.isArray(value);
143
233
  }
@@ -193,7 +283,7 @@ function isRnxScreenCapture(value) {
193
283
  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);
194
284
  }
195
285
  function isRnxCaptureRegionAtlas(value) {
196
- 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) {
286
+ 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) {
197
287
  return false;
198
288
  }
199
289
  const scale = value.pixelWidth / value.width;
@@ -214,6 +304,7 @@ function isRnxCaptureRegionAtlas(value) {
214
304
  RNX_SCREEN_CAPTURE_VERSION,
215
305
  isRnxCaptureRegionAtlas,
216
306
  isRnxScreenCapture,
307
+ packRnxCaptureRegions,
217
308
  rnxCaptureRasterRegions,
218
309
  rnxRasterFrames,
219
310
  selectRnxRasterNodeIds