rnxsim 0.1.366 → 0.1.367
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 +14 -2
- package/cli/bin.ts +18 -0
- package/cli/bridge-flow-runner.ts +14 -151
- package/cli/commands/assert.ts +1 -1
- package/cli/commands/box/checkout-plane.ts +126 -0
- package/cli/commands/box/rnx-command.ts +68 -0
- package/cli/commands/box.ts +200 -0
- package/cli/commands/control.ts +12 -32
- package/cli/commands/do-chain.ts +412 -0
- package/cli/commands/flow.ts +0 -10
- package/cli/commands/inspect/core.ts +302 -725
- package/cli/commands/inspect/describe.ts +3 -76
- package/cli/commands/inspect/find.ts +3 -6
- package/cli/commands/inspect/get-layout.ts +14 -1
- package/cli/commands/inspect/resolve-target.ts +41 -47
- package/cli/commands/inspect/shared.ts +28 -123
- package/cli/commands/inspect/tree.ts +6 -15
- package/cli/commands/inspect/wait-ready.ts +2 -2
- package/cli/commands/inspect/wait-selector.ts +4 -8
- package/cli/commands/inspect.ts +23 -17
- package/cli/commands/install-desktop.ts +44 -7
- package/cli/commands/maestro-generate.ts +0 -2
- package/cli/commands/platform.ts +68 -0
- package/cli/commands/state.ts +5 -2
- package/cli/outbound-endpoints.ts +6 -1
- package/cli/parse-args.ts +13 -2
- package/cli/ws-bridge.ts +30 -2
- package/detox/index.ts +34 -17
- package/dist-cli/bin.js +5 -5
- package/dist-cli/chunks/{agent-SO6IOPRK.js → agent-EKKIAKP5.js} +2 -2
- package/dist-cli/chunks/{agent-wrapper-PMYCB2KO.js → agent-wrapper-MHLQUGA6.js} +2 -2
- package/dist-cli/chunks/{app-fonts-LULQGFY7.js → app-fonts-HN6ADRTQ.js} +2 -2
- package/dist-cli/chunks/{assert-WGNCD4ZU.js → assert-IMR6QCN6.js} +3 -3
- package/dist-cli/chunks/{auth-G7CCHH3M.js → auth-27LPMQNH.js} +2 -2
- package/dist-cli/chunks/{beta-N4LXL5TO.js → beta-6RXBB5AI.js} +2 -2
- package/dist-cli/chunks/box-DORLF3GP.js +1709 -0
- package/dist-cli/chunks/{camera-4JHCYBQS.js → camera-2G7JZ5DL.js} +2 -2
- package/dist-cli/chunks/{chunk-2UXU53MG.js → chunk-3QZJLNYY.js} +1 -1
- package/dist-cli/chunks/{chunk-YXXKSL6R.js → chunk-3VBVEDDA.js} +1 -1
- package/dist-cli/chunks/{chunk-ZJIS3CTW.js → chunk-4BWGLTKU.js} +2 -2
- package/dist-cli/chunks/chunk-4XSNO374.js +423 -0
- package/dist-cli/chunks/{chunk-RYIMSKDO.js → chunk-5DGMFUZG.js} +2 -2
- package/dist-cli/chunks/{chunk-M3BNCF7E.js → chunk-6DBS5EGX.js} +2 -2
- package/dist-cli/chunks/{chunk-ONORMKWX.js → chunk-6SO6I3D5.js} +2 -2
- package/dist-cli/chunks/chunk-75EFOPXE.js +32 -0
- package/dist-cli/chunks/{chunk-5NMCF5VF.js → chunk-A5AJFVOF.js} +1 -1
- package/dist-cli/chunks/{chunk-N3UC7HIX.js → chunk-AM2ODYAN.js} +1 -1
- package/dist-cli/chunks/{chunk-I6KIM6PN.js → chunk-AVOROGA3.js} +1 -1
- package/dist-cli/chunks/chunk-AXMMLO7D.js +130 -0
- package/dist-cli/chunks/{chunk-ORWP2E7R.js → chunk-BSEXEYNX.js} +2 -2
- package/dist-cli/chunks/{chunk-NQFJOCSF.js → chunk-CM6ZKZW7.js} +2 -2
- package/dist-cli/chunks/{chunk-ZMJ75EDG.js → chunk-DNVABCEC.js} +2 -2
- package/dist-cli/chunks/{chunk-2S7BKUP2.js → chunk-E33A4AIR.js} +2 -2
- package/dist-cli/chunks/{chunk-GXAB52K7.js → chunk-EFMOXYDD.js} +2 -2
- package/dist-cli/chunks/{chunk-TIAEYO6I.js → chunk-EV4P5UBP.js} +2 -2
- package/dist-cli/chunks/{chunk-JHXETDVC.js → chunk-G5IUIGGC.js} +1 -1
- package/dist-cli/chunks/chunk-GMLOCV3E.js +312 -0
- package/dist-cli/chunks/{chunk-ZLVRYXZ7.js → chunk-GRN2FHVW.js} +1 -1
- package/dist-cli/chunks/{chunk-A5FXX5PJ.js → chunk-HFFXPUEP.js} +1 -1
- package/dist-cli/chunks/chunk-HG6T4OOR.js +4 -0
- package/dist-cli/chunks/{chunk-OW5XHAA4.js → chunk-HNT2OXPZ.js} +1 -1
- package/dist-cli/chunks/chunk-IPEURNLI.js +24 -0
- package/dist-cli/chunks/chunk-IR44SFBA.js +5 -0
- package/dist-cli/chunks/chunk-JMSLXZFW.js +4 -0
- package/dist-cli/chunks/{chunk-EFR5DVNP.js → chunk-JPXQKLNH.js} +1 -1
- package/dist-cli/chunks/chunk-K3EC6JDE.js +4 -0
- package/dist-cli/chunks/{chunk-YGMEDJKJ.js → chunk-KHX65HAU.js} +2 -2
- package/dist-cli/chunks/{chunk-EQK5MJCU.js → chunk-LWOVKHRC.js} +2 -2
- package/dist-cli/chunks/{chunk-5OK3LCEJ.js → chunk-MQN5DWTI.js} +2 -2
- package/dist-cli/chunks/{chunk-TTASWLKM.js → chunk-N3BXQEUF.js} +1 -1
- package/dist-cli/chunks/{chunk-UCHW7ZHR.js → chunk-N5OSV26B.js} +2 -2
- package/dist-cli/chunks/chunk-PHNURIH3.js +4 -0
- package/dist-cli/chunks/{chunk-AFV32WZT.js → chunk-PP2KNVXP.js} +3 -3
- package/dist-cli/chunks/{chunk-XJU2QIRY.js → chunk-PROKNJVY.js} +2 -2
- package/dist-cli/chunks/chunk-QXLLY4IM.js +7 -0
- package/dist-cli/chunks/{chunk-HT6GFZOZ.js → chunk-RNQTOGM7.js} +1 -1
- package/dist-cli/chunks/{chunk-CN6XYYEA.js → chunk-RPNKKVBN.js} +27 -27
- package/dist-cli/chunks/{chunk-LN3RNW6X.js → chunk-RSNLI2NH.js} +2 -2
- package/dist-cli/chunks/{chunk-YCCCPFWT.js → chunk-RZGHV2WI.js} +2 -2
- package/dist-cli/chunks/chunk-SGGL3AFV.js +497 -0
- package/dist-cli/chunks/chunk-SH5ETJKG.js +4 -0
- package/dist-cli/chunks/{chunk-77ZHQJZR.js → chunk-T6J4YP56.js} +2 -2
- package/dist-cli/chunks/{chunk-MGDSJZ37.js → chunk-TWUE7WF5.js} +2 -2
- package/dist-cli/chunks/{chunk-SA6XVI55.js → chunk-U44VEQRN.js} +1 -1
- package/dist-cli/chunks/{chunk-IZKHOM37.js → chunk-URLVQHC3.js} +1 -1
- package/dist-cli/chunks/{chunk-GAJQSITE.js → chunk-VANZFTUE.js} +1 -1
- package/dist-cli/chunks/{chunk-EGAGZHO6.js → chunk-VB2YBH46.js} +1 -1
- package/dist-cli/chunks/{chunk-H23KWIKA.js → chunk-VFU2SJ7M.js} +2 -2
- package/dist-cli/chunks/{chunk-34IQM22N.js → chunk-VUBJ6FXU.js} +15 -15
- package/dist-cli/chunks/{chunk-NM2L2QV4.js → chunk-W5PATILQ.js} +2 -2
- package/dist-cli/chunks/chunk-WI5V4TYG.js +38 -0
- package/dist-cli/chunks/{chunk-6PL2CCGP.js → chunk-XTRHQAOV.js} +2 -2
- package/dist-cli/chunks/chunk-XWUTJLBM.js +27 -0
- package/dist-cli/chunks/{chunk-5DAIA77C.js → chunk-XYRUQQUR.js} +2 -2
- package/dist-cli/chunks/chunk-YBFB3RM7.js +4 -0
- package/dist-cli/chunks/chunk-YGQVQ2VG.js +4 -0
- package/dist-cli/chunks/{chunk-CTG7HBCA.js → chunk-YINACV32.js} +1 -1
- package/dist-cli/chunks/chunk-YV7ERO2V.js +5 -0
- package/dist-cli/chunks/{cleanup-KRIATKFH.js → cleanup-GVFHQ3KH.js} +2 -2
- package/dist-cli/chunks/cli-version-DPX5MA2M.js +4 -0
- package/dist-cli/chunks/{compat-6PTQ3QEA.js → compat-AYRTXYAM.js} +2 -2
- package/dist-cli/chunks/{config-ETGDUANO.js → config-K7LOOV62.js} +2 -2
- package/dist-cli/chunks/connect-H3CNY4IO.js +4 -0
- package/dist-cli/chunks/control-SMHZ2CF5.js +4 -0
- package/dist-cli/chunks/daemon-TEZUNI73.js +4 -0
- package/dist-cli/chunks/{debug-WIIB6ZKC.js → debug-ELGHG57T.js} +3 -3
- package/dist-cli/chunks/{desktop-TD7Q5LNB.js → desktop-4G7FK3MP.js} +3 -3
- package/dist-cli/chunks/{detox-7GNR33VM.js → detox-JKXGEJIZ.js} +2 -2
- package/dist-cli/chunks/{device-QOR3PGUF.js → device-IV3AR6KG.js} +2 -2
- package/dist-cli/chunks/{diagnose-RRCBLP6E.js → diagnose-3ERGF7EW.js} +2 -2
- package/dist-cli/chunks/{disk-cleanup-MJUGJPXT.js → disk-cleanup-ALFEVZ4B.js} +2 -2
- package/dist-cli/chunks/do-chain-K2B4OHM7.js +5 -0
- package/dist-cli/chunks/drivers-KJYXA3M3.js +4 -0
- package/dist-cli/chunks/env-2KF6IYZE.js +4 -0
- package/dist-cli/chunks/{film-D7Y64LID.js → film-UQXJFAD5.js} +3 -3
- package/dist-cli/chunks/flow-ASTUH56E.js +4 -0
- package/dist-cli/chunks/help-SNNYFELA.js +4 -0
- package/dist-cli/chunks/{hidden-runtime-alias-JGURVR2Z.js → hidden-runtime-alias-VJCWCFVO.js} +2 -2
- package/dist-cli/chunks/{home-paths-4ICFIGXO.js → home-paths-EGUZZ6J6.js} +2 -2
- package/dist-cli/chunks/inspect-XANAADQT.js +4 -0
- package/dist-cli/chunks/install-desktop-JKQA7UY6.js +4 -0
- package/dist-cli/chunks/{login-RU33QR2G.js → login-6KQGD7HK.js} +2 -2
- package/dist-cli/chunks/{logout-2IUG3Y5J.js → logout-2FCOVLBC.js} +2 -2
- package/dist-cli/chunks/{maestro-2MXTGG7I.js → maestro-HPSSEVJD.js} +3 -3
- package/dist-cli/chunks/{maestro-generate-2PYIFGLR.js → maestro-generate-KQQ6JHMR.js} +5 -5
- package/dist-cli/chunks/{mode-3DAPG3HF.js → mode-SH7KRZET.js} +2 -2
- package/dist-cli/chunks/{optional-demo-registry-BWSDJF2Z.js → optional-demo-registry-NAMCAVFX.js} +2 -2
- package/dist-cli/chunks/{perf-2TKPWB4N.js → perf-LWKAHXUL.js} +2 -2
- package/dist-cli/chunks/{permissions-5CSMHYNA.js → permissions-EZTKWTXV.js} +2 -2
- package/dist-cli/chunks/platform-KSOJIVN5.js +6 -0
- package/dist-cli/chunks/privacy-EJULWP7L.js +4 -0
- package/dist-cli/chunks/{record-I2HJCFP3.js → record-2IC7BAQN.js} +3 -3
- package/dist-cli/chunks/{report-issue-A4FZWBOC.js → report-issue-MMX6HOQO.js} +2 -2
- package/dist-cli/chunks/reset-PKFZA4NC.js +4 -0
- package/dist-cli/chunks/resolve-target-AFKU3BRE.js +4 -0
- package/dist-cli/chunks/runtime-XHWWPOIN.js +4 -0
- package/dist-cli/chunks/{screenshot-command-UFW5BFHP.js → screenshot-command-JYUXDVSA.js} +3 -3
- package/dist-cli/chunks/{screenshot-layers-5BIAL4YN.js → screenshot-layers-OGRY6AA3.js} +2 -2
- package/dist-cli/chunks/{screenshots-capture-EHYS3BV2.js → screenshots-capture-QHDN65XS.js} +2 -2
- package/dist-cli/chunks/{serve-O4FDM3AC.js → serve-W4BKAG4J.js} +3 -3
- package/dist-cli/chunks/setup-WJPMPSAC.js +4 -0
- package/dist-cli/chunks/sim-client-UOQGFBPO.js +4 -0
- package/dist-cli/chunks/{skills-ZK55SY32.js → skills-OPPXOD63.js} +2 -2
- package/dist-cli/chunks/state-TDAN7BOM.js +14 -0
- package/dist-cli/chunks/{storage-WP33ISEM.js → storage-CFB2YGLY.js} +2 -2
- package/dist-cli/chunks/store-4TEED4ZU.js +4 -0
- package/dist-cli/chunks/telemetry-S6N7CV6G.js +4 -0
- package/dist-cli/chunks/{timeline-DKGW5S4I.js → timeline-ASOOVP4C.js} +2 -2
- package/dist-cli/chunks/{upgrade-LL7G4B7G.js → upgrade-6MVJISHG.js} +2 -2
- package/dist-cli/chunks/upload-CBNIRFEW.js +4 -0
- package/dist-cli/chunks/{version-YLWR5G4F.js → version-5GS7CWRL.js} +2 -2
- package/dist-cli/chunks/{web-QELZ4CP6.js → web-QRRMYMV4.js} +2 -2
- package/dist-cli/chunks/welcome-33FXMAMB.js +5 -0
- package/dist-cli/chunks/{what-happened-MIOMZWM5.js → what-happened-GAZJGKXX.js} +2 -2
- package/dist-cli/chunks/ws-bridge-DIXUAPLQ.js +4 -0
- 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 +336 -0
- package/dist-lib/bridge-contract-input.mjs +303 -0
- package/dist-lib/bridge-contract.cjs +29 -1
- package/dist-lib/bridge-contract.mjs +19 -0
- package/dist-lib/cli-constants.cjs +1 -1
- package/dist-lib/config.cjs +1 -1
- package/dist-lib/detox/index.cjs +32 -18
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +5 -1
- 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 +214 -594
- package/dist-lib/jump-to-source-babel.cjs +1 -1
- package/dist-lib/menu.cjs +1 -1
- package/dist-lib/menu.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/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 +273 -657
- package/dist-lib/sdk.mjs +271 -652
- package/dist-lib/skills.cjs +190 -287
- package/dist-lib/vite.cjs +1 -1
- package/package.json +9 -6
- package/skills/contrast/SKILL.md +3 -0
- package/skills/rnx-debug/SKILL.md +8 -0
- package/skills/rnx-test/SKILL.md +21 -0
- package/src/bridge-contract-input.ts +359 -0
- package/src/bridge-contract.ts +121 -1
- package/src/host/bridge-host.ts +7 -12
- package/src/index.ts +7 -0
- package/src/sim-client.ts +12 -3
- package/dist-cli/chunks/chunk-3X6IBUBX.js +0 -38
- package/dist-cli/chunks/chunk-7B37IZBN.js +0 -15
- package/dist-cli/chunks/chunk-DMPF2D24.js +0 -4
- package/dist-cli/chunks/chunk-DW37EVX5.js +0 -767
- package/dist-cli/chunks/chunk-DWFIH7CB.js +0 -32
- package/dist-cli/chunks/chunk-GUI3PDH2.js +0 -27
- package/dist-cli/chunks/chunk-KLF3K6ZZ.js +0 -4
- package/dist-cli/chunks/chunk-M5QCGLXR.js +0 -944
- package/dist-cli/chunks/chunk-M6SKQGAN.js +0 -5
- package/dist-cli/chunks/chunk-NAYLPOC5.js +0 -24
- package/dist-cli/chunks/chunk-VIND4SP3.js +0 -124
- package/dist-cli/chunks/chunk-XXNLYR64.js +0 -5
- package/dist-cli/chunks/chunk-ZFM24DOX.js +0 -4
- package/dist-cli/chunks/cli-version-SYGNHGFR.js +0 -4
- package/dist-cli/chunks/control-RBTNOLQM.js +0 -4
- package/dist-cli/chunks/daemon-N3SC5DK6.js +0 -4
- package/dist-cli/chunks/drivers-W6E3BZH4.js +0 -4
- package/dist-cli/chunks/flow-7QAE2V5Y.js +0 -4
- package/dist-cli/chunks/help-NLD6KWVG.js +0 -4
- package/dist-cli/chunks/inspect-C347BG52.js +0 -4
- package/dist-cli/chunks/install-desktop-YR6TWZYK.js +0 -4
- package/dist-cli/chunks/privacy-PWWC2BXO.js +0 -4
- package/dist-cli/chunks/reset-Y3PBPFYA.js +0 -4
- package/dist-cli/chunks/runtime-IC6NVVGA.js +0 -4
- package/dist-cli/chunks/setup-Y4QGSJBM.js +0 -4
- package/dist-cli/chunks/state-KFLAZNEY.js +0 -14
- package/dist-cli/chunks/store-VEE22CAY.js +0 -4
- package/dist-cli/chunks/telemetry-PZV4BISJ.js +0 -4
- package/dist-cli/chunks/upload-Y4C3YZDK.js +0 -4
- package/dist-cli/chunks/welcome-ZPKA5PZJ.js +0 -5
- package/dist-lib/mcp.cjs +0 -673
- package/dist-lib/mcp.mjs +0 -641
- package/src/mcp.ts +0 -791
package/dist-lib/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.367 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
2
|
let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
|
|
3
3
|
"use strict";
|
|
4
4
|
var __create = Object.create;
|
|
@@ -459,6 +459,9 @@ function isDevBridgeLockfileFresh(lock, now = Date.now()) {
|
|
|
459
459
|
}
|
|
460
460
|
|
|
461
461
|
// cli/ws-bridge.ts
|
|
462
|
+
function bridgeWsUrl(wsPort) {
|
|
463
|
+
return `ws://localhost:${wsPort}`;
|
|
464
|
+
}
|
|
462
465
|
var resolvedWorld = null;
|
|
463
466
|
function liveDevBridgeLockfiles() {
|
|
464
467
|
return readDevBridgeLockfiles().filter((lockfile) => isDevBridgeLockfileFresh(lockfile));
|
|
@@ -556,7 +559,7 @@ function createBridge(wsPort, opts = {}) {
|
|
|
556
559
|
let nextId = 1;
|
|
557
560
|
const commandTimeoutMs = opts.commandTimeoutMs ?? 15e3;
|
|
558
561
|
const pending = /* @__PURE__ */ new Map();
|
|
559
|
-
const ws = new import_ws.WebSocket(
|
|
562
|
+
const ws = new import_ws.WebSocket(bridgeWsUrl(wsPort));
|
|
560
563
|
const identity = getCliIdentity();
|
|
561
564
|
const ready = new Promise((resolve2, reject) => {
|
|
562
565
|
ws.on("open", () => {
|
|
@@ -576,7 +579,7 @@ function createBridge(wsPort, opts = {}) {
|
|
|
576
579
|
});
|
|
577
580
|
ws.on("error", (err) => {
|
|
578
581
|
const cause = err.message ? `: ${err.message}` : "";
|
|
579
|
-
reject(new Error(`could not connect to
|
|
582
|
+
reject(new Error(`could not connect to ${bridgeWsUrl(wsPort)}${cause}`));
|
|
580
583
|
});
|
|
581
584
|
});
|
|
582
585
|
ready.catch(() => {
|
|
@@ -1360,115 +1363,110 @@ async function tapBest(bridge, query, opts = {}) {
|
|
|
1360
1363
|
}
|
|
1361
1364
|
|
|
1362
1365
|
// cli/commands/inspect/core.ts
|
|
1363
|
-
async function inspectTree(bridge, depth = 5
|
|
1364
|
-
const
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
code: `(async () => await window.__sootsimTest.dumpTree(${depth}${optsArg}))()`
|
|
1366
|
+
async function inspectTree(bridge, depth = 5) {
|
|
1367
|
+
const result = await bridge.send({
|
|
1368
|
+
type: "query",
|
|
1369
|
+
query: { kind: "tree", depth }
|
|
1368
1370
|
});
|
|
1369
|
-
|
|
1370
|
-
try {
|
|
1371
|
-
return { depth, tree: JSON.parse(tree) };
|
|
1372
|
-
} catch {
|
|
1373
|
-
return { depth, tree };
|
|
1374
|
-
}
|
|
1375
|
-
}
|
|
1371
|
+
const tree = isSemanticQueryResult(result) ? result.nodes : [];
|
|
1376
1372
|
return { depth, tree };
|
|
1377
1373
|
}
|
|
1378
1374
|
async function inspectUrl(bridge) {
|
|
1379
1375
|
const url = await bridge.send({ type: "evaluate", code: "window.location.href" });
|
|
1380
1376
|
return { url: typeof url === "string" ? url : "" };
|
|
1381
1377
|
}
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1378
|
+
function isSemanticGeometry(value) {
|
|
1379
|
+
return value !== null && typeof value === "object" && [
|
|
1380
|
+
Reflect.get(value, "x"),
|
|
1381
|
+
Reflect.get(value, "y"),
|
|
1382
|
+
Reflect.get(value, "width"),
|
|
1383
|
+
Reflect.get(value, "height")
|
|
1384
|
+
].every(
|
|
1385
|
+
(coordinate) => typeof coordinate === "number" ? Number.isFinite(coordinate) : false
|
|
1386
|
+
);
|
|
1387
|
+
}
|
|
1388
|
+
function isSimSemanticTree(value) {
|
|
1389
|
+
if (!Array.isArray(value)) return false;
|
|
1390
|
+
return value.every((node) => {
|
|
1391
|
+
if (node === null || typeof node !== "object" || !Number.isInteger(Reflect.get(node, "nodeId")) || typeof Reflect.get(node, "type") !== "string" || !isSemanticGeometry(Reflect.get(node, "geometry"))) {
|
|
1392
|
+
return false;
|
|
1393
|
+
}
|
|
1394
|
+
for (const key of ["testID", "text", "role", "label"]) {
|
|
1395
|
+
const value2 = Reflect.get(node, key);
|
|
1396
|
+
if (value2 !== void 0 && typeof value2 !== "string") return false;
|
|
1397
|
+
}
|
|
1398
|
+
const pressable = Reflect.get(node, "pressable");
|
|
1399
|
+
if (pressable !== void 0 && pressable !== true) return false;
|
|
1400
|
+
const children = Reflect.get(node, "children");
|
|
1401
|
+
return children === void 0 || isSimSemanticTree(children);
|
|
1402
|
+
});
|
|
1403
|
+
}
|
|
1404
|
+
function isSemanticQueryResult(value) {
|
|
1405
|
+
const total = value !== null && typeof value === "object" ? Reflect.get(value, "total") : void 0;
|
|
1406
|
+
return value !== null && typeof value === "object" && typeof total === "number" && Number.isInteger(total) && total >= 0 && isSimSemanticTree(Reflect.get(value, "nodes"));
|
|
1407
|
+
}
|
|
1408
|
+
function globRegex(glob) {
|
|
1409
|
+
const escaped = glob.replace(/[.+^$(){}|[\]\\]/g, "\\$&");
|
|
1410
|
+
return new RegExp(`^${escaped.replace(/\*/g, ".*").replace(/\?/g, ".")}$`, "i");
|
|
1411
|
+
}
|
|
1412
|
+
function formatSemanticTree(nodes, opts = {}) {
|
|
1413
|
+
const filter = opts.filter?.toLowerCase() ?? "";
|
|
1414
|
+
const testIDRegex = opts.testIdLike ? globRegex(opts.testIdLike) : null;
|
|
1415
|
+
const onlyRegex = opts.onlyGlob ? globRegex(opts.onlyGlob) : null;
|
|
1416
|
+
const lines = [];
|
|
1417
|
+
const visit = (node, indent) => {
|
|
1418
|
+
const joined = [node.role, node.label, node.text, node.testID].filter((value) => typeof value === "string").join(" ");
|
|
1419
|
+
const matches = (!filter || joined.toLowerCase().includes(filter)) && (!testIDRegex || testIDRegex.test(node.testID ?? "")) && (!onlyRegex || onlyRegex.test(joined));
|
|
1420
|
+
if (matches) {
|
|
1421
|
+
const parts = [
|
|
1422
|
+
node.role ? `[${node.role}]` : node.pressable ? "[tap]" : `<${node.type}>`
|
|
1423
|
+
];
|
|
1424
|
+
if (node.text) {
|
|
1425
|
+
const text = opts.fullText || node.text.length <= 50 ? node.text : `${node.text.slice(0, 49).trimEnd()}\u2026`;
|
|
1426
|
+
parts.push(JSON.stringify(text));
|
|
1411
1427
|
}
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
if (!Array.isArray(list)) return null
|
|
1416
|
-
const filter = typeof opts.filter === 'string' ? opts.filter.toLowerCase() : ''
|
|
1417
|
-
const testIdRegex = opts.testIdLike ? globToRegex(opts.testIdLike) : null
|
|
1418
|
-
const onlyRegex = opts.onlyGlob ? globToRegex(opts.onlyGlob) : null
|
|
1419
|
-
const lines = []
|
|
1420
|
-
let matched = 0
|
|
1421
|
-
for (const node of list) {
|
|
1422
|
-
const testID = node?.testID || node?.testId || ''
|
|
1423
|
-
const role = node?.role || ''
|
|
1424
|
-
const label = node?.accessibilityLabel || node?.label || ''
|
|
1425
|
-
const text = node?.text || ''
|
|
1426
|
-
if (testIdRegex && !testIdRegex.test(testID)) continue
|
|
1427
|
-
const joined = [role, label, text, testID].filter(Boolean).join(' ')
|
|
1428
|
-
if (onlyRegex && !onlyRegex.test(joined)) continue
|
|
1429
|
-
if (filter && !joined.toLowerCase().includes(filter)) continue
|
|
1430
|
-
matched++
|
|
1431
|
-
|
|
1432
|
-
const type = String(node?.type || 'node').toLowerCase()
|
|
1433
|
-
const handlers = Array.isArray(node?.handlers) ? node.handlers : []
|
|
1434
|
-
const parts = []
|
|
1435
|
-
if (role) parts.push('[' + role + ']')
|
|
1436
|
-
else if (handlers.length > 0) parts.push('[tap]')
|
|
1437
|
-
else parts.push('<' + type + '>')
|
|
1438
|
-
const visibleText = clipForDisplay(text, 50)
|
|
1439
|
-
if (visibleText) parts.push(JSON.stringify(visibleText))
|
|
1440
|
-
if (testID) parts.push('#' + testID)
|
|
1441
|
-
if (label && label !== text) {
|
|
1442
|
-
parts.push('label=' + JSON.stringify(clipForDisplay(label, 50)))
|
|
1443
|
-
}
|
|
1444
|
-
if (!opts.hideXy) {
|
|
1445
|
-
const abs = node?.absolute || {}
|
|
1446
|
-
const layout = node?.layout || {}
|
|
1447
|
-
const x = Math.round(Number(abs.x) || 0)
|
|
1448
|
-
const y = Math.round(Number(abs.y) || 0)
|
|
1449
|
-
const w = Math.round(Number(layout.width) || 0)
|
|
1450
|
-
const h = Math.round(Number(layout.height) || 0)
|
|
1451
|
-
parts.push('@(' + x + ',' + y + ') ' + w + 'x' + h)
|
|
1452
|
-
}
|
|
1453
|
-
if (handlers.length > 0) parts.push('(tap)')
|
|
1454
|
-
lines.push(' ' + parts.join(' '))
|
|
1455
|
-
}
|
|
1456
|
-
return { tree: lines.join('\\n'), matched }
|
|
1428
|
+
if (node.testID) parts.push(`#${node.testID}`);
|
|
1429
|
+
if (node.label && node.label !== node.text) {
|
|
1430
|
+
parts.push(`label=${JSON.stringify(node.label)}`);
|
|
1457
1431
|
}
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
} else {
|
|
1464
|
-
tree = await t.dumpTree(${dumpDepth}, opts)
|
|
1432
|
+
if (!opts.hideXy) {
|
|
1433
|
+
const geometry = node.geometry;
|
|
1434
|
+
parts.push(
|
|
1435
|
+
`@(${Math.round(geometry.x)},${Math.round(geometry.y)}) ${Math.round(geometry.width)}x${Math.round(geometry.height)}`
|
|
1436
|
+
);
|
|
1465
1437
|
}
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1438
|
+
if (node.pressable) parts.push("(tap)");
|
|
1439
|
+
lines.push(`${" ".repeat(indent + 1)}${parts.join(" ")}`);
|
|
1440
|
+
}
|
|
1441
|
+
for (const child of node.children ?? []) visit(child, indent + 1);
|
|
1442
|
+
};
|
|
1443
|
+
let roots = nodes;
|
|
1444
|
+
if (opts.subtreeRoot) {
|
|
1445
|
+
const pending = [...nodes];
|
|
1446
|
+
let found;
|
|
1447
|
+
while (pending.length > 0 && !found) {
|
|
1448
|
+
const candidate = pending.shift();
|
|
1449
|
+
if (!candidate) continue;
|
|
1450
|
+
if (candidate.testID === opts.subtreeRoot) found = candidate;
|
|
1451
|
+
else pending.push(...candidate.children ?? []);
|
|
1452
|
+
}
|
|
1453
|
+
if (!found) return `__SUBTREE_NOT_FOUND__:${opts.subtreeRoot}`;
|
|
1454
|
+
roots = [found];
|
|
1455
|
+
}
|
|
1456
|
+
for (const root of roots) visit(root, 0);
|
|
1457
|
+
return lines.join("\n");
|
|
1458
|
+
}
|
|
1459
|
+
async function inspectDescribe(bridge, dumpOpts) {
|
|
1460
|
+
const dumpDepth = dumpOpts.maxDepth ?? 12;
|
|
1461
|
+
const result = await bridge.send({
|
|
1462
|
+
type: "query",
|
|
1463
|
+
query: { kind: "tree", depth: dumpDepth }
|
|
1464
|
+
});
|
|
1465
|
+
if (!isSemanticQueryResult(result)) return {};
|
|
1466
|
+
return {
|
|
1467
|
+
tree: formatSemanticTree(result.nodes, dumpOpts),
|
|
1468
|
+
nodeCount: result.total
|
|
1469
|
+
};
|
|
1472
1470
|
}
|
|
1473
1471
|
function scoreLayoutTextContrast(input) {
|
|
1474
1472
|
const required = input.fontSize >= 18 || input.fontSize >= 14 && (input.fontWeight ?? 400) >= 700 ? 3 : 4.5;
|
|
@@ -1683,9 +1681,6 @@ function layoutGetNativeEval(opts = {}) {
|
|
|
1683
1681
|
type: typeof n.type === 'string' ? n.type : 'node',
|
|
1684
1682
|
text,
|
|
1685
1683
|
box: { x: round(abs.x), y: round(abs.y), w: round(layout.width), h: round(layout.height) },
|
|
1686
|
-
borderRadius: 0,
|
|
1687
|
-
padding: { t: 0, r: 0, b: 0, l: 0 },
|
|
1688
|
-
fontSize: 0,
|
|
1689
1684
|
}
|
|
1690
1685
|
if (!includeStyle) return base
|
|
1691
1686
|
// computedStyle has the resolved values; style is the curated subset \u2014
|
|
@@ -1755,398 +1750,80 @@ function layoutGetNativeEval(opts = {}) {
|
|
|
1755
1750
|
})()`;
|
|
1756
1751
|
}
|
|
1757
1752
|
var LAYOUT_GET_NATIVE_EVAL = layoutGetNativeEval();
|
|
1758
|
-
var INSPECT_A11Y_CODE = `(async () => {
|
|
1759
|
-
const t = window.__sootsimTest
|
|
1760
|
-
if (!t) return []
|
|
1761
|
-
|
|
1762
|
-
const readLayout = (n) => n?.layout || null
|
|
1763
|
-
const readAbs = (n) => n?.absolutePosition || n?.absolute || null
|
|
1764
|
-
const readRole = (n) => n?.accessibilityRole || n?.role || null
|
|
1765
|
-
const readLabel = (n) => n?.accessibilityLabel || n?.label || n?.text || null
|
|
1766
|
-
const readHint = (n) => n?.accessibilityHint || n?.hint || null
|
|
1767
|
-
const readState = (n) => n?.accessibilityState || n?.state || null
|
|
1768
|
-
const readTestId = (n) => n?.testID || n?.testId || null
|
|
1769
|
-
const readType = (n) => typeof n?.type === 'string' ? n.type.toLowerCase() : n?.type
|
|
1770
|
-
const readPressable = (n) => {
|
|
1771
|
-
if (n?.pressable) return true
|
|
1772
|
-
const handlers = Array.isArray(n?.handlers) ? n.handlers : []
|
|
1773
|
-
const role = readRole(n)
|
|
1774
|
-
return handlers.length > 0 || role === 'button' || role === 'link' || role === 'tab'
|
|
1775
|
-
}
|
|
1776
|
-
const isTextInput = (n) => n?.isTextInput === true
|
|
1777
|
-
const isTextNode = (n) => readType(n) === 'text'
|
|
1778
|
-
const isVisibleTarget = (n) => {
|
|
1779
|
-
const layout = readLayout(n)
|
|
1780
|
-
if (!layout || layout.width <= 0 || layout.height <= 0) return false
|
|
1781
|
-
const abs = readAbs(n)
|
|
1782
|
-
if (!abs) return true
|
|
1783
|
-
const device = n?.device || {}
|
|
1784
|
-
const screenW = Number(device.width) || window.innerWidth || 0
|
|
1785
|
-
const screenH = Number(device.height) || window.innerHeight || 0
|
|
1786
|
-
return (
|
|
1787
|
-
abs.x + layout.width > 0 &&
|
|
1788
|
-
abs.y + layout.height > 0 &&
|
|
1789
|
-
abs.x < screenW &&
|
|
1790
|
-
abs.y < screenH
|
|
1791
|
-
)
|
|
1792
|
-
}
|
|
1793
|
-
const hasAccessibleSignal = (n) => {
|
|
1794
|
-
const role = readRole(n)
|
|
1795
|
-
const label = readLabel(n)
|
|
1796
|
-
const hint = readHint(n)
|
|
1797
|
-
if (role) return true
|
|
1798
|
-
if (hint) return true
|
|
1799
|
-
if (isTextInput(n)) return true
|
|
1800
|
-
if (readPressable(n)) return true
|
|
1801
|
-
if (isTextNode(n) && n?.text) return true
|
|
1802
|
-
if (typeof label === 'string' && label.length <= 30 && label !== n?.text) return true
|
|
1803
|
-
return false
|
|
1804
|
-
}
|
|
1805
|
-
const normalize = (n) => {
|
|
1806
|
-
const layout = readLayout(n)
|
|
1807
|
-
const abs = readAbs(n)
|
|
1808
|
-
const role = readRole(n) || (readPressable(n) ? 'button' : isTextInput(n) ? 'textfield' : isTextNode(n) ? 'statictext' : 'none')
|
|
1809
|
-
return {
|
|
1810
|
-
role,
|
|
1811
|
-
label: readLabel(n),
|
|
1812
|
-
hint: readHint(n),
|
|
1813
|
-
state: readState(n),
|
|
1814
|
-
testID: readTestId(n),
|
|
1815
|
-
position: abs ? { x: Math.round(abs.x), y: Math.round(abs.y) } : null,
|
|
1816
|
-
size: layout ? { w: Math.round(layout.width), h: Math.round(layout.height) } : null,
|
|
1817
|
-
}
|
|
1818
|
-
}
|
|
1819
|
-
|
|
1820
|
-
if (typeof t.listInspectable === 'function') {
|
|
1821
|
-
const list = await t.listInspectable({})
|
|
1822
|
-
if (Array.isArray(list)) {
|
|
1823
|
-
return list.filter(n => isVisibleTarget(n) && hasAccessibleSignal(n)).map(normalize)
|
|
1824
|
-
}
|
|
1825
|
-
}
|
|
1826
|
-
|
|
1827
|
-
const all = await t.queryAll({ pruneHidden: true })
|
|
1828
|
-
return all
|
|
1829
|
-
.filter(n => isVisibleTarget(n) && hasAccessibleSignal(n))
|
|
1830
|
-
.map(normalize)
|
|
1831
|
-
})()`;
|
|
1832
1753
|
async function inspectAccessibilityTree(bridge) {
|
|
1833
|
-
const
|
|
1834
|
-
|
|
1835
|
-
}
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
Math.max(0, absolutePosition.x),
|
|
1856
|
-
),
|
|
1857
|
-
height: Math.max(
|
|
1858
|
-
0,
|
|
1859
|
-
Math.min(screenH, absolutePosition.y + layout.height) -
|
|
1860
|
-
Math.max(0, absolutePosition.y),
|
|
1861
|
-
),
|
|
1862
|
-
}
|
|
1863
|
-
: null
|
|
1864
|
-
const handlers = Array.isArray(n.handlers) ? n.handlers : []
|
|
1865
|
-
const pressable =
|
|
1866
|
-
handlers.length > 0 ||
|
|
1867
|
-
role === 'button' ||
|
|
1868
|
-
role === 'link' ||
|
|
1869
|
-
role === 'tab'
|
|
1870
|
-
return {
|
|
1871
|
-
type: typeof n.type === 'string' ? n.type.toLowerCase() : n.type,
|
|
1872
|
-
id: n.id ?? n.nodeId,
|
|
1873
|
-
nodeId: n.nodeId,
|
|
1874
|
-
testID: n.testID ?? n.testId,
|
|
1875
|
-
text: n.text,
|
|
1876
|
-
layout,
|
|
1877
|
-
absolutePosition,
|
|
1878
|
-
visibleFrame,
|
|
1879
|
-
style: n.computedStyle ?? n.style ?? {},
|
|
1880
|
-
childCount: n.childCount ?? 0,
|
|
1881
|
-
pressed: n.pressed ?? false,
|
|
1882
|
-
pressable,
|
|
1883
|
-
isTextInput: n.isTextInput ?? false,
|
|
1884
|
-
accessible: n.accessible ?? true,
|
|
1885
|
-
accessibilityLabel: label,
|
|
1886
|
-
accessibilityRole: role,
|
|
1887
|
-
accessibilityHint: n.accessibilityHint,
|
|
1888
|
-
accessibilityState: n.accessibilityState,
|
|
1889
|
-
accessibilityValue: n.accessibilityValue,
|
|
1890
|
-
}
|
|
1891
|
-
})
|
|
1892
|
-
}
|
|
1893
|
-
const isVisibleTarget = (n) => {
|
|
1894
|
-
if (!n?.layout) return false
|
|
1895
|
-
const frame = n.visibleFrame
|
|
1896
|
-
if (frame) return frame.width > 0 && frame.height > 0
|
|
1897
|
-
const abs = n.absolutePosition
|
|
1898
|
-
if (!abs) return n.layout.width > 0 && n.layout.height > 0
|
|
1899
|
-
const screenW = window.innerWidth || 0
|
|
1900
|
-
const screenH = window.innerHeight || 0
|
|
1901
|
-
return (
|
|
1902
|
-
abs.x + n.layout.width > 0 &&
|
|
1903
|
-
abs.y + n.layout.height > 0 &&
|
|
1904
|
-
abs.x < screenW &&
|
|
1905
|
-
abs.y < screenH
|
|
1906
|
-
)
|
|
1754
|
+
const result = await bridge.send({
|
|
1755
|
+
type: "query",
|
|
1756
|
+
query: { kind: "find", selector: { visible: true } }
|
|
1757
|
+
});
|
|
1758
|
+
if (!isSemanticQueryResult(result)) return [];
|
|
1759
|
+
return result.nodes.flatMap((node) => {
|
|
1760
|
+
if (!node.role && !node.label && !node.text && !node.pressable) return [];
|
|
1761
|
+
return [
|
|
1762
|
+
{
|
|
1763
|
+
role: node.role ?? (node.pressable ? "button" : node.type === "text" ? "statictext" : "none"),
|
|
1764
|
+
label: node.label ?? node.text ?? null,
|
|
1765
|
+
hint: null,
|
|
1766
|
+
state: null,
|
|
1767
|
+
testID: node.testID ?? null,
|
|
1768
|
+
position: {
|
|
1769
|
+
x: Math.round(node.geometry.x),
|
|
1770
|
+
y: Math.round(node.geometry.y)
|
|
1771
|
+
},
|
|
1772
|
+
size: {
|
|
1773
|
+
w: Math.round(node.geometry.width),
|
|
1774
|
+
h: Math.round(node.geometry.height)
|
|
1775
|
+
}
|
|
1907
1776
|
}
|
|
1908
|
-
|
|
1777
|
+
];
|
|
1778
|
+
});
|
|
1779
|
+
}
|
|
1909
1780
|
function resolveFindMode(q) {
|
|
1910
1781
|
if (q.testId) {
|
|
1911
|
-
return {
|
|
1912
|
-
mode: "testid",
|
|
1913
|
-
code: `(async () => {
|
|
1914
|
-
const t = window.__sootsimTest
|
|
1915
|
-
if (!t) return null
|
|
1916
|
-
return (await t.findByTestId(${JSON.stringify(q.testId)})) || (await t.findById(${JSON.stringify(q.testId)}))
|
|
1917
|
-
})()`
|
|
1918
|
-
};
|
|
1782
|
+
return { mode: "testid", selector: { testID: q.testId } };
|
|
1919
1783
|
}
|
|
1920
1784
|
if (q.role) {
|
|
1921
|
-
return {
|
|
1922
|
-
mode: "role",
|
|
1923
|
-
code: `(async () => {
|
|
1924
|
-
const t = window.__sootsimTest
|
|
1925
|
-
if (!t) return []
|
|
1926
|
-
return await t.queryAll({ hasRole: ${JSON.stringify(q.role)}, pruneHidden: true })
|
|
1927
|
-
})()`
|
|
1928
|
-
};
|
|
1785
|
+
return { mode: "role", selector: { role: q.role } };
|
|
1929
1786
|
}
|
|
1930
1787
|
if (q.type) {
|
|
1931
|
-
return {
|
|
1932
|
-
mode: "type",
|
|
1933
|
-
code: `(async () => {
|
|
1934
|
-
const t = window.__sootsimTest
|
|
1935
|
-
if (!t) return []
|
|
1936
|
-
return await t.queryAll({ type: ${JSON.stringify(q.type)}, pruneHidden: true })
|
|
1937
|
-
})()`
|
|
1938
|
-
};
|
|
1788
|
+
return { mode: "type", selector: { type: q.type } };
|
|
1939
1789
|
}
|
|
1940
1790
|
if (q.pressable) {
|
|
1941
|
-
return {
|
|
1942
|
-
mode: "pressable",
|
|
1943
|
-
code: `(async () => {
|
|
1944
|
-
const t = window.__sootsimTest
|
|
1945
|
-
if (!t) return []
|
|
1946
|
-
${FIND_INSPECTABLE_TARGETS}
|
|
1947
|
-
const inspectable = await fromInspectable()
|
|
1948
|
-
if (inspectable) return inspectable.filter(n => n.pressable && isVisibleTarget(n))
|
|
1949
|
-
const all = await t.queryAll({ pruneHidden: true })
|
|
1950
|
-
return all.filter(n => n.pressable && isVisibleTarget(n))
|
|
1951
|
-
})()`
|
|
1952
|
-
};
|
|
1791
|
+
return { mode: "pressable", selector: { pressable: true } };
|
|
1953
1792
|
}
|
|
1954
1793
|
if (q.interactive) {
|
|
1955
|
-
return {
|
|
1956
|
-
mode: "interactive-targets",
|
|
1957
|
-
code: `(async () => {
|
|
1958
|
-
const t = window.__sootsimTest
|
|
1959
|
-
if (!t) return []
|
|
1960
|
-
${FIND_INSPECTABLE_TARGETS}
|
|
1961
|
-
const inspectable = await fromInspectable()
|
|
1962
|
-
if (inspectable) {
|
|
1963
|
-
return inspectable.filter(n => n.pressable && isVisibleTarget(n))
|
|
1964
|
-
}
|
|
1965
|
-
const all = await t.queryAll({ pruneHidden: true })
|
|
1966
|
-
return all.filter(n => n.pressable && isVisibleTarget(n))
|
|
1967
|
-
})()`
|
|
1968
|
-
};
|
|
1794
|
+
return { mode: "interactive-targets", selector: { pressable: true } };
|
|
1969
1795
|
}
|
|
1970
1796
|
if (q.visible) {
|
|
1971
|
-
return {
|
|
1972
|
-
mode: "visible",
|
|
1973
|
-
code: `(async () => {
|
|
1974
|
-
const t = window.__sootsimTest
|
|
1975
|
-
if (!t) return []
|
|
1976
|
-
const all = await t.queryAll({ pruneHidden: true })
|
|
1977
|
-
return all.filter(n => n.layout && n.layout.width > 0 && n.layout.height > 0)
|
|
1978
|
-
})()`
|
|
1979
|
-
};
|
|
1797
|
+
return { mode: "visible", selector: { visible: true } };
|
|
1980
1798
|
}
|
|
1981
1799
|
if (q.text) {
|
|
1982
|
-
return {
|
|
1983
|
-
mode: "text",
|
|
1984
|
-
code: `(async () => {
|
|
1985
|
-
const t = window.__sootsimTest
|
|
1986
|
-
if (!t) return null
|
|
1987
|
-
return await t.findByText(${JSON.stringify(q.text)})
|
|
1988
|
-
})()`
|
|
1989
|
-
};
|
|
1800
|
+
return { mode: "text", selector: { text: q.text } };
|
|
1990
1801
|
}
|
|
1991
1802
|
return null;
|
|
1992
1803
|
}
|
|
1993
1804
|
async function inspectFind(bridge, q) {
|
|
1994
1805
|
const resolved = resolveFindMode(q);
|
|
1995
1806
|
if (!resolved) return null;
|
|
1996
|
-
const result = await bridge.send({
|
|
1997
|
-
|
|
1998
|
-
}
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
}
|
|
2017
|
-
try {
|
|
2018
|
-
const loading = window.__sootsimConnectRNLoading
|
|
2019
|
-
if (loading && loading.active === true) {
|
|
2020
|
-
loadingText = [loading.title, loading.detail].map(cleanText).filter(Boolean).join(' ')
|
|
2021
|
-
}
|
|
2022
|
-
} catch {}
|
|
2023
|
-
try {
|
|
2024
|
-
if (typeof t?.listInspectable === 'function') {
|
|
2025
|
-
const list = await t.listInspectable({})
|
|
2026
|
-
if (Array.isArray(list)) {
|
|
2027
|
-
const screenW = window.innerWidth || 0
|
|
2028
|
-
const screenH = window.innerHeight || 0
|
|
2029
|
-
const isVisible = (n) => {
|
|
2030
|
-
const layout = n?.layout
|
|
2031
|
-
if (!layout || layout.width <= 0 || layout.height <= 0) return false
|
|
2032
|
-
const abs = n?.absolutePosition || n?.absolute
|
|
2033
|
-
if (!abs) return true
|
|
2034
|
-
const device = n?.device || {}
|
|
2035
|
-
const w = Number(device.width) || screenW
|
|
2036
|
-
const h = Number(device.height) || screenH
|
|
2037
|
-
return (
|
|
2038
|
-
abs.x + layout.width > 0 &&
|
|
2039
|
-
abs.y + layout.height > 0 &&
|
|
2040
|
-
abs.x < w &&
|
|
2041
|
-
abs.y < h
|
|
2042
|
-
)
|
|
2043
|
-
}
|
|
2044
|
-
const hasContentSignal = (n) => {
|
|
2045
|
-
const role = n?.accessibilityRole || n?.role
|
|
2046
|
-
const label = cleanText(n?.accessibilityLabel || n?.label)
|
|
2047
|
-
const handlers = Array.isArray(n?.handlers) ? n.handlers : []
|
|
2048
|
-
const text = cleanText(n?.text)
|
|
2049
|
-
const testID = cleanText(n?.testID || n?.testId)
|
|
2050
|
-
const loading = loadingSignal(text) || loadingSignal(label) || loadingSignal(testID)
|
|
2051
|
-
if (!loadingText && loading) {
|
|
2052
|
-
loadingText = loading
|
|
2053
|
-
}
|
|
2054
|
-
if (handlers.length > 0) return true
|
|
2055
|
-
if (role) return true
|
|
2056
|
-
if (n?.isTextInput) return true
|
|
2057
|
-
if (text) return true
|
|
2058
|
-
if (label) return true
|
|
2059
|
-
if (testID && !/^splash|loading/i.test(testID)) return true
|
|
2060
|
-
return false
|
|
2061
|
-
}
|
|
2062
|
-
targets = list.filter(n => isVisible(n) && hasContentSignal(n)).length
|
|
2063
|
-
}
|
|
2064
|
-
}
|
|
2065
|
-
} catch {}
|
|
2066
|
-
try {
|
|
2067
|
-
const stats = await t?.getImageLoaderStats?.()
|
|
2068
|
-
const presentation = stats?.liveFramePresentation
|
|
2069
|
-
liveFrameChannels = Number(presentation?.activeChannels) || 0
|
|
2070
|
-
liveFramePublishes = Number(presentation?.publishes) || 0
|
|
2071
|
-
const previous = window.__sootsimWaitReadyLiveFramePublishes
|
|
2072
|
-
liveFrameActive =
|
|
2073
|
-
liveFrameChannels > 0 &&
|
|
2074
|
-
typeof previous === 'number' &&
|
|
2075
|
-
liveFramePublishes > previous
|
|
2076
|
-
window.__sootsimWaitReadyLiveFramePublishes = liveFramePublishes
|
|
2077
|
-
} catch {}
|
|
2078
|
-
let externalReady = null
|
|
2079
|
-
let externalStatus = ''
|
|
2080
|
-
let externalError = ''
|
|
2081
|
-
let suppressedEntryError = ''
|
|
2082
|
-
try {
|
|
2083
|
-
const getExternalAppState = t?.getExternalAppState
|
|
2084
|
-
const external = typeof getExternalAppState === 'function'
|
|
2085
|
-
? await Promise.race([
|
|
2086
|
-
getExternalAppState(),
|
|
2087
|
-
new Promise((resolve) => setTimeout(
|
|
2088
|
-
() => resolve({
|
|
2089
|
-
state: {
|
|
2090
|
-
ready: false,
|
|
2091
|
-
loading: true,
|
|
2092
|
-
status: 'waiting for tenant worker...',
|
|
2093
|
-
},
|
|
2094
|
-
}),
|
|
2095
|
-
1000,
|
|
2096
|
-
)),
|
|
2097
|
-
])
|
|
2098
|
-
: null
|
|
2099
|
-
const state = external?.state
|
|
2100
|
-
if (state && typeof state === 'object') {
|
|
2101
|
-
externalReady = state.ready === true
|
|
2102
|
-
if (typeof state.status === 'string') externalStatus = state.status
|
|
2103
|
-
if (typeof state.error === 'string') externalError = state.error
|
|
2104
|
-
}
|
|
2105
|
-
if (!externalError && Array.isArray(external?.entryErrors)) {
|
|
2106
|
-
// suppressed-and-continued entry errors (bundle-loader's patched __r
|
|
2107
|
-
// swallows entry throws and moves on) must NOT gate readiness \u2014 the
|
|
2108
|
-
// app frequently runs fine after one (e.g. metro's "Unknown named
|
|
2109
|
-
// module" probing noise). they surface separately as a diagnostic
|
|
2110
|
-
// for the timeout path.
|
|
2111
|
-
const isReal = (item) => item && typeof item.message === 'string' && item.message.trim()
|
|
2112
|
-
const fatal = external.entryErrors.find((item) => isReal(item) && item.suppressed !== true)
|
|
2113
|
-
if (fatal) externalError = fatal.message
|
|
2114
|
-
const suppressed = external.entryErrors.find((item) => isReal(item) && item.suppressed === true)
|
|
2115
|
-
if (suppressed) suppressedEntryError = suppressed.message
|
|
2116
|
-
}
|
|
2117
|
-
// once the app has errored its last status ("injecting sootsim
|
|
2118
|
-
// modules...") is stale \u2014 reporting it makes a dead app read as one that
|
|
2119
|
-
// is still booting.
|
|
2120
|
-
if (externalError) loadingText = ''
|
|
2121
|
-
else if (externalReady === false && externalStatus) loadingText = externalStatus
|
|
2122
|
-
} catch {}
|
|
2123
|
-
let errors = 0
|
|
2124
|
-
try { errors = window.__sootsimConsole?.count?.()?.errors ?? 0 } catch {}
|
|
2125
|
-
return {
|
|
2126
|
-
flag: (window).__sootsimExternalAppReady,
|
|
2127
|
-
at: (window).__sootsimExternalAppReadyAt || 0,
|
|
2128
|
-
nodes,
|
|
2129
|
-
targets,
|
|
2130
|
-
liveFrameActive,
|
|
2131
|
-
liveFrameChannels,
|
|
2132
|
-
liveFramePublishes,
|
|
2133
|
-
errors,
|
|
2134
|
-
loadingText,
|
|
2135
|
-
externalReady,
|
|
2136
|
-
externalStatus,
|
|
2137
|
-
externalError,
|
|
2138
|
-
suppressedEntryError,
|
|
2139
|
-
}
|
|
2140
|
-
})()`;
|
|
2141
|
-
var READY_CONTENT_NODE_FLOOR = 100;
|
|
2142
|
-
var READY_NODE_STABLE_MS = 750;
|
|
2143
|
-
var WAIT_READY_PROGRESS_INTERVAL_MS = 2e3;
|
|
2144
|
-
var READY_PROBE_POLL_MS = 150;
|
|
2145
|
-
function readyProbeHasContent(probe) {
|
|
2146
|
-
return probe.liveFrameActive || probe.targets > 0 || probe.nodes >= READY_CONTENT_NODE_FLOOR;
|
|
2147
|
-
}
|
|
2148
|
-
function readyProbeHasTargetContent(probe) {
|
|
2149
|
-
return probe.liveFrameActive || probe.targets > 0;
|
|
1807
|
+
const result = await bridge.send({
|
|
1808
|
+
type: "query",
|
|
1809
|
+
query: { kind: "find", selector: resolved.selector }
|
|
1810
|
+
});
|
|
1811
|
+
if (!isSemanticQueryResult(result)) return { mode: resolved.mode, result: [] };
|
|
1812
|
+
const nodes = result.nodes.map(
|
|
1813
|
+
(node) => ({
|
|
1814
|
+
type: node.type,
|
|
1815
|
+
nodeId: node.nodeId,
|
|
1816
|
+
...node.testID ? { testID: node.testID } : {},
|
|
1817
|
+
...node.text ? { text: node.text } : {},
|
|
1818
|
+
...node.pressable ? { pressable: true } : {},
|
|
1819
|
+
...node.role ? { accessibilityRole: node.role } : {},
|
|
1820
|
+
...node.label ? { accessibilityLabel: node.label } : {},
|
|
1821
|
+
absolutePosition: { x: node.geometry.x, y: node.geometry.y },
|
|
1822
|
+
layout: { width: node.geometry.width, height: node.geometry.height }
|
|
1823
|
+
})
|
|
1824
|
+
);
|
|
1825
|
+
const resultValue = resolved.mode === "testid" || resolved.mode === "text" ? nodes[0] ?? null : nodes;
|
|
1826
|
+
return { mode: resolved.mode, result: resultValue };
|
|
2150
1827
|
}
|
|
2151
1828
|
function waitReadyReason(status) {
|
|
2152
1829
|
if (status.bridgeError) {
|
|
@@ -2157,98 +1834,46 @@ function waitReadyReason(status) {
|
|
|
2157
1834
|
}
|
|
2158
1835
|
async function inspectWaitReady(bridge, timeoutMs = 2e4, options = {}) {
|
|
2159
1836
|
const start = Date.now();
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
liveFramePublishes: 0,
|
|
2174
|
-
errors: 0,
|
|
2175
|
-
loadingText: "",
|
|
2176
|
-
externalReady: null,
|
|
2177
|
-
externalStatus: "",
|
|
2178
|
-
externalError: "",
|
|
2179
|
-
suppressedEntryError: ""
|
|
2180
|
-
};
|
|
2181
|
-
while (Date.now() < deadline) {
|
|
2182
|
-
try {
|
|
2183
|
-
last = await bridge.send({ type: "evaluate", code: WAIT_READY_PROBE }) ?? last;
|
|
2184
|
-
bridgeError = "";
|
|
2185
|
-
} catch (error) {
|
|
2186
|
-
bridgeError = error instanceof Error ? error.message : String(error);
|
|
2187
|
-
if (bridgeError.startsWith("multiple sims are connected:") || bridgeError.startsWith("saved sim ")) {
|
|
2188
|
-
throw error;
|
|
2189
|
-
}
|
|
1837
|
+
let result;
|
|
1838
|
+
try {
|
|
1839
|
+
result = await bridge.send(
|
|
1840
|
+
{
|
|
1841
|
+
type: "waitFor",
|
|
1842
|
+
waitForOptions: { condition: { type: "ready" }, timeoutMs }
|
|
1843
|
+
},
|
|
1844
|
+
{ timeoutMs: timeoutMs + 1e3 }
|
|
1845
|
+
);
|
|
1846
|
+
} catch (error) {
|
|
1847
|
+
const bridgeError = error instanceof Error ? error.message : String(error);
|
|
1848
|
+
if (bridgeError.startsWith("multiple sims are connected:") || bridgeError.startsWith("saved sim ")) {
|
|
1849
|
+
throw error;
|
|
2190
1850
|
}
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
targets: last.targets,
|
|
2197
|
-
liveFrameActive: last.liveFrameActive,
|
|
2198
|
-
liveFrameChannels: last.liveFrameChannels,
|
|
2199
|
-
liveFramePublishes: last.liveFramePublishes,
|
|
2200
|
-
flag: last.flag,
|
|
2201
|
-
loadingText: last.loadingText,
|
|
2202
|
-
externalReady: last.externalReady,
|
|
2203
|
-
externalStatus: last.externalStatus,
|
|
2204
|
-
externalError: last.externalError,
|
|
2205
|
-
suppressedEntryError: last.suppressedEntryError,
|
|
2206
|
-
errors: last.errors,
|
|
2207
|
-
bridgeError
|
|
1851
|
+
result = {
|
|
1852
|
+
matched: false,
|
|
1853
|
+
elapsedMs: Date.now() - start,
|
|
1854
|
+
polls: 0,
|
|
1855
|
+
error: bridgeError
|
|
2208
1856
|
};
|
|
2209
|
-
if (last.nodes !== lastNodes) {
|
|
2210
|
-
lastNodes = last.nodes;
|
|
2211
|
-
nodeStableSince = now;
|
|
2212
|
-
}
|
|
2213
|
-
const hasReadySignal = last.flag === true && readyProbeHasContent(last);
|
|
2214
|
-
const hasRenderedContentWithoutReadySignal = last.flag !== true && readyProbeHasTargetContent(last);
|
|
2215
|
-
if ((hasReadySignal || hasRenderedContentWithoutReadySignal) && last.externalReady !== false && !last.externalError && !last.loadingText) {
|
|
2216
|
-
if (now - nodeStableSince >= READY_NODE_STABLE_MS) {
|
|
2217
|
-
return {
|
|
2218
|
-
...status2,
|
|
2219
|
-
ready: true
|
|
2220
|
-
};
|
|
2221
|
-
}
|
|
2222
|
-
}
|
|
2223
|
-
if (last.externalError) return status2;
|
|
2224
|
-
if (options.onProgress && progressIntervalMs > 0 && now >= nextProgressAt) {
|
|
2225
|
-
options.onProgress(status2);
|
|
2226
|
-
do {
|
|
2227
|
-
nextProgressAt += progressIntervalMs;
|
|
2228
|
-
} while (now >= nextProgressAt);
|
|
2229
|
-
}
|
|
2230
|
-
await new Promise((r) => setTimeout(r, READY_PROBE_POLL_MS));
|
|
2231
1857
|
}
|
|
1858
|
+
const probe = result.probe;
|
|
2232
1859
|
const status = {
|
|
2233
|
-
ready:
|
|
2234
|
-
elapsedMs:
|
|
2235
|
-
nodes:
|
|
2236
|
-
targets:
|
|
2237
|
-
liveFrameActive:
|
|
2238
|
-
liveFrameChannels:
|
|
2239
|
-
liveFramePublishes:
|
|
2240
|
-
flag:
|
|
2241
|
-
loadingText:
|
|
2242
|
-
externalReady:
|
|
2243
|
-
externalStatus:
|
|
2244
|
-
externalError:
|
|
2245
|
-
suppressedEntryError:
|
|
2246
|
-
errors:
|
|
2247
|
-
bridgeError
|
|
1860
|
+
ready: result.matched,
|
|
1861
|
+
elapsedMs: result.elapsedMs,
|
|
1862
|
+
nodes: probe?.nodes ?? 0,
|
|
1863
|
+
targets: probe?.targets ?? 0,
|
|
1864
|
+
liveFrameActive: probe?.liveFrameActive ?? false,
|
|
1865
|
+
liveFrameChannels: probe?.liveFrameChannels ?? 0,
|
|
1866
|
+
liveFramePublishes: probe?.liveFramePublishes ?? 0,
|
|
1867
|
+
flag: probe?.flag,
|
|
1868
|
+
loadingText: probe?.loadingText ?? "",
|
|
1869
|
+
externalReady: probe?.externalReady ?? null,
|
|
1870
|
+
externalStatus: probe?.externalStatus ?? "",
|
|
1871
|
+
externalError: probe?.externalError ?? "",
|
|
1872
|
+
suppressedEntryError: probe?.suppressedEntryError ?? "",
|
|
1873
|
+
errors: probe?.errors ?? 0,
|
|
1874
|
+
bridgeError: result.error ?? ""
|
|
2248
1875
|
};
|
|
2249
|
-
if (
|
|
2250
|
-
options.onProgress(status);
|
|
2251
|
-
}
|
|
1876
|
+
if (!status.ready && options.onProgress) options.onProgress(status);
|
|
2252
1877
|
return status;
|
|
2253
1878
|
}
|
|
2254
1879
|
function consoleEntriesEval(level, limit) {
|
|
@@ -2371,38 +1996,30 @@ async function getShellState(bridge, readyTimeoutMs = 0) {
|
|
|
2371
1996
|
|
|
2372
1997
|
// cli/commands/inspect/resolve-target.ts
|
|
2373
1998
|
async function resolveTargetCoords(bridge, target) {
|
|
2374
|
-
const arg = JSON.stringify(target.value);
|
|
2375
|
-
const findExpr = target.mode === "testid" ? `(await t.findByTestId(${arg})) || (await t.findById(${arg}))` : `await t.findByText(${arg})`;
|
|
2376
1999
|
const result = await bridge.send({
|
|
2377
|
-
type: "
|
|
2378
|
-
|
|
2379
|
-
const t = window.__sootsimTest
|
|
2380
|
-
if (!t) return null
|
|
2381
|
-
const n = ${findExpr}
|
|
2382
|
-
if (!n || !n.absolutePosition || !n.layout) return null
|
|
2383
|
-
const resolved =
|
|
2384
|
-
typeof n.nodeId === 'number' && typeof t.resolveTapTarget === 'function'
|
|
2385
|
-
? await t.resolveTapTarget(n.nodeId)
|
|
2386
|
-
: null
|
|
2387
|
-
const cx =
|
|
2388
|
-
typeof resolved?.cx === 'number'
|
|
2389
|
-
? resolved.cx
|
|
2390
|
-
: n.absolutePosition.x + (n.layout.width || 0) / 2
|
|
2391
|
-
const cy =
|
|
2392
|
-
typeof resolved?.cy === 'number'
|
|
2393
|
-
? resolved.cy
|
|
2394
|
-
: n.absolutePosition.y + (n.layout.height || 0) / 2
|
|
2395
|
-
return {
|
|
2396
|
-
x: cx,
|
|
2397
|
-
y: cy,
|
|
2398
|
-
id: n.id ?? null,
|
|
2399
|
-
testID: n.testID ?? null,
|
|
2400
|
-
text: ${JSON.stringify(target.mode === "text")} ? ${arg} : (n.text ?? n.accessibilityLabel ?? null),
|
|
2401
|
-
type: n.type ?? null,
|
|
2402
|
-
}
|
|
2403
|
-
})()`
|
|
2000
|
+
type: "resolve",
|
|
2001
|
+
selector: target.mode === "testid" ? { testID: target.value } : { text: target.value }
|
|
2404
2002
|
});
|
|
2405
|
-
|
|
2003
|
+
if (result === null) return null;
|
|
2004
|
+
if (typeof result !== "object") throw new Error("resolve returned an invalid result");
|
|
2005
|
+
const match = Reflect.get(result, "match");
|
|
2006
|
+
const resolvedTarget = Reflect.get(result, "target");
|
|
2007
|
+
const semanticNodes = [match, resolvedTarget];
|
|
2008
|
+
const point = Reflect.get(result, "point");
|
|
2009
|
+
const x = point !== null && typeof point === "object" ? Reflect.get(point, "x") : void 0;
|
|
2010
|
+
const y = point !== null && typeof point === "object" ? Reflect.get(point, "y") : void 0;
|
|
2011
|
+
if (!isSimSemanticTree(semanticNodes) || typeof x !== "number" || !Number.isFinite(x) || typeof y !== "number" || !Number.isFinite(y)) {
|
|
2012
|
+
throw new Error("resolve returned an invalid result");
|
|
2013
|
+
}
|
|
2014
|
+
const semanticMatch = semanticNodes[0];
|
|
2015
|
+
if (!semanticMatch) throw new Error("resolve returned an invalid result");
|
|
2016
|
+
return {
|
|
2017
|
+
x,
|
|
2018
|
+
y,
|
|
2019
|
+
testID: semanticMatch.testID ?? null,
|
|
2020
|
+
text: semanticMatch.text ?? (target.mode === "text" ? target.value : semanticMatch.label),
|
|
2021
|
+
type: semanticMatch.type
|
|
2022
|
+
};
|
|
2406
2023
|
}
|
|
2407
2024
|
|
|
2408
2025
|
// src/app-url.ts
|
|
@@ -2640,9 +2257,12 @@ var SimClient = class {
|
|
|
2640
2257
|
}
|
|
2641
2258
|
async tree(depth = 5) {
|
|
2642
2259
|
const { tree } = await inspectTree(this, depth);
|
|
2643
|
-
return tree;
|
|
2260
|
+
return Array.isArray(tree) ? tree : [];
|
|
2261
|
+
}
|
|
2262
|
+
resolve(selector) {
|
|
2263
|
+
return this.send({ type: "resolve", selector });
|
|
2644
2264
|
}
|
|
2645
|
-
/** compact visible
|
|
2265
|
+
/** compact visible semantic tree, formatted as the CLI prints it. */
|
|
2646
2266
|
describe(opts = {}) {
|
|
2647
2267
|
return inspectDescribe(this, {
|
|
2648
2268
|
describe: true,
|