peachsim 0.1.657 → 0.1.659

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.
Files changed (115) hide show
  1. package/README.md +21 -2
  2. package/cli/app-config.ts +25 -9
  3. package/cli/bridge-flow-runner.ts +10 -23
  4. package/cli/commands/compat.ts +234 -10
  5. package/cli/commands/do-chain.ts +5 -1
  6. package/cli/commands/inspect/actions.ts +33 -76
  7. package/cli/commands/inspect/resolve-target.ts +67 -2
  8. package/cli/commands/inspect.ts +16 -1
  9. package/cli/drivers/playwright-provisioning.ts +95 -0
  10. package/cli/ws-bridge.ts +7 -7
  11. package/dist-lib/agent-daemon-client.cjs +1 -1
  12. package/dist-lib/agent-events.cjs +1 -1
  13. package/dist-lib/agent-identity.cjs +1 -1
  14. package/dist-lib/agent-sessions.cjs +1 -1
  15. package/dist-lib/attached-projects.cjs +1 -1
  16. package/dist-lib/auth/shared-session.cjs +1 -1
  17. package/dist-lib/backend-origin.cjs +1 -1
  18. package/dist-lib/beta.cjs +1 -1
  19. package/dist-lib/beta.mjs +1 -1
  20. package/dist-lib/bridge-constants.cjs +1 -1
  21. package/dist-lib/bridge-contract-input.cjs +9 -1
  22. package/dist-lib/bridge-contract-input.mjs +9 -1
  23. package/dist-lib/bridge-contract.cjs +1 -1
  24. package/dist-lib/bridge-contract.mjs +1 -1
  25. package/dist-lib/capture-contract.cjs +1 -1
  26. package/dist-lib/capture-contract.mjs +1 -1
  27. package/dist-lib/cli-constants.cjs +1 -1
  28. package/dist-lib/cloud-contract.cjs +1 -1
  29. package/dist-lib/cloud-contract.mjs +1 -1
  30. package/dist-lib/cloud-test-args.cjs +1 -1
  31. package/dist-lib/cloud-test-args.mjs +1 -1
  32. package/dist-lib/cloud.cjs +1 -1
  33. package/dist-lib/cloud.mjs +1 -1
  34. package/dist-lib/config.cjs +3 -2
  35. package/dist-lib/detox/index.cjs +2 -2
  36. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  37. package/dist-lib/home-paths.cjs +1 -1
  38. package/dist-lib/host/bridge-host.cjs +78 -35
  39. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  40. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  41. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  42. package/dist-lib/host/replacement-module-handler.cjs +51 -8
  43. package/dist-lib/host/websocket-proxy.cjs +1 -1
  44. package/dist-lib/index.cjs +317 -70
  45. package/dist-lib/jump-to-source-babel.cjs +1 -1
  46. package/dist-lib/jump-to-source-native.cjs +1 -1
  47. package/dist-lib/kotlin-host.cjs +14 -4
  48. package/dist-lib/menu.cjs +1 -1
  49. package/dist-lib/menu.mjs +1 -1
  50. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  51. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  52. package/dist-lib/metro-production-bundle.cjs +1 -1
  53. package/dist-lib/metro-production-bundle.mjs +1 -1
  54. package/dist-lib/metro.cjs +300 -29
  55. package/dist-lib/native-app-identity.cjs +58 -25
  56. package/dist-lib/native-app-identity.mjs +57 -25
  57. package/dist-lib/plist.cjs +64 -0
  58. package/dist-lib/plist.mjs +29 -0
  59. package/dist-lib/profiles.cjs +1 -1
  60. package/dist-lib/public-brand.cjs +1 -1
  61. package/dist-lib/react-native-host-modules.cjs +1 -1
  62. package/dist-lib/react-native-host-modules.mjs +1 -1
  63. package/dist-lib/render-mode.cjs +1 -1
  64. package/dist-lib/scripts/dev-server-scanner.cjs +2 -2
  65. package/dist-lib/sdk.cjs +36 -49
  66. package/dist-lib/sdk.mjs +36 -49
  67. package/dist-lib/skills.cjs +257 -138
  68. package/dist-lib/swift-compile.cjs +1 -1
  69. package/dist-lib/swift-compile.mjs +1 -1
  70. package/dist-lib/swift-package.cjs +1 -1
  71. package/dist-lib/swift-package.mjs +1 -1
  72. package/dist-lib/swift-submit.cjs +1 -1
  73. package/dist-lib/swift-submit.mjs +1 -1
  74. package/dist-lib/swift.cjs +1 -1
  75. package/dist-lib/vite-swift.cjs +1 -1
  76. package/dist-lib/vite.cjs +51 -8
  77. package/kotlin/AppSources/android/app/Activity.kt +44 -0
  78. package/kotlin/AppSources/android/app/UiModeManager.kt +9 -0
  79. package/kotlin/AppSources/android/content/Context.kt +9 -2
  80. package/kotlin/AppSources/android/content/res/Configuration.kt +16 -0
  81. package/kotlin/AppSources/android/content/res/Resources.kt +7 -0
  82. package/kotlin/AppSources/android/graphics/Color.kt +13 -0
  83. package/kotlin/AppSources/android/os/Bundle.kt +9 -3
  84. package/kotlin/AppSources/android/view/View.kt +2 -0
  85. package/kotlin/AppSources/android/view/Window.kt +99 -8
  86. package/kotlin/AppSources/android/view/WindowInsetsController.kt +12 -0
  87. package/kotlin/AppSources/android/view/WindowManager.kt +16 -0
  88. package/kotlin/AppSources/androidx/activity/BackEventCompat.kt +17 -0
  89. package/kotlin/AppSources/androidx/activity/Cancellable.kt +5 -0
  90. package/kotlin/AppSources/androidx/activity/ComponentActivity.kt +22 -3
  91. package/kotlin/AppSources/androidx/activity/EdgeToEdge.kt +71 -0
  92. package/kotlin/AppSources/androidx/activity/OnBackPressedCallback.kt +28 -0
  93. package/kotlin/AppSources/androidx/activity/OnBackPressedDispatcher.kt +144 -0
  94. package/kotlin/AppSources/androidx/activity/OnBackPressedDispatcherOwner.kt +7 -0
  95. package/kotlin/AppSources/androidx/activity/compose/BackHandler.kt +55 -0
  96. package/kotlin/AppSources/androidx/core/view/WindowCompat.kt +32 -0
  97. package/kotlin/AppSources/dev/peach/app/PeachApp.kt +67 -12
  98. package/kotlin/AppSources/dev/peach/app/PeachWindowHost.kt +2 -1
  99. package/kotlin/AppSources/java/lang/Runnable.kt +6 -0
  100. package/kotlin/ComposeSources/PeachComposeHost.kt +11 -1
  101. package/package.json +8 -1
  102. package/skills/peach-setup/SKILL.md +27 -0
  103. package/src/android-theme.ts +167 -0
  104. package/src/android-xml.ts +70 -0
  105. package/src/bridge-contract-input.ts +10 -1
  106. package/src/bridge-contract.ts +7 -1
  107. package/src/config.ts +8 -0
  108. package/src/generated/android-framework-themes.json +6303 -0
  109. package/src/host/replacement-module-handler.ts +33 -0
  110. package/src/index.ts +1 -0
  111. package/src/kotlin-app.ts +86 -61
  112. package/src/kotlin-host.ts +13 -3
  113. package/src/metro-plugin.ts +46 -3
  114. package/src/native-app-identity.ts +75 -26
  115. package/src/sim-client.ts +2 -4
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.657 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.659 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/native-app-identity.ts
4
4
  var NATIVE_APP_IDENTITY_PREFIX = "globalThis.__sootsimNativeAppIdentity=";
@@ -11,6 +11,8 @@ function nonEmptyString(value) {
11
11
  }
12
12
  function isNativeAppIdentity(value) {
13
13
  if (!isRecord(value)) return false;
14
+ const descriptions = value.permissionUsageDescriptions;
15
+ if (descriptions !== void 0 && !isRecord(descriptions)) return false;
14
16
  const platform = value.platform;
15
17
  if (platform !== "ios" && platform !== "android") return false;
16
18
  const applicationName = value.applicationName;
@@ -32,7 +34,10 @@ function readNativeAppIdentity(value) {
32
34
  appVersion: value.appVersion,
33
35
  buildVersion: value.buildVersion,
34
36
  bundleIdentifier: value.bundleIdentifier,
35
- platform: value.platform
37
+ platform: value.platform,
38
+ ...value.permissionUsageDescriptions === void 0 ? {} : {
39
+ permissionUsageDescriptions: { ...value.permissionUsageDescriptions }
40
+ }
36
41
  };
37
42
  }
38
43
  function applyNativeAppIdentity(identity) {
@@ -137,7 +142,7 @@ function readBuildSettingsBlock(source, start) {
137
142
  }
138
143
  function parseBuildSettings(block) {
139
144
  const settings = {};
140
- const matchPattern = /([A-Z0-9_]+) = ("(?:[^"\\]|\\.)*"|[^;{}]*);/g;
145
+ const matchPattern = /([A-Za-z0-9_]+) = ("(?:[^"\\]|\\.)*"|[^;{}]*);/g;
141
146
  let match;
142
147
  while ((match = matchPattern.exec(block)) !== null) {
143
148
  const raw = match[2].trim();
@@ -173,27 +178,29 @@ function appTargetReleaseSettings(pbxproj, targetName, infoPlistRelativePath) {
173
178
  (candidate) => candidate.name === "Release" && named(candidate.settings)
174
179
  )?.settings ?? null;
175
180
  }
176
- function nativeAppIdentityFromIos(files) {
181
+ function nativeAppIdentityFromIos(files, parseInfoPlist) {
182
+ const pbxprojPath = Object.keys(files).find(
183
+ (path) => /(?:^|\/)ios\/[^/]+\.xcodeproj\/project\.pbxproj$/.test(path)
184
+ );
185
+ if (!pbxprojPath) return null;
186
+ const pbxproj = files[pbxprojPath];
187
+ const applicationTargets = new Set(
188
+ [
189
+ ...pbxproj.matchAll(
190
+ /isa = PBXNativeTarget;[^}]*?name = "?([^";]+)"?;[^}]*?productType = "com\.apple\.product-type\.application";/g
191
+ )
192
+ ].map((match) => match[1])
193
+ );
177
194
  const appDelegatePattern = /(?:^|\/)ios\/([^/]+)\/AppDelegate\.(?:swift|mm|m)$/;
178
- let targetName = null;
179
- for (const path of Object.keys(files)) {
180
- if (path.includes("/Pods/")) continue;
181
- const match = path.match(appDelegatePattern);
182
- if (match) {
183
- targetName = match[1];
184
- break;
185
- }
186
- }
195
+ const targetName = Object.keys(files).filter((path) => !path.includes("/Pods/")).map((path) => path.match(appDelegatePattern)?.[1]).find((name) => name !== void 0 && applicationTargets.has(name)) ?? null;
187
196
  if (!targetName) return null;
188
- const plistPath = Object.keys(files).find(
189
- (path) => path === `ios/${targetName}/Info.plist`
190
- );
191
- const plist = plistPath ? files[plistPath] : null;
197
+ const plist = files[`ios/${targetName}/Info.plist`];
192
198
  if (!plist) return null;
193
- const pbxprojPath = Object.keys(files).find(
194
- (path) => /(?:^|\/)ios\/[^/]+\.xcodeproj\/project\.pbxproj$/.test(path)
199
+ const settings = appTargetReleaseSettings(
200
+ pbxproj,
201
+ targetName,
202
+ `${targetName}/Info.plist`
195
203
  );
196
- const settings = pbxprojPath ? appTargetReleaseSettings(files[pbxprojPath], targetName, `${targetName}/Info.plist`) : null;
197
204
  if (!settings) return null;
198
205
  const bundleIdentifier = identityFieldFrom(
199
206
  settings,
@@ -218,22 +225,47 @@ function nativeAppIdentityFromIos(files) {
218
225
  settings.PRODUCT_NAME
219
226
  ) ?? resolvedIdentityField(resolveBuildSetting(settings.PRODUCT_NAME ?? "", settings)) ?? resolvedIdentityField(targetName);
220
227
  if (!applicationName) return null;
228
+ const dictionary = parseInfoPlist(plist);
229
+ if (!isRecord(dictionary)) throw new Error("native app Info.plist is not a dictionary");
230
+ const permissionUsageDescriptions = {};
231
+ for (const [key, value] of Object.entries(dictionary)) {
232
+ if (!/^NS.*UsageDescription$/.test(key)) continue;
233
+ if (typeof value !== "string") {
234
+ permissionUsageDescriptions[key] = value;
235
+ continue;
236
+ }
237
+ const resolved = resolveBuildSetting(value, settings);
238
+ if (resolved === null) throw new Error(`native app Info.plist ${key} is unresolved`);
239
+ permissionUsageDescriptions[key] = resolved;
240
+ }
241
+ if (settings.GENERATE_INFOPLIST_FILE === "YES") {
242
+ for (const [setting, value] of Object.entries(settings)) {
243
+ if (!/^INFOPLIST_KEY_NS.*UsageDescription$/.test(setting)) continue;
244
+ const key = setting.slice("INFOPLIST_KEY_".length);
245
+ const resolved = resolveBuildSetting(value, settings);
246
+ if (resolved === null)
247
+ throw new Error(`native app build setting ${setting} is unresolved`);
248
+ permissionUsageDescriptions[key] = resolved;
249
+ }
250
+ }
221
251
  return {
222
252
  applicationName,
223
253
  appVersion,
224
254
  buildVersion,
225
255
  bundleIdentifier,
226
- platform: "ios"
256
+ platform: "ios",
257
+ permissionUsageDescriptions
227
258
  };
228
259
  }
229
- function nativeAppIdentityFromAndroid() {
230
- return null;
260
+ function canExtractNativeAppIdentity(platform) {
261
+ return platform === "ios";
231
262
  }
232
- function extractNativeAppIdentityFromFiles(files, platform) {
233
- return platform === "android" ? nativeAppIdentityFromAndroid() : nativeAppIdentityFromIos(files);
263
+ function extractNativeAppIdentityFromFiles(files, platform, parseInfoPlist) {
264
+ return canExtractNativeAppIdentity(platform) ? nativeAppIdentityFromIos(files, parseInfoPlist) : null;
234
265
  }
235
266
  export {
236
267
  applyNativeAppIdentity,
268
+ canExtractNativeAppIdentity,
237
269
  createNativeAppIdentityFooter,
238
270
  detachNativeAppIdentityFooter,
239
271
  extractNativeAppIdentityFromFiles,
@@ -0,0 +1,64 @@
1
+ /*! rnx v0.1.659 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
+ let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
+ "use strict";
4
+ var __create = Object.create;
5
+ var __defProp = Object.defineProperty;
6
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
7
+ var __getOwnPropNames = Object.getOwnPropertyNames;
8
+ var __getProtoOf = Object.getPrototypeOf;
9
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
10
+ var __export = (target, all) => {
11
+ for (var name in all)
12
+ __defProp(target, name, { get: all[name], enumerable: true });
13
+ };
14
+ var __copyProps = (to, from, except, desc) => {
15
+ if (from && typeof from === "object" || typeof from === "function") {
16
+ for (let key of __getOwnPropNames(from))
17
+ if (!__hasOwnProp.call(to, key) && key !== except)
18
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
19
+ }
20
+ return to;
21
+ };
22
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
23
+ // If the importer is in node compatibility mode or this is not an ESM
24
+ // file that has been converted to a CommonJS file using a Babel-
25
+ // compatible transform (i.e. "__esModule" has not been set), then set
26
+ // "default" to the CommonJS "module.exports" for node compatibility.
27
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
28
+ mod
29
+ ));
30
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
31
+
32
+ // src/plist.ts
33
+ var plist_exports = {};
34
+ __export(plist_exports, {
35
+ parsePlistSource: () => parsePlistSource
36
+ });
37
+ module.exports = __toCommonJS(plist_exports);
38
+ var import_plist = __toESM(require("@expo/plist"), 1);
39
+
40
+ // src/is-record.ts
41
+ function isRecord(value) {
42
+ return typeof value === "object" && value !== null && !Array.isArray(value);
43
+ }
44
+
45
+ // src/plist.ts
46
+ function resolvePlistParser(value) {
47
+ if (!isRecord(value)) return null;
48
+ const direct = value.parse;
49
+ if (typeof direct === "function") {
50
+ return (source) => Reflect.apply(direct, value, [source]);
51
+ }
52
+ const nested = value.default;
53
+ if (!isRecord(nested)) return null;
54
+ const nestedParse = nested.parse;
55
+ return typeof nestedParse === "function" ? (source) => Reflect.apply(nestedParse, nested, [source]) : null;
56
+ }
57
+ var parsePlist = resolvePlistParser(import_plist.default);
58
+ function parsePlistSource(source) {
59
+ return parsePlist?.(source) ?? null;
60
+ }
61
+ // Annotate the CommonJS export names for ESM import in node:
62
+ 0 && (module.exports = {
63
+ parsePlistSource
64
+ });
@@ -0,0 +1,29 @@
1
+ /*! rnx v0.1.659 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
+
3
+ // src/plist.ts
4
+ import plistModule from "@expo/plist";
5
+
6
+ // src/is-record.ts
7
+ function isRecord(value) {
8
+ return typeof value === "object" && value !== null && !Array.isArray(value);
9
+ }
10
+
11
+ // src/plist.ts
12
+ function resolvePlistParser(value) {
13
+ if (!isRecord(value)) return null;
14
+ const direct = value.parse;
15
+ if (typeof direct === "function") {
16
+ return (source) => Reflect.apply(direct, value, [source]);
17
+ }
18
+ const nested = value.default;
19
+ if (!isRecord(nested)) return null;
20
+ const nestedParse = nested.parse;
21
+ return typeof nestedParse === "function" ? (source) => Reflect.apply(nestedParse, nested, [source]) : null;
22
+ }
23
+ var parsePlist = resolvePlistParser(plistModule);
24
+ function parsePlistSource(source) {
25
+ return parsePlist?.(source) ?? null;
26
+ }
27
+ export {
28
+ parsePlistSource
29
+ };
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.657 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.659 | (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;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.657 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.659 | (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 __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.657 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.659 | (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 __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.657 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.659 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/react-native-host-modules.ts
4
4
  var REACT_NATIVE_PASSTHROUGH_SPECIFIERS = [
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.657 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.659 | (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 __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.657 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.659 | (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;
@@ -228,7 +228,7 @@ function hasOwnKeys(value) {
228
228
  }
229
229
  function hasPeachConfig(config) {
230
230
  if (!config) return false;
231
- return typeof config.runtimeVersion === "string" && config.runtimeVersion.length > 0 || hasOwnKeys(config.modules) || hasOwnKeys(config.turboModules) || hasOwnKeys(config.nativeModules) || hasOwnKeys(config.env) || hasOwnKeys(config.network?.remap) || hasOwnKeys(config.settings) || hasOwnKeys(config.initialState);
231
+ return typeof config.runtimeVersion === "string" && config.runtimeVersion.length > 0 || hasOwnKeys(config.modules) || hasOwnKeys(config.turboModules) || hasOwnKeys(config.nativeModules) || hasOwnKeys(config.nativeViews) || hasOwnKeys(config.env) || hasOwnKeys(config.network?.remap) || hasOwnKeys(config.settings) || hasOwnKeys(config.initialState);
232
232
  }
233
233
  function applyPeachConfigToUrl(url, config) {
234
234
  const parsed = new URL(url);
package/dist-lib/sdk.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.657 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.659 | (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 __defProp = Object.defineProperty;
@@ -1452,10 +1452,20 @@ function formatTimelineEvents(events) {
1452
1452
  }
1453
1453
 
1454
1454
  // cli/commands/inspect/resolve-target.ts
1455
+ function isScreenSize(value) {
1456
+ if (value === null || typeof value !== "object") return false;
1457
+ const width = Reflect.get(value, "width");
1458
+ const height = Reflect.get(value, "height");
1459
+ return typeof width === "number" && Number.isFinite(width) && width > 0 && typeof height === "number" && Number.isFinite(height) && height > 0;
1460
+ }
1461
+ function isOffscreen(x, y, screen) {
1462
+ return !(x >= 0 && y >= 0 && x <= screen.width && y <= screen.height);
1463
+ }
1464
+ var UNREPORTED_SCREEN_ERROR = "this runtime does not report the screen size a targeted gesture needs; update it with `peach runtime install`, or use coordinates";
1455
1465
  async function resolveTargetCoords(bridge, target) {
1456
1466
  const result = await bridge.send({
1457
1467
  type: "resolve",
1458
- selector: target.mode === "testid" ? { testID: target.value } : { text: target.value }
1468
+ selector: target.mode === "node" ? { nodeId: target.nodeId } : target.mode === "testid" ? { testID: target.value } : { text: target.value }
1459
1469
  });
1460
1470
  if (result === null) return null;
1461
1471
  if (typeof result !== "object") throw new Error("resolve returned an invalid result");
@@ -1463,6 +1473,9 @@ async function resolveTargetCoords(bridge, target) {
1463
1473
  const resolvedTarget = Reflect.get(result, "target");
1464
1474
  const semanticNodes = [match, resolvedTarget];
1465
1475
  const point = Reflect.get(result, "point");
1476
+ const viewport = Reflect.get(result, "viewport");
1477
+ const width = viewport !== null && typeof viewport === "object" ? Reflect.get(viewport, "width") : void 0;
1478
+ const height = viewport !== null && typeof viewport === "object" ? Reflect.get(viewport, "height") : void 0;
1466
1479
  const x = point !== null && typeof point === "object" ? Reflect.get(point, "x") : void 0;
1467
1480
  const y = point !== null && typeof point === "object" ? Reflect.get(point, "y") : void 0;
1468
1481
  if (!isSimSemanticTree(semanticNodes) || typeof x !== "number" || !Number.isFinite(x) || typeof y !== "number" || !Number.isFinite(y)) {
@@ -1476,7 +1489,8 @@ async function resolveTargetCoords(bridge, target) {
1476
1489
  nodeId: semanticMatch.nodeId,
1477
1490
  testID: semanticMatch.testID ?? null,
1478
1491
  text: semanticMatch.text ?? (target.mode === "text" ? target.value : semanticMatch.label),
1479
- type: semanticMatch.type
1492
+ type: semanticMatch.type,
1493
+ ...typeof width === "number" && typeof height === "number" ? { viewport: { width, height } } : {}
1480
1494
  };
1481
1495
  }
1482
1496
 
@@ -1671,30 +1685,6 @@ async function waitForSootsimIdle({
1671
1685
  }
1672
1686
 
1673
1687
  // cli/commands/inspect/actions.ts
1674
- function isScreenSize(value) {
1675
- if (value === null || typeof value !== "object") return false;
1676
- const width = Reflect.get(value, "width");
1677
- const height = Reflect.get(value, "height");
1678
- return typeof width === "number" && Number.isFinite(width) && width > 0 && typeof height === "number" && Number.isFinite(height) && height > 0;
1679
- }
1680
- function viewportFromCapture(value) {
1681
- if (value === null || typeof value !== "object") return void 0;
1682
- const tree = Reflect.get(value, "tree");
1683
- return tree !== null && typeof tree === "object" ? Reflect.get(tree, "viewport") : void 0;
1684
- }
1685
- async function readScreen(bridge) {
1686
- try {
1687
- const viewport = viewportFromCapture(
1688
- await bridge.send({ type: "capture", includeVisual: false })
1689
- );
1690
- return isScreenSize(viewport) ? { width: viewport.width, height: viewport.height } : null;
1691
- } catch {
1692
- return null;
1693
- }
1694
- }
1695
- function isOffscreen(cx, cy, screen) {
1696
- return !(cx >= 0 && cy >= 0 && cx <= screen.width && cy <= screen.height);
1697
- }
1698
1688
  var DEFAULT_AGENT_TIMING = {
1699
1689
  initialWaitMs: 3e3,
1700
1690
  deadlineMs: 5e3,
@@ -1756,17 +1746,24 @@ async function tapResolvedTarget(bridge, args) {
1756
1746
  }
1757
1747
  const deadline = Date.now() + timing.deadlineMs;
1758
1748
  let lastOffscreenAt = null;
1759
- const screen = bridge.plane === "cloud" ? null : await readScreen(bridge);
1760
1749
  while (Date.now() <= deadline || attempts === 0) {
1761
1750
  attempts++;
1762
- const resolved = await args.resolve();
1763
- const payload = resolved && typeof resolved.cx === "number" && typeof resolved.cy === "number" && screen && isOffscreen(resolved.cx, resolved.cy, screen) ? { ...resolved, offscreen: true, screen } : resolved;
1751
+ const { screen, ...resolved } = await args.resolve() ?? {};
1752
+ const checksScreen = bridge.plane !== "cloud" && typeof resolved.cx === "number";
1753
+ if (checksScreen && screen === void 0) {
1754
+ throw new Error(UNREPORTED_SCREEN_ERROR);
1755
+ }
1756
+ const unsized = checksScreen && !isScreenSize(screen);
1757
+ const payload = checksScreen && typeof resolved.cx === "number" && typeof resolved.cy === "number" && isScreenSize(screen) && isOffscreen(resolved.cx, resolved.cy, screen) ? { ...resolved, offscreen: true, screen } : resolved;
1764
1758
  lastPayload = payload;
1765
1759
  if (payload?.error === "bridge-not-ready" || payload?.ambiguous || payload?.nthOutOfRange) {
1766
1760
  return { payload, result: null, attempts, failure: "special" };
1767
1761
  }
1768
1762
  if (payload && typeof payload.cx === "number" && typeof payload.cy === "number") {
1769
- if (payload.offscreen) {
1763
+ if (unsized) {
1764
+ lastResult = { hit: false, reason: "screen-not-laid-out" };
1765
+ lastOffscreenAt = null;
1766
+ } else if (payload.offscreen) {
1770
1767
  lastResult = {
1771
1768
  hit: false,
1772
1769
  reason: "offscreen",
@@ -1839,6 +1836,7 @@ function payloadFromResolved(resolved, match, extra = {}) {
1839
1836
  return {
1840
1837
  cx: resolved.x,
1841
1838
  cy: resolved.y,
1839
+ screen: resolved.viewport,
1842
1840
  match: {
1843
1841
  nodeId: match.nodeId ?? null,
1844
1842
  id: match.testID ?? null,
@@ -1972,24 +1970,12 @@ async function resolveTextTapTarget(bridge, query, options = {}) {
1972
1970
  const picked = candidates[idx];
1973
1971
  if (!picked) return { matched: 0, total };
1974
1972
  const node = picked.node;
1975
- const tap = await bridge.send({
1976
- type: "call",
1977
- path: "SootSim.bridges.test.resolveTapTarget",
1978
- args: [node.nodeId]
1973
+ const resolved = await resolveTargetCoords(bridge, {
1974
+ mode: "node",
1975
+ nodeId: node.nodeId
1979
1976
  });
1980
- if (!isTapTargetInfo(tap)) return { matched: 0, total };
1981
- return payloadFromResolved(
1982
- {
1983
- x: tap.cx,
1984
- y: tap.cy,
1985
- nodeId: tap.target.nodeId,
1986
- testID: tap.target.testID,
1987
- text: tap.target.text ?? tap.target.accessibilityLabel,
1988
- type: tap.target.type
1989
- },
1990
- node,
1991
- { total, idx }
1992
- );
1977
+ if (!resolved) return { matched: 0, total };
1978
+ return payloadFromResolved(resolved, node, { total, idx });
1993
1979
  }
1994
1980
  var TAP_QUERY_FINDERS = {
1995
1981
  label: "findByLabel",
@@ -2021,7 +2007,8 @@ async function tapByQuery(bridge, query, opts = {}) {
2021
2007
  nodeId: tap.target.nodeId,
2022
2008
  testID: tap.target.testID,
2023
2009
  text: tap.target.text ?? tap.target.accessibilityLabel,
2024
- type: tap.target.type
2010
+ type: tap.target.type,
2011
+ viewport: tap.viewport
2025
2012
  },
2026
2013
  {
2027
2014
  nodeId: node.nodeId,
package/dist-lib/sdk.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.657 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.659 | (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);
@@ -1352,10 +1352,20 @@ function formatTimelineEvents(events) {
1352
1352
  }
1353
1353
 
1354
1354
  // cli/commands/inspect/resolve-target.ts
1355
+ function isScreenSize(value) {
1356
+ if (value === null || typeof value !== "object") return false;
1357
+ const width = Reflect.get(value, "width");
1358
+ const height = Reflect.get(value, "height");
1359
+ return typeof width === "number" && Number.isFinite(width) && width > 0 && typeof height === "number" && Number.isFinite(height) && height > 0;
1360
+ }
1361
+ function isOffscreen(x, y, screen) {
1362
+ return !(x >= 0 && y >= 0 && x <= screen.width && y <= screen.height);
1363
+ }
1364
+ var UNREPORTED_SCREEN_ERROR = "this runtime does not report the screen size a targeted gesture needs; update it with `peach runtime install`, or use coordinates";
1355
1365
  async function resolveTargetCoords(bridge, target) {
1356
1366
  const result = await bridge.send({
1357
1367
  type: "resolve",
1358
- selector: target.mode === "testid" ? { testID: target.value } : { text: target.value }
1368
+ selector: target.mode === "node" ? { nodeId: target.nodeId } : target.mode === "testid" ? { testID: target.value } : { text: target.value }
1359
1369
  });
1360
1370
  if (result === null) return null;
1361
1371
  if (typeof result !== "object") throw new Error("resolve returned an invalid result");
@@ -1363,6 +1373,9 @@ async function resolveTargetCoords(bridge, target) {
1363
1373
  const resolvedTarget = Reflect.get(result, "target");
1364
1374
  const semanticNodes = [match, resolvedTarget];
1365
1375
  const point = Reflect.get(result, "point");
1376
+ const viewport = Reflect.get(result, "viewport");
1377
+ const width = viewport !== null && typeof viewport === "object" ? Reflect.get(viewport, "width") : void 0;
1378
+ const height = viewport !== null && typeof viewport === "object" ? Reflect.get(viewport, "height") : void 0;
1366
1379
  const x = point !== null && typeof point === "object" ? Reflect.get(point, "x") : void 0;
1367
1380
  const y = point !== null && typeof point === "object" ? Reflect.get(point, "y") : void 0;
1368
1381
  if (!isSimSemanticTree(semanticNodes) || typeof x !== "number" || !Number.isFinite(x) || typeof y !== "number" || !Number.isFinite(y)) {
@@ -1376,7 +1389,8 @@ async function resolveTargetCoords(bridge, target) {
1376
1389
  nodeId: semanticMatch.nodeId,
1377
1390
  testID: semanticMatch.testID ?? null,
1378
1391
  text: semanticMatch.text ?? (target.mode === "text" ? target.value : semanticMatch.label),
1379
- type: semanticMatch.type
1392
+ type: semanticMatch.type,
1393
+ ...typeof width === "number" && typeof height === "number" ? { viewport: { width, height } } : {}
1380
1394
  };
1381
1395
  }
1382
1396
 
@@ -1571,30 +1585,6 @@ async function waitForSootsimIdle({
1571
1585
  }
1572
1586
 
1573
1587
  // cli/commands/inspect/actions.ts
1574
- function isScreenSize(value) {
1575
- if (value === null || typeof value !== "object") return false;
1576
- const width = Reflect.get(value, "width");
1577
- const height = Reflect.get(value, "height");
1578
- return typeof width === "number" && Number.isFinite(width) && width > 0 && typeof height === "number" && Number.isFinite(height) && height > 0;
1579
- }
1580
- function viewportFromCapture(value) {
1581
- if (value === null || typeof value !== "object") return void 0;
1582
- const tree = Reflect.get(value, "tree");
1583
- return tree !== null && typeof tree === "object" ? Reflect.get(tree, "viewport") : void 0;
1584
- }
1585
- async function readScreen(bridge) {
1586
- try {
1587
- const viewport = viewportFromCapture(
1588
- await bridge.send({ type: "capture", includeVisual: false })
1589
- );
1590
- return isScreenSize(viewport) ? { width: viewport.width, height: viewport.height } : null;
1591
- } catch {
1592
- return null;
1593
- }
1594
- }
1595
- function isOffscreen(cx, cy, screen) {
1596
- return !(cx >= 0 && cy >= 0 && cx <= screen.width && cy <= screen.height);
1597
- }
1598
1588
  var DEFAULT_AGENT_TIMING = {
1599
1589
  initialWaitMs: 3e3,
1600
1590
  deadlineMs: 5e3,
@@ -1656,17 +1646,24 @@ async function tapResolvedTarget(bridge, args) {
1656
1646
  }
1657
1647
  const deadline = Date.now() + timing.deadlineMs;
1658
1648
  let lastOffscreenAt = null;
1659
- const screen = bridge.plane === "cloud" ? null : await readScreen(bridge);
1660
1649
  while (Date.now() <= deadline || attempts === 0) {
1661
1650
  attempts++;
1662
- const resolved = await args.resolve();
1663
- const payload = resolved && typeof resolved.cx === "number" && typeof resolved.cy === "number" && screen && isOffscreen(resolved.cx, resolved.cy, screen) ? { ...resolved, offscreen: true, screen } : resolved;
1651
+ const { screen, ...resolved } = await args.resolve() ?? {};
1652
+ const checksScreen = bridge.plane !== "cloud" && typeof resolved.cx === "number";
1653
+ if (checksScreen && screen === void 0) {
1654
+ throw new Error(UNREPORTED_SCREEN_ERROR);
1655
+ }
1656
+ const unsized = checksScreen && !isScreenSize(screen);
1657
+ const payload = checksScreen && typeof resolved.cx === "number" && typeof resolved.cy === "number" && isScreenSize(screen) && isOffscreen(resolved.cx, resolved.cy, screen) ? { ...resolved, offscreen: true, screen } : resolved;
1664
1658
  lastPayload = payload;
1665
1659
  if (payload?.error === "bridge-not-ready" || payload?.ambiguous || payload?.nthOutOfRange) {
1666
1660
  return { payload, result: null, attempts, failure: "special" };
1667
1661
  }
1668
1662
  if (payload && typeof payload.cx === "number" && typeof payload.cy === "number") {
1669
- if (payload.offscreen) {
1663
+ if (unsized) {
1664
+ lastResult = { hit: false, reason: "screen-not-laid-out" };
1665
+ lastOffscreenAt = null;
1666
+ } else if (payload.offscreen) {
1670
1667
  lastResult = {
1671
1668
  hit: false,
1672
1669
  reason: "offscreen",
@@ -1739,6 +1736,7 @@ function payloadFromResolved(resolved, match, extra = {}) {
1739
1736
  return {
1740
1737
  cx: resolved.x,
1741
1738
  cy: resolved.y,
1739
+ screen: resolved.viewport,
1742
1740
  match: {
1743
1741
  nodeId: match.nodeId ?? null,
1744
1742
  id: match.testID ?? null,
@@ -1872,24 +1870,12 @@ async function resolveTextTapTarget(bridge, query, options = {}) {
1872
1870
  const picked = candidates[idx];
1873
1871
  if (!picked) return { matched: 0, total };
1874
1872
  const node = picked.node;
1875
- const tap = await bridge.send({
1876
- type: "call",
1877
- path: "SootSim.bridges.test.resolveTapTarget",
1878
- args: [node.nodeId]
1873
+ const resolved = await resolveTargetCoords(bridge, {
1874
+ mode: "node",
1875
+ nodeId: node.nodeId
1879
1876
  });
1880
- if (!isTapTargetInfo(tap)) return { matched: 0, total };
1881
- return payloadFromResolved(
1882
- {
1883
- x: tap.cx,
1884
- y: tap.cy,
1885
- nodeId: tap.target.nodeId,
1886
- testID: tap.target.testID,
1887
- text: tap.target.text ?? tap.target.accessibilityLabel,
1888
- type: tap.target.type
1889
- },
1890
- node,
1891
- { total, idx }
1892
- );
1877
+ if (!resolved) return { matched: 0, total };
1878
+ return payloadFromResolved(resolved, node, { total, idx });
1893
1879
  }
1894
1880
  var TAP_QUERY_FINDERS = {
1895
1881
  label: "findByLabel",
@@ -1921,7 +1907,8 @@ async function tapByQuery(bridge, query, opts = {}) {
1921
1907
  nodeId: tap.target.nodeId,
1922
1908
  testID: tap.target.testID,
1923
1909
  text: tap.target.text ?? tap.target.accessibilityLabel,
1924
- type: tap.target.type
1910
+ type: tap.target.type,
1911
+ viewport: tap.viewport
1925
1912
  },
1926
1913
  {
1927
1914
  nodeId: node.nodeId,