rnxsim 0.1.549 → 0.1.550

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/cli/bridge-flow-runner.ts +26 -0
  2. package/cli/commands/maestro.ts +453 -42
  3. package/cli/commands/test.ts +107 -23
  4. package/cli/flow-file.ts +7 -0
  5. package/cli/maestro-report.ts +178 -0
  6. package/dist-lib/agent-daemon-client.cjs +1 -1
  7. package/dist-lib/agent-events.cjs +1 -1
  8. package/dist-lib/agent-identity.cjs +1 -1
  9. package/dist-lib/agent-sessions.cjs +1 -1
  10. package/dist-lib/attached-projects.cjs +1 -1
  11. package/dist-lib/auth/shared-session.cjs +1 -1
  12. package/dist-lib/backend-origin.cjs +1 -1
  13. package/dist-lib/beta.cjs +1 -1
  14. package/dist-lib/beta.mjs +1 -1
  15. package/dist-lib/bridge-constants.cjs +1 -1
  16. package/dist-lib/bridge-contract-input.cjs +1 -1
  17. package/dist-lib/bridge-contract-input.mjs +1 -1
  18. package/dist-lib/bridge-contract.cjs +1 -1
  19. package/dist-lib/bridge-contract.mjs +1 -1
  20. package/dist-lib/capture-contract.cjs +1 -1
  21. package/dist-lib/capture-contract.mjs +1 -1
  22. package/dist-lib/cli-constants.cjs +1 -1
  23. package/dist-lib/cloud-contract.cjs +1 -1
  24. package/dist-lib/cloud-contract.mjs +1 -1
  25. package/dist-lib/cloud.cjs +1 -1
  26. package/dist-lib/cloud.mjs +1 -1
  27. package/dist-lib/config.cjs +1 -1
  28. package/dist-lib/detox/index.cjs +1 -1
  29. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  30. package/dist-lib/home-paths.cjs +1 -1
  31. package/dist-lib/host/bridge-host.cjs +8 -6
  32. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  33. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  34. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  35. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  36. package/dist-lib/host/websocket-proxy.cjs +1 -1
  37. package/dist-lib/index.cjs +1 -1
  38. package/dist-lib/jump-to-source-babel.cjs +1 -1
  39. package/dist-lib/jump-to-source-native.cjs +1 -1
  40. package/dist-lib/menu.cjs +1 -1
  41. package/dist-lib/menu.mjs +1 -1
  42. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  43. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  44. package/dist-lib/metro-production-bundle.cjs +1 -1
  45. package/dist-lib/metro-production-bundle.mjs +1 -1
  46. package/dist-lib/metro.cjs +1 -1
  47. package/dist-lib/profiles.cjs +1 -1
  48. package/dist-lib/public-brand.cjs +1 -1
  49. package/dist-lib/react-native-host-modules.cjs +1 -1
  50. package/dist-lib/react-native-host-modules.mjs +1 -1
  51. package/dist-lib/render-mode.cjs +1 -1
  52. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  53. package/dist-lib/sdk.cjs +1 -1
  54. package/dist-lib/sdk.mjs +1 -1
  55. package/dist-lib/skills.cjs +28 -7
  56. package/dist-lib/swift-submit.cjs +1 -1
  57. package/dist-lib/swift-submit.mjs +1 -1
  58. package/dist-lib/swift.cjs +23 -5
  59. package/dist-lib/vite.cjs +24 -11
  60. package/package.json +1 -1
  61. package/src/rolldown-plugin-swift.ts +8 -5
  62. package/src/vite-plugin-swift.ts +8 -5
@@ -15,7 +15,7 @@ import {
15
15
  } from '../cloud-test-tunnel'
16
16
  import { parseFlowFile } from '../flow-file'
17
17
  import { buildShellUrl, resolveShellBaseUrlForBridgePort } from './control'
18
- import { hoistLeadingSimFlag } from './flow'
18
+ import { FLOW_ARG_VALUE_FLAGS, hoistLeadingSimFlag } from './flow'
19
19
  import { resolveDefaultUploadOrigin } from './upload'
20
20
 
21
21
  export interface RunTestOptions {
@@ -30,6 +30,11 @@ interface TestAppTarget {
30
30
  close: () => Promise<void>
31
31
  }
32
32
 
33
+ // every flag that swallows the token after it, so the positional scan finds
34
+ // the flow/suite target no matter where the flags sit (`rnx test
35
+ // --include-tags smoke .maestro/` must not take `smoke` as the target).
36
+ // maestro compat flags live here alongside the flow-runner vocabulary;
37
+ // without them a flag-first maestro command line mis-resolves its target.
33
38
  const VALUE_FLAGS = new Set([
34
39
  '--app',
35
40
  '--bundle',
@@ -40,11 +45,22 @@ const VALUE_FLAGS = new Set([
40
45
  '--tab',
41
46
  '--port',
42
47
  '--device',
48
+ '-d',
49
+ '--env',
50
+ '-e',
43
51
  '--driver',
44
52
  '--base-url',
45
53
  '--url',
54
+ '--include-tags',
55
+ '--exclude-tags',
56
+ '--format',
57
+ '--output',
58
+ '--test-output-dir',
59
+ '--debug-output',
60
+ '--platform',
46
61
  '-t',
47
62
  '--testNamePattern',
63
+ '--testTimeout',
48
64
  '--grep',
49
65
  '--maxWorkers',
50
66
  '--shard',
@@ -56,6 +72,7 @@ const VALUE_FLAGS = new Set([
56
72
  '--timeout',
57
73
  '--sidecar-ports',
58
74
  '--cancel',
75
+ ...FLOW_ARG_VALUE_FLAGS,
59
76
  ])
60
77
 
61
78
  function valueAfter(args: string[], name: string): string | null {
@@ -67,7 +84,7 @@ function valueAfter(args: string[], name: string): string | null {
67
84
  return value && !value.startsWith('-') ? value : null
68
85
  }
69
86
 
70
- function targetFromArgs(args: string[]): string | null {
87
+ function targetIndexFromArgs(args: string[]): number {
71
88
  for (let index = 0; index < args.length; index++) {
72
89
  const arg = args[index]
73
90
  if (VALUE_FLAGS.has(arg)) {
@@ -77,20 +94,31 @@ function targetFromArgs(args: string[]): string | null {
77
94
  if (arg.startsWith('--') && arg.includes('=')) {
78
95
  continue
79
96
  }
80
- if (!arg.startsWith('-')) return arg
97
+ if (!arg.startsWith('-')) return index
81
98
  }
82
- return null
99
+ return -1
100
+ }
101
+
102
+ function targetFromArgs(args: string[]): string | null {
103
+ const index = targetIndexFromArgs(args)
104
+ return index >= 0 ? args[index] : null
105
+ }
106
+
107
+ const APP_FLAGS = ['--app', '--bundle', '--bundle-url']
108
+
109
+ function isAppFlag(arg: string): boolean {
110
+ return APP_FLAGS.some((flag) => arg === flag || arg.startsWith(`${flag}=`))
83
111
  }
84
112
 
85
113
  function withoutApp(args: string[]): string[] {
86
114
  const result: string[] = []
87
115
  for (let i = 0; i < args.length; i++) {
88
116
  const arg = args[i]
89
- if (arg === '--app') {
117
+ if (APP_FLAGS.includes(arg)) {
90
118
  i += 1
91
119
  continue
92
120
  }
93
- if (arg.startsWith('--app=')) {
121
+ if (isAppFlag(arg)) {
94
122
  continue
95
123
  }
96
124
  result.push(arg)
@@ -98,6 +126,25 @@ function withoutApp(args: string[]): string[] {
98
126
  return result
99
127
  }
100
128
 
129
+ // the maestro runner reads the app target as --url; rnx test spells it
130
+ // --app/--bundle/--bundle-url. map each spelling explicitly: a prefix
131
+ // replace would turn --bundle-url into --url-url.
132
+ function mapAppFlagToUrl(arg: string): string {
133
+ if (APP_FLAGS.includes(arg)) return '--url'
134
+ for (const flag of APP_FLAGS) {
135
+ if (arg.startsWith(`${flag}=`)) return `--url=${arg.slice(flag.length + 1)}`
136
+ }
137
+ return arg
138
+ }
139
+
140
+ function appFromArgs(args: string[]): string | null {
141
+ return (
142
+ valueAfter(args, '--app') ??
143
+ valueAfter(args, '--bundle') ??
144
+ valueAfter(args, '--bundle-url')
145
+ )
146
+ }
147
+
101
148
  function closeServer(server: Server): Promise<void> {
102
149
  return new Promise((resolveClose, rejectClose) => {
103
150
  server.close((error) => {
@@ -161,14 +208,30 @@ function restoreRnxUrl(previous: string | undefined): void {
161
208
  }
162
209
  }
163
210
 
211
+ // `--config` is claimed by both interpreters: a YAML value is a maestro
212
+ // workspace config, anything else is a detox jest config.
213
+ function isMaestroConfigValue(value: string | null): boolean {
214
+ return value !== null && /\.ya?ml$/i.test(value)
215
+ }
216
+
164
217
  // dispatch only: each interpreter retains its own selection and execution rules.
165
218
  export function resolveTestCommand(args: string[], cwd = process.cwd()) {
166
219
  const forwarded = hoistLeadingSimFlag(args)
167
- const target = forwarded[0]?.startsWith('-') ? undefined : forwarded[0]
168
- const config = forwarded.some(
220
+ const hasConfig = forwarded.some(
169
221
  (arg) => arg === '--config' || arg.startsWith('--config='),
170
222
  )
171
- if (config) return { command: 'detox', args: forwarded }
223
+ const maestroConfig =
224
+ hasConfig && isMaestroConfigValue(valueAfter(forwarded, '--config'))
225
+ if (hasConfig && !maestroConfig) return { command: 'detox', args: forwarded }
226
+ // the target is the first positional, wherever it sits: maestro users put
227
+ // flags before the path (`maestro test --include-tags smoke .maestro/`),
228
+ // so reading only argv[0] breaks the exact pasted command line.
229
+ let target = targetFromArgs(forwarded) ?? undefined
230
+ if (!target && maestroConfig) {
231
+ // `rnx test --config e2e/config.yaml`: the config describes a workspace,
232
+ // so run the directory it lives in.
233
+ target = dirname(resolve(cwd, valueAfter(forwarded, '--config') as string))
234
+ }
172
235
  if (!target) {
173
236
  throw new Error(
174
237
  'pass a Maestro flow/directory, a Jest suite, or --config <jest-config>',
@@ -190,19 +253,22 @@ export function resolveTestCommand(args: string[], cwd = process.cwd()) {
190
253
  ['.maestro', 'maestro'].includes(basename(absolute))
191
254
  : /\.ya?ml$/.test(extname(absolute))
192
255
  if (maestro) {
193
- return {
194
- command: 'maestro',
195
- args: [
196
- 'test',
197
- ...forwarded.map((arg, index) => {
198
- if (index === 0 && !directory && /^config\.ya?ml$/.test(basename(absolute)))
199
- return dirname(absolute)
200
- return arg === '--app' || arg.startsWith('--app=')
201
- ? arg.replace('--app', '--url')
202
- : arg
203
- }),
204
- ],
256
+ // when the target names a workspace config file, run its directory.
257
+ // the replacement lands on the target positional by index: with
258
+ // flag-first argv the target is not argv[0], and a path can equal a
259
+ // flag value elsewhere in the line.
260
+ const targetIndex = targetIndexFromArgs(forwarded)
261
+ const runConfigDir = !directory && /^config\.ya?ml$/.test(basename(absolute))
262
+ const mapped = forwarded.map((arg, index) => {
263
+ if (index === targetIndex && runConfigDir) return dirname(absolute)
264
+ return mapAppFlagToUrl(arg)
265
+ })
266
+ if (targetIndex < 0) {
267
+ // synthesized target (`--config e2e/config.yaml` with no positional):
268
+ // name the workspace dir so maestro does not auto-discover cwd.
269
+ mapped.push(target)
205
270
  }
271
+ return { command: 'maestro', args: ['test', ...mapped] }
206
272
  }
207
273
  if (!directory && !/\.[cm]?[jt]sx?$/.test(extname(absolute))) {
208
274
  throw new Error(`expected Maestro YAML or a Detox/Jest suite: ${target}`)
@@ -465,7 +531,11 @@ export async function runCloudTest(
465
531
  let target = targetFromArgs(forwarded)
466
532
  const config = valueAfter(forwarded, '--config')
467
533
  if (!target && config) {
468
- target = config
534
+ // a maestro workspace config uploads its directory; a jest config is
535
+ // collected from its own dir by the path logic below.
536
+ target = isMaestroConfigValue(config)
537
+ ? dirname(resolve(process.cwd(), config))
538
+ : config
469
539
  }
470
540
  if (!target) {
471
541
  for (const cand of ['.maestro', 'maestro', 'flows', 'e2e']) {
@@ -493,6 +563,20 @@ export async function runCloudTest(
493
563
  return 1
494
564
  }
495
565
 
566
+ const localOnly = [
567
+ '--include-tags',
568
+ '--exclude-tags',
569
+ '--format',
570
+ '--output',
571
+ '--test-output-dir',
572
+ '--debug-output',
573
+ ].filter((flag) => forwarded.some((arg) => arg === flag || arg.startsWith(`${flag}=`)))
574
+ if (localOnly.length > 0) {
575
+ console.warn(
576
+ ` warn: ${localOnly.join(', ')} apply to local runs only and are ignored by --cloud`,
577
+ )
578
+ }
579
+
496
580
  const auth = resolveCliAuth()
497
581
  if (!auth) {
498
582
  console.error(' error: rnx test --cloud requires `rnx login` or RNX_API_KEY')
@@ -725,7 +809,7 @@ export async function runTest(
725
809
  }
726
810
 
727
811
  const selected = resolveTestCommand(args)
728
- const app = valueAfter(args, '--app')
812
+ const app = appFromArgs(args)
729
813
  const appTarget = app
730
814
  ? await resolveTestAppTarget(app)
731
815
  : { target: '', close: async () => {} }
package/cli/flow-file.ts CHANGED
@@ -16,6 +16,13 @@ export interface FlowFrontmatter {
16
16
  env?: Record<string, string>
17
17
  // maestro-compatible: workspace tag filtering (config.yml includeTags/excludeTags)
18
18
  tags?: string[]
19
+ // maestro-compatible: display name for reports. defaults the junit
20
+ // testcase id, name, and classname when properties does not override them.
21
+ name?: string
22
+ // maestro-compatible: custom junit <property> entries. junitId and
23
+ // junitClassname are reserved: they set the testcase attributes and are
24
+ // not emitted as properties.
25
+ properties?: Record<string, string>
19
26
  }
20
27
 
21
28
  // parse flow file. accepts three shapes:
@@ -0,0 +1,178 @@
1
+ // maestro-compatible run reports: junit xml, per-flow commands.json, and the
2
+ // session/flow maestro.log text. pure builders over playback receipts — no
3
+ // bridge, no filesystem — so the workspace tests cover them without a sim.
4
+ //
5
+ // the shapes mirror maestro's own outputs (test-reports-and-artifacts): one
6
+ // <testsuite name="Test Suite"> per invocation, flow-header `properties:`
7
+ // as <property> children, junitId/junitClassname reserved for the testcase
8
+ // attributes, commands.json carrying one entry per executed step.
9
+
10
+ import type { SootSimFlowTraceStep } from './bridge-flow-runner'
11
+
12
+ export function escapeXml(value: string): string {
13
+ return value
14
+ .replace(/&/g, '&amp;')
15
+ .replace(/</g, '&lt;')
16
+ .replace(/>/g, '&gt;')
17
+ .replace(/"/g, '&quot;')
18
+ .replace(/'/g, '&apos;')
19
+ }
20
+
21
+ export interface JUnitCaseInput {
22
+ // flow path as printed in the run (relative to the invocation cwd)
23
+ file: string
24
+ name: string
25
+ id: string
26
+ classname: string
27
+ // custom flow-header properties with the reserved junit keys removed
28
+ properties: Record<string, string>
29
+ tags: string[]
30
+ timeSeconds: number
31
+ failure: { message: string; body: string } | null
32
+ }
33
+
34
+ export interface JUnitSuiteInput {
35
+ device: string
36
+ cases: JUnitCaseInput[]
37
+ }
38
+
39
+ function formatJUnitTime(seconds: number): string {
40
+ return (Number.isFinite(seconds) && seconds >= 0 ? seconds : 0).toFixed(3)
41
+ }
42
+
43
+ export function buildJUnitXml(suite: JUnitSuiteInput): string {
44
+ const failures = suite.cases.filter((c) => c.failure !== null).length
45
+ const totalTime = suite.cases.reduce(
46
+ (sum, c) => sum + (Number.isFinite(c.timeSeconds) ? c.timeSeconds : 0),
47
+ 0,
48
+ )
49
+ const lines = [
50
+ '<?xml version="1.0" encoding="UTF-8"?>',
51
+ '<testsuites>',
52
+ ` <testsuite name="Test Suite" device="${escapeXml(suite.device)}" tests="${suite.cases.length}" failures="${failures}" time="${formatJUnitTime(totalTime)}">`,
53
+ ]
54
+ for (const c of suite.cases) {
55
+ lines.push(
56
+ ` <testcase id="${escapeXml(c.id)}" name="${escapeXml(c.name)}" classname="${escapeXml(c.classname)}" file="${escapeXml(c.file)}" time="${formatJUnitTime(c.timeSeconds)}">`,
57
+ )
58
+ const properties: Array<{ name: string; value: string }> = []
59
+ if (c.tags.length > 0) {
60
+ properties.push({ name: 'tags', value: c.tags.join(',') })
61
+ }
62
+ for (const [name, value] of Object.entries(c.properties)) {
63
+ properties.push({ name, value })
64
+ }
65
+ if (properties.length > 0) {
66
+ lines.push(' <properties>')
67
+ for (const p of properties) {
68
+ lines.push(
69
+ ` <property name="${escapeXml(p.name)}" value="${escapeXml(p.value)}"/>`,
70
+ )
71
+ }
72
+ lines.push(' </properties>')
73
+ }
74
+ if (c.failure) {
75
+ lines.push(` <failure message="${escapeXml(c.failure.message)}">`)
76
+ lines.push(escapeXml(c.failure.body))
77
+ lines.push(' </failure>')
78
+ }
79
+ lines.push(' </testcase>')
80
+ }
81
+ lines.push(' </testsuite>')
82
+ lines.push('</testsuites>')
83
+ return `${lines.join('\n')}\n`
84
+ }
85
+
86
+ // the first failed trace step as a junit failure. a flow that died before
87
+ // producing steps (parse error, sim never attached) has no failed step, so
88
+ // the caller passes its own fallback text instead of an empty failure.
89
+ export function failureFromTraceSteps(
90
+ steps: SootSimFlowTraceStep[],
91
+ fallbackMessage: string,
92
+ ): { message: string; body: string } {
93
+ const failed = steps.find((step) => step.status === 'failure')
94
+ if (!failed) {
95
+ return { message: fallbackMessage, body: fallbackMessage }
96
+ }
97
+ const error = failed.error || 'flow step failed'
98
+ const message = error.split('\n')[0] || fallbackMessage
99
+ const bodyLines = [`step ${failed.stepIndex + 1} (${failed.stepName}): ${error}`]
100
+ if (failed.screenshotPath) {
101
+ bodyLines.push(`screenshot: ${failed.screenshotPath}`)
102
+ }
103
+ return { message, body: bodyLines.join('\n') }
104
+ }
105
+
106
+ export interface FlowCommandsEntry {
107
+ sequenceNumber: number
108
+ command: string
109
+ status: SootSimFlowTraceStep['status']
110
+ durationMs: number
111
+ error?: string
112
+ artifacts: string[]
113
+ }
114
+
115
+ export function buildFlowCommandsJson(steps: SootSimFlowTraceStep[]): string {
116
+ const entries: FlowCommandsEntry[] = steps.map((step) => ({
117
+ sequenceNumber: step.stepIndex + 1,
118
+ command: step.targetLabel ? `${step.stepName} ${step.targetLabel}` : step.stepName,
119
+ status: step.status,
120
+ durationMs: step.durationMs,
121
+ ...(step.error ? { error: step.error } : {}),
122
+ artifacts: step.screenshotPath ? [step.screenshotPath] : [],
123
+ }))
124
+ return `${JSON.stringify(entries, null, 2)}\n`
125
+ }
126
+
127
+ export interface SessionLogFlow {
128
+ file: string
129
+ passed: boolean
130
+ durationMs: number
131
+ steps: SootSimFlowTraceStep[]
132
+ }
133
+
134
+ function formatLogDuration(ms: number): string {
135
+ return `${(Math.max(0, ms) / 1000).toFixed(1)}s`
136
+ }
137
+
138
+ function formatLogStep(step: SootSimFlowTraceStep): string {
139
+ const label = step.targetLabel ? `${step.stepName} ${step.targetLabel}` : step.stepName
140
+ const base = ` ${step.stepIndex + 1}. ${label} - ${step.status} (${formatLogDuration(step.durationMs)})`
141
+ if (step.status === 'failure' && step.error) {
142
+ return `${base}: ${step.error.split('\n')[0]}`
143
+ }
144
+ return base
145
+ }
146
+
147
+ // one flow's section, shared by the session log and the per-flow logs file
148
+ // so the two never disagree about what a flow did.
149
+ export function buildFlowLogLines(flow: SessionLogFlow): string[] {
150
+ const lines = [
151
+ `# flow: ${flow.file} - ${flow.passed ? 'pass' : 'FAIL'} (${formatLogDuration(flow.durationMs)}, ${flow.steps.length} steps)`,
152
+ ]
153
+ for (const step of flow.steps) {
154
+ lines.push(formatLogStep(step))
155
+ }
156
+ return lines
157
+ }
158
+
159
+ export function buildSessionLog(input: {
160
+ startedAtIso: string
161
+ root: string
162
+ device: string
163
+ flows: SessionLogFlow[]
164
+ }): string {
165
+ const passed = input.flows.filter((flow) => flow.passed).length
166
+ const lines = [
167
+ '# rnx maestro session log',
168
+ `# started: ${input.startedAtIso}`,
169
+ `# root: ${input.root}`,
170
+ `# device: ${input.device}`,
171
+ `# result: ${passed}/${input.flows.length} passed`,
172
+ ]
173
+ for (const flow of input.flows) {
174
+ lines.push('#')
175
+ lines.push(...buildFlowLogLines(flow))
176
+ }
177
+ return `${lines.join('\n')}\n`
178
+ }
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.549 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.550 | (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.549 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.550 | (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.549 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.550 | (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.549 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.550 | (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.549 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.550 | (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.549 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.550 | (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.549 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.550 | (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.549 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.550 | (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.549 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.550 | (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.549 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.550 | (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.549 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.550 | (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.549 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.550 | (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.549 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.550 | (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.549 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.550 | (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.549 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.550 | (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.549 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.550 | (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.549 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.550 | (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.549 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.550 | (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.549 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.550 | (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.549 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.550 | (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.549 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.550 | (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.549 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.550 | (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.549 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.550 | (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.549 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.550 | (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.549 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.550 | (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.549 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.550 | (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;
@@ -865,10 +865,12 @@ function getDevice(model, posture) {
865
865
  cameraCutout: null,
866
866
  physicalPPI: 430,
867
867
  posture: "open",
868
- // open inner display keeps horizontal bars in this slice; preserve the
869
- // previous trailing status lane so open layouts do not pick up the closed
870
- // 84pt Vertical Bar inset.
871
- safeAreaInsets: { top: 0, bottom: 0, left: 0, right: 72 }
868
+ // open inner display in landscape carries the same trailing column as
869
+ // the closed outer display, so it takes the same 84pt inset: a ~50pt
870
+ // control at a ~23pt inset needs 73pt, and anything narrower would shift
871
+ // controls horizontally on unfold, defeating the continuous experience
872
+ // Apple states as the reason for the design.
873
+ safeAreaInsets: { top: 0, bottom: 0, left: 0, right: 84 }
872
874
  };
873
875
  }
874
876
  function listDeviceModels() {
@@ -2956,7 +2958,7 @@ var init_stub_manifest = __esm({
2956
2958
 
2957
2959
  // ../sootsim-engine/src/metro-asset-path.ts
2958
2960
  function isMetroAssetRegistryModulePath(modulePath) {
2959
- return modulePath.includes("polyfill:assets-registry") || modulePath.includes("@react-native/assets-registry/registry") || modulePath.includes("assets-registry/registry") || modulePath.includes("react-native/src/asset-registry");
2961
+ return modulePath.includes("polyfill:assets-registry") || modulePath.includes("@react-native/assets-registry/registry") || modulePath.includes("assets-registry/registry") || modulePath.includes("react-native/asset-registry") || modulePath.includes("react-native/src/asset-registry");
2960
2962
  }
2961
2963
  var init_metro_asset_path = __esm({
2962
2964
  "../sootsim-engine/src/metro-asset-path.ts"() {
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.549 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.550 | (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.549 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.550 | (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;