peachsim 0.1.900 → 0.1.908
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 +8 -0
- package/cli/commands/control.ts +29 -16
- package/cli/commands/flow.ts +8 -1
- package/cli/commands/upload.ts +53 -39
- package/cli/flow-live-status.ts +11 -6
- package/detox/expectations.ts +22 -15
- package/detox/index.ts +4 -19
- package/dist-lib/agent-daemon-client.cjs +1 -1
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/agent-identity.cjs +1 -1
- package/dist-lib/agent-sessions.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 +1 -1
- 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 +6 -3
- package/dist-lib/capture-contract.cjs +1 -1
- package/dist-lib/capture-contract.mjs +1 -1
- package/dist-lib/cli-constants.cjs +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/detox/index.cjs +15 -18
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/expo-swift-bundle.cjs +6 -3
- package/dist-lib/expo-swift-metadata.cjs +1 -1
- package/dist-lib/expo-swift-metadata.mjs +1 -1
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +6 -3
- 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 +1 -1
- 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 +1 -1
- package/dist-lib/native-app-identity.cjs +1 -1
- package/dist-lib/native-app-identity.mjs +1 -1
- 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 +1 -1
- package/dist-lib/react-native-host-modules.mjs +1 -1
- package/dist-lib/render-mode.cjs +1 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
- package/dist-lib/sdk.cjs +1 -1
- package/dist-lib/sdk.mjs +1 -1
- package/dist-lib/skills.cjs +39 -20
- package/dist-lib/swift-compile.cjs +1 -1
- package/dist-lib/swift-compile.mjs +1 -1
- package/dist-lib/swift-package.cjs +1 -1
- package/dist-lib/swift-package.mjs +1 -1
- package/dist-lib/swift-submit.cjs +1 -1
- package/dist-lib/swift-submit.mjs +1 -1
- package/dist-lib/swift-toolchain.cjs +1 -1
- package/dist-lib/swift.cjs +1 -1
- package/dist-lib/vite-swift.cjs +1 -1
- package/dist-lib/vite.cjs +1 -1
- package/dist-lib/worklet-executable.cjs +1 -1
- package/dist-lib/worklet-executable.mjs +1 -1
- package/kotlin/fork/NOTICE +3 -0
- package/kotlin/fork/README.md +10 -2
- package/kotlin/fork/build.sh +14 -14
- package/kotlin/fork/compose-worker-globals.patch +105 -0
- package/kotlin/fork/fork.json +3 -3
- package/kotlin/fork/publish.ts +8 -1
- package/kotlin/fork/skia-android-shaping.patch +30 -0
- package/kotlin/fork/skiko-local.init.gradle +10 -0
- package/kotlin/fork/skiko-worker-clock.patch +17 -0
- package/kotlin/toolchain.json +9 -9
- package/package.json +1 -1
- package/skills/peach-debug/SKILL.md +15 -153
- package/skills/peach-debug/references/accessibility.md +102 -0
- package/skills/peach-debug/references/commands.md +56 -0
- package/src/expo-swift-host.ts +5 -2
- package/src/vite-plugin.ts +3 -3
package/README.md
CHANGED
|
@@ -215,6 +215,14 @@ bridge always addresses the **shell worker**, never an undifferentiated sim.
|
|
|
215
215
|
own dev server. It is used to build the engine/shell and to load external RN
|
|
216
216
|
apps — it is **not** the published `peachsim/vite` export.
|
|
217
217
|
|
|
218
|
+
Preview uploads retain one response snapshot in the simulator and transfer
|
|
219
|
+
its base64 bodies in bounded slices, including authenticated API responses.
|
|
220
|
+
The CLI removes that temporary snapshot after transfer. A completed upload
|
|
221
|
+
publishes its trace URL through a fresh bridge connection because compression
|
|
222
|
+
and HTTP upload can outlive the interactive socket's idle lifetime.
|
|
223
|
+
The finished test duration uses the last execution status timestamp; publishing
|
|
224
|
+
the later trace URL notifies viewers without extending that duration.
|
|
225
|
+
|
|
218
226
|
## Key components
|
|
219
227
|
|
|
220
228
|
| component | role | key files |
|
package/cli/commands/control.ts
CHANGED
|
@@ -241,19 +241,30 @@ export async function ensureBridgeForOpen(
|
|
|
241
241
|
}
|
|
242
242
|
|
|
243
243
|
const { executable, prefixArgs } = rnxSelfInvocation()
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
{
|
|
248
|
-
detached: true,
|
|
249
|
-
stdio: 'ignore',
|
|
250
|
-
// this bridge does not supply the shell, so it needs no runtime updates.
|
|
251
|
-
env:
|
|
252
|
-
runtimeSource === 'external'
|
|
253
|
-
? { ...process.env, PEACH_UPDATE_CHECK: 'off' }
|
|
254
|
-
: process.env,
|
|
255
|
-
},
|
|
244
|
+
// the bridge outlives open, so it must not hold the command's captured stderr.
|
|
245
|
+
const bridgeLogPath = path.join(
|
|
246
|
+
os.tmpdir(),
|
|
247
|
+
`peach-bridge-${Date.now()}-${process.pid}.log`,
|
|
256
248
|
)
|
|
249
|
+
const bridgeLogFd = fs.openSync(bridgeLogPath, 'a')
|
|
250
|
+
let child: ReturnType<typeof spawn>
|
|
251
|
+
try {
|
|
252
|
+
child = spawn(
|
|
253
|
+
executable,
|
|
254
|
+
[...prefixArgs, 'serve', '--quiet', '--port', String(preferredPort)],
|
|
255
|
+
{
|
|
256
|
+
detached: true,
|
|
257
|
+
stdio: ['ignore', 'ignore', bridgeLogFd],
|
|
258
|
+
// this bridge does not supply the shell, so it needs no runtime updates.
|
|
259
|
+
env:
|
|
260
|
+
runtimeSource === 'external'
|
|
261
|
+
? { ...process.env, PEACH_UPDATE_CHECK: 'off' }
|
|
262
|
+
: process.env,
|
|
263
|
+
},
|
|
264
|
+
)
|
|
265
|
+
} finally {
|
|
266
|
+
fs.closeSync(bridgeLogFd)
|
|
267
|
+
}
|
|
257
268
|
if (resources) {
|
|
258
269
|
resources.bridge = child
|
|
259
270
|
resources.port = preferredPort
|
|
@@ -298,14 +309,16 @@ export async function ensureBridgeForOpen(
|
|
|
298
309
|
}
|
|
299
310
|
return selectedPort
|
|
300
311
|
}
|
|
301
|
-
if (childExit)
|
|
302
|
-
throw new Error(`peach bridge exited before becoming ready (exit ${childExit})`)
|
|
303
|
-
}
|
|
312
|
+
if (childExit) break
|
|
304
313
|
await sleep(150)
|
|
305
314
|
}
|
|
306
315
|
|
|
316
|
+
const reason = childExit
|
|
317
|
+
? `peach bridge exited before becoming ready (exit ${childExit})`
|
|
318
|
+
: `peach bridge did not start within ${Math.round(OPEN_BRIDGE_SPAWN_TIMEOUT_MS / 1000)}s`
|
|
319
|
+
const bridgeLog = fs.readFileSync(bridgeLogPath, 'utf8').trim().slice(-2000)
|
|
307
320
|
throw new Error(
|
|
308
|
-
|
|
321
|
+
`${reason}${bridgeLog ? `\n${bridgeLog}` : ''}\nbridge log: ${bridgeLogPath}`,
|
|
309
322
|
)
|
|
310
323
|
}
|
|
311
324
|
|
package/cli/commands/flow.ts
CHANGED
|
@@ -955,7 +955,14 @@ flow extension:
|
|
|
955
955
|
}
|
|
956
956
|
const { runUpload } = await import('./upload')
|
|
957
957
|
lastPreviewUploadResult = await runUpload(uploadArgs, {})
|
|
958
|
-
|
|
958
|
+
// upload can outlive the command socket's idle lifetime. publish its
|
|
959
|
+
// completed trace over a fresh connection to the same bridge world.
|
|
960
|
+
const resultBridge = createBridgeFromParsed(parsedBridgeArgs)
|
|
961
|
+
try {
|
|
962
|
+
await driver.liveStatus?.preview(lastPreviewUploadResult.previewUrl, resultBridge)
|
|
963
|
+
} finally {
|
|
964
|
+
resultBridge.close()
|
|
965
|
+
}
|
|
959
966
|
} catch (err: any) {
|
|
960
967
|
lastPreviewUploadError =
|
|
961
968
|
err instanceof PeachExit && err.reason ? err.reason : String(err?.message || err)
|
package/cli/commands/upload.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// uploads are unlisted-public (unguessable id) but require a signed-in
|
|
6
6
|
// desktop session so the share is associated with the user's account.
|
|
7
7
|
|
|
8
|
-
import { createHash } from 'crypto'
|
|
8
|
+
import { createHash, randomUUID } from 'crypto'
|
|
9
9
|
import { readFileSync } from 'fs'
|
|
10
10
|
import path from 'path'
|
|
11
11
|
import { gzipSync, gunzipSync } from 'zlib'
|
|
@@ -609,45 +609,59 @@ export async function runUpload(
|
|
|
609
609
|
` ${crossOriginStats.count} recorded cross-origin responses ` +
|
|
610
610
|
`(${(crossOriginStats.totalBytes / 1024).toFixed(1)} KiB)`,
|
|
611
611
|
)
|
|
612
|
-
//
|
|
613
|
-
//
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
612
|
+
// retain the snapshot in the page and pull bodies in bounded slices.
|
|
613
|
+
// a complete base64 dump can exceed the bridge's 100 mib frame cap.
|
|
614
|
+
const cacheKey = `__peachCliUploadFetches_${randomUUID()}`
|
|
615
|
+
try {
|
|
616
|
+
const entries = await evalInBridge<
|
|
617
|
+
Array<RecordedEntry & { index: number; bodyBase64Length: number }>
|
|
618
|
+
>(
|
|
619
|
+
bridge,
|
|
620
|
+
`(async () => {
|
|
621
|
+
const rec = window.__sootsimPreviewRecorder
|
|
622
|
+
const workerDump = window.__sootsimDumpWorkerFetchRecorder
|
|
623
|
+
const bundleUrl = ${JSON.stringify(snapshot.bundleUrl)}
|
|
624
|
+
await rec?.flush?.()
|
|
625
|
+
const keep = (r) => {
|
|
626
|
+
try {
|
|
627
|
+
const u = new URL(r.url)
|
|
628
|
+
if (u.origin !== ${JSON.stringify(bundleOrigin)}) return true
|
|
629
|
+
return r.url !== bundleUrl && u.pathname.startsWith('/api/')
|
|
630
|
+
} catch { return false }
|
|
631
|
+
}
|
|
632
|
+
const records = [
|
|
633
|
+
...(rec?.dump ? rec.dump().filter(keep) : []),
|
|
634
|
+
...(typeof workerDump === 'function'
|
|
635
|
+
? (await workerDump()).filter(keep)
|
|
636
|
+
: []),
|
|
637
|
+
]
|
|
638
|
+
window[${JSON.stringify(cacheKey)}] = records
|
|
639
|
+
return records.map(({ bodyBase64, ...entry }, index) => ({
|
|
640
|
+
...entry, index, bodyBase64Length: bodyBase64.length,
|
|
641
|
+
}))
|
|
642
|
+
})()`,
|
|
643
|
+
)
|
|
644
|
+
const dumped: RecordedFetch[] = []
|
|
645
|
+
const chunkChars = 1_000_000
|
|
646
|
+
for (const { index, bodyBase64Length, ...entry } of entries) {
|
|
647
|
+
let bodyBase64 = ''
|
|
648
|
+
for (let offset = 0; offset < bodyBase64Length; offset += chunkChars) {
|
|
649
|
+
const end = Math.min(offset + chunkChars, bodyBase64Length)
|
|
650
|
+
const chunk = await evalInBridge(
|
|
651
|
+
bridge,
|
|
652
|
+
`window[${JSON.stringify(cacheKey)}]?.[${index}]?.bodyBase64.slice(${offset}, ${end})`,
|
|
653
|
+
)
|
|
654
|
+
if (typeof chunk !== 'string' || chunk.length !== end - offset) {
|
|
655
|
+
throw new Error('recorded response snapshot changed during upload')
|
|
656
|
+
}
|
|
657
|
+
bodyBase64 += chunk
|
|
647
658
|
}
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
659
|
+
dumped.push({ ...entry, bodyBase64 })
|
|
660
|
+
}
|
|
661
|
+
recordedFetches = mergeRecordedEntries(dumped)
|
|
662
|
+
} finally {
|
|
663
|
+
await evalInBridge(bridge, `delete window[${JSON.stringify(cacheKey)}]`)
|
|
664
|
+
}
|
|
651
665
|
if (assetsOnly) {
|
|
652
666
|
const before = recordedFetches.length
|
|
653
667
|
recordedFetches = recordedFetches.filter((r) =>
|
package/cli/flow-live-status.ts
CHANGED
|
@@ -47,8 +47,9 @@ export class FlowLiveStatusReporter {
|
|
|
47
47
|
private async send(
|
|
48
48
|
payload: Record<string, unknown>,
|
|
49
49
|
simId: string | undefined,
|
|
50
|
+
bridge: WsBridge = this.bridge,
|
|
50
51
|
): Promise<unknown> {
|
|
51
|
-
const reply: unknown = await
|
|
52
|
+
const reply: unknown = await bridge.send({
|
|
52
53
|
type: 'flowStatus',
|
|
53
54
|
simId,
|
|
54
55
|
status: { runId: this.runId, flowName: this.flowName, ...payload },
|
|
@@ -57,7 +58,10 @@ export class FlowLiveStatusReporter {
|
|
|
57
58
|
return reply
|
|
58
59
|
}
|
|
59
60
|
|
|
60
|
-
private async push(
|
|
61
|
+
private async push(
|
|
62
|
+
payload: Record<string, unknown>,
|
|
63
|
+
bridge: WsBridge = this.bridge,
|
|
64
|
+
): Promise<unknown> {
|
|
61
65
|
if (this.disabled) return undefined
|
|
62
66
|
try {
|
|
63
67
|
const simId = this.getSimId?.()
|
|
@@ -75,13 +79,14 @@ export class FlowLiveStatusReporter {
|
|
|
75
79
|
source: this.flowSource,
|
|
76
80
|
},
|
|
77
81
|
simId,
|
|
82
|
+
bridge,
|
|
78
83
|
)
|
|
79
84
|
for (const step of this.completedSteps.values()) {
|
|
80
|
-
await this.send({ phase: 'step', state: 'running', step }, simId)
|
|
85
|
+
await this.send({ phase: 'step', state: 'running', step }, simId, bridge)
|
|
81
86
|
}
|
|
82
87
|
}
|
|
83
88
|
this.lastSimId = simId
|
|
84
|
-
return await this.send(payload, simId)
|
|
89
|
+
return await this.send(payload, simId, bridge)
|
|
85
90
|
} catch (error) {
|
|
86
91
|
this.disabled = true
|
|
87
92
|
console.log(
|
|
@@ -165,8 +170,8 @@ export class FlowLiveStatusReporter {
|
|
|
165
170
|
|
|
166
171
|
// the finished run's cloud trace url, once --preview has uploaded it. the
|
|
167
172
|
// devtools test pane's Open Trace button opens it.
|
|
168
|
-
async preview(previewUrl: string): Promise<void> {
|
|
169
|
-
await this.push({ phase: 'preview', previewUrl })
|
|
173
|
+
async preview(previewUrl: string, bridge: WsBridge): Promise<void> {
|
|
174
|
+
await this.push({ phase: 'preview', previewUrl }, bridge)
|
|
170
175
|
}
|
|
171
176
|
|
|
172
177
|
// park while the devtools pause toggle is on. checked between top-level
|
package/detox/expectations.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { dragScrollNode } from './gestures'
|
|
6
6
|
import type { SootSimElement } from './element-types'
|
|
7
7
|
import type { Matcher } from './matchers'
|
|
8
|
-
import type { SootSimNodeInfo } from '@peach/globals'
|
|
8
|
+
import type { SootSimFocusedNodeInfo, SootSimNodeInfo } from '@peach/globals'
|
|
9
9
|
import type { Page } from 'playwright-core'
|
|
10
10
|
|
|
11
11
|
type FindFn = (matcher: Matcher) => Promise<any>
|
|
@@ -52,20 +52,27 @@ export function isNodeVisible(node: SootSimNodeInfo | null | undefined): boolean
|
|
|
52
52
|
return visibleFraction(node) >= DEFAULT_VISIBILITY_THRESHOLD
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
//
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
55
|
+
// a native host is focused when its editor child holds focus.
|
|
56
|
+
export function focusIsWithin(focused: SootSimFocusedNodeInfo, nodeId: number): boolean {
|
|
57
|
+
return focused.nodeId === nodeId || focused.ancestorNodeIds.includes(nodeId)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// the engine owns focus for canvas inputs and hosted native editors.
|
|
61
|
+
export async function isTextInputNodeFocused(
|
|
62
|
+
node: SootSimNodeInfo,
|
|
63
|
+
page: Page,
|
|
64
|
+
): Promise<boolean> {
|
|
65
|
+
const focused = await page.evaluate(async () => window.__sootsimTest!.getFocusedNode())
|
|
66
|
+
return focused !== null && focusIsWithin(focused, node.nodeId)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function isNodeFocused(node: SootSimNodeInfo, page: Page): Promise<boolean> {
|
|
70
|
+
if (node.isTextInput) return isTextInputNodeFocused(node, page)
|
|
71
|
+
return page.evaluate(
|
|
72
|
+
(nodeId) =>
|
|
73
|
+
document.activeElement?.getAttribute('data-sootsim-id') === String(nodeId),
|
|
74
|
+
node.nodeId,
|
|
75
|
+
)
|
|
69
76
|
}
|
|
70
77
|
|
|
71
78
|
export function describeNodeVisibility(node: SootSimNodeInfo | null | undefined): string {
|
package/detox/index.ts
CHANGED
|
@@ -29,6 +29,8 @@ import {
|
|
|
29
29
|
createExpect,
|
|
30
30
|
createWaitFor,
|
|
31
31
|
describeNodeVisibility,
|
|
32
|
+
focusIsWithin,
|
|
33
|
+
isTextInputNodeFocused,
|
|
32
34
|
isNodeVisible,
|
|
33
35
|
} from './expectations'
|
|
34
36
|
import {
|
|
@@ -776,35 +778,18 @@ async function dispatchTapAtSootsimPoint(
|
|
|
776
778
|
)
|
|
777
779
|
}
|
|
778
780
|
|
|
779
|
-
async function isFocusedTextInputNode(page: Page, nodeInfo: any): Promise<boolean> {
|
|
780
|
-
const focused = await page.evaluate(
|
|
781
|
-
async () => (await window.__sootsimTest!.getFocusedNode?.()) ?? null,
|
|
782
|
-
)
|
|
783
|
-
return !!focused && focusIsWithin(focused, nodeInfo?.nodeId)
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
// espresso's typeText taps the matched view and types into whatever took
|
|
787
|
-
// focus, so a native host whose editor child holds focus takes the text.
|
|
788
|
-
function focusIsWithin(
|
|
789
|
-
focused: { nodeId: number; ancestorNodeIds: number[] },
|
|
790
|
-
nodeId: number,
|
|
791
|
-
): boolean {
|
|
792
|
-
return focused.nodeId === nodeId || focused.ancestorNodeIds.includes(nodeId)
|
|
793
|
-
}
|
|
794
|
-
|
|
795
781
|
async function focusElementForTextEntry(el: SootSimElement): Promise<void> {
|
|
796
782
|
const page = getPage()
|
|
797
783
|
const node = await findNodeByMatcher(el._matcher)
|
|
798
784
|
if (!node) {
|
|
799
785
|
throw new Error(`element not found for text entry: ${JSON.stringify(el._matcher)}`)
|
|
800
786
|
}
|
|
801
|
-
if (await
|
|
787
|
+
if (await isTextInputNodeFocused(node, page)) return
|
|
802
788
|
await el.tap()
|
|
803
789
|
// the tap reaches the worker on its own channel. keys pressed before it lands
|
|
804
790
|
// would edit around a caret the tap then moves, so wait for its focus.
|
|
805
791
|
await pollUntil(
|
|
806
|
-
() =>
|
|
807
|
-
page.evaluate(async () => (await window.__sootsimTest!.getFocusedNode?.()) ?? null),
|
|
792
|
+
() => page.evaluate(async () => window.__sootsimTest!.getFocusedNode()),
|
|
808
793
|
(focused) => focused !== null && focusIsWithin(focused, node.nodeId),
|
|
809
794
|
{
|
|
810
795
|
timeoutMs: 5000,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.908 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __create = Object.create;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.908 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.908 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.908 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __create = Object.create;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.908 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __create = Object.create;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.908 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __create = Object.create;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.908 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __defProp = Object.defineProperty;
|
package/dist-lib/beta.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.908 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __defProp = Object.defineProperty;
|
package/dist-lib/beta.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.908 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.908 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.908 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.908 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __create = Object.create;
|
|
@@ -8608,7 +8608,7 @@ async function collectExpoSwiftSources(appRoot) {
|
|
|
8608
8608
|
const namespacesSeen = /* @__PURE__ */ new Set();
|
|
8609
8609
|
let bytes = 0;
|
|
8610
8610
|
let fileCount = 0;
|
|
8611
|
-
for (const entry of Object.
|
|
8611
|
+
for (const [, entry] of Object.entries(search).sort(([a], [b]) => a.localeCompare(b))) {
|
|
8612
8612
|
if (!entry || typeof entry !== "object")
|
|
8613
8613
|
throw new Error("invalid Expo autolinking entry");
|
|
8614
8614
|
const sourceRoot = Reflect.get(entry, "path");
|
|
@@ -8660,7 +8660,10 @@ async function collectExpoSwiftSources(appRoot) {
|
|
|
8660
8660
|
fileCount++;
|
|
8661
8661
|
if (fileCount > MAX_SWIFT_COMPILE_FILES - 2 || bytes > MAX_SWIFT_COMPILE_SOURCE_BYTES)
|
|
8662
8662
|
throw new Error("Expo Swift sources exceed compile service caps");
|
|
8663
|
-
files2[`Sources/${namespace}/${import_node_path16.default.relative(ios, file).split(import_node_path16.default.sep).join("/")}`] =
|
|
8663
|
+
files2[`Sources/${namespace}/${import_node_path16.default.relative(ios, file).split(import_node_path16.default.sep).join("/")}`] = // CocoaPods' iOS umbrella header imports UIKit into every Swift file
|
|
8664
|
+
// through the underlying module; these source units have no clang pod.
|
|
8665
|
+
`import UIKit
|
|
8666
|
+
#sourceLocation(file: ${JSON.stringify(file)}, line: 1)
|
|
8664
8667
|
${import_node_fs15.default.readFileSync(file, "utf8")}`;
|
|
8665
8668
|
}
|
|
8666
8669
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.908 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.908 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.908 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.908 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __defProp = Object.defineProperty;
|
package/dist-lib/cloud.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.908 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __defProp = Object.defineProperty;
|
package/dist-lib/cloud.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.908 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
4
4
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
package/dist-lib/config.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.908 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __defProp = Object.defineProperty;
|
package/dist-lib/detox/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.908 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __create = Object.create;
|
|
@@ -7387,13 +7387,19 @@ function visibleFraction(node) {
|
|
|
7387
7387
|
function isNodeVisible(node) {
|
|
7388
7388
|
return visibleFraction(node) >= DEFAULT_VISIBILITY_THRESHOLD;
|
|
7389
7389
|
}
|
|
7390
|
+
function focusIsWithin(focused, nodeId) {
|
|
7391
|
+
return focused.nodeId === nodeId || focused.ancestorNodeIds.includes(nodeId);
|
|
7392
|
+
}
|
|
7393
|
+
async function isTextInputNodeFocused(node, page) {
|
|
7394
|
+
const focused = await page.evaluate(async () => window.__sootsimTest.getFocusedNode());
|
|
7395
|
+
return focused !== null && focusIsWithin(focused, node.nodeId);
|
|
7396
|
+
}
|
|
7390
7397
|
async function isNodeFocused(node, page) {
|
|
7391
|
-
|
|
7392
|
-
|
|
7393
|
-
|
|
7394
|
-
|
|
7395
|
-
|
|
7396
|
-
}, nodeId);
|
|
7398
|
+
if (node.isTextInput) return isTextInputNodeFocused(node, page);
|
|
7399
|
+
return page.evaluate(
|
|
7400
|
+
(nodeId) => document.activeElement?.getAttribute("data-sootsim-id") === String(nodeId),
|
|
7401
|
+
node.nodeId
|
|
7402
|
+
);
|
|
7397
7403
|
}
|
|
7398
7404
|
function describeNodeVisibility(node) {
|
|
7399
7405
|
if (!node) return "not found";
|
|
@@ -8510,25 +8516,16 @@ async function dispatchTapAtSootsimPoint(page, x, y, token) {
|
|
|
8510
8516
|
{ x, y, token }
|
|
8511
8517
|
);
|
|
8512
8518
|
}
|
|
8513
|
-
async function isFocusedTextInputNode(page, nodeInfo) {
|
|
8514
|
-
const focused = await page.evaluate(
|
|
8515
|
-
async () => await window.__sootsimTest.getFocusedNode?.() ?? null
|
|
8516
|
-
);
|
|
8517
|
-
return !!focused && focusIsWithin(focused, nodeInfo?.nodeId);
|
|
8518
|
-
}
|
|
8519
|
-
function focusIsWithin(focused, nodeId) {
|
|
8520
|
-
return focused.nodeId === nodeId || focused.ancestorNodeIds.includes(nodeId);
|
|
8521
|
-
}
|
|
8522
8519
|
async function focusElementForTextEntry(el) {
|
|
8523
8520
|
const page = getPage();
|
|
8524
8521
|
const node = await findNodeByMatcher(el._matcher);
|
|
8525
8522
|
if (!node) {
|
|
8526
8523
|
throw new Error(`element not found for text entry: ${JSON.stringify(el._matcher)}`);
|
|
8527
8524
|
}
|
|
8528
|
-
if (await
|
|
8525
|
+
if (await isTextInputNodeFocused(node, page)) return;
|
|
8529
8526
|
await el.tap();
|
|
8530
8527
|
await pollUntil(
|
|
8531
|
-
() => page.evaluate(async () =>
|
|
8528
|
+
() => page.evaluate(async () => window.__sootsimTest.getFocusedNode()),
|
|
8532
8529
|
(focused) => focused !== null && focusIsWithin(focused, node.nodeId),
|
|
8533
8530
|
{
|
|
8534
8531
|
timeoutMs: 5e3,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.908 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __defProp = Object.defineProperty;
|