peachsim 0.1.527

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 (324) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +640 -0
  3. package/cli/app-config.ts +82 -0
  4. package/cli/app-fonts.ts +388 -0
  5. package/cli/app-project.ts +59 -0
  6. package/cli/app-splash.ts +171 -0
  7. package/cli/app-state-reset.ts +26 -0
  8. package/cli/auth.ts +184 -0
  9. package/cli/bin.ts +59 -0
  10. package/cli/bridge-diagnostics.ts +226 -0
  11. package/cli/bridge-flow-runner.ts +2806 -0
  12. package/cli/browser-backend.ts +44 -0
  13. package/cli/browser-evals.ts +96 -0
  14. package/cli/cli-update.ts +261 -0
  15. package/cli/cloud-client.ts +1530 -0
  16. package/cli/cloud-dispatch.ts +206 -0
  17. package/cli/cloud-session.ts +227 -0
  18. package/cli/command-contract.ts +221 -0
  19. package/cli/command-registry.ts +304 -0
  20. package/cli/commands/agent-wrapper.ts +1048 -0
  21. package/cli/commands/agent.ts +439 -0
  22. package/cli/commands/app-fonts.ts +99 -0
  23. package/cli/commands/assert.ts +542 -0
  24. package/cli/commands/auth.ts +71 -0
  25. package/cli/commands/box/bundle-plane.ts +71 -0
  26. package/cli/commands/box/checkout-plane.ts +256 -0
  27. package/cli/commands/box/plane-share.ts +208 -0
  28. package/cli/commands/box/rnx-command.ts +60 -0
  29. package/cli/commands/box/search-command.ts +157 -0
  30. package/cli/commands/box.ts +925 -0
  31. package/cli/commands/camera.ts +266 -0
  32. package/cli/commands/cleanup.ts +169 -0
  33. package/cli/commands/compat.ts +176 -0
  34. package/cli/commands/config.ts +34 -0
  35. package/cli/commands/control.ts +2154 -0
  36. package/cli/commands/cpu-profile.ts +941 -0
  37. package/cli/commands/daemon-mac-app.ts +165 -0
  38. package/cli/commands/daemon.ts +800 -0
  39. package/cli/commands/debug.ts +751 -0
  40. package/cli/commands/desktop.ts +39 -0
  41. package/cli/commands/detect.ts +201 -0
  42. package/cli/commands/detox.ts +481 -0
  43. package/cli/commands/device.ts +134 -0
  44. package/cli/commands/diagnose.ts +589 -0
  45. package/cli/commands/do-chain.ts +408 -0
  46. package/cli/commands/electron.ts +94 -0
  47. package/cli/commands/film.ts +379 -0
  48. package/cli/commands/flow.ts +1240 -0
  49. package/cli/commands/inspect/actions.ts +516 -0
  50. package/cli/commands/inspect/core.ts +2086 -0
  51. package/cli/commands/inspect/count.ts +17 -0
  52. package/cli/commands/inspect/describe.ts +119 -0
  53. package/cli/commands/inspect/env.ts +25 -0
  54. package/cli/commands/inspect/find.ts +169 -0
  55. package/cli/commands/inspect/get-layout.ts +53 -0
  56. package/cli/commands/inspect/keyboard.ts +54 -0
  57. package/cli/commands/inspect/list.ts +175 -0
  58. package/cli/commands/inspect/memory.ts +215 -0
  59. package/cli/commands/inspect/redaction.ts +39 -0
  60. package/cli/commands/inspect/resolve-target.ts +77 -0
  61. package/cli/commands/inspect/screens.ts +78 -0
  62. package/cli/commands/inspect/settle.ts +32 -0
  63. package/cli/commands/inspect/settling.ts +197 -0
  64. package/cli/commands/inspect/shared.ts +259 -0
  65. package/cli/commands/inspect/sleep.ts +15 -0
  66. package/cli/commands/inspect/tree.ts +23 -0
  67. package/cli/commands/inspect/url.ts +17 -0
  68. package/cli/commands/inspect/wait-event.ts +211 -0
  69. package/cli/commands/inspect/wait-idle.ts +25 -0
  70. package/cli/commands/inspect/wait-ready.ts +87 -0
  71. package/cli/commands/inspect/wait-selector.ts +59 -0
  72. package/cli/commands/inspect/wait.ts +32 -0
  73. package/cli/commands/inspect.ts +4738 -0
  74. package/cli/commands/install-cli.ts +275 -0
  75. package/cli/commands/install-desktop.ts +385 -0
  76. package/cli/commands/login.ts +357 -0
  77. package/cli/commands/logout.ts +24 -0
  78. package/cli/commands/maestro-generate.ts +375 -0
  79. package/cli/commands/maestro.ts +536 -0
  80. package/cli/commands/mode.ts +58 -0
  81. package/cli/commands/no-bridge-hint.ts +80 -0
  82. package/cli/commands/perf.ts +67 -0
  83. package/cli/commands/permissions.ts +203 -0
  84. package/cli/commands/platform.ts +410 -0
  85. package/cli/commands/preview.ts +103 -0
  86. package/cli/commands/profile.ts +106 -0
  87. package/cli/commands/react.ts +353 -0
  88. package/cli/commands/record.ts +1435 -0
  89. package/cli/commands/report-issue.ts +305 -0
  90. package/cli/commands/reset.ts +89 -0
  91. package/cli/commands/runtime.ts +348 -0
  92. package/cli/commands/screenshot-command.ts +106 -0
  93. package/cli/commands/screenshot-layers.ts +144 -0
  94. package/cli/commands/screenshot-mode.ts +38 -0
  95. package/cli/commands/screenshot.ts +492 -0
  96. package/cli/commands/screenshots-capture.ts +608 -0
  97. package/cli/commands/screenshots.ts +127 -0
  98. package/cli/commands/serve.ts +139 -0
  99. package/cli/commands/setup.ts +416 -0
  100. package/cli/commands/shell-boolean-mode.ts +82 -0
  101. package/cli/commands/skills.ts +468 -0
  102. package/cli/commands/slides.ts +357 -0
  103. package/cli/commands/state.ts +94 -0
  104. package/cli/commands/storage.ts +58 -0
  105. package/cli/commands/telemetry.ts +55 -0
  106. package/cli/commands/test.ts +535 -0
  107. package/cli/commands/three-mode.ts +771 -0
  108. package/cli/commands/timeline.ts +121 -0
  109. package/cli/commands/upgrade.ts +167 -0
  110. package/cli/commands/upload.ts +1203 -0
  111. package/cli/commands/version.ts +63 -0
  112. package/cli/commands/welcome.ts +17 -0
  113. package/cli/commands/what-happened.ts +328 -0
  114. package/cli/current-sim.ts +217 -0
  115. package/cli/desktop-companion.ts +315 -0
  116. package/cli/drivers/electron.ts +70 -0
  117. package/cli/drivers/index.ts +20 -0
  118. package/cli/drivers/playwright-provisioning.ts +184 -0
  119. package/cli/drivers/playwright-sim-host.ts +537 -0
  120. package/cli/drivers/playwright.ts +209 -0
  121. package/cli/drivers/registry.ts +65 -0
  122. package/cli/drivers/types.ts +102 -0
  123. package/cli/flow-export.ts +55 -0
  124. package/cli/flow-file.ts +142 -0
  125. package/cli/flow-live-status.ts +126 -0
  126. package/cli/flow-session.ts +140 -0
  127. package/cli/help-core.ts +143 -0
  128. package/cli/help.ts +44 -0
  129. package/cli/hidden-runtime-alias.ts +19 -0
  130. package/cli/hints.ts +214 -0
  131. package/cli/inspect-notice-state.ts +114 -0
  132. package/cli/internal-child.ts +176 -0
  133. package/cli/maestro-js.ts +323 -0
  134. package/cli/main.ts +704 -0
  135. package/cli/open-url.ts +8 -0
  136. package/cli/outbound-endpoints.ts +830 -0
  137. package/cli/parent-pid.ts +204 -0
  138. package/cli/parse-args.ts +189 -0
  139. package/cli/privacy.ts +53 -0
  140. package/cli/prompt.ts +56 -0
  141. package/cli/recording-access.ts +108 -0
  142. package/cli/registry.ts +2 -0
  143. package/cli/resolve-assets.ts +63 -0
  144. package/cli/rnx-in-process.ts +98 -0
  145. package/cli/run-registry.ts +226 -0
  146. package/cli/run-rnx-core.ts +148 -0
  147. package/cli/run-rnx.ts +137 -0
  148. package/cli/runtime-notes.ts +66 -0
  149. package/cli/runtime-summary.ts +25 -0
  150. package/cli/secure-file.ts +31 -0
  151. package/cli/self-invocation.ts +58 -0
  152. package/cli/send-to-box.ts +109 -0
  153. package/cli/setup-repository.ts +191 -0
  154. package/cli/shell-init.ts +98 -0
  155. package/cli/standalone.ts +4 -0
  156. package/cli/telemetry.ts +84 -0
  157. package/cli/ui.ts +68 -0
  158. package/cli/ws-bridge.ts +1020 -0
  159. package/detox/attempt-events.cjs +23 -0
  160. package/detox/colors.ts +54 -0
  161. package/detox/config-loader.ts +135 -0
  162. package/detox/element-types.ts +48 -0
  163. package/detox/expectations.ts +534 -0
  164. package/detox/gestures.ts +501 -0
  165. package/detox/index.ts +2694 -0
  166. package/detox/jest-preset.cjs +29 -0
  167. package/detox/jest-setup-after-env.cjs +22 -0
  168. package/detox/matchers.ts +30 -0
  169. package/detox/motion-change.cjs +109 -0
  170. package/detox/motion-change.d.cts +35 -0
  171. package/detox/navigation-lifecycle.ts +290 -0
  172. package/detox/navigation.ts +43 -0
  173. package/detox/proof-frame.cjs +114 -0
  174. package/detox/record-block-execution.cjs +78 -0
  175. package/detox/screenshots/animated-color-test-rest-norngh.png +0 -0
  176. package/detox/screenshots/color-test-after-drag-norngh.png +0 -0
  177. package/detox/screenshots/color-test-rest-norngh.png +0 -0
  178. package/detox/screenshots/theme-blue-toggle.png +0 -0
  179. package/detox/screenshots/theme-blue.png +0 -0
  180. package/detox/screenshots/theme-red-toggle.png +0 -0
  181. package/detox/screenshots/theme-red.png +0 -0
  182. package/dist-lib/agent-daemon-client.cjs +502 -0
  183. package/dist-lib/agent-events.cjs +48 -0
  184. package/dist-lib/agent-identity.cjs +53 -0
  185. package/dist-lib/agent-sessions.cjs +759 -0
  186. package/dist-lib/attached-projects.cjs +526 -0
  187. package/dist-lib/auth/shared-session.cjs +208 -0
  188. package/dist-lib/backend-origin.cjs +88 -0
  189. package/dist-lib/beta.cjs +41 -0
  190. package/dist-lib/beta.mjs +13 -0
  191. package/dist-lib/bridge-constants.cjs +161 -0
  192. package/dist-lib/bridge-contract-input.cjs +451 -0
  193. package/dist-lib/bridge-contract-input.mjs +414 -0
  194. package/dist-lib/bridge-contract.cjs +130 -0
  195. package/dist-lib/bridge-contract.mjs +94 -0
  196. package/dist-lib/capture-contract.cjs +220 -0
  197. package/dist-lib/capture-contract.mjs +190 -0
  198. package/dist-lib/cli-constants.cjs +32 -0
  199. package/dist-lib/cloud-contract.cjs +194 -0
  200. package/dist-lib/cloud-contract.mjs +145 -0
  201. package/dist-lib/cloud.cjs +670 -0
  202. package/dist-lib/cloud.mjs +639 -0
  203. package/dist-lib/config.cjs +96 -0
  204. package/dist-lib/detox/index.cjs +3789 -0
  205. package/dist-lib/detox/jest-preset.cjs +29 -0
  206. package/dist-lib/dev-bundle-resolution.cjs +290 -0
  207. package/dist-lib/home-paths.cjs +638 -0
  208. package/dist-lib/host/bridge-host.cjs +10903 -0
  209. package/dist-lib/host/fetch-proxy-handler.cjs +421 -0
  210. package/dist-lib/host/fetch-proxy-overrides.cjs +63 -0
  211. package/dist-lib/host/fetch-proxy-overrides.mjs +37 -0
  212. package/dist-lib/host/replacement-module-handler.cjs +469 -0
  213. package/dist-lib/host/websocket-proxy.cjs +298 -0
  214. package/dist-lib/index.cjs +4268 -0
  215. package/dist-lib/jump-to-source-babel.cjs +93 -0
  216. package/dist-lib/jump-to-source-native.cjs +112 -0
  217. package/dist-lib/menu.cjs +646 -0
  218. package/dist-lib/menu.mjs +604 -0
  219. package/dist-lib/metro-fingerprint-registry.cjs +186 -0
  220. package/dist-lib/metro-fingerprint-registry.mjs +156 -0
  221. package/dist-lib/metro-production-bundle.cjs +329 -0
  222. package/dist-lib/metro-production-bundle.mjs +295 -0
  223. package/dist-lib/metro.cjs +904 -0
  224. package/dist-lib/profiles.cjs +246 -0
  225. package/dist-lib/public-brand.cjs +39 -0
  226. package/dist-lib/react-native-host-modules.cjs +51 -0
  227. package/dist-lib/react-native-host-modules.mjs +25 -0
  228. package/dist-lib/render-mode.cjs +54 -0
  229. package/dist-lib/scripts/dev-server-scanner.cjs +1232 -0
  230. package/dist-lib/sdk.cjs +2433 -0
  231. package/dist-lib/sdk.mjs +2336 -0
  232. package/dist-lib/skills.cjs +39132 -0
  233. package/dist-lib/swift-submit.cjs +92 -0
  234. package/dist-lib/swift-submit.mjs +65 -0
  235. package/dist-lib/swift.cjs +1646 -0
  236. package/dist-lib/vite.cjs +1591 -0
  237. package/package.json +290 -0
  238. package/scripts/dev-server-scanner.ts +1291 -0
  239. package/scripts/optional-demo-registry.ts +113 -0
  240. package/skills/contrast/SKILL.md +165 -0
  241. package/skills/rnx-debug/SKILL.md +433 -0
  242. package/skills/rnx-perf/SKILL.md +399 -0
  243. package/skills/rnx-setup/SKILL.md +172 -0
  244. package/skills/rnx-test/SKILL.md +321 -0
  245. package/skills/rnx-visual/SKILL.md +126 -0
  246. package/src/agent-daemon-client.ts +410 -0
  247. package/src/agent-events.ts +71 -0
  248. package/src/agent-identity.ts +23 -0
  249. package/src/agent-prompt.ts +71 -0
  250. package/src/agent-sessions.ts +580 -0
  251. package/src/app-url.ts +39 -0
  252. package/src/attached-projects.ts +539 -0
  253. package/src/auth/shared-session.ts +235 -0
  254. package/src/backend-origin.ts +70 -0
  255. package/src/beta.ts +19 -0
  256. package/src/bridge-constants.ts +175 -0
  257. package/src/bridge-contract-input.ts +502 -0
  258. package/src/bridge-contract.ts +558 -0
  259. package/src/browser-cache.ts +16 -0
  260. package/src/capture-contract.ts +587 -0
  261. package/src/cli-constants.ts +1 -0
  262. package/src/cli-version.ts +40 -0
  263. package/src/cloud-contract.ts +274 -0
  264. package/src/cloud.ts +762 -0
  265. package/src/codex-client.ts +234 -0
  266. package/src/config.ts +132 -0
  267. package/src/connect.ts +101 -0
  268. package/src/dev-bundle-resolution.ts +346 -0
  269. package/src/dev-server-open.ts +198 -0
  270. package/src/disk-cleanup.ts +540 -0
  271. package/src/flow-timeout-scale.ts +15 -0
  272. package/src/home-paths.ts +806 -0
  273. package/src/host/agent-host.ts +666 -0
  274. package/src/host/bridge-host.ts +2788 -0
  275. package/src/host/fetch-proxy-handler.ts +449 -0
  276. package/src/host/fetch-proxy-overrides.ts +51 -0
  277. package/src/host/open-url.ts +89 -0
  278. package/src/host/plane-host.ts +203 -0
  279. package/src/host/replacement-module-handler.ts +158 -0
  280. package/src/host/websocket-proxy.ts +315 -0
  281. package/src/index.ts +71 -0
  282. package/src/jump-to-source-babel.ts +135 -0
  283. package/src/jump-to-source-native.ts +102 -0
  284. package/src/menu.ts +777 -0
  285. package/src/metro-fingerprint-registry.ts +302 -0
  286. package/src/metro-plugin.ts +341 -0
  287. package/src/metro-production-bundle.ts +471 -0
  288. package/src/native-dev-bundle-url.ts +118 -0
  289. package/src/native-seam-manifest.ts +341 -0
  290. package/src/playwright-host-log.ts +16 -0
  291. package/src/plist.ts +27 -0
  292. package/src/poll-until.ts +123 -0
  293. package/src/profiles.ts +179 -0
  294. package/src/public-brand.ts +9 -0
  295. package/src/react-native-host-modules.ts +26 -0
  296. package/src/render-mode.ts +27 -0
  297. package/src/rolldown-plugin-swift.ts +240 -0
  298. package/src/runtime-assets.ts +233 -0
  299. package/src/runtime-delivery.ts +54 -0
  300. package/src/screenshots/compose.ts +422 -0
  301. package/src/screenshots/frame-compose.ts +514 -0
  302. package/src/screenshots/orchestrate.ts +256 -0
  303. package/src/screenshots/org-deck.ts +91 -0
  304. package/src/screenshots/registry.ts +58 -0
  305. package/src/screenshots/schema.ts +364 -0
  306. package/src/sdk.ts +15 -0
  307. package/src/settings-bundle.ts +418 -0
  308. package/src/shell-bridge-identity.ts +15 -0
  309. package/src/sim-client.ts +697 -0
  310. package/src/skills/builtin/a11y-review.ts +132 -0
  311. package/src/skills/builtin/compat-check.ts +71 -0
  312. package/src/skills/builtin/maestro-test.ts +121 -0
  313. package/src/skills/builtin/perf-profile.ts +90 -0
  314. package/src/skills/builtin/screenshot-all.ts +52 -0
  315. package/src/skills/builtin/visual-diff.ts +100 -0
  316. package/src/skills/registry.ts +107 -0
  317. package/src/skills/types.ts +41 -0
  318. package/src/swift-submit.ts +185 -0
  319. package/src/swift.ts +7 -0
  320. package/src/team-machine-prompt.ts +73 -0
  321. package/src/vite-plugin-one.ts +264 -0
  322. package/src/vite-plugin-swift.ts +620 -0
  323. package/src/vite-plugin.ts +1946 -0
  324. package/src/worklets-babel.ts +250 -0
@@ -0,0 +1,587 @@
1
+ // public, runtime-neutral screen capture contract. semantic evidence and pixel
2
+ // evidence are separate because neither one proves the other.
3
+
4
+ export const RNX_SCREEN_CAPTURE_VERSION = 1 as const
5
+
6
+ export interface RnxCaptureRect {
7
+ x: number
8
+ y: number
9
+ width: number
10
+ height: number
11
+ }
12
+
13
+ export interface RnxCaptureAccessibilityState {
14
+ disabled: boolean
15
+ selected: boolean
16
+ checked: boolean | 'mixed'
17
+ busy: boolean
18
+ expanded: boolean
19
+ }
20
+
21
+ export interface RnxCaptureAccessibilityValue {
22
+ min?: number
23
+ max?: number
24
+ now?: number
25
+ text?: string
26
+ }
27
+
28
+ export interface RnxCaptureAccessibility {
29
+ accessible: boolean
30
+ label: string | null
31
+ role: string | null
32
+ hint: string | null
33
+ liveRegion: 'none' | 'polite' | 'assertive' | null
34
+ state: RnxCaptureAccessibilityState | null
35
+ value: RnxCaptureAccessibilityValue | null
36
+ }
37
+
38
+ export interface RnxCaptureInteractionState {
39
+ pressed: boolean
40
+ pressable: boolean
41
+ textInput: boolean
42
+ textSelection: { start: number | null; end: number | null } | null
43
+ secureTextEntry: boolean
44
+ placeholder: string | null
45
+ scrollOffset: { x: number; y: number }
46
+ zoomScale: number | null
47
+ nativePinchEnabled: boolean
48
+ contentOffsetProp: { x?: number; y?: number } | null
49
+ decelerationRateProp: unknown
50
+ strokeDashoffsetProp: number | null
51
+ }
52
+
53
+ export interface RnxCaptureStyleTokenBinding {
54
+ token: string
55
+ theme: string
56
+ }
57
+
58
+ export interface RnxCaptureTextSegment {
59
+ text: string
60
+ color?: string | number
61
+ fontFamily?: string
62
+ fontSize?: number
63
+ fontStyle?: string
64
+ fontWeight?: string | number
65
+ letterSpacing?: number
66
+ textDecorationLine?: string
67
+ }
68
+
69
+ export interface RnxCaptureTextLine {
70
+ text: string
71
+ x: number
72
+ y: number
73
+ width: number
74
+ height: number
75
+ baseline: number
76
+ segments?: RnxCaptureTextSegment[]
77
+ }
78
+
79
+ export interface RnxCaptureSource {
80
+ location?: string
81
+ file?: string
82
+ componentName?: string
83
+ componentStack?: string[]
84
+ react?: {
85
+ component?: string
86
+ owners?: string[]
87
+ source?: string
88
+ key?: string
89
+ hasRef?: boolean
90
+ hooks?: {
91
+ total: number
92
+ counts: Record<string, number>
93
+ }
94
+ contexts?: string[]
95
+ lastCommit?: {
96
+ ageMs: number
97
+ causes: string[]
98
+ propDiffs?: string[]
99
+ stateChanged?: boolean
100
+ actualDurationMs?: number
101
+ }
102
+ }
103
+ }
104
+
105
+ export interface RnxCaptureNode {
106
+ nodeId: number
107
+ parentNodeId: number | null
108
+ childNodeIds: number[]
109
+ paintIndex: number
110
+ type: string
111
+ id: string | null
112
+ testID: string | null
113
+ text: string | null
114
+ childCount: number
115
+ accessibility: RnxCaptureAccessibility
116
+ interaction: RnxCaptureInteractionState
117
+ layout: {
118
+ local: RnxCaptureRect
119
+ frame: RnxCaptureRect | null
120
+ absolutePosition: { x: number; y: number }
121
+ visibleFrame: RnxCaptureRect | null
122
+ }
123
+ style: {
124
+ resolved: Record<string, unknown>
125
+ authored: Record<string, unknown>
126
+ computed: Record<string, unknown>
127
+ tokenBindings: Record<string, RnxCaptureStyleTokenBinding>
128
+ }
129
+ source: RnxCaptureSource
130
+ props: Record<string, unknown>
131
+ textLines?: RnxCaptureTextLine[]
132
+ /** additional generic inspect fields preserved for tooling adapters */
133
+ inspection: Record<string, unknown>
134
+ }
135
+
136
+ export interface RnxCaptureTree {
137
+ viewport: { width: number; height: number }
138
+ rootNodeId: number
139
+ nodes: RnxCaptureNode[]
140
+ /** reconciler commit shared by semantic, layout, and visual evidence */
141
+ commitRevision?: number
142
+ }
143
+
144
+ export interface RnxCaptureVisualEvidence {
145
+ kind: 'png'
146
+ /** names the runtime that produced these pixels without claiming visibility */
147
+ producer: 'headless-cpu-app-surface' | 'browser-compositor-app-surface'
148
+ dataUri: string
149
+ pixelWidth: number
150
+ pixelHeight: number
151
+ }
152
+
153
+ export interface RnxCaptureRegionRequest {
154
+ key: string
155
+ /** host node whose own subtree is rendered into this transparent atlas region */
156
+ nodeId: number
157
+ frame: RnxCaptureRect
158
+ }
159
+
160
+ export interface RnxRasterRegionTree {
161
+ width: number
162
+ height: number
163
+ nodes: readonly {
164
+ nodeId: number
165
+ parentNodeId: number | null
166
+ type: string
167
+ text?: string | null
168
+ style: Record<string, unknown>
169
+ layout: RnxCaptureRect
170
+ frame: RnxCaptureRect | null
171
+ absolutePosition: { x: number; y: number }
172
+ visibleFrame?: RnxCaptureRect | null
173
+ }[]
174
+ }
175
+
176
+ export function selectRnxRasterNodeIds(tree: RnxRasterRegionTree): Set<number> {
177
+ const nodesById = new Map(tree.nodes.map((node) => [node.nodeId, node]))
178
+ const unsupported = new Set<number>()
179
+ for (const node of tree.nodes) {
180
+ const visibleFrame = node.visibleFrame ?? node.frame
181
+ if (!visibleFrame || visibleFrame.width <= 0 || visibleFrame.height <= 0) continue
182
+ if (
183
+ node.type === 'masked-view' ||
184
+ (node.type === 'text' &&
185
+ typeof node.text === 'string' &&
186
+ (/\p{Emoji_Presentation}/u.test(node.text) ||
187
+ /\uFE0F|\u200D|\u20E3|\p{Emoji_Modifier}|\p{Regional_Indicator}/u.test(
188
+ node.text,
189
+ ))) ||
190
+ node.style._liquidGlass !== undefined ||
191
+ node.style._liquidGlassEdge !== undefined
192
+ ) {
193
+ unsupported.add(node.nodeId)
194
+ }
195
+ }
196
+
197
+ for (const nodeId of [...unsupported]) {
198
+ let parentNodeId = nodesById.get(nodeId)?.parentNodeId ?? null
199
+ while (parentNodeId !== null) {
200
+ if (unsupported.has(parentNodeId)) {
201
+ unsupported.delete(nodeId)
202
+ break
203
+ }
204
+ parentNodeId = nodesById.get(parentNodeId)?.parentNodeId ?? null
205
+ }
206
+ }
207
+
208
+ const screenArea = tree.width * tree.height
209
+ const maxLayerArea = screenArea * 0.25
210
+ const maxAtlasArea = screenArea * 0.5
211
+ const candidates = [...unsupported]
212
+ .map((nodeId) => {
213
+ const node = nodesById.get(nodeId)
214
+ const frame = node?.visibleFrame ?? node?.frame
215
+ return frame ? { nodeId, area: frame.width * frame.height } : null
216
+ })
217
+ .filter(
218
+ (candidate): candidate is { nodeId: number; area: number } =>
219
+ candidate !== null && candidate.area <= maxLayerArea,
220
+ )
221
+ .sort((left, right) => left.area - right.area || left.nodeId - right.nodeId)
222
+ const selected = new Set<number>()
223
+ let atlasArea = 0
224
+ for (const candidate of candidates) {
225
+ if (atlasArea + candidate.area > maxAtlasArea) continue
226
+ selected.add(candidate.nodeId)
227
+ atlasArea += candidate.area
228
+ }
229
+ return selected
230
+ }
231
+
232
+ export function rnxRasterFrames(
233
+ tree: RnxRasterRegionTree,
234
+ nodeIds: ReadonlySet<number>,
235
+ ): Map<number, RnxCaptureRect> {
236
+ const frames = new Map<number, RnxCaptureRect>()
237
+ for (const node of tree.nodes) {
238
+ if (!nodeIds.has(node.nodeId)) continue
239
+ const frame = node.frame ?? {
240
+ x: node.absolutePosition.x,
241
+ y: node.absolutePosition.y,
242
+ width: node.layout.width,
243
+ height: node.layout.height,
244
+ }
245
+ const liquidGlass = node.style._liquidGlass
246
+ const capturePadding =
247
+ liquidGlass &&
248
+ typeof liquidGlass === 'object' &&
249
+ typeof Reflect.get(liquidGlass, 'capturePadding') === 'number'
250
+ ? Reflect.get(liquidGlass, 'capturePadding')
251
+ : 0
252
+ const shadowRadius =
253
+ typeof node.style.shadowRadius === 'number' ? node.style.shadowRadius : 0
254
+ const shadowOffset = node.style.shadowOffset
255
+ const shadowOffsetX =
256
+ shadowOffset &&
257
+ typeof shadowOffset === 'object' &&
258
+ typeof Reflect.get(shadowOffset, 'width') === 'number'
259
+ ? Reflect.get(shadowOffset, 'width')
260
+ : 0
261
+ const shadowOffsetY =
262
+ shadowOffset &&
263
+ typeof shadowOffset === 'object' &&
264
+ typeof Reflect.get(shadowOffset, 'height') === 'number'
265
+ ? Reflect.get(shadowOffset, 'height')
266
+ : 0
267
+ const left = Math.max(
268
+ 0,
269
+ frame.x - Math.max(capturePadding, shadowRadius - shadowOffsetX),
270
+ )
271
+ const top = Math.max(
272
+ 0,
273
+ frame.y - Math.max(capturePadding, shadowRadius - shadowOffsetY),
274
+ )
275
+ const right = Math.min(
276
+ tree.width,
277
+ frame.x + frame.width + Math.max(capturePadding, shadowRadius + shadowOffsetX),
278
+ )
279
+ const bottom = Math.min(
280
+ tree.height,
281
+ frame.y + frame.height + Math.max(capturePadding, shadowRadius + shadowOffsetY),
282
+ )
283
+ if (right > left && bottom > top) {
284
+ frames.set(node.nodeId, {
285
+ x: left,
286
+ y: top,
287
+ width: right - left,
288
+ height: bottom - top,
289
+ })
290
+ }
291
+ }
292
+ return frames
293
+ }
294
+
295
+ export function rnxCaptureRasterRegions(
296
+ capture: RnxScreenCapture,
297
+ ): RnxCaptureRegionRequest[] {
298
+ const tree: RnxRasterRegionTree = {
299
+ width: capture.tree.viewport.width,
300
+ height: capture.tree.viewport.height,
301
+ nodes: capture.tree.nodes.map((node) => ({
302
+ nodeId: node.nodeId,
303
+ parentNodeId: node.parentNodeId,
304
+ type: node.type,
305
+ text: node.text,
306
+ style: node.style.resolved,
307
+ layout: node.layout.local,
308
+ frame: node.layout.frame,
309
+ absolutePosition: node.layout.absolutePosition,
310
+ visibleFrame: node.layout.visibleFrame,
311
+ })),
312
+ }
313
+ const nodeIds = selectRnxRasterNodeIds(tree)
314
+ const frames = rnxRasterFrames(tree, nodeIds)
315
+ return [...nodeIds].flatMap((nodeId) => {
316
+ const frame = frames.get(nodeId)
317
+ return frame ? [{ key: String(nodeId), nodeId, frame }] : []
318
+ })
319
+ }
320
+
321
+ export interface RnxCaptureAtlasRegion extends RnxCaptureRegionRequest {
322
+ /** source rectangle inside the packed atlas, in logical atlas coordinates */
323
+ source: RnxCaptureRect
324
+ }
325
+
326
+ export interface RnxCaptureRegionAtlas {
327
+ kind: 'png-atlas'
328
+ producer: 'headless-cpu-app-surface'
329
+ dataUri: string
330
+ /** logical atlas dimensions; the encoded PNG uses pixelWidth × pixelHeight */
331
+ width: number
332
+ height: number
333
+ pixelWidth: number
334
+ pixelHeight: number
335
+ regions: RnxCaptureAtlasRegion[]
336
+ }
337
+
338
+ export interface RnxCaptureFontRegistration {
339
+ url?: string
340
+ family: string
341
+ sourceLabel?: string
342
+ parsedFamily?: string | null
343
+ parsedPostScript?: string | null
344
+ fullName?: string | null
345
+ }
346
+
347
+ export interface RnxScreenCapture {
348
+ contractVersion: typeof RNX_SCREEN_CAPTURE_VERSION
349
+ capturedAt: number
350
+ tree: RnxCaptureTree
351
+ /** app fonts registered in the captured tenant, included only when requested */
352
+ fonts?: RnxCaptureFontRegistration[]
353
+ /** optional full-frame diagnostic evidence, never required by tree consumers */
354
+ visual?: RnxCaptureVisualEvidence
355
+ }
356
+
357
+ function captureRecord(value: unknown): value is Record<string, unknown> {
358
+ return value !== null && typeof value === 'object' && !Array.isArray(value)
359
+ }
360
+
361
+ function captureInteger(value: unknown): value is number {
362
+ return typeof value === 'number' && Number.isSafeInteger(value)
363
+ }
364
+
365
+ function captureNumber(value: unknown): value is number {
366
+ return typeof value === 'number' && Number.isFinite(value)
367
+ }
368
+
369
+ function captureNullableString(value: unknown): value is string | null {
370
+ return value === null || typeof value === 'string'
371
+ }
372
+
373
+ function captureRect(value: unknown): value is RnxCaptureRect {
374
+ return (
375
+ captureRecord(value) &&
376
+ captureNumber(value.x) &&
377
+ captureNumber(value.y) &&
378
+ captureNumber(value.width) &&
379
+ captureNumber(value.height)
380
+ )
381
+ }
382
+
383
+ function captureOptionalString(value: unknown): value is string | undefined {
384
+ return value === undefined || typeof value === 'string'
385
+ }
386
+
387
+ function captureOptionalNullableString(
388
+ value: unknown,
389
+ ): value is string | null | undefined {
390
+ return value === undefined || captureNullableString(value)
391
+ }
392
+
393
+ function captureFontRegistration(value: unknown): value is RnxCaptureFontRegistration {
394
+ return (
395
+ captureRecord(value) &&
396
+ typeof value.family === 'string' &&
397
+ value.family.length > 0 &&
398
+ captureOptionalString(value.url) &&
399
+ captureOptionalString(value.sourceLabel) &&
400
+ captureOptionalNullableString(value.parsedFamily) &&
401
+ captureOptionalNullableString(value.parsedPostScript) &&
402
+ captureOptionalNullableString(value.fullName)
403
+ )
404
+ }
405
+
406
+ function capturePoint(value: unknown): value is { x: number; y: number } {
407
+ return captureRecord(value) && captureNumber(value.x) && captureNumber(value.y)
408
+ }
409
+
410
+ function captureNode(value: unknown): value is RnxCaptureNode {
411
+ if (
412
+ !captureRecord(value) ||
413
+ !captureInteger(value.nodeId) ||
414
+ !(value.parentNodeId === null || captureInteger(value.parentNodeId)) ||
415
+ !Array.isArray(value.childNodeIds) ||
416
+ !value.childNodeIds.every(captureInteger) ||
417
+ !captureInteger(value.paintIndex) ||
418
+ typeof value.type !== 'string' ||
419
+ !captureNullableString(value.id) ||
420
+ !captureNullableString(value.testID) ||
421
+ !captureNullableString(value.text) ||
422
+ !captureInteger(value.childCount) ||
423
+ !captureRecord(value.accessibility) ||
424
+ !captureRecord(value.interaction) ||
425
+ !captureRecord(value.layout) ||
426
+ !captureRecord(value.style) ||
427
+ !captureRecord(value.source) ||
428
+ !captureRecord(value.props) ||
429
+ !captureRecord(value.inspection)
430
+ ) {
431
+ return false
432
+ }
433
+ const accessibility = value.accessibility
434
+ if (
435
+ typeof accessibility.accessible !== 'boolean' ||
436
+ !captureNullableString(accessibility.label) ||
437
+ !captureNullableString(accessibility.role) ||
438
+ !captureNullableString(accessibility.hint) ||
439
+ !(
440
+ accessibility.liveRegion === null ||
441
+ accessibility.liveRegion === 'none' ||
442
+ accessibility.liveRegion === 'polite' ||
443
+ accessibility.liveRegion === 'assertive'
444
+ ) ||
445
+ !(accessibility.state === null || captureRecord(accessibility.state)) ||
446
+ !(accessibility.value === null || captureRecord(accessibility.value))
447
+ ) {
448
+ return false
449
+ }
450
+ const interaction = value.interaction
451
+ if (
452
+ typeof interaction.pressed !== 'boolean' ||
453
+ typeof interaction.pressable !== 'boolean' ||
454
+ typeof interaction.textInput !== 'boolean' ||
455
+ !(interaction.textSelection === null || captureRecord(interaction.textSelection)) ||
456
+ typeof interaction.secureTextEntry !== 'boolean' ||
457
+ !captureNullableString(interaction.placeholder) ||
458
+ !capturePoint(interaction.scrollOffset) ||
459
+ !(interaction.zoomScale === null || captureNumber(interaction.zoomScale)) ||
460
+ typeof interaction.nativePinchEnabled !== 'boolean' ||
461
+ !(
462
+ interaction.contentOffsetProp === null ||
463
+ captureRecord(interaction.contentOffsetProp)
464
+ ) ||
465
+ !(
466
+ interaction.strokeDashoffsetProp === null ||
467
+ captureNumber(interaction.strokeDashoffsetProp)
468
+ )
469
+ ) {
470
+ return false
471
+ }
472
+ const layout = value.layout
473
+ if (
474
+ !captureRect(layout.local) ||
475
+ !(layout.frame === null || captureRect(layout.frame)) ||
476
+ !capturePoint(layout.absolutePosition) ||
477
+ !(layout.visibleFrame === null || captureRect(layout.visibleFrame))
478
+ ) {
479
+ return false
480
+ }
481
+ const style = value.style
482
+ return (
483
+ captureRecord(style.resolved) &&
484
+ captureRecord(style.authored) &&
485
+ captureRecord(style.computed) &&
486
+ captureRecord(style.tokenBindings) &&
487
+ (value.textLines === undefined || Array.isArray(value.textLines))
488
+ )
489
+ }
490
+
491
+ export function isRnxScreenCapture(value: unknown): value is RnxScreenCapture {
492
+ if (
493
+ !captureRecord(value) ||
494
+ value.contractVersion !== RNX_SCREEN_CAPTURE_VERSION ||
495
+ !captureInteger(value.capturedAt) ||
496
+ !captureRecord(value.tree) ||
497
+ !(
498
+ value.fonts === undefined ||
499
+ (Array.isArray(value.fonts) &&
500
+ value.fonts.length <= 200 &&
501
+ value.fonts.every(captureFontRegistration))
502
+ ) ||
503
+ !(value.visual === undefined || captureRecord(value.visual))
504
+ ) {
505
+ return false
506
+ }
507
+ const tree = value.tree
508
+ const visual = value.visual
509
+ return (
510
+ captureRecord(tree.viewport) &&
511
+ captureInteger(tree.viewport.width) &&
512
+ tree.viewport.width > 0 &&
513
+ captureInteger(tree.viewport.height) &&
514
+ tree.viewport.height > 0 &&
515
+ captureInteger(tree.rootNodeId) &&
516
+ (tree.commitRevision === undefined || captureInteger(tree.commitRevision)) &&
517
+ Array.isArray(tree.nodes) &&
518
+ tree.nodes.every(captureNode) &&
519
+ (visual === undefined ||
520
+ (visual.kind === 'png' &&
521
+ (visual.producer === 'headless-cpu-app-surface' ||
522
+ visual.producer === 'browser-compositor-app-surface') &&
523
+ typeof visual.dataUri === 'string' &&
524
+ visual.dataUri.startsWith('data:image/png;base64,') &&
525
+ captureInteger(visual.pixelWidth) &&
526
+ visual.pixelWidth > 0 &&
527
+ captureInteger(visual.pixelHeight) &&
528
+ visual.pixelHeight > 0))
529
+ )
530
+ }
531
+
532
+ export function isRnxCaptureRegionAtlas(value: unknown): value is RnxCaptureRegionAtlas {
533
+ if (
534
+ !captureRecord(value) ||
535
+ value.kind !== 'png-atlas' ||
536
+ value.producer !== 'headless-cpu-app-surface' ||
537
+ typeof value.dataUri !== 'string' ||
538
+ !value.dataUri.startsWith('data:image/png;base64,') ||
539
+ !captureNumber(value.width) ||
540
+ value.width <= 0 ||
541
+ !captureNumber(value.height) ||
542
+ value.height <= 0 ||
543
+ !captureInteger(value.pixelWidth) ||
544
+ value.pixelWidth <= 0 ||
545
+ !captureInteger(value.pixelHeight) ||
546
+ value.pixelHeight <= 0 ||
547
+ !Array.isArray(value.regions) ||
548
+ value.regions.length === 0 ||
549
+ value.regions.length > 256
550
+ ) {
551
+ return false
552
+ }
553
+ const scale = value.pixelWidth / value.width
554
+ if (
555
+ !Number.isInteger(scale) ||
556
+ scale < 1 ||
557
+ scale > 4 ||
558
+ Math.abs(value.height * scale - value.pixelHeight) > 0.000_001
559
+ ) {
560
+ return false
561
+ }
562
+ const keys = new Set<string>()
563
+ for (const region of value.regions) {
564
+ if (
565
+ !captureRecord(region) ||
566
+ typeof region.key !== 'string' ||
567
+ region.key.length === 0 ||
568
+ keys.has(region.key) ||
569
+ !captureInteger(region.nodeId) ||
570
+ region.nodeId <= 0 ||
571
+ !captureRect(region.frame) ||
572
+ region.frame.width <= 0 ||
573
+ region.frame.height <= 0 ||
574
+ !captureRect(region.source) ||
575
+ region.source.x < 0 ||
576
+ region.source.y < 0 ||
577
+ region.source.width !== region.frame.width ||
578
+ region.source.height !== region.frame.height ||
579
+ region.source.x + region.source.width > value.width ||
580
+ region.source.y + region.source.height > value.height
581
+ ) {
582
+ return false
583
+ }
584
+ keys.add(region.key)
585
+ }
586
+ return true
587
+ }
@@ -0,0 +1 @@
1
+ export const DEFAULT_SOOTSIM_SHELL_URL = 'http://localhost:5173/'
@@ -0,0 +1,40 @@
1
+ // rnx cli version, resolved once and cached. standalone builds embed it so
2
+ // lazy command chunks do not depend on an npm package layout at runtime.
3
+
4
+ import { readFileSync } from 'node:fs'
5
+ import { fileURLToPath } from 'node:url'
6
+ import { rnxPublicBrand } from './public-brand.ts'
7
+
8
+ declare const __RNX_CLI_VERSION__: string
9
+
10
+ let cached: string | null = null
11
+
12
+ export function getCliVersion(): string {
13
+ if (cached != null) return cached
14
+ if (
15
+ typeof __RNX_CLI_VERSION__ !== 'undefined' &&
16
+ typeof __RNX_CLI_VERSION__ === 'string' &&
17
+ __RNX_CLI_VERSION__
18
+ ) {
19
+ cached = __RNX_CLI_VERSION__
20
+ return cached
21
+ }
22
+ // prefer package resolution (works for npm/global installs); fall back to
23
+ // the package.json next to this module's source for repo / bundled runs.
24
+ const candidates: Array<() => string> = [
25
+ () =>
26
+ fileURLToPath(import.meta.resolve(`${rnxPublicBrand.packageName}/package.json`)),
27
+ () => fileURLToPath(new URL('../package.json', import.meta.url)),
28
+ ]
29
+ for (const resolve of candidates) {
30
+ try {
31
+ const version = JSON.parse(readFileSync(resolve(), 'utf8')).version
32
+ if (typeof version === 'string' && version) {
33
+ cached = version
34
+ return cached
35
+ }
36
+ } catch {}
37
+ }
38
+ cached = '0.0.0'
39
+ return cached
40
+ }