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,266 @@
|
|
|
1
|
+
// rnx camera drives the host-owned camera source. fixture video uses the
|
|
2
|
+
// same hidden video, canvas frame pump, ImageBitmap transfer, and
|
|
3
|
+
// BarcodeDetector loop as a real device camera. `do scan` remains the narrow
|
|
4
|
+
// synthetic special case for callers that have a payload string but no media.
|
|
5
|
+
|
|
6
|
+
import { createHash, randomUUID } from 'node:crypto'
|
|
7
|
+
import { createReadStream } from 'node:fs'
|
|
8
|
+
import fs from 'node:fs/promises'
|
|
9
|
+
import path from 'node:path'
|
|
10
|
+
import { cameraFixturesDir } from '../../src/home-paths'
|
|
11
|
+
import { printBridgeFailureDiagnostics } from '../bridge-diagnostics'
|
|
12
|
+
import {
|
|
13
|
+
createBridgeFromParsed,
|
|
14
|
+
evalInBridge,
|
|
15
|
+
parseBridgeCliArgs,
|
|
16
|
+
type WsBridge,
|
|
17
|
+
} from '../ws-bridge'
|
|
18
|
+
import type { CameraCommand, CameraResult } from '../../src/bridge-contract'
|
|
19
|
+
|
|
20
|
+
const CAMERA_FIXTURE_ROUTE = '/__camera-fixtures/'
|
|
21
|
+
const VIDEO_EXTENSIONS = new Set(['.m4v', '.mov', '.mp4', '.webm'])
|
|
22
|
+
|
|
23
|
+
function printHelp() {
|
|
24
|
+
console.log(`
|
|
25
|
+
rnx camera — control the simulator camera source
|
|
26
|
+
|
|
27
|
+
usage:
|
|
28
|
+
rnx camera play <file> [--loop] [--rate <number>]
|
|
29
|
+
rnx camera stop
|
|
30
|
+
rnx camera status [--json]
|
|
31
|
+
rnx do scan <text> [--format <web-format>]
|
|
32
|
+
|
|
33
|
+
subcommands:
|
|
34
|
+
play <file> replace the active camera source with a local video fixture
|
|
35
|
+
stop clear fixture mode and restore the live camera source
|
|
36
|
+
status report live, fixture, or idle source state
|
|
37
|
+
scan <text> inject a synthetic barcode result into an active scan session
|
|
38
|
+
|
|
39
|
+
options:
|
|
40
|
+
--loop loop fixture playback
|
|
41
|
+
--rate <n> fixture playback rate (default: 1)
|
|
42
|
+
--format <f> synthetic BarcodeDetector format (default: qr_code)
|
|
43
|
+
--json print status/result as JSON
|
|
44
|
+
--sim <id> target a specific sim
|
|
45
|
+
--port <p> WS bridge port (default: 7668)
|
|
46
|
+
|
|
47
|
+
examples:
|
|
48
|
+
rnx camera play ./fixtures/qr.mp4 --loop
|
|
49
|
+
rnx camera play ./fixtures/document.webm --rate 2
|
|
50
|
+
rnx camera status
|
|
51
|
+
rnx camera stop
|
|
52
|
+
rnx do scan "https://example.com" --format qr_code
|
|
53
|
+
`)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function isCameraResult(value: unknown): value is CameraResult {
|
|
57
|
+
if (!value || typeof value !== 'object') return false
|
|
58
|
+
const ok = Reflect.get(value, 'ok')
|
|
59
|
+
const source = Reflect.get(value, 'source')
|
|
60
|
+
return (
|
|
61
|
+
typeof ok === 'boolean' &&
|
|
62
|
+
(source === 'live' || source === 'fixture' || source === 'idle')
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function sendCameraCommand(
|
|
67
|
+
bridge: WsBridge,
|
|
68
|
+
camera: CameraCommand,
|
|
69
|
+
): Promise<CameraResult> {
|
|
70
|
+
const result: unknown = await bridge.send({ type: 'camera', camera })
|
|
71
|
+
if (!isCameraResult(result)) {
|
|
72
|
+
throw new Error('camera bridge returned an invalid result')
|
|
73
|
+
}
|
|
74
|
+
return result
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function printCameraResult(result: CameraResult, json: boolean): void {
|
|
78
|
+
if (json) {
|
|
79
|
+
console.log(JSON.stringify(result))
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
if (result.source === 'fixture') {
|
|
83
|
+
console.log(
|
|
84
|
+
` camera source: fixture (${result.url ?? 'unknown url'}, ${
|
|
85
|
+
result.loop ? 'looping' : 'once'
|
|
86
|
+
}, ${result.rate ?? 1}x)`,
|
|
87
|
+
)
|
|
88
|
+
} else if (result.source === 'live') {
|
|
89
|
+
console.log(' camera source: live')
|
|
90
|
+
} else if (result.url) {
|
|
91
|
+
console.log(
|
|
92
|
+
` camera source: idle (fixture queued: ${result.url}, ${
|
|
93
|
+
result.loop ? 'looping' : 'once'
|
|
94
|
+
}, ${result.rate ?? 1}x)`,
|
|
95
|
+
)
|
|
96
|
+
} else {
|
|
97
|
+
console.log(' camera source: idle')
|
|
98
|
+
}
|
|
99
|
+
if (!result.ok && result.error) console.error(` camera failed: ${result.error}`)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export async function runCamera(
|
|
103
|
+
args: string[],
|
|
104
|
+
opts: { port?: number } = {},
|
|
105
|
+
): Promise<number> {
|
|
106
|
+
const parsed = parseBridgeCliArgs(args, {
|
|
107
|
+
port: opts.port,
|
|
108
|
+
stripBooleanFlags: ['--help', '-h', '--json', '--loop'],
|
|
109
|
+
stripValueFlags: ['--format', '--rate'],
|
|
110
|
+
})
|
|
111
|
+
const positional = parsed.positional
|
|
112
|
+
const subcommand = positional[0]
|
|
113
|
+
if (
|
|
114
|
+
!subcommand ||
|
|
115
|
+
subcommand === 'help' ||
|
|
116
|
+
args.includes('--help') ||
|
|
117
|
+
args.includes('-h')
|
|
118
|
+
) {
|
|
119
|
+
printHelp()
|
|
120
|
+
return subcommand ? 0 : 1
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const bridge = createBridgeFromParsed(parsed)
|
|
124
|
+
const json = args.includes('--json')
|
|
125
|
+
try {
|
|
126
|
+
if (subcommand === 'scan') {
|
|
127
|
+
const text = positional[1]
|
|
128
|
+
if (!text) {
|
|
129
|
+
console.error(' usage: rnx do scan <text>')
|
|
130
|
+
return 1
|
|
131
|
+
}
|
|
132
|
+
const formatFlag = args.indexOf('--format')
|
|
133
|
+
const format = formatFlag >= 0 ? args[formatFlag + 1] : 'qr_code'
|
|
134
|
+
if (!format) {
|
|
135
|
+
console.error(' --format requires a value')
|
|
136
|
+
return 1
|
|
137
|
+
}
|
|
138
|
+
const injected = await evalInBridge<boolean>(
|
|
139
|
+
bridge,
|
|
140
|
+
`globalThis.__sootsimCameraInjectScan?.(${JSON.stringify(text)}, ${JSON.stringify(format)}) ?? false`,
|
|
141
|
+
)
|
|
142
|
+
if (!injected) {
|
|
143
|
+
console.error(
|
|
144
|
+
' no active barcode scan session. open a scanning <CameraView> first',
|
|
145
|
+
)
|
|
146
|
+
return 1
|
|
147
|
+
}
|
|
148
|
+
console.log(` injected ${format} scan: ${text}`)
|
|
149
|
+
return 0
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (subcommand === 'status') {
|
|
153
|
+
const result = await sendCameraCommand(bridge, { action: 'status' })
|
|
154
|
+
printCameraResult(result, json)
|
|
155
|
+
return result.ok ? 0 : 1
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (subcommand === 'stop') {
|
|
159
|
+
const result = await sendCameraCommand(bridge, { action: 'stop' })
|
|
160
|
+
printCameraResult(result, json)
|
|
161
|
+
return result.ok ? 0 : 1
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (subcommand === 'play') {
|
|
165
|
+
const input = positional[1]
|
|
166
|
+
if (!input) {
|
|
167
|
+
console.error(' usage: rnx camera play <file> [--loop] [--rate <number>]')
|
|
168
|
+
return 1
|
|
169
|
+
}
|
|
170
|
+
const sourcePath = path.resolve(input)
|
|
171
|
+
const extension = path.extname(sourcePath).toLowerCase()
|
|
172
|
+
if (!VIDEO_EXTENSIONS.has(extension)) {
|
|
173
|
+
console.error(
|
|
174
|
+
` unsupported camera fixture extension ${extension || '(none)'}. use mp4, webm, mov, or m4v`,
|
|
175
|
+
)
|
|
176
|
+
return 1
|
|
177
|
+
}
|
|
178
|
+
let sourceStat
|
|
179
|
+
try {
|
|
180
|
+
sourceStat = await fs.stat(sourcePath)
|
|
181
|
+
} catch {
|
|
182
|
+
console.error(` camera fixture not found: ${sourcePath}`)
|
|
183
|
+
return 1
|
|
184
|
+
}
|
|
185
|
+
if (!sourceStat.isFile() || sourceStat.size === 0) {
|
|
186
|
+
console.error(` camera fixture must be a non-empty file: ${sourcePath}`)
|
|
187
|
+
return 1
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const rateFlag = args.indexOf('--rate')
|
|
191
|
+
const rawRate = rateFlag >= 0 ? args[rateFlag + 1] : undefined
|
|
192
|
+
const rate = rawRate === undefined ? 1 : Number(rawRate)
|
|
193
|
+
if (!Number.isFinite(rate) || rate <= 0) {
|
|
194
|
+
console.error(' --rate must be a positive finite number')
|
|
195
|
+
return 1
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const fixtureDirectory = cameraFixturesDir()
|
|
199
|
+
await fs.mkdir(fixtureDirectory, { recursive: true })
|
|
200
|
+
const digest = createHash('sha256')
|
|
201
|
+
await new Promise<void>((resolve, reject) => {
|
|
202
|
+
const input = createReadStream(sourcePath)
|
|
203
|
+
input.on('data', (chunk) => digest.update(chunk))
|
|
204
|
+
input.once('error', reject)
|
|
205
|
+
input.once('end', resolve)
|
|
206
|
+
})
|
|
207
|
+
const filename = `${digest.digest('hex')}${extension}`
|
|
208
|
+
const servedPath = path.join(fixtureDirectory, filename)
|
|
209
|
+
let staged = false
|
|
210
|
+
try {
|
|
211
|
+
const servedStat = await fs.lstat(servedPath)
|
|
212
|
+
if (!servedStat.isFile()) {
|
|
213
|
+
throw new Error(`camera fixture cache entry is not a file: ${servedPath}`)
|
|
214
|
+
}
|
|
215
|
+
staged = true
|
|
216
|
+
} catch (error) {
|
|
217
|
+
if (
|
|
218
|
+
!error ||
|
|
219
|
+
typeof error !== 'object' ||
|
|
220
|
+
Reflect.get(error, 'code') !== 'ENOENT'
|
|
221
|
+
) {
|
|
222
|
+
throw error
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (!staged) {
|
|
226
|
+
const temporaryPath = path.join(
|
|
227
|
+
fixtureDirectory,
|
|
228
|
+
`.${filename}.${process.pid}-${randomUUID()}.tmp`,
|
|
229
|
+
)
|
|
230
|
+
await fs.copyFile(sourcePath, temporaryPath)
|
|
231
|
+
try {
|
|
232
|
+
await fs.link(temporaryPath, servedPath)
|
|
233
|
+
} catch (error) {
|
|
234
|
+
if (
|
|
235
|
+
!error ||
|
|
236
|
+
typeof error !== 'object' ||
|
|
237
|
+
Reflect.get(error, 'code') !== 'EEXIST'
|
|
238
|
+
) {
|
|
239
|
+
throw error
|
|
240
|
+
}
|
|
241
|
+
} finally {
|
|
242
|
+
await fs.unlink(temporaryPath).catch(() => {})
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const url = `http://127.0.0.1:${parsed.wsPort}${CAMERA_FIXTURE_ROUTE}${filename}`
|
|
246
|
+
const result = await sendCameraCommand(bridge, {
|
|
247
|
+
action: 'play',
|
|
248
|
+
url,
|
|
249
|
+
loop: args.includes('--loop'),
|
|
250
|
+
rate,
|
|
251
|
+
})
|
|
252
|
+
printCameraResult(result, json)
|
|
253
|
+
return result.ok ? 0 : 1
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
console.error(` unknown camera subcommand: ${subcommand}`)
|
|
257
|
+
printHelp()
|
|
258
|
+
return 1
|
|
259
|
+
} catch (error) {
|
|
260
|
+
console.error(` camera failed: ${error instanceof Error ? error.message : error}`)
|
|
261
|
+
await printBridgeFailureDiagnostics(bridge)
|
|
262
|
+
return 1
|
|
263
|
+
} finally {
|
|
264
|
+
bridge.close()
|
|
265
|
+
}
|
|
266
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import {
|
|
2
|
+
applyAutomaticSootsimCleanup,
|
|
3
|
+
applySootsimCleanup,
|
|
4
|
+
formatSootsimBytes,
|
|
5
|
+
planSootsimCleanup,
|
|
6
|
+
type SootsimCleanupEntry,
|
|
7
|
+
type SootsimCleanupKind,
|
|
8
|
+
} from '../../src/disk-cleanup'
|
|
9
|
+
|
|
10
|
+
const KIND_LABELS: Record<SootsimCleanupKind, string> = {
|
|
11
|
+
'browser-cache': 'browser caches',
|
|
12
|
+
'camera-fixture': 'staged camera fixture videos',
|
|
13
|
+
recording: 'recordings and captured frames',
|
|
14
|
+
'runtime-archive': 'downloaded runtime archives',
|
|
15
|
+
'runtime-version': 'inactive runtime versions',
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function summarize(entries: SootsimCleanupEntry[]): string[] {
|
|
19
|
+
const byKind = new Map<SootsimCleanupKind, { bytes: number; count: number }>()
|
|
20
|
+
for (const entry of entries) {
|
|
21
|
+
const current = byKind.get(entry.kind) ?? { bytes: 0, count: 0 }
|
|
22
|
+
current.bytes += entry.bytes
|
|
23
|
+
current.count++
|
|
24
|
+
byKind.set(entry.kind, current)
|
|
25
|
+
}
|
|
26
|
+
return [...byKind]
|
|
27
|
+
.sort((left, right) => right[1].bytes - left[1].bytes)
|
|
28
|
+
.map(
|
|
29
|
+
([kind, summary]) =>
|
|
30
|
+
` ${KIND_LABELS[kind]}: ${formatSootsimBytes(summary.bytes)} (${summary.count} ${
|
|
31
|
+
summary.count === 1 ? 'item' : 'items'
|
|
32
|
+
})`,
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function printHelp(): void {
|
|
37
|
+
console.log(`
|
|
38
|
+
rnx cleanup — inspect and reclaim local rnx disk usage
|
|
39
|
+
|
|
40
|
+
usage:
|
|
41
|
+
rnx cleanup
|
|
42
|
+
rnx cleanup --apply
|
|
43
|
+
rnx cleanup --aggressive
|
|
44
|
+
rnx cleanup --apply --aggressive
|
|
45
|
+
|
|
46
|
+
The default is a read-only preview. --apply removes downloaded runtime archives,
|
|
47
|
+
old runtimes outside a two-version rollback window, and disposable browser
|
|
48
|
+
caches. It never removes the active runtime, a runtime serving a connected
|
|
49
|
+
simulator, cookies, localStorage, IndexedDB, service workers, or other persistent
|
|
50
|
+
app storage.
|
|
51
|
+
|
|
52
|
+
--aggressive additionally removes the rollback runtime plus user-created
|
|
53
|
+
recordings and captured frames. It still preserves the active runtime and app storage.
|
|
54
|
+
`)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export async function runCleanup(args: string[]): Promise<number> {
|
|
58
|
+
if (args.length === 1 && args[0] === '--automatic-worker') {
|
|
59
|
+
try {
|
|
60
|
+
const result = applyAutomaticSootsimCleanup()
|
|
61
|
+
if (result.status === 'complete') {
|
|
62
|
+
process.stderr.write(
|
|
63
|
+
result.reclaimedBytes > 0
|
|
64
|
+
? ` rnx automatic cleanup reclaimed ${formatSootsimBytes(
|
|
65
|
+
result.reclaimedBytes,
|
|
66
|
+
)}; active runtimes and app data were kept.\n`
|
|
67
|
+
: ' rnx automatic cleanup found no obsolete local data.\n',
|
|
68
|
+
)
|
|
69
|
+
} else if (result.status === 'deferred') {
|
|
70
|
+
process.stderr.write(
|
|
71
|
+
` rnx automatic cleanup reclaimed ${formatSootsimBytes(
|
|
72
|
+
result.reclaimedBytes,
|
|
73
|
+
)}; ${formatSootsimBytes(
|
|
74
|
+
result.deferredInUseBytes,
|
|
75
|
+
)} is in use and will be retried on a later run.\n`,
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
return 0
|
|
79
|
+
} catch (error) {
|
|
80
|
+
process.stderr.write(
|
|
81
|
+
` rnx automatic cleanup will retry on the next run: ${
|
|
82
|
+
error instanceof Error ? error.message : String(error)
|
|
83
|
+
}\n`,
|
|
84
|
+
)
|
|
85
|
+
return 0
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
89
|
+
printHelp()
|
|
90
|
+
return 0
|
|
91
|
+
}
|
|
92
|
+
const known = new Set(['--aggressive', '--apply'])
|
|
93
|
+
const unknown = args.find((arg) => !known.has(arg))
|
|
94
|
+
if (unknown) {
|
|
95
|
+
console.error(` cleanup: unknown option ${unknown}`)
|
|
96
|
+
printHelp()
|
|
97
|
+
return 1
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const aggressive = args.includes('--aggressive')
|
|
101
|
+
const apply = args.includes('--apply')
|
|
102
|
+
const plan = planSootsimCleanup({ aggressive })
|
|
103
|
+
const selected = plan.entries.filter((entry) => entry.selected)
|
|
104
|
+
const safe = selected.filter((entry) => entry.state === 'safe')
|
|
105
|
+
const aggressiveOnly = plan.entries.filter(
|
|
106
|
+
(entry) => entry.state === 'aggressive' && entry.selected,
|
|
107
|
+
)
|
|
108
|
+
const inUse = plan.entries.filter((entry) => entry.state === 'in-use')
|
|
109
|
+
const inUseBrowserCaches = inUse.filter((entry) => entry.kind === 'browser-cache')
|
|
110
|
+
const inUseRuntimes = inUse.filter((entry) => entry.kind === 'runtime-version')
|
|
111
|
+
const rollback = plan.entries.find(
|
|
112
|
+
(entry) => entry.kind === 'runtime-version' && entry.state === 'aggressive',
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
console.log(` rnx data home: ${plan.home}`)
|
|
116
|
+
console.log(` current size: ${formatSootsimBytes(plan.totalBytes)}`)
|
|
117
|
+
console.log(` active runtime: ${plan.activeRuntime ?? '(none installed)'}`)
|
|
118
|
+
console.log('')
|
|
119
|
+
console.log(apply ? ' safe items selected for removal:' : ' safe to reclaim:')
|
|
120
|
+
for (const line of summarize(safe)) console.log(line)
|
|
121
|
+
if (safe.length === 0) console.log(' nothing')
|
|
122
|
+
|
|
123
|
+
if (aggressiveOnly.length > 0) {
|
|
124
|
+
console.log('')
|
|
125
|
+
console.log(' aggressive additions:')
|
|
126
|
+
for (const line of summarize(aggressiveOnly)) console.log(line)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
console.log('')
|
|
130
|
+
console.log(' protected:')
|
|
131
|
+
if (plan.activeRuntime) console.log(` active runtime ${plan.activeRuntime} (in use)`)
|
|
132
|
+
for (const entry of inUseRuntimes) {
|
|
133
|
+
console.log(` running daemon runtime ${entry.path}`)
|
|
134
|
+
}
|
|
135
|
+
if (rollback && !aggressive) {
|
|
136
|
+
console.log(` rollback runtime ${rollback.path}`)
|
|
137
|
+
}
|
|
138
|
+
console.log(' cookies, localStorage, IndexedDB, service workers, and profile data')
|
|
139
|
+
if (!aggressive) {
|
|
140
|
+
console.log(' recordings and captured frames (use --aggressive to remove)')
|
|
141
|
+
}
|
|
142
|
+
if (inUseBrowserCaches.length > 0) {
|
|
143
|
+
console.log(
|
|
144
|
+
` ${formatSootsimBytes(
|
|
145
|
+
inUseBrowserCaches.reduce((total, entry) => total + entry.bytes, 0),
|
|
146
|
+
)} ` + `of browser cache is open; close rnx and rerun cleanup`,
|
|
147
|
+
)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
console.log('')
|
|
151
|
+
console.log(` reclaimable now: ${formatSootsimBytes(plan.reclaimableBytes)}`)
|
|
152
|
+
if (!apply) {
|
|
153
|
+
console.log(
|
|
154
|
+
` preview only — run \`rnx cleanup --apply${
|
|
155
|
+
aggressive ? ' --aggressive' : ''
|
|
156
|
+
}\` to remove these items`,
|
|
157
|
+
)
|
|
158
|
+
return 0
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const result = applySootsimCleanup({ aggressive })
|
|
162
|
+
console.log(
|
|
163
|
+
` removed ${result.removedPaths.length} ${
|
|
164
|
+
result.removedPaths.length === 1 ? 'item' : 'items'
|
|
165
|
+
}; reclaimed ${formatSootsimBytes(result.reclaimedBytes)}`,
|
|
166
|
+
)
|
|
167
|
+
console.log(` remaining rnx data: ${formatSootsimBytes(result.remainingBytes)}`)
|
|
168
|
+
return 0
|
|
169
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
// rnx compat — scan dependencies with the shared compatibility engine.
|
|
2
|
+
|
|
3
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
4
|
+
import { dirname, join, relative, resolve } from 'node:path'
|
|
5
|
+
import {
|
|
6
|
+
COMPAT_RESULT_CAVEAT,
|
|
7
|
+
formatDetailedScanLines,
|
|
8
|
+
formatScanCounts,
|
|
9
|
+
scanDeps,
|
|
10
|
+
type ScanResult,
|
|
11
|
+
} from '@sootsim/compat/web'
|
|
12
|
+
import { findProjectRoot, scanWorkspace } from './detect'
|
|
13
|
+
|
|
14
|
+
interface PackageManifest {
|
|
15
|
+
name?: string
|
|
16
|
+
dependencies?: Record<string, string>
|
|
17
|
+
devDependencies?: Record<string, string>
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function runCompat(args: string[]): Promise<void> {
|
|
21
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
22
|
+
console.log(`
|
|
23
|
+
rnx compat — check package compatibility
|
|
24
|
+
|
|
25
|
+
usage:
|
|
26
|
+
rnx compat [package-name]
|
|
27
|
+
rnx compat --json
|
|
28
|
+
rnx compat --brief
|
|
29
|
+
|
|
30
|
+
flags:
|
|
31
|
+
--json machine-readable output
|
|
32
|
+
--brief one-line summary
|
|
33
|
+
--app <dir> target directory (default: cwd)
|
|
34
|
+
`)
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const appIndex = args.indexOf('--app')
|
|
39
|
+
const appDir =
|
|
40
|
+
appIndex >= 0 && args[appIndex + 1] ? resolve(args[appIndex + 1]) : process.cwd()
|
|
41
|
+
const packageName = args.find(
|
|
42
|
+
(arg, index) => !arg.startsWith('-') && (appIndex < 0 || index !== appIndex + 1),
|
|
43
|
+
)
|
|
44
|
+
const pkg = readPackageJson(appDir)
|
|
45
|
+
if (!pkg) {
|
|
46
|
+
console.error(` no package.json found in ${appDir}`)
|
|
47
|
+
process.exitCode = 1
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (packageName) {
|
|
52
|
+
report(args, scanOnePackage(appDir, pkg, packageName))
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const here = scanDeps(installedManifest(appDir, pkg))
|
|
57
|
+
if (here.packages.length > 0 || appIndex >= 0) {
|
|
58
|
+
report(args, here)
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// a monorepo root manifest is not the app. it carries react-native and expo
|
|
63
|
+
// so the workspace resolves and little else, so scanning it reports a
|
|
64
|
+
// project with nothing to check while the real app sits in a subdirectory.
|
|
65
|
+
const workspaceRoot = findProjectRoot(appDir)
|
|
66
|
+
const elsewhere = scanWorkspace(workspaceRoot).apps.flatMap((app) => {
|
|
67
|
+
if (resolve(app.dir) === appDir) return []
|
|
68
|
+
const manifest = readPackageJson(app.dir)
|
|
69
|
+
if (!manifest) return []
|
|
70
|
+
const result = scanDeps(installedManifest(app.dir, manifest))
|
|
71
|
+
return result.packages.length > 0 ? [{ dir: app.dir, result }] : []
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
if (elsewhere.length === 1) {
|
|
75
|
+
const only = elsewhere[0]
|
|
76
|
+
console.log(`\n scanning ${relative(appDir, only.dir) || '.'}`)
|
|
77
|
+
report(args, only.result)
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
if (elsewhere.length > 1) {
|
|
81
|
+
console.log(`\n this workspace has more than one app. scan one with --app:`)
|
|
82
|
+
for (const entry of elsewhere) {
|
|
83
|
+
console.log(` rnx compat --app ${relative(appDir, entry.dir)}`)
|
|
84
|
+
}
|
|
85
|
+
console.log()
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
report(args, here)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function report(args: string[], result: ScanResult): void {
|
|
92
|
+
if (args.includes('--json')) {
|
|
93
|
+
console.log(JSON.stringify(result, null, 2))
|
|
94
|
+
return
|
|
95
|
+
}
|
|
96
|
+
if (args.includes('--brief')) {
|
|
97
|
+
console.log(` ${formatScanCounts(result)}`)
|
|
98
|
+
console.log(` ${COMPAT_RESULT_CAVEAT}`)
|
|
99
|
+
return
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
console.log(`\n rnx compatibility report`)
|
|
103
|
+
console.log(` ${formatScanCounts(result)}\n`)
|
|
104
|
+
for (const line of formatDetailedScanLines(result)) console.log(` ${line}`)
|
|
105
|
+
const supported = result.packages.filter((item) => item.status === 'full').length
|
|
106
|
+
if (supported > 0) console.log(` supported: ${supported} packages`)
|
|
107
|
+
const notRelevant = result.packages.filter(
|
|
108
|
+
(item) => item.status === 'not-relevant',
|
|
109
|
+
).length
|
|
110
|
+
if (notRelevant > 0) console.log(` not relevant at runtime: ${notRelevant} packages`)
|
|
111
|
+
if (result.packages.length === 0) {
|
|
112
|
+
console.log(` no React Native ecosystem packages detected`)
|
|
113
|
+
}
|
|
114
|
+
console.log(`\n ${COMPAT_RESULT_CAVEAT}\n`)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// the declared range is often not a version: pnpm and bun catalogs write
|
|
118
|
+
// `catalog:`, a workspace dependency writes `workspace:*`, and a pinned tarball
|
|
119
|
+
// writes a URL. none of those match a verified version range, so a whole app
|
|
120
|
+
// reports as unverified. the installed tree holds the version that will
|
|
121
|
+
// actually run, so ask it, and keep the declared range for anything the project
|
|
122
|
+
// has not installed.
|
|
123
|
+
function installedManifest(appDir: string, pkg: PackageManifest): PackageManifest {
|
|
124
|
+
const resolveVersion = (name: string, declared: string): string => {
|
|
125
|
+
let dir = appDir
|
|
126
|
+
while (true) {
|
|
127
|
+
const manifest = join(dir, 'node_modules', name, 'package.json')
|
|
128
|
+
if (existsSync(manifest)) {
|
|
129
|
+
try {
|
|
130
|
+
const parsed: unknown = JSON.parse(readFileSync(manifest, 'utf8'))
|
|
131
|
+
if (parsed && typeof parsed === 'object' && 'version' in parsed) {
|
|
132
|
+
const version = parsed.version
|
|
133
|
+
if (typeof version === 'string') return version
|
|
134
|
+
}
|
|
135
|
+
} catch {}
|
|
136
|
+
return declared
|
|
137
|
+
}
|
|
138
|
+
const parent = dirname(dir)
|
|
139
|
+
if (parent === dir) return declared
|
|
140
|
+
dir = parent
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
const mapped = (deps: Record<string, string> | undefined) =>
|
|
144
|
+
deps &&
|
|
145
|
+
Object.fromEntries(
|
|
146
|
+
Object.entries(deps).map(([name, declared]) => [
|
|
147
|
+
name,
|
|
148
|
+
resolveVersion(name, declared),
|
|
149
|
+
]),
|
|
150
|
+
)
|
|
151
|
+
return {
|
|
152
|
+
name: pkg.name,
|
|
153
|
+
dependencies: mapped(pkg.dependencies),
|
|
154
|
+
devDependencies: mapped(pkg.devDependencies),
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function readPackageJson(appDir: string): PackageManifest | null {
|
|
159
|
+
const packagePath = resolve(appDir, 'package.json')
|
|
160
|
+
if (!existsSync(packagePath)) return null
|
|
161
|
+
const parsed: unknown = JSON.parse(readFileSync(packagePath, 'utf8'))
|
|
162
|
+
return parsed && typeof parsed === 'object' ? parsed : null
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function scanOnePackage(
|
|
166
|
+
appDir: string,
|
|
167
|
+
pkg: PackageManifest,
|
|
168
|
+
packageName: string,
|
|
169
|
+
): ScanResult {
|
|
170
|
+
const installed = installedManifest(appDir, pkg)
|
|
171
|
+
const version =
|
|
172
|
+
installed.dependencies?.[packageName] ??
|
|
173
|
+
installed.devDependencies?.[packageName] ??
|
|
174
|
+
'*'
|
|
175
|
+
return scanDeps({ dependencies: { [packageName]: version } })
|
|
176
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { rnxExit } from '../run-rnx'
|
|
2
|
+
import { runTelemetry } from './telemetry'
|
|
3
|
+
|
|
4
|
+
function printHelp(): void {
|
|
5
|
+
console.log(`
|
|
6
|
+
rnx config — manage local CLI preferences
|
|
7
|
+
|
|
8
|
+
usage:
|
|
9
|
+
rnx config telemetry [on|off|status]
|
|
10
|
+
|
|
11
|
+
environment:
|
|
12
|
+
RNX_HINTS=off suppress CLI hints
|
|
13
|
+
RNX_HINTS=always show every CLI hint every time
|
|
14
|
+
RNX_UPDATE_CHECK=off disable background CLI update checks and notices
|
|
15
|
+
`)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function runConfig(args: string[]): Promise<void> {
|
|
19
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
20
|
+
printHelp()
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const sub = args.find((arg) => !arg.startsWith('-'))
|
|
25
|
+
if (sub === 'telemetry') {
|
|
26
|
+
const index = args.indexOf(sub)
|
|
27
|
+
await runTelemetry([...args.slice(0, index), ...args.slice(index + 1)])
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
console.error(` unknown config command: ${sub ?? '(missing)'}`)
|
|
32
|
+
printHelp()
|
|
33
|
+
rnxExit(1)
|
|
34
|
+
}
|