peachsim 0.1.1635 → 0.1.1641
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/README.md +10 -1
- package/cli/app-config.ts +14 -5
- package/cli/app-stubs.ts +96 -22
- package/cli/bridge-flow-runner.ts +2 -2
- package/cli/cloud-client.ts +60 -40
- package/cli/cloud-session.ts +16 -0
- package/cli/commands/assert.ts +17 -4
- package/cli/commands/box.ts +33 -19
- package/cli/commands/doctor.ts +37 -20
- package/cli/commands/flow.ts +1 -1
- package/cli/commands/inspect/actions.ts +3 -2
- package/cli/commands/inspect/core.ts +0 -8
- package/cli/commands/inspect/shared.ts +9 -4
- package/cli/commands/inspect.ts +65 -61
- package/cli/commands/platform.ts +2 -1
- package/cli/commands/preview.ts +1 -1
- package/cli/commands/record.ts +154 -68
- package/cli/commands/swift.ts +18 -4
- package/cli/commands/three-mode.ts +4 -38
- package/cli/commands/upload.ts +55 -49
- package/cli/drivers/playwright-sim-host.ts +65 -42
- package/cli/flow-live-status.ts +0 -2
- package/cli/main.ts +2 -2
- package/cli/outbound-endpoints.ts +7 -5
- package/cli/pause-aware-timeout.ts +4 -0
- package/cli/ws-bridge.ts +19 -16
- package/detox/attempt-events.cjs +99 -6
- package/detox/attempt-events.d.cts +7 -0
- package/detox/expectations.ts +7 -3
- package/detox/index.ts +259 -139
- package/detox/jest-environment.cjs +5 -1
- package/detox/motion-change.cjs +170 -21
- package/detox/motion-change.d.cts +32 -0
- 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 +17 -7
- package/dist-lib/beta.cjs +1 -1
- package/dist-lib/beta.mjs +1 -1
- package/dist-lib/bridge-constants.cjs +1 -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 +1 -1
- package/dist-lib/bridge-contract.mjs +1 -1
- package/dist-lib/bundle-channel.cjs +5014 -2268
- 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 +1 -1
- package/dist-lib/config.mjs +1 -1
- package/dist-lib/detox/index.cjs +1036 -166
- package/dist-lib/detox/jest-environment.cjs +5 -1
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/expo-swift-bundle.cjs +4613 -1963
- package/dist-lib/expo-swift-metadata.cjs +9 -7
- package/dist-lib/expo-swift-metadata.mjs +9 -7
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +58072 -16400
- 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 +2790 -143
- 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 +2579 -95
- package/dist-lib/native-app-identity-project.cjs +243 -53
- package/dist-lib/native-app-identity-project.mjs +243 -53
- package/dist-lib/native-app-identity.cjs +29 -30
- package/dist-lib/native-app-identity.mjs +29 -30
- 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 +2 -2
- package/dist-lib/react-native-host-modules.mjs +2 -2
- package/dist-lib/scripts/dev-server-scanner.cjs +8 -6
- package/dist-lib/sdk.cjs +4 -3
- package/dist-lib/sdk.mjs +4 -3
- package/dist-lib/skills.cjs +66475 -23097
- package/dist-lib/swift-compile.cjs +452 -260
- package/dist-lib/swift-compile.mjs +451 -252
- package/dist-lib/swift-package.cjs +1 -1
- package/dist-lib/swift-package.mjs +1 -1
- package/dist-lib/swift-submit.cjs +14 -7
- package/dist-lib/swift-submit.mjs +14 -7
- package/dist-lib/swift-toolchain.cjs +316 -41
- package/dist-lib/swift.cjs +1 -1
- package/dist-lib/vite-swift.cjs +8188 -5636
- package/dist-lib/vite.cjs +2857 -12
- package/dist-lib/worklet-executable.cjs +14 -2
- package/dist-lib/worklet-executable.mjs +14 -2
- package/package.json +1 -1
- package/src/backend-origin.ts +21 -6
- package/src/expo-swift-host.ts +5 -2
- package/src/expoKotlinContract.ts +4 -7
- package/src/expoSwiftContract.ts +4 -6
- package/src/host/staged-host-files.ts +9 -0
- package/src/native-app-identity-project.ts +1 -4
- package/src/native-app-identity.ts +1 -4
- package/src/nativeAppIconContract.ts +1 -4
- package/src/output-lock-stdio.ts +36 -0
- package/src/react-native-bridge-modules.ts +106 -78
- package/src/react-native-host-modules.ts +4 -1
- package/src/settings-bundle.ts +1 -4
- package/src/swift-compile.ts +224 -139
- package/src/swift-distribution.lock.json +23 -12
- package/src/swift-distribution.ts +103 -4
- package/src/swift-host.ts +95 -30
- package/src/swift-icu.ts +16 -10
- package/src/swift-objc-compile.ts +547 -0
- package/src/swift-objc-metadata.ts +204 -0
- package/src/swift-objc-sdk.ts +883 -19
- package/src/swift-objc-standin.ts +126 -14
- package/src/swift-package-manifest.ts +42 -1
- package/src/swift-packages.ts +35 -21
- package/src/swift-root-lock.ts +153 -0
- package/src/swift-standin-emit.ts +40 -7
- package/src/swift-standins.ts +404 -138
- package/src/swift-submit.ts +18 -7
- package/src/swift-toolchain.ts +378 -46
- package/src/swiftContract.ts +12 -0
- package/src/worker-system-chunks.ts +11 -12
- package/src/worklet-executable.ts +19 -1
- package/src/xcode-project.ts +280 -33
package/cli/commands/doctor.ts
CHANGED
|
@@ -373,9 +373,8 @@ function printVersions(findings: DoctorFindings): void {
|
|
|
373
373
|
}
|
|
374
374
|
}
|
|
375
375
|
|
|
376
|
-
//
|
|
377
|
-
//
|
|
378
|
-
// one line: there is nothing to check in them.
|
|
376
|
+
// packages appear worst first with the published implementation percent and
|
|
377
|
+
// complete capability details. javascript-only packages sit last on one line.
|
|
379
378
|
function printPackages(
|
|
380
379
|
packages: readonly PackageResult[],
|
|
381
380
|
verbose: boolean,
|
|
@@ -411,10 +410,26 @@ function printPackages(
|
|
|
411
410
|
const columns = process.stdout.columns || 120
|
|
412
411
|
for (const row of rows) {
|
|
413
412
|
const percent = percentLabel(row.item)
|
|
414
|
-
const detail =
|
|
413
|
+
const detail = row.detail
|
|
415
414
|
console.log(
|
|
416
415
|
` ${row.glyph} ${row.item.name.padEnd(nameWidth)}${muted(row.item.version.padEnd(versionWidth))}${percent.padStart(5)}${detail ? ` ${row.tone(detail)}` : ''}`,
|
|
417
416
|
)
|
|
417
|
+
if (!answering.has(row.item.name)) {
|
|
418
|
+
for (const [label, text] of [
|
|
419
|
+
['working', row.item.rnx.working],
|
|
420
|
+
['missing', row.item.rnx.missing],
|
|
421
|
+
]) {
|
|
422
|
+
if (!text) continue
|
|
423
|
+
let line = `${label}:`
|
|
424
|
+
for (const word of text.split(/\s+/)) {
|
|
425
|
+
if (line.length + word.length + 1 > columns - 6) {
|
|
426
|
+
console.log(` ${muted(line)}`)
|
|
427
|
+
line = word
|
|
428
|
+
} else line += ` ${word}`
|
|
429
|
+
}
|
|
430
|
+
console.log(` ${muted(line)}`)
|
|
431
|
+
}
|
|
432
|
+
}
|
|
418
433
|
if (verbose && row.item.note) console.log(` ${muted(row.item.note)}`)
|
|
419
434
|
}
|
|
420
435
|
if (javascriptOnly.length > 0) {
|
|
@@ -476,7 +491,10 @@ function packageRow(
|
|
|
476
491
|
return {
|
|
477
492
|
rank: RANK.unknown,
|
|
478
493
|
glyph: accent('?'),
|
|
479
|
-
|
|
494
|
+
// fonts and assets, or names built at runtime, leave nothing to stub by name
|
|
495
|
+
detail: unknownNative.requests.length
|
|
496
|
+
? 'native code Peach does not know; stub it below'
|
|
497
|
+
: 'native code; names no native module',
|
|
480
498
|
tone: muted,
|
|
481
499
|
}
|
|
482
500
|
// a stub answers ahead of Peach's own support for the package
|
|
@@ -514,22 +532,12 @@ function packageRow(
|
|
|
514
532
|
return {
|
|
515
533
|
rank: RANK.partial,
|
|
516
534
|
glyph: warning('◐'),
|
|
517
|
-
detail:
|
|
535
|
+
detail: '',
|
|
518
536
|
tone: muted,
|
|
519
537
|
}
|
|
520
538
|
return { rank: RANK.supported, glyph: success('✓'), detail: '', tone: muted }
|
|
521
539
|
}
|
|
522
540
|
|
|
523
|
-
function firstClause(text: string): string {
|
|
524
|
-
const end = text.search(/[.;,](\s|$)/)
|
|
525
|
-
return end > 0 ? text.slice(0, end) : text
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
// a detail ends at the terminal edge rather than wrapping under the columns
|
|
529
|
-
function fit(text: string, width: number): string {
|
|
530
|
-
return text.length > width ? `${text.slice(0, Math.max(0, width - 1))}…` : text
|
|
531
|
-
}
|
|
532
|
-
|
|
533
541
|
function printNativeGuidance(
|
|
534
542
|
unknownNative: readonly UnknownNativePackage[],
|
|
535
543
|
local: readonly LocalNative[],
|
|
@@ -560,19 +568,28 @@ function printNativeGuidance(
|
|
|
560
568
|
)
|
|
561
569
|
}
|
|
562
570
|
// the open names grouped by the package they belong to, the key `packages` takes
|
|
563
|
-
const open = new Map<string, NativeRequest
|
|
571
|
+
const open = new Map<string, Map<string, NativeRequest>>()
|
|
564
572
|
for (const [owner, requests] of [
|
|
565
573
|
...unknownNative.map((item) => [item.name, item.requests] as const),
|
|
566
574
|
...local.map((item) => [item.package, [item]] as const),
|
|
567
575
|
]) {
|
|
568
|
-
const
|
|
569
|
-
|
|
576
|
+
for (const request of requests) {
|
|
577
|
+
if (request.answered) continue
|
|
578
|
+
const names = open.get(owner) ?? new Map<string, NativeRequest>()
|
|
579
|
+
names.set(`${request.kind}:${request.name}`, request)
|
|
580
|
+
open.set(owner, names)
|
|
581
|
+
}
|
|
570
582
|
}
|
|
571
583
|
if (open.size) {
|
|
572
584
|
console.log(`\n stub them in peach.config.ts, by package:\n`)
|
|
585
|
+
console.log(
|
|
586
|
+
muted(` import { defineConfig } from '${rnxPublicBrand.packageName}/config'`),
|
|
587
|
+
)
|
|
588
|
+
console.log()
|
|
573
589
|
console.log(muted(` export default defineConfig({`))
|
|
574
590
|
console.log(muted(` packages: {`))
|
|
575
|
-
for (const [owner,
|
|
591
|
+
for (const [owner, requestsByName] of open) {
|
|
592
|
+
const requests = [...requestsByName.values()]
|
|
576
593
|
console.log(muted(` '${owner}': {`))
|
|
577
594
|
for (const kind of ['module', 'view'] as const) {
|
|
578
595
|
const names = requests.filter((request) => request.kind === kind)
|
package/cli/commands/flow.ts
CHANGED
|
@@ -973,7 +973,7 @@ flow extension:
|
|
|
973
973
|
// canvas during scrub for smooth motion while the real app
|
|
974
974
|
// catches up.
|
|
975
975
|
if (videoPath) uploadArgs.push('--video', videoPath)
|
|
976
|
-
if (scenePath) uploadArgs.push('--scene', scenePath)
|
|
976
|
+
if (scenePath) uploadArgs.push('--scene', scenePath, '--live-bundle')
|
|
977
977
|
if (previewScreenshotPath) {
|
|
978
978
|
uploadArgs.push('--screenshot', previewScreenshotPath)
|
|
979
979
|
}
|
|
@@ -380,9 +380,10 @@ async function resolveTextTapTarget(
|
|
|
380
380
|
query: string,
|
|
381
381
|
options: TapTextOptions = {},
|
|
382
382
|
) {
|
|
383
|
+
const needle = options.exact ? query : query.trim().toLowerCase()
|
|
384
|
+
if (!needle) return { matched: 0, total: 0 }
|
|
383
385
|
const { tree } = await inspectTree(bridge, 50)
|
|
384
386
|
if (!Array.isArray(tree)) return { matched: 0, total: 0 }
|
|
385
|
-
const needle = options.exact ? query : query.toLowerCase()
|
|
386
387
|
let candidates = collectTextCandidates(tree).filter(
|
|
387
388
|
({ node, ancestorTestIDs, role }) => {
|
|
388
389
|
const text = nodeText(node)
|
|
@@ -411,7 +412,7 @@ async function resolveTextTapTarget(
|
|
|
411
412
|
if (actionable.length > 0) candidates = actionable
|
|
412
413
|
if (!options.exact) {
|
|
413
414
|
const whole = candidates.filter(
|
|
414
|
-
({ node }) => nodeText(node).trim().toLowerCase() === needle
|
|
415
|
+
({ node }) => nodeText(node).trim().toLowerCase() === needle,
|
|
415
416
|
)
|
|
416
417
|
if (whole.length > 0) candidates = whole
|
|
417
418
|
}
|
|
@@ -556,12 +556,6 @@ export interface LayoutElement {
|
|
|
556
556
|
ellipsisActive?: boolean
|
|
557
557
|
}
|
|
558
558
|
|
|
559
|
-
export interface LayoutResult {
|
|
560
|
-
preview: 'web' | 'native'
|
|
561
|
-
count: number
|
|
562
|
-
elements: LayoutElement[]
|
|
563
|
-
}
|
|
564
|
-
|
|
565
559
|
// in-page extractor for the native preview. mirrors the visibility + device
|
|
566
560
|
// sizing the a11y / wait-ready kernels use so "what's on screen" agrees across
|
|
567
561
|
// the inspect surface. returns LayoutElement[] (or [] when the bridge is cold).
|
|
@@ -2013,8 +2007,6 @@ export const DEBUG_CHANNELS = [
|
|
|
2013
2007
|
'yoga',
|
|
2014
2008
|
] as const
|
|
2015
2009
|
|
|
2016
|
-
export type DebugChannel = (typeof DEBUG_CHANNELS)[number]
|
|
2017
|
-
|
|
2018
2010
|
// `debug status` — which debug channels are currently enabled + their counts.
|
|
2019
2011
|
export async function inspectDebugStatus(bridge: InspectBridge): Promise<unknown> {
|
|
2020
2012
|
return bridge.send({ type: 'evaluate', code: 'window.__sootsimDebug.status()' })
|
|
@@ -92,10 +92,15 @@ export async function maybeWaitForStartedScreenTransitions(
|
|
|
92
92
|
) {
|
|
93
93
|
if (bridge.plane === 'cloud') return
|
|
94
94
|
try {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
95
|
+
// the sim bounds this wait itself, so the round trip gets a short budget of
|
|
96
|
+
// its own; under the command budget a wedged sim cost every read twice
|
|
97
|
+
const result: SootSimScreenTransitionWaitResult | undefined = await bridge.send(
|
|
98
|
+
{
|
|
99
|
+
type: 'call',
|
|
100
|
+
path: '__sootsimTest.waitForScreenTransitions',
|
|
101
|
+
args: [SCREEN_TRANSITION_PREFLIGHT],
|
|
102
|
+
},
|
|
103
|
+
{ timeoutMs: SCREEN_TRANSITION_PREFLIGHT.timeoutMs + 1200 },
|
|
99
104
|
)
|
|
100
105
|
if (!opts.verbose || !result?.started) return
|
|
101
106
|
if (result.timedOut) {
|
package/cli/commands/inspect.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// peach inspect — dump node tree, accessibility info, query nodes
|
|
2
2
|
// connects to the peach WS bridge (no Playwright)
|
|
3
3
|
|
|
4
|
+
import { Script } from 'node:vm'
|
|
4
5
|
import { getCliInspectVerbMeta } from '@peach/skills/cli'
|
|
5
6
|
import { renderCliCommandHelp, renderCliGroupHelp } from '@peach/skills/cli/renderers'
|
|
6
7
|
import { DEFAULT_SOOTSIM_BRIDGE_PORT } from '../../src/bridge-constants'
|
|
@@ -194,6 +195,8 @@ async function performSingleStep(
|
|
|
194
195
|
}
|
|
195
196
|
}
|
|
196
197
|
|
|
198
|
+
// runs after a command already spent its whole budget, so an explicit --timeout
|
|
199
|
+
// must not make the liveness check wait that long a second time
|
|
197
200
|
async function probeSimResponsive(bridge: WsBridge): Promise<boolean> {
|
|
198
201
|
try {
|
|
199
202
|
await bridge.send({ type: 'evaluate', code: '1' }, { timeoutMs: 3000 })
|
|
@@ -210,6 +213,18 @@ async function reportMissingTarget(
|
|
|
210
213
|
verb: 'tap' | 'double-tap' | 'long-press',
|
|
211
214
|
target: { mode: 'testid' | 'text'; value: string },
|
|
212
215
|
): Promise<void> {
|
|
216
|
+
if (target.mode === 'testid') {
|
|
217
|
+
// the tree can hold the id while the engine has no point to press: the node
|
|
218
|
+
// is not painted yet, or was replaced since the query. say so, so the
|
|
219
|
+
// suggestion list never names the id that was just reported missing.
|
|
220
|
+
const exact = await inspectFind(bridge, { testId: target.value }).catch(() => null)
|
|
221
|
+
if (exact?.result) {
|
|
222
|
+
console.error(
|
|
223
|
+
` found ${target.value} in the tree but it has no tap target yet; it may be unpainted, hidden or covered: wait for it with peach wait selector ${target.value}`,
|
|
224
|
+
)
|
|
225
|
+
return
|
|
226
|
+
}
|
|
227
|
+
}
|
|
213
228
|
console.error(` not found: ${target.value}`)
|
|
214
229
|
if (target.mode !== 'testid') return
|
|
215
230
|
const byText = await inspectFind(bridge, { text: target.value }).catch(() => null)
|
|
@@ -858,11 +873,10 @@ async function waitForKeyboardVisible(
|
|
|
858
873
|
}
|
|
859
874
|
|
|
860
875
|
/**
|
|
861
|
-
*
|
|
862
|
-
*
|
|
863
|
-
* whatever holds focus and fails the batch when nothing does.
|
|
876
|
+
* dispatch through the shared input executor, which checks the requested
|
|
877
|
+
* node identity before targeted typing and carries refusals out of the CLI.
|
|
864
878
|
*/
|
|
865
|
-
async function
|
|
879
|
+
async function performKeys(
|
|
866
880
|
bridge: WsBridge,
|
|
867
881
|
steps: Array<Extract<PerformStep, { type: 'type' | 'typeInto' | 'key' }>>,
|
|
868
882
|
actionLabel: string,
|
|
@@ -957,20 +971,6 @@ function keyCodeToVisualKey(code: string) {
|
|
|
957
971
|
return null
|
|
958
972
|
}
|
|
959
973
|
|
|
960
|
-
function getInspectCommandName(subcommand: string, directSubcommand: string | null) {
|
|
961
|
-
if (directSubcommand === subcommand) return `peach ${subcommand}`
|
|
962
|
-
const group = getCliInspectVerbMeta(subcommand)?.group
|
|
963
|
-
return group ? `peach ${group} ${subcommand}` : `peach ${subcommand}`
|
|
964
|
-
}
|
|
965
|
-
|
|
966
|
-
function getInspectUsage(
|
|
967
|
-
subcommand: string,
|
|
968
|
-
tail: string,
|
|
969
|
-
directSubcommand: string | null,
|
|
970
|
-
) {
|
|
971
|
-
return ` usage: ${getInspectCommandName(subcommand, directSubcommand)}${tail ? ` ${tail}` : ''}`
|
|
972
|
-
}
|
|
973
|
-
|
|
974
974
|
function normalizeRecordedText(value: unknown) {
|
|
975
975
|
if (typeof value !== 'string') return null
|
|
976
976
|
const text = value.replace(/\s+/g, ' ').trim()
|
|
@@ -2462,7 +2462,7 @@ export async function runInspect(args: string[], opts: InspectOptions) {
|
|
|
2462
2462
|
console.error(' type-into requires a resolved text input node identity')
|
|
2463
2463
|
peachExit(1)
|
|
2464
2464
|
}
|
|
2465
|
-
const typed = await
|
|
2465
|
+
const typed = await performKeys(
|
|
2466
2466
|
bridge,
|
|
2467
2467
|
[{ type: 'typeInto', text, nodeId }],
|
|
2468
2468
|
'type-into',
|
|
@@ -2500,6 +2500,7 @@ export async function runInspect(args: string[], opts: InspectOptions) {
|
|
|
2500
2500
|
return {
|
|
2501
2501
|
cx: n.absolutePosition.x + (n.layout.width || 0) / 2,
|
|
2502
2502
|
cy: n.absolutePosition.y + (n.layout.height || 0) / 2,
|
|
2503
|
+
nodeId: n.nodeId,
|
|
2503
2504
|
id: n.id,
|
|
2504
2505
|
testID: n.testID,
|
|
2505
2506
|
type: n.type,
|
|
@@ -2514,7 +2515,13 @@ export async function runInspect(args: string[], opts: InspectOptions) {
|
|
|
2514
2515
|
peachExit(1)
|
|
2515
2516
|
}
|
|
2516
2517
|
if (!tiPayload.isTextInput) {
|
|
2517
|
-
console.error(`
|
|
2518
|
+
console.error(` ${targetId} is not a text input (isTextInput: false)`)
|
|
2519
|
+
peachExit(1)
|
|
2520
|
+
}
|
|
2521
|
+
const nodeId: unknown = tiPayload.nodeId
|
|
2522
|
+
if (typeof nodeId !== 'number' || !Number.isSafeInteger(nodeId) || nodeId === 0) {
|
|
2523
|
+
console.error(' type-into requires a resolved text input node identity')
|
|
2524
|
+
peachExit(1)
|
|
2518
2525
|
}
|
|
2519
2526
|
// step 2: tap to focus
|
|
2520
2527
|
const tapResult = await bridge.send({
|
|
@@ -2522,12 +2529,17 @@ export async function runInspect(args: string[], opts: InspectOptions) {
|
|
|
2522
2529
|
x: tiPayload.cx,
|
|
2523
2530
|
y: tiPayload.cy,
|
|
2524
2531
|
target: {
|
|
2532
|
+
nodeId,
|
|
2525
2533
|
id: tiPayload.id ?? targetId,
|
|
2526
2534
|
testID: tiPayload.testID ?? targetId,
|
|
2527
2535
|
text: null,
|
|
2528
2536
|
type: tiPayload.type ?? null,
|
|
2529
2537
|
},
|
|
2530
2538
|
})
|
|
2539
|
+
if (!isTapSuccess(tapResult)) {
|
|
2540
|
+
console.error(` type-into could not tap ${targetId}`)
|
|
2541
|
+
peachExit(1)
|
|
2542
|
+
}
|
|
2531
2543
|
const targetSecureTextEntry = tiPayload.secureTextEntry === true
|
|
2532
2544
|
const keyboardState = await waitForKeyboardVisible(bridge, 1000, {
|
|
2533
2545
|
targetId,
|
|
@@ -2558,38 +2570,26 @@ export async function runInspect(args: string[], opts: InspectOptions) {
|
|
|
2558
2570
|
peachExit(1)
|
|
2559
2571
|
}
|
|
2560
2572
|
}
|
|
2561
|
-
//
|
|
2562
|
-
//
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
peachExit(1)
|
|
2576
|
-
}
|
|
2577
|
-
return count
|
|
2578
|
-
}
|
|
2579
|
-
const existingCharacters = await drawnCharacters()
|
|
2580
|
-
for (let i = 0; i < existingCharacters; i++) {
|
|
2581
|
-
await bridge.send({ type: 'keyboard', action: 'press', text: 'delete' })
|
|
2582
|
-
}
|
|
2583
|
-
// backspace clears only what precedes the caret the tap placed
|
|
2584
|
-
if (existingCharacters > 0 && (await drawnCharacters()) > 0) {
|
|
2585
|
-
console.error(
|
|
2586
|
-
` type-into could not clear ${targetId}: text remains after the caret the tap placed`,
|
|
2587
|
-
)
|
|
2573
|
+
// type at the live selection through the same guarded operation as
|
|
2574
|
+
// chained and cloud typing; clearing first changes controlled values.
|
|
2575
|
+
const typed = await performKeys(
|
|
2576
|
+
bridge,
|
|
2577
|
+
[{ type: 'typeInto', text, nodeId }],
|
|
2578
|
+
'type-into',
|
|
2579
|
+
)
|
|
2580
|
+
const receipt: unknown = typed.steps[0]?.value
|
|
2581
|
+
if (
|
|
2582
|
+
!receipt ||
|
|
2583
|
+
typeof receipt !== 'object' ||
|
|
2584
|
+
typeof Reflect.get(receipt, 'secureTextEntry') !== 'boolean'
|
|
2585
|
+
) {
|
|
2586
|
+
console.error(' type-into did not return its matched input secure state')
|
|
2588
2587
|
peachExit(1)
|
|
2589
2588
|
}
|
|
2590
|
-
await bridge.send({ type: 'keyboard', action: 'type', text })
|
|
2591
2589
|
const secureTextEntry =
|
|
2592
|
-
targetSecureTextEntry ||
|
|
2590
|
+
targetSecureTextEntry ||
|
|
2591
|
+
Reflect.get(receipt, 'secureTextEntry') === true ||
|
|
2592
|
+
isSecureKeyboardState(keyboardState as any)
|
|
2593
2593
|
const outputText = secureTextEntry
|
|
2594
2594
|
? SECURE_TEXT_REDACTION
|
|
2595
2595
|
: redactTextForKeyboardState(text, keyboardState as any)
|
|
@@ -2629,7 +2629,7 @@ export async function runInspect(args: string[], opts: InspectOptions) {
|
|
|
2629
2629
|
peachExit(1)
|
|
2630
2630
|
}
|
|
2631
2631
|
if (bridge.plane === 'cloud') {
|
|
2632
|
-
await
|
|
2632
|
+
await performKeys(bridge, [{ type: 'type', text }], 'type')
|
|
2633
2633
|
await recordInspectAction(
|
|
2634
2634
|
'inspect type',
|
|
2635
2635
|
{ inputText: text },
|
|
@@ -2663,7 +2663,7 @@ export async function runInspect(args: string[], opts: InspectOptions) {
|
|
|
2663
2663
|
peachExit(1)
|
|
2664
2664
|
}
|
|
2665
2665
|
if (bridge.plane === 'cloud') {
|
|
2666
|
-
await
|
|
2666
|
+
await performKeys(bridge, [{ type: 'key', key: name }], 'key')
|
|
2667
2667
|
} else {
|
|
2668
2668
|
await requireVisualKeyboard(bridge, 'key')
|
|
2669
2669
|
await bridge.send({ type: 'keyboard', action: 'press', text: name })
|
|
@@ -2680,7 +2680,7 @@ export async function runInspect(args: string[], opts: InspectOptions) {
|
|
|
2680
2680
|
peachExit(1)
|
|
2681
2681
|
}
|
|
2682
2682
|
if (bridge.plane === 'cloud') {
|
|
2683
|
-
await
|
|
2683
|
+
await performKeys(
|
|
2684
2684
|
bridge,
|
|
2685
2685
|
keys.map((key) => ({ type: 'key' as const, key })),
|
|
2686
2686
|
'key-sequence',
|
|
@@ -3717,13 +3717,17 @@ export async function runInspect(args: string[], opts: InspectOptions) {
|
|
|
3717
3717
|
// wrap in async to support await in worker mode. no identifier
|
|
3718
3718
|
// rewriting — the snippet runs verbatim so tokens like `root`,
|
|
3719
3719
|
// `debug`, `test` refer to whatever the user actually typed.
|
|
3720
|
-
//
|
|
3721
|
-
//
|
|
3720
|
+
// compile without executing to distinguish expressions from statements;
|
|
3721
|
+
// punctuation inside callbacks, literals and comments is not a separator.
|
|
3722
3722
|
let expandedCode = code
|
|
3723
3723
|
if (!expandedCode.startsWith('(async')) {
|
|
3724
|
-
expandedCode =
|
|
3725
|
-
|
|
3726
|
-
|
|
3724
|
+
expandedCode = `(async () => (\n${code}\n))()`
|
|
3725
|
+
try {
|
|
3726
|
+
new Script(expandedCode)
|
|
3727
|
+
} catch (error) {
|
|
3728
|
+
if (!(error instanceof SyntaxError)) throw error
|
|
3729
|
+
expandedCode = `(async () => {\n${code}\n})()`
|
|
3730
|
+
}
|
|
3727
3731
|
}
|
|
3728
3732
|
const result = await bridge.send({ type: 'evaluate', code: expandedCode })
|
|
3729
3733
|
printJson(result)
|
|
@@ -3959,11 +3963,11 @@ export async function runInspect(args: string[], opts: InspectOptions) {
|
|
|
3959
3963
|
case 'start': {
|
|
3960
3964
|
const result = await bridge.send({
|
|
3961
3965
|
type: 'evaluate',
|
|
3962
|
-
code: `(() => {
|
|
3966
|
+
code: `(async () => {
|
|
3963
3967
|
if (!window.__sootsimShellPerf) {
|
|
3964
3968
|
return { error: 'shell frame profile unavailable (__sootsimShellPerf missing on the page)' }
|
|
3965
3969
|
}
|
|
3966
|
-
window.__sootsimShellPerf.start()
|
|
3970
|
+
await window.__sootsimShellPerf.start()
|
|
3967
3971
|
return { started: true }
|
|
3968
3972
|
})()`,
|
|
3969
3973
|
})
|
|
@@ -4038,7 +4042,7 @@ export async function runInspect(args: string[], opts: InspectOptions) {
|
|
|
4038
4042
|
if (!window.__sootsimShellPerf) {
|
|
4039
4043
|
return { error: 'shell frame profile unavailable (__sootsimShellPerf missing on the page)' }
|
|
4040
4044
|
}
|
|
4041
|
-
window.__sootsimShellPerf.start()
|
|
4045
|
+
await window.__sootsimShellPerf.start()
|
|
4042
4046
|
// give a frame for profiling to engage before the event
|
|
4043
4047
|
await new Promise(r => requestAnimationFrame(() => r(undefined)))
|
|
4044
4048
|
window.dispatchEvent(new Event('${eventName}'))
|
|
@@ -4626,7 +4630,7 @@ export async function runInspect(args: string[], opts: InspectOptions) {
|
|
|
4626
4630
|
// re-incurs the full per-command timeout. three sequential probes turn a
|
|
4627
4631
|
// single 15s timeout into ~60s of apparent hang (QA F19-1). there is
|
|
4628
4632
|
// nothing useful to fetch from a sim that just timed out, so skip them.
|
|
4629
|
-
const timedOut = /^command timed out after (\d+)s$/.exec(message)
|
|
4633
|
+
const timedOut = /^command timed out after (\d+(?:\.\d+)?)s$/.exec(message)
|
|
4630
4634
|
// a disconnected bridge is genuinely gone; recover is right and probing
|
|
4631
4635
|
// it would just re-incur a full timeout. no bridge at all means there is
|
|
4632
4636
|
// no sim to recover, only one to open.
|
package/cli/commands/platform.ts
CHANGED
|
@@ -528,7 +528,7 @@ async function runRemoteBox(
|
|
|
528
528
|
} else if (existingSession) {
|
|
529
529
|
await closeCloudSession(existingSession)
|
|
530
530
|
}
|
|
531
|
-
const created = await box.createPrebuiltBundleBox({
|
|
531
|
+
const { box: created, appFontsWireSha256 } = await box.createPrebuiltBundleBox({
|
|
532
532
|
bundle: new Uint8Array(artifact.bytes),
|
|
533
533
|
nativeProjectDir: input.nativeProjectDir,
|
|
534
534
|
device: selectedDevice,
|
|
@@ -546,6 +546,7 @@ async function runRemoteBox(
|
|
|
546
546
|
apiOrigin: endpoint,
|
|
547
547
|
platform,
|
|
548
548
|
device: selectedDevice,
|
|
549
|
+
appFontsWireSha256,
|
|
549
550
|
artifact: {
|
|
550
551
|
sha256: artifactSha256,
|
|
551
552
|
bytes: artifact.bytes.length,
|
package/cli/commands/preview.ts
CHANGED
|
@@ -88,7 +88,7 @@ export async function runPreview(args: string[]): Promise<number> {
|
|
|
88
88
|
bundleSizeBytes: artifact.bytes.byteLength,
|
|
89
89
|
bundleContentType: 'application/javascript',
|
|
90
90
|
bundleEncoding: 'br',
|
|
91
|
-
bundleMinified:
|
|
91
|
+
bundleMinified: artifact.minified,
|
|
92
92
|
bundleOrigin: null,
|
|
93
93
|
entry: null,
|
|
94
94
|
isTransformed: false,
|