peachsim 0.1.1625 → 0.1.1640
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.
- package/LICENSE +1 -1
- package/README.md +15 -2
- package/cli/app-config.ts +18 -6
- package/cli/app-fonts.ts +37 -17
- package/cli/app-stubs.ts +284 -20
- package/cli/bridge-flow-runner.ts +66 -45
- package/cli/cloud-client.ts +60 -40
- package/cli/command-registry.ts +1 -0
- package/cli/commands/control.ts +283 -49
- package/cli/commands/doctor.ts +30 -189
- package/cli/commands/flow.ts +79 -11
- package/cli/commands/inspect/core.ts +10 -5
- package/cli/commands/inspect/settling.ts +14 -2
- package/cli/commands/inspect/shared.ts +9 -4
- package/cli/commands/inspect/wait-ready.ts +12 -0
- package/cli/commands/inspect.ts +91 -50
- package/cli/commands/install-desktop.ts +1 -0
- package/cli/commands/maestro-compare.ts +97 -38
- package/cli/commands/maestro.ts +9 -1
- package/cli/commands/preview.ts +1 -1
- package/cli/commands/record.ts +118 -48
- package/cli/commands/screenshot.ts +23 -1
- package/cli/commands/serve.ts +7 -2
- package/cli/commands/setup.ts +19 -42
- package/cli/commands/swift.ts +14 -3
- package/cli/commands/upload.ts +93 -48
- package/cli/desktop-companion.ts +102 -17
- package/cli/drivers/desktop.ts +14 -1
- package/cli/drivers/playwright-sim-host.ts +103 -45
- package/cli/drivers/playwright.ts +9 -8
- package/cli/drivers/types.ts +5 -0
- package/cli/maestro-accessibility.ts +1 -1
- package/cli/maestro-compare.ts +84 -12
- package/cli/main.ts +3 -2
- package/cli/outbound-endpoints.ts +20 -6
- package/cli/pause-aware-timeout.ts +106 -0
- package/cli/scene-video.ts +89 -0
- package/cli/ws-bridge.ts +37 -19
- package/detox/attempt-events.cjs +66 -5
- package/detox/attempt-events.d.cts +7 -0
- package/detox/expectations.ts +7 -3
- package/detox/index.ts +269 -170
- package/detox/motion-change.cjs +206 -21
- package/detox/motion-change.d.cts +39 -2
- package/detox/navigation-lifecycle.ts +35 -3
- package/dist-lib/agent-daemon-client.cjs +1 -1
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/attached-projects.cjs +1 -1
- package/dist-lib/auth/shared-session.cjs +1 -1
- package/dist-lib/backend-origin.cjs +8 -7
- package/dist-lib/beta.cjs +1 -1
- package/dist-lib/beta.mjs +1 -1
- package/dist-lib/bridge-constants.cjs +13 -1
- package/dist-lib/bridge-contract-input.cjs +1 -1
- package/dist-lib/bridge-contract-input.mjs +1 -1
- package/dist-lib/bridge-contract.cjs +14 -8
- package/dist-lib/bridge-contract.mjs +13 -7
- package/dist-lib/bundle-channel.cjs +11887 -7900
- package/dist-lib/capture-contract.cjs +1 -1
- package/dist-lib/capture-contract.mjs +1 -1
- package/dist-lib/cloud-contract.cjs +1 -1
- package/dist-lib/cloud-contract.mjs +1 -1
- package/dist-lib/cloud-test-args.cjs +1 -1
- package/dist-lib/cloud-test-args.mjs +1 -1
- package/dist-lib/cloud.cjs +1 -1
- package/dist-lib/cloud.mjs +1 -1
- package/dist-lib/config.cjs +6 -3
- package/dist-lib/config.mjs +6 -3
- package/dist-lib/detox/index.cjs +1379 -725
- package/dist-lib/dev-bundle-resolution.cjs +14 -2
- package/dist-lib/expo-swift-bundle.cjs +9341 -5462
- package/dist-lib/expo-swift-metadata.cjs +11 -7
- package/dist-lib/expo-swift-metadata.mjs +11 -7
- package/dist-lib/home-paths.cjs +32 -3
- package/dist-lib/host/bridge-host.cjs +50204 -7258
- package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
- package/dist-lib/host/replacement-module-handler.cjs +1 -1
- package/dist-lib/host/websocket-proxy.cjs +1 -1
- package/dist-lib/index.cjs +3541 -448
- package/dist-lib/jump-to-source-babel.cjs +1 -1
- package/dist-lib/jump-to-source-native.cjs +1 -1
- package/dist-lib/kotlin-host.cjs +1 -1
- package/dist-lib/menu.cjs +1 -1
- package/dist-lib/menu.mjs +1 -1
- package/dist-lib/metro-fingerprint-registry.cjs +1 -1
- package/dist-lib/metro-fingerprint-registry.mjs +1 -1
- package/dist-lib/metro-production-bundle.cjs +1 -1
- package/dist-lib/metro-production-bundle.mjs +1 -1
- package/dist-lib/metro.cjs +2931 -104
- package/dist-lib/native-app-identity-project.cjs +289 -49
- package/dist-lib/native-app-identity-project.mjs +289 -49
- package/dist-lib/native-app-identity.cjs +77 -26
- package/dist-lib/native-app-identity.mjs +76 -26
- package/dist-lib/native-firebase-metadata.cjs +1 -1
- package/dist-lib/native-firebase-metadata.mjs +1 -1
- package/dist-lib/plist.cjs +1 -1
- package/dist-lib/plist.mjs +1 -1
- package/dist-lib/profiles.cjs +1 -1
- package/dist-lib/public-brand.cjs +1 -1
- package/dist-lib/react-native-host-modules.cjs +6 -2
- package/dist-lib/react-native-host-modules.mjs +6 -2
- package/dist-lib/scripts/dev-server-scanner.cjs +22 -8
- package/dist-lib/sdk.cjs +27 -11
- package/dist-lib/sdk.mjs +27 -10
- package/dist-lib/skills.cjs +72201 -26208
- package/dist-lib/swift-compile.cjs +1007 -140
- package/dist-lib/swift-compile.mjs +1049 -174
- package/dist-lib/swift-package.cjs +12 -3
- package/dist-lib/swift-package.mjs +12 -3
- package/dist-lib/swift-submit.cjs +13 -5
- package/dist-lib/swift-submit.mjs +11 -5
- package/dist-lib/swift-toolchain.cjs +423 -33
- package/dist-lib/swift.cjs +1 -1
- package/dist-lib/vite-swift.cjs +8628 -5268
- package/dist-lib/vite.cjs +2859 -12
- package/dist-lib/worklet-executable.cjs +14 -2
- package/dist-lib/worklet-executable.mjs +14 -2
- package/kotlin/AppSources/dev/peach/app/PeachDrawable.kt +21 -14
- package/kotlin/Sources/androidx/annotation/DrawableRes.kt +14 -0
- package/package.json +1 -1
- package/scripts/dev-server-scanner.ts +2 -1
- package/skills/peach-perf/SKILL.md +11 -5
- package/skills/peach-setup/SKILL.md +16 -19
- package/src/backend-origin.ts +13 -6
- package/src/bridge-constants.ts +11 -0
- package/src/bridge-contract.ts +22 -34
- package/src/config.ts +15 -0
- package/src/desktop-host-delivery.ts +5 -1
- package/src/dev-bundle-resolution.ts +2 -1
- package/src/expo-manifest-body.ts +13 -0
- package/src/expo-swift-host.ts +196 -49
- package/src/expo-swift-ready.ts +78 -0
- package/src/expoKotlinContract.ts +4 -7
- package/src/expoSwiftContract.ts +17 -6
- package/src/home-paths.ts +48 -2
- package/src/host/bridge-host.ts +5 -2
- package/src/host/staged-host-files.ts +9 -0
- package/src/native-app-identity-project.ts +27 -6
- package/src/native-app-identity.ts +91 -4
- package/src/native-firebase-config.ts +209 -13
- package/src/nativeAppIconContract.ts +1 -4
- package/src/output-lock-stdio.ts +36 -0
- package/src/process-group.ts +25 -2
- package/src/react-native-bridge-modules.ts +431 -0
- package/src/react-native-host-modules.ts +8 -1
- package/src/settings-bundle.ts +1 -4
- package/src/swift-binary-modules.ts +2 -2
- package/src/swift-compile.ts +918 -109
- package/src/swift-compiler-digest.ts +14 -0
- package/src/swift-distribution.lock.json +24 -12
- package/src/swift-distribution.ts +103 -4
- package/src/swift-host.ts +180 -55
- package/src/swift-icu.ts +27 -13
- package/src/swift-objc-compile.ts +355 -0
- package/src/swift-objc-sdk.ts +821 -27
- package/src/swift-objc-standin.ts +148 -14
- package/src/swift-package-manifest.ts +60 -7
- package/src/swift-package.ts +15 -1
- package/src/swift-packages.ts +154 -81
- package/src/swift-standin-emit.ts +96 -15
- package/src/swift-standins.ts +530 -116
- package/src/swift-submit.ts +13 -5
- package/src/swift-toolchain.ts +500 -42
- package/src/vite-plugin.ts +12 -1
- package/src/worker-system-chunks.ts +11 -12
- package/src/worklet-executable.ts +19 -1
- package/src/xcode-project.ts +270 -30
|
@@ -18,15 +18,18 @@ import {
|
|
|
18
18
|
// require, in a child process the CLI spawns as itself.
|
|
19
19
|
import {
|
|
20
20
|
chromeDisabledFeaturesSwitch,
|
|
21
|
+
LINUX_CHROME_SCOPE_ENV,
|
|
21
22
|
softwareChromeLaunchOptions,
|
|
22
23
|
} from '../../../../scripts/lib/reap-browser'
|
|
23
24
|
import { createBrowserPageTargetReader } from '../../../peach-engine/src/browser-page-target'
|
|
25
|
+
import { captureBrowserAccessibility } from '../../../peach-engine/src/capture/browser-accessibility-host'
|
|
24
26
|
import {
|
|
25
27
|
readBrowserCompositeCaptureRequest,
|
|
26
28
|
resolveBrowserCompositeCdpClip,
|
|
27
29
|
} from '../../../peach-engine/src/capture/browser-composite'
|
|
28
30
|
import { WEBVIEW_HOST_BOOTSTRAP_SOURCE } from '../../../peach-engine/src/components/native-ui/webview-frame-bridge'
|
|
29
31
|
import { createBrowserPointerDispatcher } from '../../../peach-engine/src/render-worker/browser-pointer'
|
|
32
|
+
import { automateWebAuthenticationSessions } from '../../../peach-engine/src/web-authentication-automation-host'
|
|
30
33
|
import {
|
|
31
34
|
automateWebViewFrames,
|
|
32
35
|
WEBVIEW_HOST_BINDING,
|
|
@@ -69,6 +72,9 @@ const callerUserDataDir = process.env.SOOTSIM_PW_USERDATADIR || '';
|
|
|
69
72
|
// SOOTSIM_PW_OWNER_POLL_MS is a test-only override of the 5s poll interval;
|
|
70
73
|
// production never sets it.
|
|
71
74
|
const ownerPid = Number(process.env.SOOTSIM_PW_OWNER_PID || 0);
|
|
75
|
+
// the open command waiting for this host to connect; it may queue for the
|
|
76
|
+
// browser slot and then be killed by its own caller's budget.
|
|
77
|
+
const launcherPid = Number(process.env.SOOTSIM_PW_LAUNCHER_PID || 0);
|
|
72
78
|
const ownerPollMs = Number(process.env.SOOTSIM_PW_OWNER_POLL_MS || 5000);
|
|
73
79
|
// optional CDP port — exposes Chrome's remote-debugging endpoint so a
|
|
74
80
|
// bridge-driven sim can also be cpu-profiled (peach perf cpu --cdp-port).
|
|
@@ -103,13 +109,14 @@ const viewportSpec = /^(\\d+)x(\\d+)$/.exec(process.env.SOOTSIM_PW_VIEWPORT || '
|
|
|
103
109
|
const viewport = viewportSpec
|
|
104
110
|
? { width: Number(viewportSpec[1]), height: Number(viewportSpec[2]) }
|
|
105
111
|
: { width: 600, height: 900 };
|
|
106
|
-
//
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
//
|
|
112
|
+
// host display pixels per css pixel; default to a retina Mac browser's 2.
|
|
113
|
+
// medium renders at 2 and high at device native, independently of browser DPR.
|
|
114
|
+
// low follows measured on-screen density, capped at device native.
|
|
115
|
+
// SOOTSIM_PW_DSF changes browser DPR, not the selected render resolution.
|
|
110
116
|
const deviceScaleFactor = Number(process.env.SOOTSIM_PW_DSF || 2);
|
|
111
117
|
const closeTimeoutMs = Number(process.env.SOOTSIM_PW_CLOSE_TIMEOUT_MS || 2500);
|
|
112
118
|
const connectAckFile = process.env.SOOTSIM_PW_CONNECTED_ACK_FILE || '';
|
|
119
|
+
const admittedFile = process.env.SOOTSIM_PW_ADMITTED_FILE || '';
|
|
113
120
|
const completionFile = process.env.PEACH_PW_COMPLETION_FILE || '';
|
|
114
121
|
const disposableBrowserCacheDirs = new Set(${JSON.stringify(
|
|
115
122
|
DISPOSABLE_BROWSER_CACHE_DIR_NAMES,
|
|
@@ -120,7 +127,9 @@ const listProcessChildren = ${listProcessChildren.toString()};
|
|
|
120
127
|
const { tmpdir } = require('os');
|
|
121
128
|
const { join } = require('path');
|
|
122
129
|
const createBrowserPointerDispatcher = ${createBrowserPointerDispatcher.toString()};
|
|
130
|
+
const captureBrowserAccessibility = ${captureBrowserAccessibility.toString()};
|
|
123
131
|
const automateWebViewFrames = ${automateWebViewFrames.toString()};
|
|
132
|
+
const automateWebAuthenticationSessions = ${automateWebAuthenticationSessions.toString()};
|
|
124
133
|
const webViewAutomationHelpers = ${webViewAutomationHelpersSource()};
|
|
125
134
|
const readBrowserCompositeCaptureRequest = ${readBrowserCompositeCaptureRequest.toString()};
|
|
126
135
|
const resolveBrowserCompositeCdpClip = ${resolveBrowserCompositeCdpClip.toString()};
|
|
@@ -136,12 +145,9 @@ const readChromiumQueue = ${readChromiumQueue.toString()};
|
|
|
136
145
|
// aborted/closed \`peach open\` leaked a renderer + gpu helper burning 100%+
|
|
137
146
|
// CPU until manually killed (multiple incidents 2026-06).
|
|
138
147
|
//
|
|
139
|
-
//
|
|
140
|
-
//
|
|
141
|
-
//
|
|
142
|
-
// — Chrome for Testing and bundled playwright chromium alike. as a side benefit we
|
|
143
|
-
// no longer need the launch / launchPersistentContext fork: we ALWAYS use a
|
|
144
|
-
// profile dir (mkdtemp'd if the caller didn't pass one, then removed at exit).
|
|
148
|
+
// a named profile can already belong to another host. its path finds browser
|
|
149
|
+
// candidates; the unique launch environment proves which ones we may reap,
|
|
150
|
+
// including helpers reparented after the browser root exits.
|
|
145
151
|
const reapEnabled = process.platform !== 'win32';
|
|
146
152
|
let profileDir = callerUserDataDir;
|
|
147
153
|
let ephemeralProfile = false;
|
|
@@ -153,33 +159,35 @@ if (!profileDir) {
|
|
|
153
159
|
// host a bounded temporary cache so persistent named profiles retain cookies
|
|
154
160
|
// and storage without multiplying a private cache for every profile.
|
|
155
161
|
const diskCacheDir = mkdtempSync(join(tmpdir(), 'peach-playwright-cache-'));
|
|
156
|
-
|
|
162
|
+
let profileLaunched = false;
|
|
163
|
+
// [pid, command line] for every process on the profile. linux reads /proc,
|
|
164
|
+
// since minimal and rootless images ship no ps.
|
|
165
|
+
const profileProcesses = () => {
|
|
157
166
|
if (!reapEnabled) return [];
|
|
158
167
|
try {
|
|
159
168
|
// match the whole --user-data-dir argument: a pattern search also found
|
|
160
169
|
// browsers whose profile path merely starts with this one.
|
|
161
170
|
const flag = ' --user-data-dir=' + profileDir + ' ';
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
maxBuffer: Infinity,
|
|
180
|
-
}).toString().split('\\n').filter(Boolean);
|
|
181
|
-
} catch { return []; }
|
|
171
|
+
const rows = process.platform === 'linux'
|
|
172
|
+
? readdirSync('/proc').filter((name) => /^\\d+$/.test(name)).map((name) => {
|
|
173
|
+
let command = '';
|
|
174
|
+
// a process can exit between the listing and this read
|
|
175
|
+
try { command = readFileSync('/proc/' + name + '/cmdline', 'utf8').split('\\0').join(' ').trim(); } catch {}
|
|
176
|
+
return [Number(name), command];
|
|
177
|
+
})
|
|
178
|
+
: execSync('ps -axo pid=,command=', {
|
|
179
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
180
|
+
maxBuffer: Infinity,
|
|
181
|
+
})
|
|
182
|
+
.toString().split('\\n').map((line) => line.trim().match(/^(\\d+)\\s+(.*)$/))
|
|
183
|
+
.filter(Boolean).map((row) => [Number(row[1]), row[2]]);
|
|
184
|
+
return rows.filter(([pid, command]) => pid && pid !== process.pid && (' ' + command + ' ').includes(flag));
|
|
185
|
+
} catch (error) {
|
|
186
|
+
throw new Error('[playwright-driver] cannot establish browser profile ownership: ' + String(error));
|
|
187
|
+
}
|
|
182
188
|
};
|
|
189
|
+
const chromePidsByProfile = () => profileProcesses().map(([pid]) => pid);
|
|
190
|
+
const chromeCommandsByProfile = () => profileProcesses().map(([, command]) => command);
|
|
183
191
|
const assertNoUnsafeSwiftShaderFlag = () => {
|
|
184
192
|
if (!hardwareGpuRequired) return;
|
|
185
193
|
const unsafe = chromeCommandsByProfile().find((command) =>
|
|
@@ -200,7 +208,29 @@ const killTree = (pid) => {
|
|
|
200
208
|
for (const k of kids) killTree(k);
|
|
201
209
|
try { process.kill(pid, 'SIGKILL'); } catch {}
|
|
202
210
|
};
|
|
203
|
-
const reapChrome = () => {
|
|
211
|
+
const reapChrome = () => {
|
|
212
|
+
try {
|
|
213
|
+
for (const pid of chromePidsByProfile()) {
|
|
214
|
+
let owned = false;
|
|
215
|
+
if (process.platform === 'linux') {
|
|
216
|
+
try {
|
|
217
|
+
owned = readFileSync('/proc/' + pid + '/environ', 'utf8').split('\\0')
|
|
218
|
+
.includes('PEACH_PW_BROWSER_OWNER=' + diskCacheDir);
|
|
219
|
+
} catch {}
|
|
220
|
+
} else {
|
|
221
|
+
const plain = spawnSync('ps', ['-o', 'command=', '-p', String(pid)], { encoding: 'utf8' });
|
|
222
|
+
const withEnv = spawnSync('ps', ['eww', '-o', 'command=', '-p', String(pid)], { encoding: 'utf8' });
|
|
223
|
+
const argv = (plain.stdout || '').trimEnd();
|
|
224
|
+
const full = (withEnv.stdout || '').trimEnd();
|
|
225
|
+
owned = plain.status === 0 && withEnv.status === 0 && full.startsWith(argv) &&
|
|
226
|
+
(full.slice(argv.length) + ' ').includes(' PEACH_PW_BROWSER_OWNER=' + diskCacheDir + ' ');
|
|
227
|
+
}
|
|
228
|
+
if (owned) killTree(pid);
|
|
229
|
+
}
|
|
230
|
+
} catch (error) {
|
|
231
|
+
process.stderr.write('[playwright-driver] browser teardown refused: ' + String(error) + '\\n');
|
|
232
|
+
}
|
|
233
|
+
};
|
|
204
234
|
const removeDisposableCaches = (root) => {
|
|
205
235
|
let children = [];
|
|
206
236
|
try { children = readdirSync(root, { withFileTypes: true }); } catch { return; }
|
|
@@ -230,7 +260,7 @@ const cleanupProfile = () => {
|
|
|
230
260
|
try { rmSync(profileDir, { recursive: true, force: true }); } catch {}
|
|
231
261
|
return;
|
|
232
262
|
}
|
|
233
|
-
removeDisposableCaches(profileDir);
|
|
263
|
+
if (profileLaunched) removeDisposableCaches(profileDir);
|
|
234
264
|
};
|
|
235
265
|
(async () => {
|
|
236
266
|
const pw = require(modulePath);
|
|
@@ -394,14 +424,16 @@ const cleanupProfile = () => {
|
|
|
394
424
|
await bounded('desktop host stop', stopDesktopHost, timeoutMs);
|
|
395
425
|
if (context) await bounded('context.close', () => context.close(), timeoutMs);
|
|
396
426
|
await terminateBrowserProcess();
|
|
397
|
-
// close()
|
|
398
|
-
//
|
|
399
|
-
// we always sweep by our unique profile dir as the load-bearing teardown.
|
|
427
|
+
// close() can leave reparented helpers; reap only this launch's browser
|
|
428
|
+
// and helpers, even if another host now uses the same named profile.
|
|
400
429
|
reapChrome();
|
|
401
430
|
cleanupProfile();
|
|
402
431
|
if (connectAckFile) {
|
|
403
432
|
try { unlinkSync(connectAckFile); } catch {}
|
|
404
433
|
}
|
|
434
|
+
if (admittedFile) {
|
|
435
|
+
try { unlinkSync(admittedFile); } catch {}
|
|
436
|
+
}
|
|
405
437
|
process.exit(code);
|
|
406
438
|
};
|
|
407
439
|
process.once('SIGTERM', () => { void shutdown(0); });
|
|
@@ -434,13 +466,25 @@ const cleanupProfile = () => {
|
|
|
434
466
|
throw new Error('[playwright-driver] missing valid owning process; refusing unsupervised browser host');
|
|
435
467
|
}
|
|
436
468
|
const intervalMs = Number.isFinite(ownerPollMs) && ownerPollMs > 0 ? ownerPollMs : 5000;
|
|
437
|
-
|
|
469
|
+
const gone = (pid) => {
|
|
438
470
|
try {
|
|
439
|
-
process.kill(
|
|
471
|
+
process.kill(pid, 0);
|
|
472
|
+
return false;
|
|
440
473
|
} catch (err) {
|
|
441
|
-
|
|
474
|
+
return !(err && err.code === 'EPERM');
|
|
475
|
+
}
|
|
476
|
+
};
|
|
477
|
+
ownerWatch = setInterval(() => {
|
|
478
|
+
if (gone(ownerPid)) {
|
|
442
479
|
process.stderr.write('[owner-gone] owning process ' + ownerPid + ' exited; closing playwright host\\n');
|
|
443
480
|
void shutdown(0);
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
// until the sim connects, the open command is the only one waiting for
|
|
484
|
+
// it; once that command is gone nobody receives the sim.
|
|
485
|
+
if (!connected && !ackFileExists() && Number.isInteger(launcherPid) && launcherPid > 1 && gone(launcherPid)) {
|
|
486
|
+
process.stderr.write('[launcher-gone] open command ' + launcherPid + ' exited before the sim connected; closing playwright host\\n');
|
|
487
|
+
void shutdown(2);
|
|
444
488
|
}
|
|
445
489
|
}, intervalMs);
|
|
446
490
|
if (typeof ownerWatch.unref === 'function') ownerWatch.unref();
|
|
@@ -472,13 +516,19 @@ const cleanupProfile = () => {
|
|
|
472
516
|
const opts = channel
|
|
473
517
|
? { headless, channel, viewport, deviceScaleFactor }
|
|
474
518
|
: { headless, viewport, deviceScaleFactor };
|
|
519
|
+
opts.env = { ...process.env, PEACH_PW_BROWSER_OWNER: diskCacheDir };
|
|
475
520
|
opts.executablePath = executablePath;
|
|
476
521
|
if (libraryPath) {
|
|
477
522
|
opts.env = {
|
|
478
|
-
...
|
|
523
|
+
...opts.env,
|
|
479
524
|
LD_LIBRARY_PATH: [libraryPath, process.env.LD_LIBRARY_PATH].filter(Boolean).join(':'),
|
|
480
525
|
};
|
|
481
526
|
}
|
|
527
|
+
// keep the browser main process with its children in the caller's cgroup.
|
|
528
|
+
// otherwise chromium moves only main into a systemd app scope over dbus.
|
|
529
|
+
if (process.platform === 'linux') {
|
|
530
|
+
opts.env = { ...process.env, ...opts.env, ...${JSON.stringify(LINUX_CHROME_SCOPE_ENV)} };
|
|
531
|
+
}
|
|
482
532
|
// playwright injects --force-color-profile=srgb by default, which reads
|
|
483
533
|
// visibly washed-out next to a normal chrome window on a wide-gamut mac
|
|
484
534
|
// display. strip it so driver-launched sims color-match the user's own
|
|
@@ -534,11 +584,9 @@ const cleanupProfile = () => {
|
|
|
534
584
|
// room for chrome's own toolbar, so the window isn't desktop-shaped
|
|
535
585
|
// around a letterboxed page.
|
|
536
586
|
if (!headless) opts.args.push('--window-size=' + viewport.width + ',' + (viewport.height + 88));
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
// an orphaned helper back to us, which is the leak we just fixed.
|
|
541
|
-
return await pw.chromium.launchPersistentContext(profileDir, opts);
|
|
587
|
+
const launched = await pw.chromium.launchPersistentContext(profileDir, opts);
|
|
588
|
+
profileLaunched = true;
|
|
589
|
+
return launched;
|
|
542
590
|
};
|
|
543
591
|
// the host-wide chromium slot is held for this host's life; its exit frees it
|
|
544
592
|
await acquireChromiumAdmission({
|
|
@@ -549,6 +597,11 @@ const cleanupProfile = () => {
|
|
|
549
597
|
label: 'peach sim host ' + profileDir,
|
|
550
598
|
readQueue: readChromiumQueue,
|
|
551
599
|
});
|
|
600
|
+
// the launcher's connect wait starts here: time queued for the slot is not
|
|
601
|
+
// time the sim had to connect.
|
|
602
|
+
if (admittedFile) {
|
|
603
|
+
try { writeFileSync(admittedFile, JSON.stringify({ admittedAt: Date.now(), pid: process.pid }) + '\\n'); } catch {}
|
|
604
|
+
}
|
|
552
605
|
clearStaleSingletonLock();
|
|
553
606
|
let launchErr = null;
|
|
554
607
|
let launchedChannel = null;
|
|
@@ -591,6 +644,8 @@ const cleanupProfile = () => {
|
|
|
591
644
|
await context.grantPermissions(['camera']);
|
|
592
645
|
const hostMeta = {
|
|
593
646
|
sootsimHostDriver: 'playwright',
|
|
647
|
+
peachRenderer: hostCommand ? 'native' : 'chromium',
|
|
648
|
+
peachHostExecutable: hostCommand ? hostCommand[0] : null,
|
|
594
649
|
sootsimHostPid: process.pid,
|
|
595
650
|
sootsimHostOwnerPid: ownerPid,
|
|
596
651
|
sootsimHostStartedAt: Date.now(),
|
|
@@ -629,6 +684,8 @@ const cleanupProfile = () => {
|
|
|
629
684
|
await shutdown(0);
|
|
630
685
|
});
|
|
631
686
|
const pointerHosts = new WeakMap();
|
|
687
|
+
await context.exposeBinding('__peachHostAccessibility', (source, instanceIds) =>
|
|
688
|
+
captureBrowserAccessibility(source.page, instanceIds));
|
|
632
689
|
await context.exposeBinding('__sootsimHostPointer', async (source, value) => {
|
|
633
690
|
let pending = pointerHosts.get(source.page);
|
|
634
691
|
if (!pending) {
|
|
@@ -677,6 +734,7 @@ const cleanupProfile = () => {
|
|
|
677
734
|
}, Number.isFinite(connectTimeoutMs) && connectTimeoutMs > 0 ? connectTimeoutMs : 120000);
|
|
678
735
|
if (typeof connectTimer.unref === 'function') connectTimer.unref();
|
|
679
736
|
const page = await context.newPage();
|
|
737
|
+
await automateWebAuthenticationSessions(context, '__peachWebAuthenticationHost', page, !headless);
|
|
680
738
|
if (hardwareGpuRequired) {
|
|
681
739
|
assertNoUnsafeSwiftShaderFlag();
|
|
682
740
|
const gpu = await page.evaluate(() => {
|
|
@@ -151,18 +151,15 @@ async function launch(opts: DriverLaunchOptions): Promise<DriverLaunchResult> {
|
|
|
151
151
|
// renders natively and lets the page present only on capture; the host
|
|
152
152
|
// reaches the browser over cdp, so it needs a debugging port.
|
|
153
153
|
let hostCommand: string[] | null = null
|
|
154
|
-
if (headless && desktopHostPlatform()) {
|
|
154
|
+
if (headless && !opts.chromium && desktopHostPlatform()) {
|
|
155
155
|
try {
|
|
156
156
|
hostCommand = headlessHostCommand()
|
|
157
157
|
if (!hostCommand && desktopHostPlatform() === 'linux-x64') {
|
|
158
|
-
//
|
|
159
|
-
// before the compatible headless host existed leaves this run on the browser engine alone
|
|
158
|
+
// delivery honors PEACH_DESKTOP_VERSION; a pin never follows channel latest.
|
|
160
159
|
await desktopHostDelivery().install()
|
|
161
160
|
hostCommand = headlessHostCommand()
|
|
162
161
|
if (!hostCommand)
|
|
163
|
-
|
|
164
|
-
' native host release has no compatible headless host yet; rendering in the browser',
|
|
165
|
-
)
|
|
162
|
+
throw new Error('selected native host has no compatible headless entrypoint')
|
|
166
163
|
}
|
|
167
164
|
} catch (error) {
|
|
168
165
|
return {
|
|
@@ -175,6 +172,7 @@ async function launch(opts: DriverLaunchOptions): Promise<DriverLaunchResult> {
|
|
|
175
172
|
|
|
176
173
|
const errLog = join(tmpdir(), playwrightHostLogName(Date.now(), process.pid))
|
|
177
174
|
const connectAckFile = `${errLog}.connected`
|
|
175
|
+
const admittedFile = `${errLog}.admitted`
|
|
178
176
|
const completionFile = `${errLog}.completed`
|
|
179
177
|
const errFd = openSync(errLog, 'a')
|
|
180
178
|
try {
|
|
@@ -210,9 +208,11 @@ async function launch(opts: DriverLaunchOptions): Promise<DriverLaunchResult> {
|
|
|
210
208
|
? { SOOTSIM_PW_VIEWPORT: `${opts.viewport.width}x${opts.viewport.height}` }
|
|
211
209
|
: {}),
|
|
212
210
|
SOOTSIM_PW_CONNECTED_ACK_FILE: connectAckFile,
|
|
211
|
+
SOOTSIM_PW_ADMITTED_FILE: admittedFile,
|
|
213
212
|
PEACH_PW_COMPLETION_FILE: completionFile,
|
|
214
213
|
SOOTSIM_PW_CONNECT_TIMEOUT_MS: String(opts.connectTimeoutMs ?? 120_000),
|
|
215
214
|
SOOTSIM_PW_OWNER_PID: String(ownerPid),
|
|
215
|
+
SOOTSIM_PW_LAUNCHER_PID: String(process.pid),
|
|
216
216
|
SOOTSIM_PW_LIBRARY_PATH: provisioning.libraryPath ?? '',
|
|
217
217
|
},
|
|
218
218
|
},
|
|
@@ -233,11 +233,12 @@ async function launch(opts: DriverLaunchOptions): Promise<DriverLaunchResult> {
|
|
|
233
233
|
const ownerNote = ` — browser closes when pid ${ownerPid}${ownerComm ? ` (${ownerComm})` : ''} exits, or after ${idleMinutes} min with no peach command or input if another browser launch is waiting for its slot`
|
|
234
234
|
return {
|
|
235
235
|
launched: true,
|
|
236
|
-
message: `playwright
|
|
236
|
+
message: `playwright ${hostCommand ? 'native host' : 'Chromium'} launched → ${targetUrl.href}${ownerNote}`,
|
|
237
237
|
pid: child.pid,
|
|
238
238
|
target: resolved.spec,
|
|
239
239
|
attachUrl: targetUrl.href,
|
|
240
240
|
connectAckFile,
|
|
241
|
+
admittedFile,
|
|
241
242
|
diagnosticLogPath: errLog,
|
|
242
243
|
completion,
|
|
243
244
|
completionFile,
|
|
@@ -257,7 +258,7 @@ async function launch(opts: DriverLaunchOptions): Promise<DriverLaunchResult> {
|
|
|
257
258
|
export const playwrightDriver: Driver = {
|
|
258
259
|
id: 'playwright',
|
|
259
260
|
name: 'playwright',
|
|
260
|
-
description: '
|
|
261
|
+
description: 'isolated Chromium; headless uses the native host when available',
|
|
261
262
|
kind: 'automation',
|
|
262
263
|
availability: probe,
|
|
263
264
|
launch,
|
package/cli/drivers/types.ts
CHANGED
|
@@ -30,6 +30,8 @@ export interface DriverLaunchOptions {
|
|
|
30
30
|
url?: string
|
|
31
31
|
// run in headless mode where the driver supports it. ignored by electron.
|
|
32
32
|
headless?: boolean
|
|
33
|
+
// opt this open into browser rendering without acquiring or attaching a native host.
|
|
34
|
+
chromium?: boolean
|
|
33
35
|
// initial device model seed forwarded to the peach shell. the electron
|
|
34
36
|
// driver forwards it via argv (--device=<model>); browser drivers receive
|
|
35
37
|
// it already stamped on the URL (?device=<model>) by runOpenCommand.
|
|
@@ -86,6 +88,9 @@ export interface DriverLaunchResult {
|
|
|
86
88
|
target?: string
|
|
87
89
|
attachUrl?: string
|
|
88
90
|
connectAckFile?: string
|
|
91
|
+
// written once the host holds its machine-wide browser slot; its connect
|
|
92
|
+
// budget starts there, so the launcher's wait starts there too.
|
|
93
|
+
admittedFile?: string
|
|
89
94
|
diagnosticLogPath?: string
|
|
90
95
|
// foreground guest hosts await the detached driver's exit without polling.
|
|
91
96
|
completion?: Promise<DriverCompletion>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { PeachAccessibilityNode } from '@peach/globals'
|
|
2
2
|
|
|
3
3
|
// maestro reads ios frames as integer edges (AXElement.boundsString).
|
|
4
|
-
export function accessibilityBounds(node: PeachAccessibilityNode): {
|
|
4
|
+
export function accessibilityBounds(node: Pick<PeachAccessibilityNode, 'frame'>): {
|
|
5
5
|
x: number
|
|
6
6
|
y: number
|
|
7
7
|
width: number
|
package/cli/maestro-compare.ts
CHANGED
|
@@ -121,8 +121,8 @@ function absolutizeRefs(steps: unknown, flowDir: string): unknown {
|
|
|
121
121
|
})
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
// insert
|
|
125
|
-
// as one step and are captured once, after they return.
|
|
124
|
+
// insert maestro's pixel-stillness wait before each checkpoint on both sides.
|
|
125
|
+
// subflows run as one step and are captured once, after they return.
|
|
126
126
|
export function instrumentFlowSource(
|
|
127
127
|
source: string,
|
|
128
128
|
flowDir: string,
|
|
@@ -159,6 +159,7 @@ export function instrumentFlowSource(
|
|
|
159
159
|
const label = stepLabel(step)
|
|
160
160
|
const name = `${COMPARE_SHOT_DIR}/${String(captures.length + 1).padStart(3, '0')}-${slugify(label)}`
|
|
161
161
|
captures.push({ name, step: index + 1, label })
|
|
162
|
+
out.push('waitForAnimationToEnd')
|
|
162
163
|
out.push({ takeScreenshot: name })
|
|
163
164
|
}
|
|
164
165
|
// maestro reads flows as yaml 1.1, where a bare yes, no, on or off is a
|
|
@@ -280,14 +281,52 @@ export function nativeStepsFromCommandsJson(json: unknown): CompareStep[] {
|
|
|
280
281
|
return steps
|
|
281
282
|
}
|
|
282
283
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
284
|
+
const XML_ENTITIES: Record<string, string> = {
|
|
285
|
+
amp: '&',
|
|
286
|
+
lt: '<',
|
|
287
|
+
gt: '>',
|
|
288
|
+
quot: '"',
|
|
289
|
+
apos: "'",
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function decodeXml(text: string): string {
|
|
293
|
+
return text.replace(/&(?:#(\d+)|#x([\da-f]+)|(\w+));/gi, (match, dec, hex, name) => {
|
|
294
|
+
if (dec || hex) return String.fromCodePoint(parseInt(dec ?? hex, dec ? 10 : 16))
|
|
295
|
+
return XML_ENTITIES[name] ?? match
|
|
288
296
|
})
|
|
289
|
-
|
|
290
|
-
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export function junitFlowKey(name: string, fileStem: string): string {
|
|
300
|
+
return JSON.stringify([name, fileStem])
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// maestro's own per-flow report (--format junit): one testcase per flow, whose
|
|
304
|
+
// status and failure text are the verdict whatever the flow did or printed.
|
|
305
|
+
// values hold the failure message per testcase, undefined for a pass, in
|
|
306
|
+
// report order so flows that share a name and file stem pair in run order.
|
|
307
|
+
export function nativeFlowVerdictsFromJunit(
|
|
308
|
+
xml: string,
|
|
309
|
+
): Map<string, Array<string | undefined>> {
|
|
310
|
+
const verdicts = new Map<string, Array<string | undefined>>()
|
|
311
|
+
for (const match of xml.matchAll(/<testcase\b([^>]*?)(\/>|>([\s\S]*?)<\/testcase>)/g)) {
|
|
312
|
+
const attrs = new Map<string, string>()
|
|
313
|
+
for (const attr of (match[1] ?? '').matchAll(/([\w:-]+)="([^"]*)"/g)) {
|
|
314
|
+
attrs.set(attr[1]!, decodeXml(attr[2]!))
|
|
315
|
+
}
|
|
316
|
+
const file = attrs.get('file')
|
|
317
|
+
const name = attrs.get('name')
|
|
318
|
+
if (file === undefined || name === undefined) continue
|
|
319
|
+
const body = match[3] ?? ''
|
|
320
|
+
const failure =
|
|
321
|
+
/<(?:failure|error)\b[^>]*?(?:\/>|>([\s\S]*?)<\/(?:failure|error)>)/.exec(body)
|
|
322
|
+
const failed = failure !== null || attrs.get('status') === 'ERROR'
|
|
323
|
+
const message = failure?.[1] ? decodeXml(failure[1]).trim() : ''
|
|
324
|
+
const key = junitFlowKey(name, path.basename(file).replace(/\.(ya?ml)$/i, ''))
|
|
325
|
+
const list = verdicts.get(key) ?? []
|
|
326
|
+
list.push(failed ? `Maestro failed this flow: ${message || 'no message'}` : undefined)
|
|
327
|
+
verdicts.set(key, list)
|
|
328
|
+
}
|
|
329
|
+
return verdicts
|
|
291
330
|
}
|
|
292
331
|
|
|
293
332
|
// peach's commands.json (maestro-report.ts buildFlowCommandsJson)
|
|
@@ -325,6 +364,30 @@ export interface ScreenDiff {
|
|
|
325
364
|
maskedTopPx: number
|
|
326
365
|
}
|
|
327
366
|
|
|
367
|
+
// captures of one framebuffer can differ by a column or row: maestro's xctest
|
|
368
|
+
// shot drops the rightmost column of an odd framebuffer. such a pair is scored
|
|
369
|
+
// on the top-left area both have, with no resampling, which would shift every
|
|
370
|
+
// edge. null when the sizes match or differ by more than one pixel.
|
|
371
|
+
export function commonAreaSize(
|
|
372
|
+
a: { width: number; height: number },
|
|
373
|
+
b: { width: number; height: number },
|
|
374
|
+
): { width: number; height: number } | null {
|
|
375
|
+
const dw = Math.abs(a.width - b.width)
|
|
376
|
+
const dh = Math.abs(a.height - b.height)
|
|
377
|
+
if ((dw === 0 && dh === 0) || dw > 1 || dh > 1) return null
|
|
378
|
+
return { width: Math.min(a.width, b.width), height: Math.min(a.height, b.height) }
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function cropTopLeft(src: PNG, width: number, height: number): PNG {
|
|
382
|
+
if (src.width === width && src.height === height) return src
|
|
383
|
+
const out = new PNG({ width, height })
|
|
384
|
+
for (let y = 0; y < height; y++) {
|
|
385
|
+
const from = y * src.width * 4
|
|
386
|
+
src.data.copy(out.data, y * width * 4, from, from + width * 4)
|
|
387
|
+
}
|
|
388
|
+
return out
|
|
389
|
+
}
|
|
390
|
+
|
|
328
391
|
function resample(src: PNG, width: number, height: number): PNG {
|
|
329
392
|
const out = new PNG({ width, height })
|
|
330
393
|
const sx = src.width / width
|
|
@@ -402,8 +465,13 @@ export function compareScreens(
|
|
|
402
465
|
peachPng: Buffer,
|
|
403
466
|
opts: { maskTopPx?: number } = {},
|
|
404
467
|
): { diff: ScreenDiff; triptych: Buffer } {
|
|
405
|
-
|
|
468
|
+
let native: PNG = PNG.sync.read(nativePng)
|
|
406
469
|
let peach: PNG = PNG.sync.read(peachPng)
|
|
470
|
+
const common = commonAreaSize(native, peach)
|
|
471
|
+
if (common) {
|
|
472
|
+
native = cropTopLeft(native, common.width, common.height)
|
|
473
|
+
peach = cropTopLeft(peach, common.width, common.height)
|
|
474
|
+
}
|
|
407
475
|
const { width, height } = native
|
|
408
476
|
const aspectMismatch = Math.abs(peach.width / peach.height - width / height) > 0.01
|
|
409
477
|
const resized = peach.width !== width || peach.height !== height
|
|
@@ -602,10 +670,14 @@ function screenCard(screen: CompareScreen, threshold: number, flowIndex: number)
|
|
|
602
670
|
].filter(Boolean)
|
|
603
671
|
let body = ''
|
|
604
672
|
if (screen.image) {
|
|
673
|
+
const imageUrl = screen.image.split('/').map(encodeURIComponent).join('/')
|
|
605
674
|
body = `<div class="cols"><span>iOS simulator</span><span>Peach</span><span>pixel diff</span></div>
|
|
606
|
-
<a href="${esc(
|
|
675
|
+
<a href="${esc(imageUrl)}" target="_blank"><img loading="lazy" src="${esc(imageUrl)}" alt="${esc(screen.label)}"></a>`
|
|
607
676
|
} else {
|
|
608
|
-
const only = screen.nativeOnly ?? screen.peachOnly
|
|
677
|
+
const only = (screen.nativeOnly ?? screen.peachOnly)
|
|
678
|
+
?.split('/')
|
|
679
|
+
.map(encodeURIComponent)
|
|
680
|
+
.join('/')
|
|
609
681
|
body = only
|
|
610
682
|
? `<div class="cols one"><span>${screen.nativeOnly ? 'iOS simulator' : 'Peach'} only</span></div><a href="${esc(only)}" target="_blank"><img class="single" loading="lazy" src="${esc(only)}" alt="${esc(screen.label)}"></a>`
|
|
611
683
|
: '<p class="quiet">Neither side reached this step.</p>'
|
package/cli/main.ts
CHANGED
|
@@ -42,7 +42,7 @@ async function handleTopLevelError(err: unknown): Promise<never> {
|
|
|
42
42
|
// peach's known top-level errors are all single-line, narrow strings.
|
|
43
43
|
// everything else gets the full stack so unexpected crashes still surface.
|
|
44
44
|
const isKnownCliFailure =
|
|
45
|
-
/^command timed out after \d+s$/.test(msg) ||
|
|
45
|
+
/^command timed out after \d+(?:\.\d+)?s$/.test(msg) ||
|
|
46
46
|
/^cannot hand off bridge on port \d+: pid \d+ is not this Peach invocation$/.test(
|
|
47
47
|
msg,
|
|
48
48
|
) ||
|
|
@@ -63,7 +63,8 @@ async function handleTopLevelError(err: unknown): Promise<never> {
|
|
|
63
63
|
msg.startsWith(
|
|
64
64
|
`package.json already defines scripts.${rnxPublicBrand.commandName} as `,
|
|
65
65
|
) ||
|
|
66
|
-
msg.startsWith('could not resolve a native bundle for ')
|
|
66
|
+
msg.startsWith('could not resolve a native bundle for ') ||
|
|
67
|
+
msg.startsWith('could not load ')
|
|
67
68
|
if (isKnownCliFailure && !process.env.SOOTSIM_VERBOSE) {
|
|
68
69
|
process.stderr.write(` ${msg}\n`)
|
|
69
70
|
await exitWithFlushedOutput(1)
|
|
@@ -491,7 +491,7 @@ export const DECLARED_OUTBOUND_CALLS: Record<string, OutboundCallDeclaration> =
|
|
|
491
491
|
},
|
|
492
492
|
'packages/peach-engine/src/engine/lottie-runtime.ts :: fetch :: descriptor.value': {
|
|
493
493
|
category: 'guest_app_network',
|
|
494
|
-
count:
|
|
494
|
+
count: 1,
|
|
495
495
|
},
|
|
496
496
|
'packages/peach-engine/src/bundle-fetch.ts :: fetch-alias :: buildBrowserAssetProxyUrl(devServerUrl)':
|
|
497
497
|
{ category: 'guest_app_network', count: 1 },
|
|
@@ -528,6 +528,8 @@ export const DECLARED_OUTBOUND_CALLS: Record<string, OutboundCallDeclaration> =
|
|
|
528
528
|
category: 'guest_app_network',
|
|
529
529
|
count: 1,
|
|
530
530
|
},
|
|
531
|
+
"packages/peach-engine/src/mcp/shared-config-transport.ts :: fetch :: '/__sootsim/shared-config'":
|
|
532
|
+
{ category: 'same_origin_runtime', count: 1 },
|
|
531
533
|
'packages/peach-engine/src/mcp/ws-bridge.ts :: fetch :: bridgeHealthUrl': {
|
|
532
534
|
category: 'local_development',
|
|
533
535
|
count: 1,
|
|
@@ -561,8 +563,6 @@ export const DECLARED_OUTBOUND_CALLS: Record<string, OutboundCallDeclaration> =
|
|
|
561
563
|
category: 'guest_app_network',
|
|
562
564
|
count: 1,
|
|
563
565
|
},
|
|
564
|
-
"packages/peach-engine/src/projects/projects-persistence.ts :: fetch :: '/__sootsim/shared-config'":
|
|
565
|
-
{ category: 'same_origin_runtime', count: 1 },
|
|
566
566
|
'packages/peach-engine/src/react-native/index.ts :: fetch :: resource': {
|
|
567
567
|
category: 'guest_app_network',
|
|
568
568
|
count: 1,
|
|
@@ -655,7 +655,7 @@ export const DECLARED_OUTBOUND_CALLS: Record<string, OutboundCallDeclaration> =
|
|
|
655
655
|
count: 1,
|
|
656
656
|
},
|
|
657
657
|
"packages/peach-engine/src/settings/persistence.ts :: fetch :: '/__sootsim/shared-config'":
|
|
658
|
-
{ category: 'same_origin_runtime', count:
|
|
658
|
+
{ category: 'same_origin_runtime', count: 1 },
|
|
659
659
|
'packages/peachsim/cli/app-fonts.ts :: fetch :: `${origin}/`': {
|
|
660
660
|
category: 'local_development',
|
|
661
661
|
count: 1,
|
|
@@ -673,6 +673,14 @@ export const DECLARED_OUTBOUND_CALLS: Record<string, OutboundCallDeclaration> =
|
|
|
673
673
|
category: 'local_development',
|
|
674
674
|
count: 1,
|
|
675
675
|
},
|
|
676
|
+
'packages/peachsim/cli/commands/control.ts :: net.connect :: port': {
|
|
677
|
+
category: 'local_development',
|
|
678
|
+
count: 1,
|
|
679
|
+
},
|
|
680
|
+
'packages/peachsim/cli/commands/control.ts :: fetch :: origin': {
|
|
681
|
+
category: 'local_development',
|
|
682
|
+
count: 1,
|
|
683
|
+
},
|
|
676
684
|
'packages/peachsim/cli/commands/control.ts :: websocket :: `ws://127.0.0.1:${port}`': {
|
|
677
685
|
category: 'local_development',
|
|
678
686
|
count: 1,
|
|
@@ -805,6 +813,10 @@ export const DECLARED_OUTBOUND_CALLS: Record<string, OutboundCallDeclaration> =
|
|
|
805
813
|
category: 'local_development',
|
|
806
814
|
count: 1,
|
|
807
815
|
},
|
|
816
|
+
'packages/peachsim/src/expo-swift-ready.ts :: fetch :: endpointUrl': {
|
|
817
|
+
category: 'local_development',
|
|
818
|
+
count: 1,
|
|
819
|
+
},
|
|
808
820
|
'packages/peach-engine/src/expo-swift-compilation.ts :: fetch-alias :: address': {
|
|
809
821
|
category: 'local_development',
|
|
810
822
|
count: 1,
|
|
@@ -855,8 +867,10 @@ export const DECLARED_OUTBOUND_CALLS: Record<string, OutboundCallDeclaration> =
|
|
|
855
867
|
category: 'same_origin_runtime',
|
|
856
868
|
count: 1,
|
|
857
869
|
},
|
|
858
|
-
'packages/peach-engine/src/engine/image-loader.ts :: fetch-alias ::
|
|
859
|
-
|
|
870
|
+
'packages/peach-engine/src/engine/image-loader.ts :: fetch-alias :: uri': {
|
|
871
|
+
category: 'guest_app_network',
|
|
872
|
+
count: 1,
|
|
873
|
+
},
|
|
860
874
|
'packages/peach-engine/src/ExternalApp.tsx :: fetch-alias :: requestUrl': {
|
|
861
875
|
category: 'guest_app_network',
|
|
862
876
|
count: 1,
|