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.
- package/LICENSE +22 -0
- package/README.md +640 -0
- package/cli/app-config.ts +82 -0
- package/cli/app-fonts.ts +388 -0
- package/cli/app-project.ts +59 -0
- package/cli/app-splash.ts +171 -0
- package/cli/app-state-reset.ts +26 -0
- package/cli/auth.ts +184 -0
- package/cli/bin.ts +59 -0
- package/cli/bridge-diagnostics.ts +226 -0
- package/cli/bridge-flow-runner.ts +2806 -0
- package/cli/browser-backend.ts +44 -0
- package/cli/browser-evals.ts +96 -0
- package/cli/cli-update.ts +261 -0
- package/cli/cloud-client.ts +1530 -0
- package/cli/cloud-dispatch.ts +206 -0
- package/cli/cloud-session.ts +227 -0
- package/cli/command-contract.ts +221 -0
- package/cli/command-registry.ts +304 -0
- package/cli/commands/agent-wrapper.ts +1048 -0
- package/cli/commands/agent.ts +439 -0
- package/cli/commands/app-fonts.ts +99 -0
- package/cli/commands/assert.ts +542 -0
- package/cli/commands/auth.ts +71 -0
- package/cli/commands/box/bundle-plane.ts +71 -0
- package/cli/commands/box/checkout-plane.ts +256 -0
- package/cli/commands/box/plane-share.ts +208 -0
- package/cli/commands/box/rnx-command.ts +60 -0
- package/cli/commands/box/search-command.ts +157 -0
- package/cli/commands/box.ts +925 -0
- package/cli/commands/camera.ts +266 -0
- package/cli/commands/cleanup.ts +169 -0
- package/cli/commands/compat.ts +176 -0
- package/cli/commands/config.ts +34 -0
- package/cli/commands/control.ts +2154 -0
- package/cli/commands/cpu-profile.ts +941 -0
- package/cli/commands/daemon-mac-app.ts +165 -0
- package/cli/commands/daemon.ts +800 -0
- package/cli/commands/debug.ts +751 -0
- package/cli/commands/desktop.ts +39 -0
- package/cli/commands/detect.ts +201 -0
- package/cli/commands/detox.ts +481 -0
- package/cli/commands/device.ts +134 -0
- package/cli/commands/diagnose.ts +589 -0
- package/cli/commands/do-chain.ts +408 -0
- package/cli/commands/electron.ts +94 -0
- package/cli/commands/film.ts +379 -0
- package/cli/commands/flow.ts +1240 -0
- package/cli/commands/inspect/actions.ts +516 -0
- package/cli/commands/inspect/core.ts +2086 -0
- package/cli/commands/inspect/count.ts +17 -0
- package/cli/commands/inspect/describe.ts +119 -0
- package/cli/commands/inspect/env.ts +25 -0
- package/cli/commands/inspect/find.ts +169 -0
- package/cli/commands/inspect/get-layout.ts +53 -0
- package/cli/commands/inspect/keyboard.ts +54 -0
- package/cli/commands/inspect/list.ts +175 -0
- package/cli/commands/inspect/memory.ts +215 -0
- package/cli/commands/inspect/redaction.ts +39 -0
- package/cli/commands/inspect/resolve-target.ts +77 -0
- package/cli/commands/inspect/screens.ts +78 -0
- package/cli/commands/inspect/settle.ts +32 -0
- package/cli/commands/inspect/settling.ts +197 -0
- package/cli/commands/inspect/shared.ts +259 -0
- package/cli/commands/inspect/sleep.ts +15 -0
- package/cli/commands/inspect/tree.ts +23 -0
- package/cli/commands/inspect/url.ts +17 -0
- package/cli/commands/inspect/wait-event.ts +211 -0
- package/cli/commands/inspect/wait-idle.ts +25 -0
- package/cli/commands/inspect/wait-ready.ts +87 -0
- package/cli/commands/inspect/wait-selector.ts +59 -0
- package/cli/commands/inspect/wait.ts +32 -0
- package/cli/commands/inspect.ts +4738 -0
- package/cli/commands/install-cli.ts +275 -0
- package/cli/commands/install-desktop.ts +385 -0
- package/cli/commands/login.ts +357 -0
- package/cli/commands/logout.ts +24 -0
- package/cli/commands/maestro-generate.ts +375 -0
- package/cli/commands/maestro.ts +536 -0
- package/cli/commands/mode.ts +58 -0
- package/cli/commands/no-bridge-hint.ts +80 -0
- package/cli/commands/perf.ts +67 -0
- package/cli/commands/permissions.ts +203 -0
- package/cli/commands/platform.ts +410 -0
- package/cli/commands/preview.ts +103 -0
- package/cli/commands/profile.ts +106 -0
- package/cli/commands/react.ts +353 -0
- package/cli/commands/record.ts +1435 -0
- package/cli/commands/report-issue.ts +305 -0
- package/cli/commands/reset.ts +89 -0
- package/cli/commands/runtime.ts +348 -0
- package/cli/commands/screenshot-command.ts +106 -0
- package/cli/commands/screenshot-layers.ts +144 -0
- package/cli/commands/screenshot-mode.ts +38 -0
- package/cli/commands/screenshot.ts +492 -0
- package/cli/commands/screenshots-capture.ts +608 -0
- package/cli/commands/screenshots.ts +127 -0
- package/cli/commands/serve.ts +139 -0
- package/cli/commands/setup.ts +416 -0
- package/cli/commands/shell-boolean-mode.ts +82 -0
- package/cli/commands/skills.ts +468 -0
- package/cli/commands/slides.ts +357 -0
- package/cli/commands/state.ts +94 -0
- package/cli/commands/storage.ts +58 -0
- package/cli/commands/telemetry.ts +55 -0
- package/cli/commands/test.ts +535 -0
- package/cli/commands/three-mode.ts +771 -0
- package/cli/commands/timeline.ts +121 -0
- package/cli/commands/upgrade.ts +167 -0
- package/cli/commands/upload.ts +1203 -0
- package/cli/commands/version.ts +63 -0
- package/cli/commands/welcome.ts +17 -0
- package/cli/commands/what-happened.ts +328 -0
- package/cli/current-sim.ts +217 -0
- package/cli/desktop-companion.ts +315 -0
- package/cli/drivers/electron.ts +70 -0
- package/cli/drivers/index.ts +20 -0
- package/cli/drivers/playwright-provisioning.ts +184 -0
- package/cli/drivers/playwright-sim-host.ts +537 -0
- package/cli/drivers/playwright.ts +209 -0
- package/cli/drivers/registry.ts +65 -0
- package/cli/drivers/types.ts +102 -0
- package/cli/flow-export.ts +55 -0
- package/cli/flow-file.ts +142 -0
- package/cli/flow-live-status.ts +126 -0
- package/cli/flow-session.ts +140 -0
- package/cli/help-core.ts +143 -0
- package/cli/help.ts +44 -0
- package/cli/hidden-runtime-alias.ts +19 -0
- package/cli/hints.ts +214 -0
- package/cli/inspect-notice-state.ts +114 -0
- package/cli/internal-child.ts +176 -0
- package/cli/maestro-js.ts +323 -0
- package/cli/main.ts +704 -0
- package/cli/open-url.ts +8 -0
- package/cli/outbound-endpoints.ts +830 -0
- package/cli/parent-pid.ts +204 -0
- package/cli/parse-args.ts +189 -0
- package/cli/privacy.ts +53 -0
- package/cli/prompt.ts +56 -0
- package/cli/recording-access.ts +108 -0
- package/cli/registry.ts +2 -0
- package/cli/resolve-assets.ts +63 -0
- package/cli/rnx-in-process.ts +98 -0
- package/cli/run-registry.ts +226 -0
- package/cli/run-rnx-core.ts +148 -0
- package/cli/run-rnx.ts +137 -0
- package/cli/runtime-notes.ts +66 -0
- package/cli/runtime-summary.ts +25 -0
- package/cli/secure-file.ts +31 -0
- package/cli/self-invocation.ts +58 -0
- package/cli/send-to-box.ts +109 -0
- package/cli/setup-repository.ts +191 -0
- package/cli/shell-init.ts +98 -0
- package/cli/standalone.ts +4 -0
- package/cli/telemetry.ts +84 -0
- package/cli/ui.ts +68 -0
- package/cli/ws-bridge.ts +1020 -0
- package/detox/attempt-events.cjs +23 -0
- package/detox/colors.ts +54 -0
- package/detox/config-loader.ts +135 -0
- package/detox/element-types.ts +48 -0
- package/detox/expectations.ts +534 -0
- package/detox/gestures.ts +501 -0
- package/detox/index.ts +2694 -0
- package/detox/jest-preset.cjs +29 -0
- package/detox/jest-setup-after-env.cjs +22 -0
- package/detox/matchers.ts +30 -0
- package/detox/motion-change.cjs +109 -0
- package/detox/motion-change.d.cts +35 -0
- package/detox/navigation-lifecycle.ts +290 -0
- package/detox/navigation.ts +43 -0
- package/detox/proof-frame.cjs +114 -0
- package/detox/record-block-execution.cjs +78 -0
- package/detox/screenshots/animated-color-test-rest-norngh.png +0 -0
- package/detox/screenshots/color-test-after-drag-norngh.png +0 -0
- package/detox/screenshots/color-test-rest-norngh.png +0 -0
- package/detox/screenshots/theme-blue-toggle.png +0 -0
- package/detox/screenshots/theme-blue.png +0 -0
- package/detox/screenshots/theme-red-toggle.png +0 -0
- package/detox/screenshots/theme-red.png +0 -0
- package/dist-lib/agent-daemon-client.cjs +502 -0
- package/dist-lib/agent-events.cjs +48 -0
- package/dist-lib/agent-identity.cjs +53 -0
- package/dist-lib/agent-sessions.cjs +759 -0
- package/dist-lib/attached-projects.cjs +526 -0
- package/dist-lib/auth/shared-session.cjs +208 -0
- package/dist-lib/backend-origin.cjs +88 -0
- package/dist-lib/beta.cjs +41 -0
- package/dist-lib/beta.mjs +13 -0
- package/dist-lib/bridge-constants.cjs +161 -0
- package/dist-lib/bridge-contract-input.cjs +451 -0
- package/dist-lib/bridge-contract-input.mjs +414 -0
- package/dist-lib/bridge-contract.cjs +130 -0
- package/dist-lib/bridge-contract.mjs +94 -0
- package/dist-lib/capture-contract.cjs +220 -0
- package/dist-lib/capture-contract.mjs +190 -0
- package/dist-lib/cli-constants.cjs +32 -0
- package/dist-lib/cloud-contract.cjs +194 -0
- package/dist-lib/cloud-contract.mjs +145 -0
- package/dist-lib/cloud.cjs +670 -0
- package/dist-lib/cloud.mjs +639 -0
- package/dist-lib/config.cjs +96 -0
- package/dist-lib/detox/index.cjs +3789 -0
- package/dist-lib/detox/jest-preset.cjs +29 -0
- package/dist-lib/dev-bundle-resolution.cjs +290 -0
- package/dist-lib/home-paths.cjs +638 -0
- package/dist-lib/host/bridge-host.cjs +10903 -0
- package/dist-lib/host/fetch-proxy-handler.cjs +421 -0
- package/dist-lib/host/fetch-proxy-overrides.cjs +63 -0
- package/dist-lib/host/fetch-proxy-overrides.mjs +37 -0
- package/dist-lib/host/replacement-module-handler.cjs +469 -0
- package/dist-lib/host/websocket-proxy.cjs +298 -0
- package/dist-lib/index.cjs +4268 -0
- package/dist-lib/jump-to-source-babel.cjs +93 -0
- package/dist-lib/jump-to-source-native.cjs +112 -0
- package/dist-lib/menu.cjs +646 -0
- package/dist-lib/menu.mjs +604 -0
- package/dist-lib/metro-fingerprint-registry.cjs +186 -0
- package/dist-lib/metro-fingerprint-registry.mjs +156 -0
- package/dist-lib/metro-production-bundle.cjs +329 -0
- package/dist-lib/metro-production-bundle.mjs +295 -0
- package/dist-lib/metro.cjs +904 -0
- package/dist-lib/profiles.cjs +246 -0
- package/dist-lib/public-brand.cjs +39 -0
- package/dist-lib/react-native-host-modules.cjs +51 -0
- package/dist-lib/react-native-host-modules.mjs +25 -0
- package/dist-lib/render-mode.cjs +54 -0
- package/dist-lib/scripts/dev-server-scanner.cjs +1232 -0
- package/dist-lib/sdk.cjs +2433 -0
- package/dist-lib/sdk.mjs +2336 -0
- package/dist-lib/skills.cjs +39132 -0
- package/dist-lib/swift-submit.cjs +92 -0
- package/dist-lib/swift-submit.mjs +65 -0
- package/dist-lib/swift.cjs +1646 -0
- package/dist-lib/vite.cjs +1591 -0
- package/package.json +290 -0
- package/scripts/dev-server-scanner.ts +1291 -0
- package/scripts/optional-demo-registry.ts +113 -0
- package/skills/contrast/SKILL.md +165 -0
- package/skills/rnx-debug/SKILL.md +433 -0
- package/skills/rnx-perf/SKILL.md +399 -0
- package/skills/rnx-setup/SKILL.md +172 -0
- package/skills/rnx-test/SKILL.md +321 -0
- package/skills/rnx-visual/SKILL.md +126 -0
- package/src/agent-daemon-client.ts +410 -0
- package/src/agent-events.ts +71 -0
- package/src/agent-identity.ts +23 -0
- package/src/agent-prompt.ts +71 -0
- package/src/agent-sessions.ts +580 -0
- package/src/app-url.ts +39 -0
- package/src/attached-projects.ts +539 -0
- package/src/auth/shared-session.ts +235 -0
- package/src/backend-origin.ts +70 -0
- package/src/beta.ts +19 -0
- package/src/bridge-constants.ts +175 -0
- package/src/bridge-contract-input.ts +502 -0
- package/src/bridge-contract.ts +558 -0
- package/src/browser-cache.ts +16 -0
- package/src/capture-contract.ts +587 -0
- package/src/cli-constants.ts +1 -0
- package/src/cli-version.ts +40 -0
- package/src/cloud-contract.ts +274 -0
- package/src/cloud.ts +762 -0
- package/src/codex-client.ts +234 -0
- package/src/config.ts +132 -0
- package/src/connect.ts +101 -0
- package/src/dev-bundle-resolution.ts +346 -0
- package/src/dev-server-open.ts +198 -0
- package/src/disk-cleanup.ts +540 -0
- package/src/flow-timeout-scale.ts +15 -0
- package/src/home-paths.ts +806 -0
- package/src/host/agent-host.ts +666 -0
- package/src/host/bridge-host.ts +2788 -0
- package/src/host/fetch-proxy-handler.ts +449 -0
- package/src/host/fetch-proxy-overrides.ts +51 -0
- package/src/host/open-url.ts +89 -0
- package/src/host/plane-host.ts +203 -0
- package/src/host/replacement-module-handler.ts +158 -0
- package/src/host/websocket-proxy.ts +315 -0
- package/src/index.ts +71 -0
- package/src/jump-to-source-babel.ts +135 -0
- package/src/jump-to-source-native.ts +102 -0
- package/src/menu.ts +777 -0
- package/src/metro-fingerprint-registry.ts +302 -0
- package/src/metro-plugin.ts +341 -0
- package/src/metro-production-bundle.ts +471 -0
- package/src/native-dev-bundle-url.ts +118 -0
- package/src/native-seam-manifest.ts +341 -0
- package/src/playwright-host-log.ts +16 -0
- package/src/plist.ts +27 -0
- package/src/poll-until.ts +123 -0
- package/src/profiles.ts +179 -0
- package/src/public-brand.ts +9 -0
- package/src/react-native-host-modules.ts +26 -0
- package/src/render-mode.ts +27 -0
- package/src/rolldown-plugin-swift.ts +240 -0
- package/src/runtime-assets.ts +233 -0
- package/src/runtime-delivery.ts +54 -0
- package/src/screenshots/compose.ts +422 -0
- package/src/screenshots/frame-compose.ts +514 -0
- package/src/screenshots/orchestrate.ts +256 -0
- package/src/screenshots/org-deck.ts +91 -0
- package/src/screenshots/registry.ts +58 -0
- package/src/screenshots/schema.ts +364 -0
- package/src/sdk.ts +15 -0
- package/src/settings-bundle.ts +418 -0
- package/src/shell-bridge-identity.ts +15 -0
- package/src/sim-client.ts +697 -0
- package/src/skills/builtin/a11y-review.ts +132 -0
- package/src/skills/builtin/compat-check.ts +71 -0
- package/src/skills/builtin/maestro-test.ts +121 -0
- package/src/skills/builtin/perf-profile.ts +90 -0
- package/src/skills/builtin/screenshot-all.ts +52 -0
- package/src/skills/builtin/visual-diff.ts +100 -0
- package/src/skills/registry.ts +107 -0
- package/src/skills/types.ts +41 -0
- package/src/swift-submit.ts +185 -0
- package/src/swift.ts +7 -0
- package/src/team-machine-prompt.ts +73 -0
- package/src/vite-plugin-one.ts +264 -0
- package/src/vite-plugin-swift.ts +620 -0
- package/src/vite-plugin.ts +1946 -0
- package/src/worklets-babel.ts +250 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto'
|
|
2
|
+
import { brotliBundle } from 'sootsim-engine/preview/compress-node'
|
|
3
|
+
import {
|
|
4
|
+
runPresignedUpload,
|
|
5
|
+
type InitRequestBody,
|
|
6
|
+
} from 'sootsim-engine/preview/presigned-upload'
|
|
7
|
+
import { authHeaderValue, cloudAccountIdOrExit, resolveCliAuth } from '../auth'
|
|
8
|
+
import { producePreviewArtifact } from '../cloud-client'
|
|
9
|
+
import { openUrl } from '../open-url'
|
|
10
|
+
import { resolveDefaultUploadOrigin, resolvePublicPreviewOrigin } from './upload'
|
|
11
|
+
|
|
12
|
+
function takeValue(args: string[], flag: string): string | null {
|
|
13
|
+
const index = args.indexOf(flag)
|
|
14
|
+
if (index < 0) return null
|
|
15
|
+
const value = args[index + 1]
|
|
16
|
+
args.splice(index, 2)
|
|
17
|
+
if (!value || value.startsWith('-')) throw new Error(`${flag} requires a value`)
|
|
18
|
+
return value
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function runPreview(args: string[]): Promise<number> {
|
|
22
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
23
|
+
console.log(`
|
|
24
|
+
rnx preview — publish a prebuilt React Native bundle as a standalone share
|
|
25
|
+
|
|
26
|
+
usage:
|
|
27
|
+
rnx preview <bundle.js|https://bundle-url> [--platform ios|android] [--open]
|
|
28
|
+
|
|
29
|
+
The share contains the immutable build and its automatically discovered Metro assets. It is not a recorded replay.
|
|
30
|
+
`)
|
|
31
|
+
return 0
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
const input = [...args]
|
|
35
|
+
const origin = await resolveDefaultUploadOrigin(
|
|
36
|
+
takeValue(input, '--origin') ?? undefined,
|
|
37
|
+
)
|
|
38
|
+
const publicOrigin = resolvePublicPreviewOrigin(
|
|
39
|
+
origin,
|
|
40
|
+
takeValue(input, '--public-origin') ?? undefined,
|
|
41
|
+
)
|
|
42
|
+
const platform = takeValue(input, '--platform') ?? 'ios'
|
|
43
|
+
const openIndex = input.indexOf('--open')
|
|
44
|
+
const open = openIndex >= 0
|
|
45
|
+
if (open) input.splice(openIndex, 1)
|
|
46
|
+
if (platform !== 'ios' && platform !== 'android') {
|
|
47
|
+
throw new Error('--platform must be ios or android')
|
|
48
|
+
}
|
|
49
|
+
if (input.length !== 1 || input[0]?.startsWith('-')) {
|
|
50
|
+
throw new Error('usage: rnx preview <bundle.js|https://bundle-url>')
|
|
51
|
+
}
|
|
52
|
+
const auth = resolveCliAuth()
|
|
53
|
+
if (!auth) throw new Error('rnx preview needs `rnx login` or RNX_API_KEY')
|
|
54
|
+
const accountId = cloudAccountIdOrExit(auth)
|
|
55
|
+
|
|
56
|
+
const artifact = await producePreviewArtifact(input[0])
|
|
57
|
+
const bundle = brotliBundle(artifact.bytes)
|
|
58
|
+
const initBody: InitRequestBody = {
|
|
59
|
+
kind: 'build',
|
|
60
|
+
buildPlatform: platform,
|
|
61
|
+
contentHash: createHash('sha256').update(artifact.bytes).digest('hex'),
|
|
62
|
+
bundleSizeBytes: artifact.bytes.byteLength,
|
|
63
|
+
bundleContentType: 'application/javascript',
|
|
64
|
+
bundleEncoding: 'br',
|
|
65
|
+
bundleMinified: true,
|
|
66
|
+
bundleOrigin: null,
|
|
67
|
+
entry: null,
|
|
68
|
+
isTransformed: false,
|
|
69
|
+
deviceSpec:
|
|
70
|
+
platform === 'android'
|
|
71
|
+
? { model: 'pixel-8', width: 412, height: 915 }
|
|
72
|
+
: { model: 'iphone-16', width: 402, height: 874 },
|
|
73
|
+
files: [],
|
|
74
|
+
}
|
|
75
|
+
const { finalize } = await runPresignedUpload({
|
|
76
|
+
originBase: origin,
|
|
77
|
+
endpoints: {
|
|
78
|
+
init: '/api/v1/previews',
|
|
79
|
+
finalize: '/api/v1/previews/finalize',
|
|
80
|
+
},
|
|
81
|
+
initBody,
|
|
82
|
+
bundleBytes: bundle,
|
|
83
|
+
filesByHash: new Map(),
|
|
84
|
+
authHeader: authHeaderValue(auth),
|
|
85
|
+
accountId,
|
|
86
|
+
})
|
|
87
|
+
const url = finalize.url.startsWith('http')
|
|
88
|
+
? finalize.url
|
|
89
|
+
: `${publicOrigin}${finalize.url}`
|
|
90
|
+
console.log(` preview: ${url}`)
|
|
91
|
+
console.log(
|
|
92
|
+
` artifact: sha256:${artifact.sha256} (${artifact.bytes.byteLength} bytes)`,
|
|
93
|
+
)
|
|
94
|
+
for (const warning of artifact.warnings) console.warn(` warning: ${warning}`)
|
|
95
|
+
if (open) await openUrl(url)
|
|
96
|
+
return 0
|
|
97
|
+
} catch (error) {
|
|
98
|
+
console.error(
|
|
99
|
+
` rnx preview failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
100
|
+
)
|
|
101
|
+
return 1
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// rnx storage profile — manage isolated app storage profiles.
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
clearProfileStorage,
|
|
5
|
+
createProfile,
|
|
6
|
+
deleteProfile,
|
|
7
|
+
ensureProfile,
|
|
8
|
+
listProfiles,
|
|
9
|
+
} from '../../src/profiles'
|
|
10
|
+
|
|
11
|
+
interface ProfileOptions {
|
|
12
|
+
port?: number
|
|
13
|
+
verbose?: boolean
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function printHelp() {
|
|
17
|
+
console.log(`
|
|
18
|
+
rnx storage profile — manage isolated storage profiles
|
|
19
|
+
|
|
20
|
+
usage:
|
|
21
|
+
rnx storage profile list
|
|
22
|
+
rnx storage profile create <id>
|
|
23
|
+
rnx storage profile clear <id>
|
|
24
|
+
rnx storage profile delete <id>
|
|
25
|
+
|
|
26
|
+
profiles isolate cookies, localStorage, IndexedDB, cache, permissions, and
|
|
27
|
+
service workers in Electron and Playwright launches. Plain browser-tab launches
|
|
28
|
+
cannot honor profiles and will fail clearly if a profile is requested.
|
|
29
|
+
|
|
30
|
+
examples:
|
|
31
|
+
rnx storage profile list
|
|
32
|
+
rnx storage profile create qa
|
|
33
|
+
rnx open 8081 --profile qa
|
|
34
|
+
rnx open 8081 --profile qa --driver playwright
|
|
35
|
+
rnx storage profile clear qa
|
|
36
|
+
rnx storage profile delete qa
|
|
37
|
+
`)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function runProfile(args: string[], _opts: ProfileOptions): Promise<number> {
|
|
41
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
42
|
+
printHelp()
|
|
43
|
+
return 0
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const subcommand = args[0] ?? 'list'
|
|
47
|
+
try {
|
|
48
|
+
switch (subcommand) {
|
|
49
|
+
case 'list': {
|
|
50
|
+
const profiles = listProfiles()
|
|
51
|
+
for (const profile of profiles) {
|
|
52
|
+
console.log(` ${profile.id}`)
|
|
53
|
+
}
|
|
54
|
+
return 0
|
|
55
|
+
}
|
|
56
|
+
case 'create': {
|
|
57
|
+
const id = args[1]
|
|
58
|
+
if (!id) {
|
|
59
|
+
console.error(' profile create expects <id>')
|
|
60
|
+
return 1
|
|
61
|
+
}
|
|
62
|
+
const profile = createProfile(id)
|
|
63
|
+
console.log(` created profile: ${profile.id}`)
|
|
64
|
+
return 0
|
|
65
|
+
}
|
|
66
|
+
case 'ensure': {
|
|
67
|
+
const id = args[1]
|
|
68
|
+
if (!id) {
|
|
69
|
+
console.error(' profile ensure expects <id>')
|
|
70
|
+
return 1
|
|
71
|
+
}
|
|
72
|
+
const profile = ensureProfile(id)
|
|
73
|
+
console.log(` profile: ${profile.id}`)
|
|
74
|
+
return 0
|
|
75
|
+
}
|
|
76
|
+
case 'clear': {
|
|
77
|
+
const id = args[1]
|
|
78
|
+
if (!id) {
|
|
79
|
+
console.error(' profile clear expects <id>')
|
|
80
|
+
return 1
|
|
81
|
+
}
|
|
82
|
+
ensureProfile(id)
|
|
83
|
+
clearProfileStorage(id)
|
|
84
|
+
console.log(` cleared profile: ${id}`)
|
|
85
|
+
return 0
|
|
86
|
+
}
|
|
87
|
+
case 'delete': {
|
|
88
|
+
const id = args[1]
|
|
89
|
+
if (!id) {
|
|
90
|
+
console.error(' profile delete expects <id>')
|
|
91
|
+
return 1
|
|
92
|
+
}
|
|
93
|
+
const profile = deleteProfile(id)
|
|
94
|
+
console.log(` deleted profile: ${profile.id}`)
|
|
95
|
+
return 0
|
|
96
|
+
}
|
|
97
|
+
default:
|
|
98
|
+
console.error(` unknown profile command: ${subcommand}`)
|
|
99
|
+
printHelp()
|
|
100
|
+
return 1
|
|
101
|
+
}
|
|
102
|
+
} catch (err) {
|
|
103
|
+
console.error(` profile failed: ${err instanceof Error ? err.message : String(err)}`)
|
|
104
|
+
return 1
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
import {
|
|
2
|
+
callInBridge,
|
|
3
|
+
callInBridgeWrite,
|
|
4
|
+
createBridgeFromParsed,
|
|
5
|
+
parseBridgeCliArgs,
|
|
6
|
+
} from '../ws-bridge'
|
|
7
|
+
|
|
8
|
+
interface ReactOptions {
|
|
9
|
+
port?: number
|
|
10
|
+
verbose?: boolean
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface SlowRow {
|
|
14
|
+
displayName: string
|
|
15
|
+
totalMs: number
|
|
16
|
+
avgMs: number
|
|
17
|
+
commitCount: number
|
|
18
|
+
slowestCommit: number
|
|
19
|
+
slowestMs: number
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface RerenderRow {
|
|
23
|
+
displayName: string
|
|
24
|
+
count: number
|
|
25
|
+
renders?: number
|
|
26
|
+
causes: Record<string, number>
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface TreeRow {
|
|
30
|
+
id: number
|
|
31
|
+
displayName: string
|
|
32
|
+
kind: string
|
|
33
|
+
depth: number
|
|
34
|
+
childCount: number
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function printHelp() {
|
|
38
|
+
console.log(`
|
|
39
|
+
rnx perf react — inspect React component render cost in the tenant worker
|
|
40
|
+
|
|
41
|
+
usage:
|
|
42
|
+
rnx perf react profile start [--include-props] [--max-commits N]
|
|
43
|
+
rnx perf react profile stop
|
|
44
|
+
rnx perf react summary [--limit 10] [--json]
|
|
45
|
+
rnx perf react slow [--limit 5] [--json]
|
|
46
|
+
rnx perf react rerenders [--limit 5] [--json]
|
|
47
|
+
rnx perf react tree [--depth 3] [--find <name>] [--json]
|
|
48
|
+
rnx perf react why <fiber-id> [--json]
|
|
49
|
+
|
|
50
|
+
\`react summary\` is the demo-friendly shortcut: it runs both slow and
|
|
51
|
+
rerenders in one round-trip, prints them as a single table per side,
|
|
52
|
+
and surfaces the duration-zero warning prominently when the bundle is
|
|
53
|
+
running without React profiler timers.
|
|
54
|
+
|
|
55
|
+
examples:
|
|
56
|
+
rnx perf react profile start
|
|
57
|
+
rnx do tap-text Inbox
|
|
58
|
+
rnx perf react profile stop
|
|
59
|
+
rnx perf react summary --limit 10
|
|
60
|
+
rnx perf react slow --limit 10
|
|
61
|
+
rnx perf react rerenders --json
|
|
62
|
+
`)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function valueOf(args: string[], flag: string): string | undefined {
|
|
66
|
+
const idx = args.indexOf(flag)
|
|
67
|
+
if (idx >= 0 && idx + 1 < args.length) return args[idx + 1]
|
|
68
|
+
const prefix = `${flag}=`
|
|
69
|
+
const found = args.find((arg) => arg.startsWith(prefix))
|
|
70
|
+
return found ? found.slice(prefix.length) : undefined
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function numberFlag(args: string[], flag: string, fallback: number): number {
|
|
74
|
+
const raw = valueOf(args, flag)
|
|
75
|
+
if (raw === undefined) return fallback
|
|
76
|
+
const value = Number(raw)
|
|
77
|
+
return Number.isFinite(value) && value > 0 ? Math.round(value) : fallback
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function formatTable(headers: string[], rows: string[][]): string {
|
|
81
|
+
const widths = headers.map((h, i) =>
|
|
82
|
+
Math.max(h.length, ...rows.map((row) => row[i]?.length ?? 0)),
|
|
83
|
+
)
|
|
84
|
+
const line = (cells: string[]) =>
|
|
85
|
+
cells
|
|
86
|
+
.map((cell, i) => cell.padEnd(widths[i]))
|
|
87
|
+
.join(' ')
|
|
88
|
+
.trimEnd()
|
|
89
|
+
return [
|
|
90
|
+
line(headers),
|
|
91
|
+
line(headers.map((h) => '-'.repeat(h.length))),
|
|
92
|
+
...rows.map(line),
|
|
93
|
+
]
|
|
94
|
+
.map((row) => ` ${row}`)
|
|
95
|
+
.join('\n')
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function printJson(value: unknown) {
|
|
99
|
+
console.log(JSON.stringify(value, null, 2))
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function printSlow(result: {
|
|
103
|
+
rows?: SlowRow[]
|
|
104
|
+
commits?: number
|
|
105
|
+
error?: string
|
|
106
|
+
durationWarning?: string
|
|
107
|
+
}) {
|
|
108
|
+
if (result.error) console.error(` warning: ${result.error}`)
|
|
109
|
+
if (result.durationWarning) console.error(` warning: ${result.durationWarning}`)
|
|
110
|
+
const rows = result.rows ?? []
|
|
111
|
+
if (rows.length === 0) {
|
|
112
|
+
console.log(` no React render samples yet (${result.commits ?? 0} commit(s))`)
|
|
113
|
+
return
|
|
114
|
+
}
|
|
115
|
+
console.log(
|
|
116
|
+
formatTable(
|
|
117
|
+
['component', 'total', 'avg', 'commits', 'slowest'],
|
|
118
|
+
rows.map((row) => [
|
|
119
|
+
row.displayName,
|
|
120
|
+
`${row.totalMs.toFixed(2)}ms`,
|
|
121
|
+
`${row.avgMs.toFixed(2)}ms`,
|
|
122
|
+
String(row.commitCount),
|
|
123
|
+
`#${row.slowestCommit} ${row.slowestMs.toFixed(2)}ms`,
|
|
124
|
+
]),
|
|
125
|
+
),
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function printRerenders(result: {
|
|
130
|
+
rows?: RerenderRow[]
|
|
131
|
+
commits?: number
|
|
132
|
+
error?: string
|
|
133
|
+
}) {
|
|
134
|
+
if (result.error) console.error(` warning: ${result.error}`)
|
|
135
|
+
const rows = result.rows ?? []
|
|
136
|
+
if (rows.length === 0) {
|
|
137
|
+
console.log(` no React render samples yet (${result.commits ?? 0} commit(s))`)
|
|
138
|
+
return
|
|
139
|
+
}
|
|
140
|
+
console.log(
|
|
141
|
+
formatTable(
|
|
142
|
+
['component', 'renders', 'causes'],
|
|
143
|
+
rows.map((row) => {
|
|
144
|
+
const causes = Object.entries(row.causes ?? {})
|
|
145
|
+
.filter(([, count]) => count > 0)
|
|
146
|
+
.sort((a, b) => b[1] - a[1])
|
|
147
|
+
.slice(0, 4)
|
|
148
|
+
.map(([cause, count]) => `${cause}:${count}`)
|
|
149
|
+
.join(', ')
|
|
150
|
+
return [row.displayName, String(row.renders ?? row.count), causes || '-']
|
|
151
|
+
}),
|
|
152
|
+
),
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function printTree(result: { rows?: TreeRow[]; roots?: number; error?: string }) {
|
|
157
|
+
if (result.error) console.error(` warning: ${result.error}`)
|
|
158
|
+
const rows = result.rows ?? []
|
|
159
|
+
if (rows.length === 0) {
|
|
160
|
+
console.log(` no React tree data yet (${result.roots ?? 0} root(s))`)
|
|
161
|
+
return
|
|
162
|
+
}
|
|
163
|
+
for (const row of rows) {
|
|
164
|
+
const indent = ' '.repeat(row.depth)
|
|
165
|
+
console.log(
|
|
166
|
+
` ${indent}@${row.id} ${row.displayName} [${row.kind}] children=${row.childCount}`,
|
|
167
|
+
)
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function printWhy(result: { sample?: any; error?: string }) {
|
|
172
|
+
if (result.error) {
|
|
173
|
+
console.error(` ${result.error}`)
|
|
174
|
+
return
|
|
175
|
+
}
|
|
176
|
+
const sample = result.sample
|
|
177
|
+
if (!sample) {
|
|
178
|
+
console.log(' no sample')
|
|
179
|
+
return
|
|
180
|
+
}
|
|
181
|
+
console.log(` @${sample.fiberId} ${sample.displayName}`)
|
|
182
|
+
console.log(` duration: ${Number(sample.actualDuration ?? 0).toFixed(2)}ms`)
|
|
183
|
+
console.log(` causes: ${(sample.causes ?? []).join(', ') || '-'}`)
|
|
184
|
+
if (sample.propDiffs?.length) {
|
|
185
|
+
console.log(' props:')
|
|
186
|
+
for (const diff of sample.propDiffs) {
|
|
187
|
+
const values =
|
|
188
|
+
'old' in diff || 'new' in diff
|
|
189
|
+
? ` ${JSON.stringify(diff.old)} → ${JSON.stringify(diff.new)}`
|
|
190
|
+
: ''
|
|
191
|
+
console.log(` ${diff.key}${values}`)
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (sample.parentChain?.length) {
|
|
195
|
+
console.log(` parents: ${sample.parentChain.join(' ← ')}`)
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export async function runReact(args: string[], opts: ReactOptions): Promise<number> {
|
|
200
|
+
const parsed = parseBridgeCliArgs(args, {
|
|
201
|
+
port: opts.port,
|
|
202
|
+
stripBooleanFlags: ['--json', '--include-props', '--help', '-h'],
|
|
203
|
+
stripValueFlags: ['--limit', '--depth', '--find', '--max-commits'],
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
207
|
+
printHelp()
|
|
208
|
+
return 0
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const [first, second] = parsed.positional
|
|
212
|
+
const json = args.includes('--json')
|
|
213
|
+
const bridge = createBridgeFromParsed(parsed)
|
|
214
|
+
|
|
215
|
+
try {
|
|
216
|
+
if (first === 'profile' && second === 'start') {
|
|
217
|
+
const result = await callInBridgeWrite(
|
|
218
|
+
bridge,
|
|
219
|
+
'SootSim.bridges.reactProfile.start',
|
|
220
|
+
{
|
|
221
|
+
includeProps: args.includes('--include-props'),
|
|
222
|
+
maxCommits: numberFlag(args, '--max-commits', 2000),
|
|
223
|
+
},
|
|
224
|
+
)
|
|
225
|
+
if (json) printJson(result)
|
|
226
|
+
else console.log(' React profile started')
|
|
227
|
+
return 0
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (first === 'profile' && second === 'stop') {
|
|
231
|
+
const result = (await callInBridgeWrite(
|
|
232
|
+
bridge,
|
|
233
|
+
'SootSim.bridges.reactProfile.stop',
|
|
234
|
+
)) as { commits?: number; error?: string }
|
|
235
|
+
if (json) printJson(result)
|
|
236
|
+
else {
|
|
237
|
+
console.log(` React profile stopped (${result.commits ?? 0} commit(s))`)
|
|
238
|
+
if (result.error) console.error(` warning: ${result.error}`)
|
|
239
|
+
}
|
|
240
|
+
return result.error ? 1 : 0
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (first === 'summary') {
|
|
244
|
+
// run slow + rerenders in parallel against the existing bridge.
|
|
245
|
+
// demo-friendly: one command, one table per dimension, plus a
|
|
246
|
+
// single duration-zero warning that's easy to call out on stage.
|
|
247
|
+
const limit = numberFlag(args, '--limit', 10)
|
|
248
|
+
const [slowResult, rerendersResult] = (await Promise.all([
|
|
249
|
+
callInBridge(bridge, 'SootSim.bridges.reactProfile.slow', { limit }),
|
|
250
|
+
callInBridge(bridge, 'SootSim.bridges.reactProfile.rerenders', { limit }),
|
|
251
|
+
])) as [
|
|
252
|
+
{
|
|
253
|
+
rows?: SlowRow[]
|
|
254
|
+
commits?: number
|
|
255
|
+
error?: string
|
|
256
|
+
durationWarning?: string
|
|
257
|
+
},
|
|
258
|
+
{ rows?: RerenderRow[]; commits?: number; error?: string },
|
|
259
|
+
]
|
|
260
|
+
if (json) {
|
|
261
|
+
printJson({
|
|
262
|
+
commits: slowResult.commits ?? rerendersResult.commits ?? 0,
|
|
263
|
+
slow: slowResult,
|
|
264
|
+
rerenders: rerendersResult,
|
|
265
|
+
})
|
|
266
|
+
return 0
|
|
267
|
+
}
|
|
268
|
+
const commits = slowResult.commits ?? rerendersResult.commits ?? 0
|
|
269
|
+
console.log(` React profile summary (${commits} commit(s)):`)
|
|
270
|
+
if (slowResult.error) console.error(` warning: ${slowResult.error}`)
|
|
271
|
+
if (slowResult.durationWarning) {
|
|
272
|
+
console.error(` warning: ${slowResult.durationWarning}`)
|
|
273
|
+
}
|
|
274
|
+
if (rerendersResult.error) {
|
|
275
|
+
console.error(` warning: ${rerendersResult.error}`)
|
|
276
|
+
}
|
|
277
|
+
console.log('\n slowest renders:')
|
|
278
|
+
printSlow(slowResult)
|
|
279
|
+
console.log('\n most rerenders:')
|
|
280
|
+
printRerenders(rerendersResult)
|
|
281
|
+
// hint about the actualDuration-zero limitation when the slow
|
|
282
|
+
// table is empty/zero — the demo-stage signal is "no timer
|
|
283
|
+
// data, expect rerenders rows only".
|
|
284
|
+
const slowestRow = slowResult.rows?.[0]
|
|
285
|
+
const allZero =
|
|
286
|
+
slowResult.rows && slowResult.rows.length > 0
|
|
287
|
+
? slowResult.rows.every((r) => r.totalMs <= 0 && r.slowestMs <= 0)
|
|
288
|
+
: false
|
|
289
|
+
if (allZero && !slowResult.durationWarning) {
|
|
290
|
+
console.error(
|
|
291
|
+
`\n warning: every commit reports actualDuration=0ms — the React profiler timer is disabled in this bundle (rerender counts are still accurate; ${slowestRow?.displayName ? `top-listed: ${slowestRow.displayName}` : 'no slow signal'}).`,
|
|
292
|
+
)
|
|
293
|
+
}
|
|
294
|
+
return 0
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (first === 'slow') {
|
|
298
|
+
const result = (await callInBridge(bridge, 'SootSim.bridges.reactProfile.slow', {
|
|
299
|
+
limit: numberFlag(args, '--limit', 10),
|
|
300
|
+
})) as {
|
|
301
|
+
rows?: SlowRow[]
|
|
302
|
+
commits?: number
|
|
303
|
+
error?: string
|
|
304
|
+
durationWarning?: string
|
|
305
|
+
}
|
|
306
|
+
if (json) printJson(result)
|
|
307
|
+
else printSlow(result)
|
|
308
|
+
return 0
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (first === 'rerenders') {
|
|
312
|
+
const result = (await callInBridge(
|
|
313
|
+
bridge,
|
|
314
|
+
'SootSim.bridges.reactProfile.rerenders',
|
|
315
|
+
{ limit: numberFlag(args, '--limit', 10) },
|
|
316
|
+
)) as { rows?: RerenderRow[]; commits?: number; error?: string }
|
|
317
|
+
if (json) printJson(result)
|
|
318
|
+
else printRerenders(result)
|
|
319
|
+
return 0
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (first === 'tree') {
|
|
323
|
+
const result = (await callInBridge(bridge, 'SootSim.bridges.reactProfile.tree', {
|
|
324
|
+
depth: numberFlag(args, '--depth', 3),
|
|
325
|
+
find: valueOf(args, '--find'),
|
|
326
|
+
})) as { rows?: TreeRow[]; roots?: number; error?: string }
|
|
327
|
+
if (json) printJson(result)
|
|
328
|
+
else printTree(result)
|
|
329
|
+
return 0
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (first === 'why') {
|
|
333
|
+
const fiberId = Number(second)
|
|
334
|
+
if (!Number.isFinite(fiberId) || fiberId <= 0) {
|
|
335
|
+
console.error(' usage: rnx perf react why <fiber-id>')
|
|
336
|
+
return 1
|
|
337
|
+
}
|
|
338
|
+
const result = (await callInBridge(
|
|
339
|
+
bridge,
|
|
340
|
+
'SootSim.bridges.reactProfile.why',
|
|
341
|
+
Math.round(fiberId),
|
|
342
|
+
)) as { sample?: unknown; error?: string }
|
|
343
|
+
if (json) printJson(result)
|
|
344
|
+
else printWhy(result)
|
|
345
|
+
return result.error ? 1 : 0
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
printHelp()
|
|
349
|
+
return first ? 1 : 0
|
|
350
|
+
} finally {
|
|
351
|
+
bridge.close()
|
|
352
|
+
}
|
|
353
|
+
}
|