peachsim 0.1.627 → 0.1.628

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 (92) hide show
  1. package/LICENSE +1 -1
  2. package/cli/bin.ts +6 -2
  3. package/cli/bridge-flow-runner.ts +79 -72
  4. package/cli/commands/cleanup.ts +6 -6
  5. package/cli/commands/control.ts +4 -5
  6. package/cli/commands/detox.ts +25 -11
  7. package/cli/commands/flow.ts +3 -2
  8. package/cli/commands/inspect.ts +22 -37
  9. package/cli/commands/mode.ts +11 -7
  10. package/cli/commands/report-issue.ts +1 -1
  11. package/cli/commands/setup.ts +16 -11
  12. package/cli/commands/show-touches-mode.ts +33 -0
  13. package/cli/commands/skills.ts +16 -0
  14. package/cli/commands/test.ts +8 -1
  15. package/cli/commands/welcome.ts +3 -3
  16. package/cli/drivers/playwright-sim-host.ts +6 -5
  17. package/cli/help-core.ts +9 -4
  18. package/cli/internal-child.ts +14 -2
  19. package/cli/levenshtein.ts +27 -0
  20. package/cli/main.ts +28 -18
  21. package/cli/ws-bridge.ts +3 -2
  22. package/detox/index.ts +234 -7
  23. package/dist-lib/agent-daemon-client.cjs +1 -1
  24. package/dist-lib/agent-events.cjs +1 -1
  25. package/dist-lib/agent-identity.cjs +1 -1
  26. package/dist-lib/agent-sessions.cjs +1 -1
  27. package/dist-lib/attached-projects.cjs +1 -1
  28. package/dist-lib/auth/shared-session.cjs +1 -1
  29. package/dist-lib/backend-origin.cjs +1 -1
  30. package/dist-lib/beta.cjs +1 -1
  31. package/dist-lib/beta.mjs +1 -1
  32. package/dist-lib/bridge-constants.cjs +1 -1
  33. package/dist-lib/bridge-contract-input.cjs +1 -1
  34. package/dist-lib/bridge-contract-input.mjs +1 -1
  35. package/dist-lib/bridge-contract.cjs +1 -1
  36. package/dist-lib/bridge-contract.mjs +1 -1
  37. package/dist-lib/capture-contract.cjs +1 -1
  38. package/dist-lib/capture-contract.mjs +1 -1
  39. package/dist-lib/cli-constants.cjs +1 -1
  40. package/dist-lib/cloud-contract.cjs +1 -1
  41. package/dist-lib/cloud-contract.mjs +1 -1
  42. package/dist-lib/cloud-test-args.cjs +1 -1
  43. package/dist-lib/cloud-test-args.mjs +1 -1
  44. package/dist-lib/cloud.cjs +1 -1
  45. package/dist-lib/cloud.mjs +1 -1
  46. package/dist-lib/config.cjs +1 -1
  47. package/dist-lib/detox/index.cjs +170 -13
  48. package/dist-lib/dev-bundle-resolution.cjs +2 -2
  49. package/dist-lib/home-paths.cjs +1 -1
  50. package/dist-lib/host/bridge-host.cjs +42 -37
  51. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  52. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  53. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  54. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  55. package/dist-lib/host/websocket-proxy.cjs +1 -1
  56. package/dist-lib/index.cjs +44 -5
  57. package/dist-lib/jump-to-source-babel.cjs +1 -1
  58. package/dist-lib/jump-to-source-native.cjs +1 -1
  59. package/dist-lib/menu.cjs +7 -1
  60. package/dist-lib/menu.mjs +7 -1
  61. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  62. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  63. package/dist-lib/metro-production-bundle.cjs +1 -1
  64. package/dist-lib/metro-production-bundle.mjs +1 -1
  65. package/dist-lib/metro.cjs +43 -4
  66. package/dist-lib/profiles.cjs +1 -1
  67. package/dist-lib/public-brand.cjs +1 -1
  68. package/dist-lib/react-native-host-modules.cjs +1 -1
  69. package/dist-lib/react-native-host-modules.mjs +1 -1
  70. package/dist-lib/render-mode.cjs +1 -1
  71. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  72. package/dist-lib/sdk.cjs +1 -1
  73. package/dist-lib/sdk.mjs +1 -1
  74. package/dist-lib/skills.cjs +140 -149
  75. package/dist-lib/swift-compile.cjs +1 -1
  76. package/dist-lib/swift-compile.mjs +1 -1
  77. package/dist-lib/swift-package.cjs +1 -1
  78. package/dist-lib/swift-package.mjs +1 -1
  79. package/dist-lib/swift-submit.cjs +1 -1
  80. package/dist-lib/swift-submit.mjs +1 -1
  81. package/dist-lib/swift.cjs +7234 -7069
  82. package/dist-lib/vite-kotlin.cjs +1 -1
  83. package/dist-lib/vite-swift.cjs +1 -1
  84. package/dist-lib/vite.cjs +43 -4
  85. package/package.json +1 -1
  86. package/skills/contrast/SKILL.md +1 -1
  87. package/src/dev-bundle-resolution.ts +1 -1
  88. package/src/host/bridge-host.ts +2 -34
  89. package/src/menu.ts +7 -0
  90. package/src/native-seam-manifest.ts +4 -16
  91. package/src/runtime-assets.ts +59 -2
  92. package/src/vite-plugin.ts +723 -25
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.627 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (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.627 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (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.627 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (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.627 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (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;
package/dist-lib/beta.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.627 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (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;
package/dist-lib/beta.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.627 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/beta.ts
4
4
  var IS_BETA = true;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.627 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (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.627 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (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.627 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/bridge-contract.ts
4
4
  var SIM_LONG_PRESS_MAX_MS = 5e3;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.627 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (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.627 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/bridge-contract.ts
4
4
  var REFUSED_PERFORM_STEP_TYPES = [
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.627 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (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.627 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/capture-contract.ts
4
4
  var RNX_SCREEN_CAPTURE_VERSION = 1;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.627 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (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.627 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (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.627 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/cloud-contract.ts
4
4
  var RNX_CLOUD_MAX_ARTIFACT_BYTES = 20 * 1024 * 1024;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.627 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (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.627 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/cloud-test-args.ts
4
4
  var CLOUD_TEST_ARGS_MAX_COUNT = 32;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.627 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (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.627 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (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);
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.627 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (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.627 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (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;
@@ -776,12 +776,22 @@ function resolveBrowserCompositeCdpClip(request, metrics) {
776
776
  }
777
777
  const devicePixelRatio = physicalWidth / cssWidth;
778
778
  const padding = 1;
779
+ const { rect } = request;
780
+ const left = Math.floor((rect.x - padding) * devicePixelRatio) / devicePixelRatio;
781
+ const top = Math.floor((rect.y - padding) * devicePixelRatio) / devicePixelRatio;
782
+ const right = Math.ceil((rect.x + rect.width + padding) * devicePixelRatio) / devicePixelRatio;
783
+ const bottom = Math.ceil((rect.y + rect.height + padding) * devicePixelRatio) / devicePixelRatio;
784
+ const outputScale = request.outputWidth / rect.width;
779
785
  return {
780
- x: request.rect.x - padding,
781
- y: request.rect.y - padding,
782
- width: request.rect.width + padding * 2,
783
- height: request.rect.height + padding * 2,
784
- scale: request.outputWidth / request.rect.width / devicePixelRatio
786
+ clip: {
787
+ x: left,
788
+ y: top,
789
+ width: right - left,
790
+ height: bottom - top,
791
+ scale: outputScale / devicePixelRatio
792
+ },
793
+ originX: Math.round((rect.x - left) * outputScale),
794
+ originY: Math.round((rect.y - top) * outputScale)
785
795
  };
786
796
  }
787
797
  function readBrowserCompositeCaptureRequest(value) {
@@ -2194,6 +2204,41 @@ function reapOwnedBrowser() {
2194
2204
  } catch {
2195
2205
  }
2196
2206
  }
2207
+ var CONTEXT_MEDIA_QUIESCE_TIMEOUT_MS = 2500;
2208
+ async function quiesceContextMedia(context) {
2209
+ const pages = context.pages();
2210
+ if (pages.length === 0) return;
2211
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
2212
+ await Promise.race([
2213
+ Promise.all(
2214
+ pages.map(
2215
+ (page) => page.evaluate(() => {
2216
+ const doc = globalThis.document;
2217
+ if (!doc) return;
2218
+ if (doc.pictureInPictureElement && typeof doc.exitPictureInPicture === "function") {
2219
+ void doc.exitPictureInPicture().catch(() => {
2220
+ });
2221
+ }
2222
+ doc.querySelectorAll("video").forEach((v) => {
2223
+ try {
2224
+ if (v.srcObject && typeof v.srcObject.getTracks === "function") {
2225
+ ;
2226
+ v.srcObject.getTracks().forEach((t) => t.stop());
2227
+ v.srcObject = null;
2228
+ }
2229
+ v.pause();
2230
+ v.remove();
2231
+ } catch {
2232
+ }
2233
+ });
2234
+ }).catch(() => {
2235
+ })
2236
+ )
2237
+ ),
2238
+ sleep(CONTEXT_MEDIA_QUIESCE_TIMEOUT_MS)
2239
+ ]).catch(() => {
2240
+ });
2241
+ }
2197
2242
  async function closeContext() {
2198
2243
  if (_browserCompromised) {
2199
2244
  throw new Error(`sootsim browser is compromised: ${_browserCompromised}`);
@@ -2202,6 +2247,7 @@ async function closeContext() {
2202
2247
  const context = _context;
2203
2248
  const closingContextId = contextIdentity;
2204
2249
  try {
2250
+ await quiesceContextMedia(context);
2205
2251
  await withLifecycleTimeout("context.close()", closingContextId, () => context.close());
2206
2252
  _context = null;
2207
2253
  _page = null;
@@ -2456,7 +2502,9 @@ async function focusElementForTextEntry(el) {
2456
2502
  if (await isFocusedTextInputNode(page, node)) return;
2457
2503
  await el.tap();
2458
2504
  await pollUntil(
2459
- () => page.evaluate(async () => (await window.__sootsimTest.getFocusedNode?.())?.nodeId ?? null),
2505
+ () => page.evaluate(
2506
+ async () => (await window.__sootsimTest.getFocusedNode?.())?.nodeId ?? null
2507
+ ),
2460
2508
  (focusedNodeId) => focusedNodeId === node.nodeId,
2461
2509
  {
2462
2510
  timeoutMs: 5e3,
@@ -3072,7 +3120,13 @@ function createSootSimElement(matcher) {
3072
3120
  identifier: node.testID || node.id || "",
3073
3121
  visible: node.layout.width > 0 && node.layout.height > 0,
3074
3122
  enabled: !node.accessibilityState?.disabled,
3075
- ...node
3123
+ ...node,
3124
+ frame: node.absolutePosition ? {
3125
+ x: node.absolutePosition.x,
3126
+ y: node.absolutePosition.y,
3127
+ width: node.absolutePosition.width ?? node.frame?.width ?? 0,
3128
+ height: node.absolutePosition.height ?? node.frame?.height ?? 0
3129
+ } : node.frame
3076
3130
  };
3077
3131
  },
3078
3132
  atIndex(index) {
@@ -3084,6 +3138,72 @@ function createSootSimElement(matcher) {
3084
3138
  function element(matcher) {
3085
3139
  return createSootSimElement(matcher);
3086
3140
  }
3141
+ function detoxNotificationToApnsPayload(params) {
3142
+ const trigger = objectOrEmpty(objectOrEmpty(params).trigger);
3143
+ const supported = ["push", "calendar", "location", "timeInterval"];
3144
+ if (typeof trigger.type !== "string" || !supported.includes(trigger.type)) {
3145
+ throw new Error(
3146
+ `Missing trigger or invalid trigger type. A 'trigger' key must exist, with one of the following types: '${supported.join("', '")}'`
3147
+ );
3148
+ }
3149
+ if (trigger.type !== "push") {
3150
+ throw new Error(
3151
+ `sootsim device.sendUserNotification: trigger type '${trigger.type}' is not simulated, only 'push'`
3152
+ );
3153
+ }
3154
+ const assign = (record, key, value) => {
3155
+ if (value === void 0) delete record[key];
3156
+ else record[key] = value;
3157
+ };
3158
+ const payload = { ...objectOrEmpty(params.payload) };
3159
+ const aps = { ...objectOrEmpty(payload.aps) };
3160
+ const alert = { ...objectOrEmpty(aps.alert) };
3161
+ assign(alert, "title", params.title);
3162
+ assign(alert, "subtitle", params.subtitle);
3163
+ assign(alert, "body", params.body);
3164
+ aps.alert = alert;
3165
+ assign(aps, "badge", params.badge);
3166
+ assign(aps, "category", params.category);
3167
+ assign(aps, "content-available", params["content-available"]);
3168
+ payload.aps = aps;
3169
+ return {
3170
+ payload,
3171
+ actionId: typeof params["action-identifier"] === "string" ? params["action-identifier"] : void 0,
3172
+ userText: typeof params["user-text"] === "string" ? params["user-text"] : void 0
3173
+ };
3174
+ }
3175
+ function objectOrEmpty(value) {
3176
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? { ...value } : {};
3177
+ }
3178
+ async function postPreparedRemotePush(prepared, launch) {
3179
+ const { payload, actionId, userText } = prepared;
3180
+ const page = getPage();
3181
+ await page.evaluate(
3182
+ ({ request }) => {
3183
+ const bridges = window.SootSim?.bridges;
3184
+ const target = bridges?.shellHost;
3185
+ const post = target?.postNativeUIRequest;
3186
+ if (typeof post !== "function") {
3187
+ throw new Error(
3188
+ "sootsim device.sendUserNotification: simulator shell unavailable \u2014 call device.launchApp() first"
3189
+ );
3190
+ }
3191
+ post.call(target, request);
3192
+ },
3193
+ {
3194
+ request: {
3195
+ kind: "notification.remote-push",
3196
+ payload,
3197
+ launch,
3198
+ ...actionId !== void 0 ? { actionId } : {},
3199
+ ...userText !== void 0 ? { userText } : {}
3200
+ }
3201
+ }
3202
+ );
3203
+ }
3204
+ async function postRemotePushToSimulator(params, launch) {
3205
+ await postPreparedRemotePush(detoxNotificationToApnsPayload(params), launch);
3206
+ }
3087
3207
  var device = {
3088
3208
  _currentUrl: "",
3089
3209
  _platform: DETOX_PLATFORM,
@@ -3111,6 +3231,9 @@ var device = {
3111
3231
  );
3112
3232
  _suspendedAppId = null;
3113
3233
  await waitForSootsimTree(page, 1e4);
3234
+ if (opts?.userNotification) {
3235
+ await postRemotePushToSimulator(opts.userNotification, true);
3236
+ }
3114
3237
  return;
3115
3238
  }
3116
3239
  if (!_browser) {
@@ -3151,16 +3274,17 @@ var device = {
3151
3274
  const request = readBrowserCompositeCaptureRequest(value);
3152
3275
  const session = await source.page.context().newCDPSession(source.page);
3153
3276
  try {
3277
+ const { clip, originX, originY } = resolveBrowserCompositeCdpClip(
3278
+ request,
3279
+ await session.send("Page.getLayoutMetrics")
3280
+ );
3154
3281
  const result = await session.send("Page.captureScreenshot", {
3155
3282
  format: "png",
3156
3283
  fromSurface: true,
3157
3284
  captureBeyondViewport: true,
3158
- clip: resolveBrowserCompositeCdpClip(
3159
- request,
3160
- await session.send("Page.getLayoutMetrics")
3161
- )
3285
+ clip
3162
3286
  });
3163
- return "data:image/png;base64," + result.data;
3287
+ return { dataUrl: "data:image/png;base64," + result.data, originX, originY };
3164
3288
  } finally {
3165
3289
  await session.detach();
3166
3290
  }
@@ -3185,6 +3309,20 @@ var device = {
3185
3309
  contextId: contextIdentity,
3186
3310
  operation: "launchApp"
3187
3311
  });
3312
+ if (opts?.userNotification) {
3313
+ const prepared = detoxNotificationToApnsPayload(opts.userNotification);
3314
+ await pollUntil(
3315
+ () => postPreparedRemotePush(prepared, true).then(
3316
+ () => true,
3317
+ () => false
3318
+ ),
3319
+ Boolean,
3320
+ {
3321
+ timeoutMs: 6e4,
3322
+ label: "SootSim shell to accept launch notification"
3323
+ }
3324
+ );
3325
+ }
3188
3326
  await waitForSootsimTree(_page, 3e4);
3189
3327
  await waitForSootsimSurfaceMetrics(_page, 3e4);
3190
3328
  await waitForSootsimShellReady(_page, 3e4);
@@ -3219,6 +3357,12 @@ var device = {
3219
3357
  await waitForSootsimShellReady(page, 1e4);
3220
3358
  await waitForSootsimWorkletsIdle(page, 3e3);
3221
3359
  },
3360
+ // detox device.sendUserNotification on ios: the notification goes straight
3361
+ // to the app's notification center delegate. an active app gets willPresent;
3362
+ // otherwise the app gets the tap response.
3363
+ async sendUserNotification(params) {
3364
+ await postRemotePushToSimulator(params, false);
3365
+ },
3222
3366
  async takeScreenshot(name2) {
3223
3367
  return writeSootsimScreenshot(name2);
3224
3368
  },
@@ -3924,7 +4068,20 @@ var device = {
3924
4068
  },
3925
4069
  async shake() {
3926
4070
  },
4071
+ // set the simulated device location at the platform level, the same step
4072
+ // native detox's setLocation takes: on iOS it drives the simulator location
4073
+ // (AppleSimUtils/simctl location set) and CLLocationManager delivers the fix
4074
+ // asynchronously. here the platform is the harness browser, so grant page
4075
+ // geolocation and override the context fix; the guest location seam observes
4076
+ // it through navigator.geolocation exactly like the app observes Core
4077
+ // Location. app permission state is untouched, matching native.
3927
4078
  async setLocation(lat, lon) {
4079
+ const page = getPage();
4080
+ const context = _context ?? page.context();
4081
+ await context.grantPermissions(["geolocation"], {
4082
+ origin: new URL(page.url()).origin
4083
+ });
4084
+ await context.setGeolocation({ latitude: lat, longitude: lon });
3928
4085
  },
3929
4086
  async setStatusBar(config) {
3930
4087
  await dispatchStatusBarOverride(config);
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.627 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (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;
@@ -93,7 +93,7 @@ function normalizeNativeDevBundleUrl(bundleUrl) {
93
93
  var DEFAULT_TEST_DRIVE_PORT = 8081;
94
94
  function buildUnresolvedTargetError(targetLabel) {
95
95
  return new Error(
96
- `could not resolve a native bundle for ${targetLabel}. pass an explicit bundle URL or open Connect and choose the app there.`
96
+ `could not resolve a native bundle for ${targetLabel}. start the app's dev server (for example npx expo start), pass an explicit bundle URL, or open Connect and choose the app there.`
97
97
  );
98
98
  }
99
99
  function inferManifestFramework(launchUrl, sdkVersion) {
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.627 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (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.627 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (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;
@@ -6161,10 +6161,12 @@ function createRuntimeDelivery(product) {
6161
6161
  );
6162
6162
  import_node_fs6.default.rmSync(tmpDir, { recursive: true, force: true });
6163
6163
  try {
6164
- process.stderr.write(`${product.id}: downloading runtime ${version}\u2026
6164
+ if (!opts.quiet)
6165
+ process.stderr.write(`${product.id}: downloading runtime ${version}\u2026
6165
6166
  `);
6166
6167
  await downloadToFile(tarUrl, runtimeArchivePath);
6167
- process.stderr.write(`${product.id}: extracting runtime ${version}\u2026
6168
+ if (!opts.quiet)
6169
+ process.stderr.write(`${product.id}: extracting runtime ${version}\u2026
6168
6170
  `);
6169
6171
  const actualSha = await sha256File(runtimeArchivePath);
6170
6172
  if (actualSha !== entry.sha256) {
@@ -6483,6 +6485,41 @@ function resolveRuntimeRootForHostname(hostname) {
6483
6485
  const version = runtimeVersionFromHostname(hostname);
6484
6486
  return version ? resolveInstalledRuntimeRoot(version) : null;
6485
6487
  }
6488
+ function sendFileWithRanges(req, res, filePath, total, headers) {
6489
+ const head = (req?.method || "GET").toUpperCase() === "HEAD";
6490
+ const range = /^bytes=(\d*)-(\d*)$/.exec(req?.headers?.range || "");
6491
+ const invalid = range && range[1] && range[2] && Number(range[2]) < Number(range[1]);
6492
+ if (range && (range[1] || range[2]) && !invalid) {
6493
+ const [, startRaw, endRaw] = range;
6494
+ const start = startRaw ? Number(startRaw) : Math.max(0, total - Number(endRaw));
6495
+ const end = startRaw && endRaw ? Math.min(Number(endRaw), total - 1) : total - 1;
6496
+ if (start > end || start >= total) {
6497
+ res.writeHead(416, { ...headers, "Content-Range": `bytes */${total}` });
6498
+ res.end();
6499
+ return;
6500
+ }
6501
+ res.writeHead(206, {
6502
+ ...headers,
6503
+ "Content-Range": `bytes ${start}-${end}/${total}`,
6504
+ "Accept-Ranges": "bytes",
6505
+ "Content-Length": String(end - start + 1)
6506
+ });
6507
+ if (head) res.end();
6508
+ else streamFile(res, import_fs.default.createReadStream(filePath, { start, end }));
6509
+ return;
6510
+ }
6511
+ res.writeHead(200, {
6512
+ ...headers,
6513
+ "Accept-Ranges": "bytes",
6514
+ "Content-Length": String(total)
6515
+ });
6516
+ if (head || total === 0) res.end();
6517
+ else streamFile(res, import_fs.default.createReadStream(filePath, { start: 0, end: total - 1 }));
6518
+ }
6519
+ function streamFile(res, stream) {
6520
+ stream.on("error", (error) => res.destroy(error));
6521
+ stream.pipe(res);
6522
+ }
6486
6523
 
6487
6524
  // src/runtime-delivery.ts
6488
6525
  init_home_paths();
@@ -10189,42 +10226,10 @@ var SootSimBridgeHost = class _SootSimBridgeHost {
10189
10226
  return;
10190
10227
  }
10191
10228
  const contentType = HTTP_MIME_TYPES[import_path3.default.extname(filePath).toLowerCase()] || "application/octet-stream";
10192
- const total = stats.size;
10193
- const range = /^bytes=(\d*)-(\d*)$/.exec(req.headers.range || "");
10194
- if (range) {
10195
- const [, startRaw, endRaw] = range;
10196
- const start = startRaw ? Number(startRaw) : 0;
10197
- const end = endRaw ? Math.min(Number(endRaw), total - 1) : total - 1;
10198
- if (!Number.isFinite(start) || start > end || start >= total) {
10199
- res.writeHead(416, { ...cors, "Content-Range": `bytes */${total}` });
10200
- res.end();
10201
- return;
10202
- }
10203
- res.writeHead(206, {
10204
- ...cors,
10205
- "Content-Type": contentType,
10206
- "Content-Range": `bytes ${start}-${end}/${total}`,
10207
- "Accept-Ranges": "bytes",
10208
- "Content-Length": String(end - start + 1)
10209
- });
10210
- if (method === "HEAD") {
10211
- res.end();
10212
- return;
10213
- }
10214
- import_fs3.default.createReadStream(filePath, { start, end }).pipe(res);
10215
- return;
10216
- }
10217
- res.writeHead(200, {
10229
+ sendFileWithRanges(req, res, filePath, stats.size, {
10218
10230
  ...cors,
10219
- "Content-Type": contentType,
10220
- "Accept-Ranges": "bytes",
10221
- "Content-Length": String(total)
10231
+ "Content-Type": contentType
10222
10232
  });
10223
- if (method === "HEAD") {
10224
- res.end();
10225
- return;
10226
- }
10227
- import_fs3.default.createReadStream(filePath).pipe(res);
10228
10233
  }
10229
10234
  handleServerScan(res) {
10230
10235
  const sendJson = (body) => {
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.627 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (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.627 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (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.627 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/host/fetch-proxy-overrides.ts
4
4
  var FETCH_PROXY_BROWSER_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36";
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.627 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (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.627 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.628 | (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;