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,830 @@
1
+ import fs from 'node:fs'
2
+ import path from 'node:path'
3
+ import {
4
+ parseSync,
5
+ Visitor,
6
+ type CallExpression,
7
+ type Expression,
8
+ type NewExpression,
9
+ type ParamPattern,
10
+ type Program,
11
+ } from 'oxc-parser'
12
+
13
+ export type OutboundCategory =
14
+ | 'runtime-delivery'
15
+ | 'bounded-diagnostics'
16
+ | 'explicit-user-action'
17
+ | 'local-development'
18
+ | 'same-origin-runtime'
19
+ | 'guest-app-network'
20
+ | 'release-delivery'
21
+
22
+ export interface OutboundEndpointDeclaration {
23
+ category: OutboundCategory
24
+ destination: string
25
+ preConsent: boolean
26
+ payload: string
27
+ }
28
+
29
+ export const OUTBOUND_ENDPOINTS: Record<string, OutboundEndpointDeclaration> = {
30
+ runtime_delivery: {
31
+ category: 'runtime-delivery',
32
+ destination:
33
+ 'https://contrast.dev/runtimes/manifest.json and selected runtime tarball',
34
+ preConsent: true,
35
+ payload: 'HTTP runtime version request; no project or app data',
36
+ },
37
+ bounded_diagnostics: {
38
+ category: 'bounded-diagnostics',
39
+ destination: 'configured PostHog ingestion host',
40
+ preConsent: false,
41
+ payload: 'package name/version/unsupported API or error class only',
42
+ },
43
+ contrast_user_action: {
44
+ category: 'explicit-user-action',
45
+ destination: 'configured Contrast origin',
46
+ preConsent: false,
47
+ payload:
48
+ 'explicit auth, billing, preview, issue report, test, or generated-flow request; a stored login may refresh auth/subscription state',
49
+ },
50
+ cli_delivery: {
51
+ category: 'explicit-user-action',
52
+ destination:
53
+ 'https://r2.rnxsim.com/rnx-cli/channels/stable and the selected executable/checksum',
54
+ preConsent: false,
55
+ payload: 'requested CLI version, platform, and architecture only',
56
+ },
57
+ cli_release_publish: {
58
+ category: 'release-delivery',
59
+ destination: 'configured R2 origin and https://r2.rnxsim.com/rnx-cli',
60
+ preConsent: false,
61
+ payload: 'release executables, checksums, version, and source identity',
62
+ },
63
+ release_notes: {
64
+ category: 'explicit-user-action',
65
+ destination: 'https://contrast.dev/api/changelog/sootsim',
66
+ preConsent: false,
67
+ payload: 'requested runtime version only',
68
+ },
69
+ electron_download: {
70
+ category: 'explicit-user-action',
71
+ destination: 'https://contrast.dev/api/electron-release/',
72
+ preConsent: false,
73
+ payload: 'platform, architecture, and requested desktop release',
74
+ },
75
+ local_development: {
76
+ category: 'local-development',
77
+ destination:
78
+ 'loopback development server, bridge, browser debugging, or app asset URL',
79
+ preConsent: true,
80
+ payload:
81
+ 'local protocol traffic needed to discover, load, and drive the selected app',
82
+ },
83
+ same_origin_runtime: {
84
+ category: 'same-origin-runtime',
85
+ destination: 'the origin already serving the rnx runtime',
86
+ preConsent: true,
87
+ payload: 'runtime assets and shared local settings',
88
+ },
89
+ guest_app_network: {
90
+ category: 'guest-app-network',
91
+ destination: 'URLs requested by the user app or supplied explicitly by the user',
92
+ preConsent: true,
93
+ payload: 'the app request itself; rnx does not add telemetry fields',
94
+ },
95
+ remote_display_server: {
96
+ category: 'explicit-user-action',
97
+ destination: 'display-server WebSocket URL configured by the embedding host',
98
+ preConsent: true,
99
+ payload:
100
+ 'display protocol identity, local input events, scroll lifecycle, resync requests, and applied/painted acknowledgements',
101
+ },
102
+ presigned_upload: {
103
+ category: 'explicit-user-action',
104
+ destination: 'server-issued presigned object upload URL',
105
+ preConsent: false,
106
+ payload: 'preview objects selected by the user for upload',
107
+ },
108
+ // its own entry, deliberately not folded into contrast_user_action. this is
109
+ // the only egress that sends the CONTENTS of the user's project somewhere,
110
+ // and to an operator-chosen host rather than a Contrast one, so it must not
111
+ // be readable as "the usual Contrast call".
112
+ rnx_cloud_box: {
113
+ category: 'explicit-user-action',
114
+ destination:
115
+ 'the RNX cloud box service named by --endpoint or RNX_BOX_ENDPOINT; no default, so this call cannot happen unless the user names a host',
116
+ preConsent: false,
117
+ payload:
118
+ 'every file git reports for the current checkout, then shell commands and file reads and writes for as long as the box shell is open',
119
+ },
120
+ swift_compile: {
121
+ category: 'explicit-user-action',
122
+ destination:
123
+ 'the origin already serving the app, or the absolute Contrast origin a node caller passes',
124
+ preConsent: false,
125
+ payload:
126
+ 'the project .swift sources, packed bundle resources and images, plus the signed preview token or ccdn_ api key that authorizes the compile',
127
+ },
128
+ rnx_cloud_sim: {
129
+ category: 'explicit-user-action',
130
+ destination:
131
+ 'https://rnxsim.com by default, or the test or disposable origin named by RNX_CLOUD_ORIGIN',
132
+ preConsent: false,
133
+ payload:
134
+ 'the Metro JavaScript bundle selected by rnx ios --remote, then structured simulator commands and lifecycle requests',
135
+ },
136
+ }
137
+
138
+ export interface OutboundCallDeclaration {
139
+ category: keyof typeof OUTBOUND_ENDPOINTS
140
+ count: number
141
+ }
142
+
143
+ // generated from the AST-level check below, then reviewed and classified.
144
+ // a new fetch, websocket, node HTTP request, or TCP connection has no entry and
145
+ // fails the package test until its destination and consent behavior are declared.
146
+ export const DECLARED_OUTBOUND_CALLS: Record<string, OutboundCallDeclaration> = {
147
+ 'packages/sootsim/cli/cloud-client.ts :: fetch :: `${apiOrigin}/v1/boxes`': {
148
+ category: 'rnx_cloud_sim',
149
+ count: 1,
150
+ },
151
+ 'packages/sootsim/cli/cloud-client.ts :: fetch :: `${session.apiOrigin}/v1/sims/${encodeURIComponent(session.simId)}/claim`':
152
+ { category: 'rnx_cloud_sim', count: 1 },
153
+ // closing a nano-box simulator deletes its box; the local names changed
154
+ // with the close but the destination did not.
155
+ 'packages/sootsim/cli/cloud-client.ts :: fetch :: `${session.apiOrigin}/v1/boxes/${encodeURIComponent(boxId)}`':
156
+ { category: 'rnx_cloud_sim', count: 1 },
157
+ // closing an instance simulator deletes it by its own id with its own token.
158
+ 'packages/sootsim/cli/cloud-client.ts :: fetch :: `${session.apiOrigin}/v1/sims/${encodeURIComponent(session.simId)}`':
159
+ { category: 'rnx_cloud_sim', count: 1 },
160
+ // `rnx remote` creates through the instance api and lists what it made.
161
+ // the create carries labels and wait=true in the query; the list filters
162
+ // by the remote label.
163
+ 'packages/sootsim/cli/cloud-client.ts :: fetch :: `${apiOrigin}/v1/sims?${query}`': {
164
+ category: 'rnx_cloud_sim',
165
+ count: 1,
166
+ },
167
+ 'packages/sootsim/cli/cloud-client.ts :: fetch :: `${apiOrigin}/v1/sims?${selector}`': {
168
+ category: 'rnx_cloud_sim',
169
+ count: 1,
170
+ },
171
+ // rereading one simulator and deleting one by id share the route: the
172
+ // reattach probe reads with either credential and `rnx remote stop`
173
+ // deletes with the account credential.
174
+ 'packages/sootsim/cli/cloud-client.ts :: fetch :: `${apiOrigin}/v1/sims/${encodeURIComponent(options.simId)}`':
175
+ { category: 'rnx_cloud_sim', count: 2 },
176
+ 'packages/sootsim/cli/cloud-client.ts :: fetch :: `${this.session.apiOrigin}/v1/sims/${encodeURIComponent(this.session.simId)}/commands`':
177
+ { category: 'rnx_cloud_sim', count: 1 },
178
+ 'packages/sootsim/cli/cloud-client.ts :: fetch :: bundleUrl': {
179
+ category: 'guest_app_network',
180
+ count: 1,
181
+ },
182
+ 'packages/sootsim/cli/commands/test.ts :: fetch :: endpoint': {
183
+ category: 'contrast_user_action',
184
+ count: 1,
185
+ },
186
+ 'packages/sootsim/cli/commands/test.ts :: fetch :: pollUrl': {
187
+ category: 'contrast_user_action',
188
+ count: 1,
189
+ },
190
+ // compiling a project's .swift sources needs a swift toolchain, so the
191
+ // bundler posts them to the app's own /api/swift-compile. it only fires with
192
+ // a compile credential and only for a project that has .swift sources.
193
+ 'packages/sootsim/src/swift-submit.ts :: fetch :: url': {
194
+ category: 'swift_compile',
195
+ count: 1,
196
+ },
197
+ 'packages/sootsim/src/cloud.ts :: fetch :: url': {
198
+ category: 'rnx_cloud_sim',
199
+ count: 1,
200
+ },
201
+ // maestro's flow `http` API. the url is whatever the flow author wrote, and
202
+ // it is fetched from a child process because that API is synchronous.
203
+ 'packages/sootsim/cli/internal-child.ts :: fetch :: request.url': {
204
+ category: 'guest_app_network',
205
+ count: 1,
206
+ },
207
+ // `rnx box create` uploads the checkout to the box service and then talks to
208
+ // it. the destination is whatever the user named; there is no default.
209
+ 'packages/rnx-cloud-box/src/client.ts :: fetch :: endpointOf(base, name, verb, search)':
210
+ {
211
+ category: 'rnx_cloud_box',
212
+ count: 1,
213
+ },
214
+ 'packages/rnx-cloud-box/src/client.ts :: websocket :: url': {
215
+ category: 'rnx_cloud_box',
216
+ count: 1,
217
+ },
218
+ // a micro box's own Browser Run session, over the worker's BROWSER binding.
219
+ // the binding routes on path alone and ignores the host, so `browser.invalid`
220
+ // is a placeholder that exists only so URL can parse the search parameters.
221
+ // nothing leaves Cloudflare and no project content is sent.
222
+ 'packages/rnx-cloud-box/src/browser-run.ts :: fetch :: sessionEndpoint(undefined, { keepAliveMs, includeTargets: true })':
223
+ { category: 'rnx_cloud_box', count: 1 },
224
+ 'packages/rnx-cloud-box/src/browser-run.ts :: fetch :: `https://browser.invalid/v1/devtools/browser/${sessionId}/json/list`':
225
+ { category: 'rnx_cloud_box', count: 1 },
226
+ // twice: closing the session, and the CDP websocket upgrade that navigates a
227
+ // target. both address the same session over the same binding.
228
+ 'packages/rnx-cloud-box/src/browser-run.ts :: fetch :: `https://browser.invalid/v1/devtools/browser/${sessionId}`':
229
+ { category: 'rnx_cloud_box', count: 2 },
230
+ // the account's own session meter: starting, listing and heartbeating the
231
+ // session a cloud box is billed against. no project content.
232
+ "packages/rnx-cloud-box/src/client.ts :: fetch :: `${options.apiOrigin.replace(/\\/+$/, '')}/api/v1/rnx/sessions${ options.accountId ? `?accountId=${encodeURIComponent(options.accountId)}` : '' }`":
233
+ { category: 'contrast_user_action', count: 1 },
234
+ 'packages/rnx-cloud-box/src/client.ts :: fetch :: url': {
235
+ category: 'contrast_user_action',
236
+ count: 1,
237
+ },
238
+ // `rnx box connect` asks the account service which box that ID or name is
239
+ // and for the box's own token. the account is the only thing that knows, so
240
+ // this is the one call that turns a name into an authorized Box reference.
241
+ 'packages/rnx-cloud-box/src/client.ts :: fetch :: connectUrl': {
242
+ category: 'contrast_user_action',
243
+ count: 1,
244
+ },
245
+ "packages/rnx-cloud-box/src/box-do.ts :: fetch :: `${origin}/api/v1/rnx/sessions${serviceAuthorized ? '/internal' : ''}`":
246
+ { category: 'contrast_user_action', count: 1 },
247
+ "packages/rnx-cloud-box/src/box-do.ts :: fetch :: `${origin}/api/v1/rnx/sessions/${ serviceAuthority ? 'internal/' : '' }${encodeURIComponent(sessionId)}${ accountId ? `?accountId=${encodeURIComponent(accountId)}` : '' }`":
248
+ { category: 'contrast_user_action', count: 1 },
249
+ 'packages/rnx-cloud-box/src/box-do.ts :: fetch :: `${origin}/api/v1/rnx/sessions/internal/${encodeURIComponent(sessionId)}`':
250
+ { category: 'contrast_user_action', count: 1 },
251
+ // the Cloudflare Worker forwards the already-authenticated request to the
252
+ // selected Durable Object in the same service.
253
+ 'packages/rnx-cloud-box/src/worker.ts :: fetch :: request': {
254
+ category: 'same_origin_runtime',
255
+ count: 1,
256
+ },
257
+ // a LOCAL box authorizing its own checkout with the loopback bridge daemon.
258
+ // this one refuses to guess a port, so it cannot reach anything but a bridge
259
+ // the user's own session is running; see the note on ws-bridge.ts below
260
+ // about splitting these if bridgeWsUrl ever returns a remote endpoint.
261
+ 'packages/sootsim/cli/commands/box/plane-share.ts :: websocket :: bridgeWsUrl(port)': {
262
+ category: 'local_development',
263
+ count: 2,
264
+ },
265
+ 'packages/contrast-runtime-delivery/src/index.ts :: fetch :: url': {
266
+ category: 'runtime_delivery',
267
+ count: 2,
268
+ },
269
+ "packages/sootsim/scripts/demo-app-registry.ts :: fetch :: 'http://localhost:1986/'": {
270
+ category: 'local_development',
271
+ count: 1,
272
+ },
273
+ 'packages/sootsim/scripts/dev-server-scanner.ts :: http.request :: { hostname, port, path, method, headers }':
274
+ {
275
+ category: 'local_development',
276
+ count: 1,
277
+ },
278
+ 'packages/sootsim/scripts/dev-server-scanner.ts :: sock.connect :: port': {
279
+ category: 'local_development',
280
+ count: 1,
281
+ },
282
+ 'packages/sootsim/scripts/smoke-cli-binary.ts :: fetch :: `http://127.0.0.1:${port}/healthz`':
283
+ { category: 'local_development', count: 1 },
284
+ // the binary smoke's replacement-module probe against its own loopback server
285
+ 'packages/sootsim/scripts/smoke-cli-binary.ts :: fetch :: url': {
286
+ category: 'local_development',
287
+ count: 1,
288
+ },
289
+ 'packages/sootsim-engine/src/auth/openLogin.ts :: fetch :: `${CONTRAST_ORIGIN}/api/dev-login`':
290
+ { category: 'contrast_user_action', count: 1 },
291
+ 'packages/sootsim-engine/src/auth/shared-session.ts :: fetch :: `${CONTRAST_ORIGIN}/api/auth/me`':
292
+ { category: 'contrast_user_action', count: 1 },
293
+ 'packages/sootsim-engine/src/auth/shared-session.ts :: fetch :: `${CONTRAST_ORIGIN}/api/auth/sign-out`':
294
+ { category: 'contrast_user_action', count: 1 },
295
+ "packages/sootsim-engine/src/billing/client.ts :: fetch :: apiUrl('/api/sootsim/billing/checkout')":
296
+ { category: 'contrast_user_action', count: 1 },
297
+ 'packages/sootsim-engine/src/billing/client.ts :: fetch :: apiUrl(path)': {
298
+ category: 'contrast_user_action',
299
+ count: 3,
300
+ },
301
+ 'packages/sootsim-engine/src/demo/demo-share-register.ts :: fetch :: shareUrl': {
302
+ category: 'guest_app_network',
303
+ count: 1,
304
+ },
305
+ 'packages/sootsim-engine/src/engine/android-emoji-font.ts :: fetch :: url': {
306
+ category: 'same_origin_runtime',
307
+ count: 1,
308
+ },
309
+ 'packages/sootsim-engine/src/engine/app-font-loader.ts :: fetch :: manifestFetchUrl(origin)':
310
+ { category: 'local_development', count: 1 },
311
+ 'packages/sootsim-engine/src/engine/app-font-registry.ts :: fetch :: resolvedUrl': {
312
+ category: 'guest_app_network',
313
+ count: 1,
314
+ },
315
+ 'packages/sootsim-engine/src/engine/config-plugin-splash-loader.ts :: fetch :: manifestFetchUrl(origin)':
316
+ { category: 'local_development', count: 1 },
317
+ 'packages/sootsim-engine/src/engine/emoji-metrics-font-loader.ts :: fetch :: attemptedBundledUrl':
318
+ { category: 'same_origin_runtime', count: 1 },
319
+ 'packages/sootsim-engine/src/engine/expo-app-config.ts :: fetch :: manifestFetchUrl(origin)':
320
+ { category: 'local_development', count: 1 },
321
+ 'packages/sootsim-engine/src/engine/image-loader.ts :: fetch :: resolveImageFetchUri(uri)':
322
+ { category: 'guest_app_network', count: 1 },
323
+ 'packages/sootsim-engine/src/engine/language-font-loader.ts :: fetch :: url': {
324
+ category: 'same_origin_runtime',
325
+ count: 1,
326
+ },
327
+ 'packages/sootsim-engine/src/engine/lottie-runtime.ts :: fetch :: descriptor.value': {
328
+ category: 'guest_app_network',
329
+ count: 2,
330
+ },
331
+ 'packages/sootsim-engine/src/ios/sf-symbol-catalog.ts :: fetch :: `${catalogBaseUrl}${asset}.json`':
332
+ {
333
+ category: 'same_origin_runtime',
334
+ count: 1,
335
+ },
336
+ 'packages/sootsim-engine/src/engine/system-font-extras.ts :: fetch :: url': {
337
+ category: 'same_origin_runtime',
338
+ count: 1,
339
+ },
340
+ 'packages/sootsim-engine/src/ExternalApp.tsx :: fetch :: getBundleProxyUrl(devServerUrl)':
341
+ { category: 'guest_app_network', count: 1 },
342
+ 'packages/sootsim-engine/src/ExternalApp.tsx :: fetch :: requestUrl': {
343
+ category: 'guest_app_network',
344
+ count: 1,
345
+ },
346
+ 'packages/sootsim-engine/src-electron/tray.ts :: client.get :: parsed': {
347
+ category: 'guest_app_network',
348
+ count: 1,
349
+ },
350
+ 'packages/sootsim-engine/src/mcp/ws-bridge.ts :: fetch :: bridgeHealthUrl': {
351
+ category: 'local_development',
352
+ count: 1,
353
+ },
354
+ 'packages/sootsim-engine/src/mcp/ws-bridge.ts :: websocket :: resolveSootsimBridgeWebSocketUrl({ bridgePort, href: window.location.href, })':
355
+ { category: 'local_development', count: 1 },
356
+ 'packages/sootsim-engine/src/remote-stream/browser-tenant-proxy.ts :: websocket :: this.config.url':
357
+ { category: 'remote_display_server', count: 1 },
358
+ 'packages/sootsim-engine/src/menu/menuActions.ts :: fetch :: screenshot': {
359
+ category: 'guest_app_network',
360
+ count: 1,
361
+ },
362
+ 'packages/sootsim-engine/src/preview/fetch-recorder.ts :: fetch :: absolute': {
363
+ category: 'guest_app_network',
364
+ count: 2,
365
+ },
366
+ // the path is a parameter so one uploader serves both callers: recordings use
367
+ // /api/preview/upload/{init,finalize} (the default) and `rnx preview` passes
368
+ // /api/v1/previews{,/finalize}. both are first-party contrast endpoints.
369
+ 'packages/sootsim-engine/src/preview/presigned-upload.ts :: fetch :: `${origin}${endpoints.finalize}`':
370
+ { category: 'contrast_user_action', count: 1 },
371
+ 'packages/sootsim-engine/src/preview/presigned-upload.ts :: fetch :: `${origin}${endpoints.init}`':
372
+ { category: 'contrast_user_action', count: 1 },
373
+ 'packages/sootsim-engine/src/preview/presigned-upload.ts :: fetch :: job.url': {
374
+ category: 'presigned_upload',
375
+ count: 1,
376
+ },
377
+ 'packages/sootsim-engine/src/preview/warm-bundle-cache.ts :: fetch :: url': {
378
+ category: 'guest_app_network',
379
+ count: 1,
380
+ },
381
+ "packages/sootsim-engine/src/projects/projects-persistence.ts :: fetch :: '/__sootsim/shared-config'":
382
+ { category: 'same_origin_runtime', count: 1 },
383
+ 'packages/sootsim-engine/src/react-native/index.ts :: fetch :: resource': {
384
+ category: 'guest_app_network',
385
+ count: 1,
386
+ },
387
+ 'packages/sootsim-engine/src/react-native/Keyboard.ts :: fetch :: url': {
388
+ category: 'guest_app_network',
389
+ count: 1,
390
+ },
391
+ 'packages/sootsim-engine/src/render-worker/canvaskit-shared.ts :: fetch :: fontUrl': {
392
+ category: 'same_origin_runtime',
393
+ count: 1,
394
+ },
395
+ 'packages/sootsim-engine/src/render-worker/canvaskit-shared.ts :: fetch :: pipelineCacheUrl':
396
+ { category: 'same_origin_runtime', count: 1 },
397
+ 'packages/sootsim-engine/src/render-worker/canvaskit-shared.ts :: fetch :: wasmUrl': {
398
+ category: 'same_origin_runtime',
399
+ count: 1,
400
+ },
401
+ // the cpu simulator iframe document's dedicated worker loads the headless
402
+ // canvaskit wasm and the font and asset manifests from the page's own
403
+ // `/deps-native/engine-assets/` directory.
404
+ 'packages/sootsim-engine/src/headless/browser-tenant-worker.ts :: fetch :: new URL(name, assetBase).href':
405
+ { category: 'same_origin_runtime', count: 1 },
406
+ // hashes its own delivered script to identify the engine to display clients.
407
+ 'packages/sootsim-engine/src/headless/browser-tenant-worker.ts :: fetch :: workerSourceUrl':
408
+ { category: 'same_origin_runtime', count: 1 },
409
+ // the tenant build's built-in app chunk prefetch list and the chunk bodies
410
+ // it names, resolved against the worker chunk's own url.
411
+ 'packages/sootsim-engine/src/render-worker/app-in-worker.tsx :: fetch :: listUrl': {
412
+ category: 'same_origin_runtime',
413
+ count: 1,
414
+ },
415
+ 'packages/sootsim-engine/src/render-worker/app-in-worker.tsx :: fetch :: new URL(file, listUrl)':
416
+ { category: 'same_origin_runtime', count: 1 },
417
+ 'packages/sootsim-engine/src/render-worker/host.ts :: fetch :: msg.fallbackUrl': {
418
+ category: 'guest_app_network',
419
+ count: 1,
420
+ },
421
+ 'packages/sootsim-engine/src/render-worker/video-host.ts :: fetch :: uri': {
422
+ category: 'guest_app_network',
423
+ count: 1,
424
+ },
425
+ 'packages/sootsim-engine/src/render-worker/worker-font-loader.ts :: fetch :: url': {
426
+ category: 'same_origin_runtime',
427
+ count: 1,
428
+ },
429
+ 'packages/sootsim-engine/src/render-worker/worker-handlers/lifecycle.ts :: fetch :: url':
430
+ { category: 'guest_app_network', count: 1 },
431
+ 'packages/sootsim-engine/src/screenshots/capture-store.ts :: fetch :: cap.dataUrl': {
432
+ category: 'same_origin_runtime',
433
+ count: 1,
434
+ },
435
+ 'packages/sootsim-engine/src/menu/menuActions.ts :: fetch :: `http://127.0.0.1:${port}/__send-to-box`':
436
+ { category: 'local_development', count: 1 },
437
+ "packages/sootsim-engine/src/screenshots/capture-store.ts :: fetch :: captureApiUrl(ref, 'grid')":
438
+ { category: 'contrast_user_action', count: 1 },
439
+ 'packages/sootsim-engine/src/screenshots/capture-store.ts :: fetch :: captureApiUrl(ref, variant)':
440
+ { category: 'contrast_user_action', count: 1 },
441
+ 'packages/sootsim-engine/src/screenshots/capture-store.ts :: fetch :: captureApiUrl(ref)':
442
+ { category: 'contrast_user_action', count: 1 },
443
+ "packages/sootsim-engine/src/server-discovery.ts :: fetch :: '/__server-scan'": {
444
+ category: 'local_development',
445
+ count: 1,
446
+ },
447
+ 'packages/sootsim-engine/src/server-discovery.ts :: fetch :: `${gatewayOrigin}/__scan`':
448
+ { category: 'local_development', count: 1 },
449
+ "packages/sootsim-engine/src/settings/persistence.ts :: fetch :: '/__sootsim/shared-config'":
450
+ { category: 'same_origin_runtime', count: 2 },
451
+ 'packages/sootsim/cli/app-fonts.ts :: fetch :: `${origin}/`': {
452
+ category: 'local_development',
453
+ count: 1,
454
+ },
455
+ 'packages/sootsim/cli/app-fonts.ts :: fetch :: url': {
456
+ category: 'local_development',
457
+ count: 1,
458
+ },
459
+ 'packages/sootsim/cli/app-splash.ts :: fetch :: url': {
460
+ category: 'local_development',
461
+ count: 1,
462
+ },
463
+ 'packages/sootsim/cli/commands/control.ts :: fetch :: imageUrl': {
464
+ category: 'local_development',
465
+ count: 1,
466
+ },
467
+ 'packages/sootsim/cli/commands/control.ts :: fetch :: spec.url': {
468
+ category: 'local_development',
469
+ count: 1,
470
+ },
471
+ 'packages/sootsim/cli/commands/control.ts :: websocket :: `ws://127.0.0.1:${port}`': {
472
+ category: 'local_development',
473
+ count: 1,
474
+ },
475
+ 'packages/sootsim/cli/commands/cpu-profile.ts :: fetch :: `http://localhost:${cdpPort}/json/list`':
476
+ { category: 'local_development', count: 1 },
477
+ 'packages/sootsim/cli/commands/cpu-profile.ts :: websocket :: wsUrl': {
478
+ category: 'local_development',
479
+ count: 1,
480
+ },
481
+ 'packages/sootsim/cli/commands/install-desktop.ts :: fetch :: url': {
482
+ category: 'electron_download',
483
+ count: 1,
484
+ },
485
+ "packages/sootsim/cli/commands/login.ts :: fetch :: `${origin.replace(/\\/$/, '')}/api/dev-login`":
486
+ { category: 'local_development', count: 1 },
487
+ "packages/sootsim/cli/commands/logout.ts :: fetch :: `${current.origin.replace(/\\/$/, '')}/api/auth/sign-out`":
488
+ { category: 'contrast_user_action', count: 1 },
489
+ "packages/sootsim/cli/commands/maestro-generate.ts :: fetch :: `${args.origin.replace(/\\/$/, '')}/api/llm`":
490
+ { category: 'contrast_user_action', count: 1 },
491
+ 'packages/sootsim/cli/commands/report-issue.ts :: fetch :: url': {
492
+ category: 'contrast_user_action',
493
+ count: 1,
494
+ },
495
+ 'packages/sootsim/cli/commands/runtime.ts :: websocket :: `ws://127.0.0.1:${port}`': {
496
+ category: 'local_development',
497
+ count: 1,
498
+ },
499
+ 'packages/sootsim/cli/commands/screenshots-capture.ts :: fetch :: `${apiOrigin}/api/sootsim/screenshot-capture?ref=${encodeURIComponent(ref)}&org=${encodeURIComponent(org)}`':
500
+ { category: 'contrast_user_action', count: 1 },
501
+ 'packages/sootsim/cli/commands/screenshots-capture.ts :: fetch :: `${apiOrigin}/api/sootsim/screenshot-decks?org=${encodeURIComponent(org)}&repo=${encodeURIComponent(repo)}`':
502
+ { category: 'contrast_user_action', count: 1 },
503
+ 'packages/sootsim/cli/cli-update.ts :: fetch :: url': {
504
+ category: 'cli_delivery',
505
+ count: 1,
506
+ },
507
+ 'packages/sootsim/scripts/publish-cli-binaries.ts :: fetch :: `${url}?sha256=${digest}`':
508
+ {
509
+ category: 'cli_release_publish',
510
+ count: 1,
511
+ },
512
+ 'packages/sootsim/scripts/publish-cli-binaries.ts :: fetch :: `${publicOrigin}/${channelKey}?sourceSha=${sourceSha}`':
513
+ {
514
+ category: 'cli_release_publish',
515
+ count: 1,
516
+ },
517
+ 'packages/sootsim/cli/commands/upload.ts :: fetch :: url': {
518
+ category: 'presigned_upload',
519
+ count: 1,
520
+ },
521
+ 'packages/sootsim/cli/recording-access.ts :: fetch :: `${billingOrigin}/api/sootsim/billing/subscription`':
522
+ { category: 'contrast_user_action', count: 1 },
523
+ "packages/sootsim/cli/run-registry.ts :: fetch :: `${args.origin.replace(/\\/$/, '')}/api/sootsim/test-runs`":
524
+ { category: 'contrast_user_action', count: 1 },
525
+ 'packages/sootsim/cli/runtime-notes.ts :: fetch-alias :: url': {
526
+ category: 'release_notes',
527
+ count: 1,
528
+ },
529
+ 'packages/sootsim/cli/telemetry.ts :: fetch :: `${config.host}/batch/`': {
530
+ category: 'bounded_diagnostics',
531
+ count: 1,
532
+ },
533
+ // the inventory records the call expression, not the URL, so this entry can
534
+ // no longer show its own destination. bridgeWsUrl() is loopback-only today;
535
+ // when a cloud box makes it return a remote wss:// endpoint, that egress
536
+ // would land under this same local_development entry and go unnoticed. split
537
+ // the declaration then, rather than letting one line cover both.
538
+ 'packages/sootsim/cli/ws-bridge.ts :: websocket :: bridgeWsUrl(wsPort)': {
539
+ category: 'local_development',
540
+ count: 1,
541
+ },
542
+ 'packages/sootsim/src/agent-daemon-client.ts :: socket.connect :: port': {
543
+ category: 'local_development',
544
+ count: 1,
545
+ },
546
+ 'packages/sootsim/src/agent-daemon-client.ts :: websocket :: `ws://127.0.0.1:${this.port}`':
547
+ { category: 'local_development', count: 1 },
548
+ "packages/sootsim/src/auth/shared-session.ts :: fetch :: `${origin.replace(/\\/$/, '')}/api/auth/me`":
549
+ { category: 'contrast_user_action', count: 1 },
550
+ "packages/sootsim/src/dev-bundle-resolution.ts :: fetch :: '/__server-scan'": {
551
+ category: 'local_development',
552
+ count: 1,
553
+ },
554
+ 'packages/sootsim/src/dev-bundle-resolution.ts :: fetch :: `/__fetch-proxy?url=${encodeURIComponent(targetUrl)}`':
555
+ { category: 'guest_app_network', count: 1 },
556
+ 'packages/sootsim/src/dev-bundle-resolution.ts :: fetch :: targetUrl': {
557
+ category: 'guest_app_network',
558
+ count: 1,
559
+ },
560
+ 'packages/sootsim/src/dev-server-open.ts :: http.get :: `http://127.0.0.1:${port}/__server-scan`':
561
+ { category: 'local_development', count: 1 },
562
+ "packages/sootsim/src/host/fetch-proxy-handler.ts :: transport.request :: { hostname: normalizeHostname(targetUrl.hostname), port: targetUrl.port || (targetUrl.protocol === 'https:' ? 443 : 80), path: targetUrl.pathname + targetUrl.search, method, headers: targetHeaders, ...loopbackTlsOptions(targetUrl), }":
563
+ { category: 'guest_app_network', count: 1 },
564
+ "packages/sootsim/src/host/fetch-proxy-handler.ts :: transport.request :: { hostname: normalizeHostname(targetUrl.hostname), port: targetUrl.port || (targetUrl.protocol === 'https:' ? 443 : 80), path: targetUrl.pathname + targetUrl.search, method: req.method, headers: fwdHeaders, ...loopbackTlsOptions(targetUrl), }":
565
+ { category: 'guest_app_network', count: 1 },
566
+ 'packages/sootsim/src/host/websocket-proxy.ts :: node-request :: upstreamRequestUrl': {
567
+ category: 'guest_app_network',
568
+ count: 1,
569
+ },
570
+ 'packages/sootsim/src/screenshots/org-deck.ts :: fetch :: url': {
571
+ category: 'contrast_user_action',
572
+ count: 1,
573
+ },
574
+ }
575
+
576
+ interface DetectedOutboundCall {
577
+ key: string
578
+ count: number
579
+ }
580
+
581
+ function sourceFilesUnder(root: string): string[] {
582
+ const files: string[] = []
583
+ const pending = [root]
584
+ while (pending.length > 0) {
585
+ const current = pending.pop()
586
+ if (!current) continue
587
+ for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
588
+ const fullPath = path.join(current, entry.name)
589
+ if (entry.isDirectory()) {
590
+ if (entry.name !== 'vendor' && entry.name !== 'test-fixtures')
591
+ pending.push(fullPath)
592
+ continue
593
+ }
594
+ if (/\.(?:ts|tsx)$/.test(entry.name) && !entry.name.endsWith('.d.ts')) {
595
+ files.push(fullPath)
596
+ }
597
+ }
598
+ }
599
+ return files.sort()
600
+ }
601
+
602
+ interface NetworkNames {
603
+ fetch: Set<string>
604
+ httpClient: Set<string>
605
+ netClient: Set<string>
606
+ socket: Set<string>
607
+ nodeRequest: Set<string>
608
+ nodeGet: Set<string>
609
+ }
610
+
611
+ function networkNames(source: Program): NetworkNames {
612
+ const names: NetworkNames = {
613
+ fetch: new Set(['fetch']),
614
+ httpClient: new Set(['http', 'https']),
615
+ netClient: new Set(['net']),
616
+ socket: new Set(),
617
+ nodeRequest: new Set(),
618
+ nodeGet: new Set(),
619
+ }
620
+ const isClientExpression = (node: Expression, clients: Set<string>): boolean => {
621
+ if (node.type === 'Identifier') return clients.has(node.name)
622
+ if (node.type === 'ParenthesizedExpression')
623
+ return isClientExpression(node.expression, clients)
624
+ return (
625
+ node.type === 'ConditionalExpression' &&
626
+ isClientExpression(node.consequent, clients) &&
627
+ isClientExpression(node.alternate, clients)
628
+ )
629
+ }
630
+ const collectFetchParameters = (parameters: ParamPattern[]): void => {
631
+ for (const parameter of parameters) {
632
+ if (
633
+ parameter.type === 'AssignmentPattern' &&
634
+ parameter.left.type === 'Identifier' &&
635
+ parameter.right.type === 'Identifier' &&
636
+ names.fetch.has(parameter.right.name)
637
+ ) {
638
+ names.fetch.add(parameter.left.name)
639
+ }
640
+ }
641
+ }
642
+ new Visitor({
643
+ ArrowFunctionExpression(node) {
644
+ collectFetchParameters(node.params)
645
+ },
646
+ FunctionDeclaration(node) {
647
+ collectFetchParameters(node.params)
648
+ },
649
+ FunctionExpression(node) {
650
+ collectFetchParameters(node.params)
651
+ },
652
+ ImportDeclaration(node) {
653
+ const moduleName = node.source.value.replace(/^node:/, '')
654
+ if (moduleName === 'http' || moduleName === 'https') {
655
+ for (const specifier of node.specifiers) {
656
+ if (specifier.type === 'ImportSpecifier') {
657
+ const imported =
658
+ specifier.imported.type === 'Identifier'
659
+ ? specifier.imported.name
660
+ : specifier.imported.value
661
+ if (imported === 'request') names.nodeRequest.add(specifier.local.name)
662
+ if (imported === 'get') names.nodeGet.add(specifier.local.name)
663
+ }
664
+ if (
665
+ specifier.type === 'ImportDefaultSpecifier' ||
666
+ specifier.type === 'ImportNamespaceSpecifier'
667
+ ) {
668
+ names.httpClient.add(specifier.local.name)
669
+ }
670
+ }
671
+ }
672
+ if (moduleName === 'net') {
673
+ for (const specifier of node.specifiers) {
674
+ if (
675
+ specifier.type === 'ImportDefaultSpecifier' ||
676
+ specifier.type === 'ImportNamespaceSpecifier'
677
+ ) {
678
+ names.netClient.add(specifier.local.name)
679
+ }
680
+ }
681
+ }
682
+ },
683
+ VariableDeclarator(node) {
684
+ if (node.id.type !== 'Identifier' || !node.init) return
685
+ if (node.init.type === 'Identifier' && names.fetch.has(node.init.name)) {
686
+ names.fetch.add(node.id.name)
687
+ }
688
+ if (isClientExpression(node.init, names.httpClient)) {
689
+ names.httpClient.add(node.id.name)
690
+ }
691
+ if (isClientExpression(node.init, names.netClient)) {
692
+ names.netClient.add(node.id.name)
693
+ }
694
+ if (isClientExpression(node.init, names.nodeRequest)) {
695
+ names.nodeRequest.add(node.id.name)
696
+ }
697
+ if (
698
+ node.init.type === 'NewExpression' &&
699
+ node.init.callee.type === 'MemberExpression' &&
700
+ !node.init.callee.computed &&
701
+ node.init.callee.property.type === 'Identifier' &&
702
+ node.init.callee.property.name === 'Socket' &&
703
+ isClientExpression(node.init.callee.object, names.netClient)
704
+ ) {
705
+ names.socket.add(node.id.name)
706
+ }
707
+ },
708
+ }).visit(source)
709
+ return names
710
+ }
711
+
712
+ function callKind(
713
+ node: CallExpression | NewExpression,
714
+ names: NetworkNames,
715
+ source: string,
716
+ ): string | null {
717
+ if (node.type === 'NewExpression' && node.callee.type === 'Identifier') {
718
+ if (node.callee.name === 'WebSocket') return 'websocket'
719
+ if (node.callee.name === 'EventSource') return 'event-source'
720
+ if (node.callee.name === 'XMLHttpRequest') return 'xml-http-request'
721
+ return null
722
+ }
723
+ if (node.type !== 'CallExpression') return null
724
+ const expression = node.callee
725
+ if (expression.type === 'Identifier') {
726
+ if (names.fetch.has(expression.name)) {
727
+ return expression.name === 'fetch' ? 'fetch' : 'fetch-alias'
728
+ }
729
+ if (names.nodeRequest.has(expression.name)) return 'node-request'
730
+ if (names.nodeGet.has(expression.name)) return 'node-get'
731
+ return null
732
+ }
733
+ if (
734
+ expression.type !== 'MemberExpression' ||
735
+ expression.computed ||
736
+ expression.property.type !== 'Identifier'
737
+ ) {
738
+ return null
739
+ }
740
+ const method = expression.property.name
741
+ if (method === 'fetch') return 'fetch'
742
+ if (method === 'sendBeacon') return 'send-beacon'
743
+ const ownerExpression = expression.object
744
+ const owner = source
745
+ .slice(ownerExpression.start, ownerExpression.end)
746
+ .replace(/\s+/g, ' ')
747
+ const knownHttpClient =
748
+ ownerExpression.type === 'Identifier' && names.httpClient.has(ownerExpression.name)
749
+ const knownNetClient =
750
+ ownerExpression.type === 'Identifier' && names.netClient.has(ownerExpression.name)
751
+ const knownSocket =
752
+ ownerExpression.type === 'Identifier' && names.socket.has(ownerExpression.name)
753
+ if (method === 'request' && (knownHttpClient || owner === 'transport')) {
754
+ return `${owner}.request`
755
+ }
756
+ if (method === 'get' && knownHttpClient) return `${owner}.get`
757
+ if (
758
+ (method === 'connect' || method === 'createConnection') &&
759
+ (knownNetClient || knownSocket)
760
+ ) {
761
+ return `${owner}.${method}`
762
+ }
763
+ return null
764
+ }
765
+
766
+ function firstArgument(node: CallExpression | NewExpression, source: string): string {
767
+ const first = node.arguments[0]
768
+ return first
769
+ ? source.slice(first.start, first.end).replace(/\s+/g, ' ').trim()
770
+ : '<none>'
771
+ }
772
+
773
+ export function detectOutboundCalls(repositoryRoot: string): DetectedOutboundCall[] {
774
+ const roots = [
775
+ path.join(repositoryRoot, 'packages/sootsim/cli'),
776
+ path.join(repositoryRoot, 'packages/sootsim/scripts'),
777
+ path.join(repositoryRoot, 'packages/sootsim/src'),
778
+ path.join(repositoryRoot, 'packages/sootsim-engine/src'),
779
+ path.join(repositoryRoot, 'packages/sootsim-engine/src-electron'),
780
+ path.join(repositoryRoot, 'packages/contrast-runtime-delivery/src'),
781
+ path.join(repositoryRoot, 'packages/rnx-cloud-box/src'),
782
+ ]
783
+ const counts = new Map<string, number>()
784
+ for (const file of roots.flatMap(sourceFilesUnder)) {
785
+ if (file.endsWith('/outbound-endpoints.ts')) continue
786
+ const source = fs.readFileSync(file, 'utf8')
787
+ const program = parseSync(file, source, {
788
+ lang: file.endsWith('.tsx') ? 'tsx' : 'ts',
789
+ }).program
790
+ const names = networkNames(program)
791
+ const relative = path.relative(repositoryRoot, file).split(path.sep).join('/')
792
+ const recordCall = (node: CallExpression | NewExpression): void => {
793
+ const kind = callKind(node, names, source)
794
+ if (kind) {
795
+ const key = `${relative} :: ${kind} :: ${firstArgument(node, source)}`
796
+ counts.set(key, (counts.get(key) ?? 0) + 1)
797
+ }
798
+ }
799
+ new Visitor({
800
+ CallExpression: recordCall,
801
+ NewExpression: recordCall,
802
+ }).visit(program)
803
+ }
804
+ return [...counts.entries()]
805
+ .map(([key, count]) => ({ key, count }))
806
+ .sort((a, b) => a.key.localeCompare(b.key))
807
+ }
808
+
809
+ export function undeclaredOutboundCalls(repositoryRoot: string): string[] {
810
+ const detected = detectOutboundCalls(repositoryRoot)
811
+ const failures: string[] = []
812
+ const seen = new Set<string>()
813
+ for (const call of detected) {
814
+ seen.add(call.key)
815
+ const declaration = DECLARED_OUTBOUND_CALLS[call.key]
816
+ if (!declaration) {
817
+ failures.push(`undeclared: ${call.key} (count ${call.count})`)
818
+ continue
819
+ }
820
+ if (declaration.count !== call.count) {
821
+ failures.push(
822
+ `count changed: ${call.key} (declared ${declaration.count}, found ${call.count})`,
823
+ )
824
+ }
825
+ }
826
+ for (const key of Object.keys(DECLARED_OUTBOUND_CALLS)) {
827
+ if (!seen.has(key)) failures.push(`stale declaration: ${key}`)
828
+ }
829
+ return failures
830
+ }