peachsim 0.1.628 → 0.1.630

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 (111) hide show
  1. package/README.md +8 -4
  2. package/cli/bridge-flow-runner.ts +10 -3
  3. package/cli/cli-update.ts +2 -7
  4. package/cli/cloud-test-tunnel.ts +16 -3
  5. package/cli/commands/config.ts +1 -1
  6. package/cli/commands/control.ts +28 -3
  7. package/cli/commands/daemon-mac-app.ts +4 -2
  8. package/cli/commands/daemon.ts +4 -4
  9. package/cli/commands/device.ts +2 -3
  10. package/cli/commands/inspect/actions.ts +125 -90
  11. package/cli/commands/inspect/core.ts +5 -0
  12. package/cli/commands/inspect.ts +27 -13
  13. package/cli/commands/platform.ts +1 -1
  14. package/cli/commands/screenshot-layers.ts +1 -1
  15. package/cli/commands/screenshot.ts +1 -1
  16. package/cli/commands/skills.ts +0 -31
  17. package/cli/drivers/playwright-provisioning.ts +3 -2
  18. package/cli/drivers/playwright-sim-host.ts +25 -9
  19. package/cli/main.ts +8 -2
  20. package/cli/outbound-endpoints.ts +0 -4
  21. package/detox/index.ts +74 -43
  22. package/dist-lib/agent-daemon-client.cjs +1 -1
  23. package/dist-lib/agent-events.cjs +1 -1
  24. package/dist-lib/agent-identity.cjs +1 -1
  25. package/dist-lib/agent-sessions.cjs +1 -1
  26. package/dist-lib/attached-projects.cjs +1 -1
  27. package/dist-lib/auth/shared-session.cjs +1 -1
  28. package/dist-lib/backend-origin.cjs +1 -1
  29. package/dist-lib/beta.cjs +1 -1
  30. package/dist-lib/beta.mjs +1 -1
  31. package/dist-lib/bridge-constants.cjs +1 -1
  32. package/dist-lib/bridge-contract-input.cjs +1 -1
  33. package/dist-lib/bridge-contract-input.mjs +1 -1
  34. package/dist-lib/bridge-contract.cjs +1 -1
  35. package/dist-lib/bridge-contract.mjs +1 -1
  36. package/dist-lib/capture-contract.cjs +1 -1
  37. package/dist-lib/capture-contract.mjs +1 -1
  38. package/dist-lib/cli-constants.cjs +1 -1
  39. package/dist-lib/cloud-contract.cjs +1 -1
  40. package/dist-lib/cloud-contract.mjs +1 -1
  41. package/dist-lib/cloud-test-args.cjs +1 -1
  42. package/dist-lib/cloud-test-args.mjs +1 -1
  43. package/dist-lib/cloud.cjs +1 -1
  44. package/dist-lib/cloud.mjs +1 -1
  45. package/dist-lib/config.cjs +1 -1
  46. package/dist-lib/detox/index.cjs +129 -27
  47. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  48. package/dist-lib/home-paths.cjs +1 -1
  49. package/dist-lib/host/bridge-host.cjs +290 -315
  50. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  51. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  52. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  53. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  54. package/dist-lib/host/websocket-proxy.cjs +1 -1
  55. package/dist-lib/index.cjs +253 -121
  56. package/dist-lib/jump-to-source-babel.cjs +1 -1
  57. package/dist-lib/jump-to-source-native.cjs +1 -1
  58. package/dist-lib/{vite-kotlin.cjs → kotlin-host.cjs} +130 -19
  59. package/dist-lib/menu.cjs +1 -1
  60. package/dist-lib/menu.mjs +1 -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 +132 -9
  66. package/dist-lib/native-app-identity.cjs +278 -0
  67. package/dist-lib/native-app-identity.mjs +245 -0
  68. package/dist-lib/profiles.cjs +1 -1
  69. package/dist-lib/public-brand.cjs +1 -1
  70. package/dist-lib/react-native-host-modules.cjs +1 -1
  71. package/dist-lib/react-native-host-modules.mjs +1 -1
  72. package/dist-lib/render-mode.cjs +1 -1
  73. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  74. package/dist-lib/sdk.cjs +110 -87
  75. package/dist-lib/sdk.mjs +110 -87
  76. package/dist-lib/skills.cjs +841 -643
  77. package/dist-lib/swift-compile.cjs +1 -1
  78. package/dist-lib/swift-compile.mjs +1 -1
  79. package/dist-lib/swift-package.cjs +1 -1
  80. package/dist-lib/swift-package.mjs +1 -1
  81. package/dist-lib/swift-submit.cjs +1 -1
  82. package/dist-lib/swift-submit.mjs +1 -1
  83. package/dist-lib/swift.cjs +282 -177
  84. package/dist-lib/vite-swift.cjs +1 -1
  85. package/dist-lib/vite.cjs +11 -28
  86. package/kotlin/README.md +32 -4
  87. package/kotlin/fixtures/counter/Counter.kt +26 -0
  88. package/kotlin/fixtures/counter/CounterScreen.kt +40 -0
  89. package/kotlin/fixtures/headless-counter/Headless.kt +228 -0
  90. package/kotlin/fixtures/runtime-state/RuntimeState.kt +200 -0
  91. package/kotlin/fork/LICENSE +178 -0
  92. package/kotlin/fork/NOTICE +25 -0
  93. package/kotlin/fork/README.md +59 -0
  94. package/kotlin/fork/build.sh +128 -0
  95. package/kotlin/fork/compose-worker-globals.patch +96 -0
  96. package/kotlin/fork/fork.json +33 -0
  97. package/kotlin/fork/publish.ts +77 -0
  98. package/kotlin/fork/skiko-worker-clock.patch +33 -0
  99. package/kotlin/toolchain.json +288 -0
  100. package/package.json +12 -5
  101. package/src/automatic-update-policy.ts +17 -0
  102. package/src/host/bridge-host.ts +3 -1
  103. package/src/{vite-plugin-kotlin.ts → kotlin-host.ts} +155 -12
  104. package/src/metro-plugin.ts +18 -0
  105. package/src/native-app-identity.ts +376 -0
  106. package/src/native-firebase-config.ts +90 -0
  107. package/src/screenshots/frame-compose.ts +5 -2
  108. package/src/screenshots/registry.ts +1 -1
  109. package/src/screenshots/schema.ts +2 -2
  110. package/src/vite-plugin-one.ts +11 -28
  111. package/src/vite-plugin.ts +6 -1
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.628 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.630 | (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.628 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.630 | (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.628 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.630 | (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.628 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.630 | (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.628 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.630 | (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.628 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.630 | (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;
@@ -2461,16 +2461,26 @@ function payloadFromResolved(resolved, match, extra = {}) {
2461
2461
  ...extra
2462
2462
  };
2463
2463
  }
2464
- function collectTextCandidates(nodes, ancestors = [], pressableOwnerText = "") {
2464
+ function collectTextCandidates(nodes, ancestors = [], pressableOwnerText = "", actionableAncestor = false, actionableRole) {
2465
2465
  const out = [];
2466
2466
  for (const node of nodes) {
2467
2467
  const text = nodeText(node);
2468
2468
  const caption = !!text && text === pressableOwnerText && !node.pressable;
2469
- if (!caption) out.push({ node, ancestorTestIDs: ancestors });
2469
+ const actionable = !!node.pressable || actionableAncestor;
2470
+ const role = node.role ?? actionableRole;
2471
+ if (!caption) out.push({ node, ancestorTestIDs: ancestors, actionable, role });
2470
2472
  if (node.children?.length) {
2471
2473
  const next = node.testID ? [...ancestors, node.testID] : ancestors;
2472
2474
  const owner = text ? node.pressable ? text : "" : pressableOwnerText;
2473
- out.push(...collectTextCandidates(node.children, next, owner));
2475
+ out.push(
2476
+ ...collectTextCandidates(
2477
+ node.children,
2478
+ next,
2479
+ owner,
2480
+ actionable,
2481
+ node.pressable ? node.role : actionableRole
2482
+ )
2483
+ );
2474
2484
  }
2475
2485
  }
2476
2486
  return out;
@@ -2494,85 +2504,102 @@ async function tapByText(bridge, query, options = {}, opts = {}) {
2494
2504
  agent: opts.agent,
2495
2505
  timing: opts.timing,
2496
2506
  textFallback: query,
2497
- resolve: async () => {
2498
- const { tree } = await inspectTree(bridge, 50);
2499
- if (!Array.isArray(tree)) return { matched: 0, total: 0 };
2500
- const needle = options.exact ? query : query.toLowerCase();
2501
- let candidates = collectTextCandidates(tree).filter(({ node: node2, ancestorTestIDs }) => {
2502
- const text = nodeText(node2);
2503
- if (!text) return false;
2504
- if (options.exact ? text !== needle : !text.toLowerCase().includes(needle)) {
2505
- return false;
2506
- }
2507
- if (options.role && node2.role !== options.role) return false;
2508
- if (options.within && !ancestorTestIDs.includes(options.within)) return false;
2509
- const ax = node2.geometry.x;
2510
- const ay = node2.geometry.y;
2511
- if (options.minX != null && !(ax >= options.minX)) return false;
2512
- if (options.maxX != null && !(ax <= options.maxX)) return false;
2513
- if (options.minY != null && !(ay >= options.minY)) return false;
2514
- if (options.maxY != null && !(ay <= options.maxY)) return false;
2515
- return true;
2516
- });
2517
- if (options.near) {
2518
- const { x, y } = options.near;
2519
- candidates.sort(
2520
- (a, b) => Math.hypot(a.node.geometry.x - x, a.node.geometry.y - y) - Math.hypot(b.node.geometry.x - x, b.node.geometry.y - y)
2521
- );
2522
- } else {
2523
- candidates.sort((a, b) => {
2524
- if (Math.abs(a.node.geometry.y - b.node.geometry.y) > 2) {
2525
- return a.node.geometry.y - b.node.geometry.y;
2526
- }
2527
- return a.node.geometry.x - b.node.geometry.x;
2528
- });
2529
- }
2530
- const total = candidates.length;
2531
- if (total === 0) return { matched: 0, total: 0 };
2532
- let idx = 0;
2533
- if (options.nth != null) {
2534
- idx = options.nth < 0 ? total + options.nth : options.nth;
2535
- if (idx < 0 || idx >= total) {
2536
- return { matched: 0, total, nthOutOfRange: true, nth: options.nth };
2537
- }
2538
- } else if (total > 1 && !options.first && !options.near) {
2539
- return {
2540
- ambiguous: true,
2541
- total,
2542
- candidates: candidates.slice(0, 10).map((candidate, index) => ({
2543
- idx: index,
2544
- nodeId: candidate.node.nodeId,
2545
- type: candidate.node.type,
2546
- testID: candidate.node.testID ?? null,
2547
- text: nodeText(candidate.node).slice(0, 60),
2548
- abs: { x: candidate.node.geometry.x, y: candidate.node.geometry.y },
2549
- layout: {
2550
- width: Math.round(candidate.node.geometry.width),
2551
- height: Math.round(candidate.node.geometry.height)
2552
- },
2553
- ancestorTestIDs: candidate.ancestorTestIDs.slice(0, 5)
2554
- }))
2555
- };
2507
+ resolve: () => resolveTextTapTarget(bridge, query, options)
2508
+ });
2509
+ }
2510
+ async function resolveTextTapTarget(bridge, query, options = {}) {
2511
+ const { tree } = await inspectTree(bridge, 50);
2512
+ if (!Array.isArray(tree)) return { matched: 0, total: 0 };
2513
+ const needle = options.exact ? query : query.toLowerCase();
2514
+ let candidates = collectTextCandidates(tree).filter(
2515
+ ({ node: node2, ancestorTestIDs, role }) => {
2516
+ const text = nodeText(node2);
2517
+ if (!text) return false;
2518
+ if (options.exact ? text !== needle : !text.toLowerCase().includes(needle)) {
2519
+ return false;
2556
2520
  }
2557
- const picked = candidates[idx];
2558
- if (!picked) return { matched: 0, total };
2559
- const node = picked.node;
2560
- let resolved = node.testID ? await resolveTargetCoords(bridge, { mode: "testid", value: node.testID }) : await resolveTargetCoords(bridge, {
2561
- mode: "text",
2562
- value: nodeText(node) || query
2563
- });
2564
- if (!resolved) {
2565
- resolved = {
2566
- x: node.geometry.x + node.geometry.width / 2,
2567
- y: node.geometry.y + node.geometry.height / 2,
2568
- testID: node.testID ?? null,
2569
- text: nodeText(node) || query,
2570
- type: node.type
2571
- };
2521
+ if (options.role && role !== options.role) return false;
2522
+ if (options.within && !ancestorTestIDs.includes(options.within)) return false;
2523
+ const ax = node2.geometry.x;
2524
+ const ay = node2.geometry.y;
2525
+ if (options.minX != null && !(ax >= options.minX)) return false;
2526
+ if (options.maxX != null && !(ax <= options.maxX)) return false;
2527
+ if (options.minY != null && !(ay >= options.minY)) return false;
2528
+ if (options.maxY != null && !(ay <= options.maxY)) return false;
2529
+ return true;
2530
+ }
2531
+ );
2532
+ if (options.nth == null && !options.first && !options.near) {
2533
+ const actionable = candidates.filter((candidate) => candidate.actionable);
2534
+ if (actionable.length > 0) candidates = actionable;
2535
+ }
2536
+ if (options.near) {
2537
+ const { x, y } = options.near;
2538
+ candidates.sort(
2539
+ (a, b) => Math.hypot(a.node.geometry.x - x, a.node.geometry.y - y) - Math.hypot(b.node.geometry.x - x, b.node.geometry.y - y)
2540
+ );
2541
+ } else {
2542
+ candidates.sort((a, b) => {
2543
+ if (Math.abs(a.node.geometry.y - b.node.geometry.y) > 2) {
2544
+ return a.node.geometry.y - b.node.geometry.y;
2572
2545
  }
2573
- return payloadFromResolved(resolved, node, { total, idx });
2546
+ return a.node.geometry.x - b.node.geometry.x;
2547
+ });
2548
+ }
2549
+ const total = candidates.length;
2550
+ if (total === 0) return { matched: 0, total: 0 };
2551
+ let idx = 0;
2552
+ if (options.nth != null) {
2553
+ idx = options.nth < 0 ? total + options.nth : options.nth;
2554
+ if (idx < 0 || idx >= total) {
2555
+ return { matched: 0, total, nthOutOfRange: true, nth: options.nth };
2574
2556
  }
2557
+ } else if (total > 1 && !options.first && !options.near) {
2558
+ return {
2559
+ ambiguous: true,
2560
+ total,
2561
+ candidates: candidates.slice(0, 10).map((candidate, index) => ({
2562
+ idx: index,
2563
+ nodeId: candidate.node.nodeId,
2564
+ type: candidate.node.type,
2565
+ testID: candidate.node.testID ?? null,
2566
+ text: nodeText(candidate.node).slice(0, 60),
2567
+ abs: { x: candidate.node.geometry.x, y: candidate.node.geometry.y },
2568
+ layout: {
2569
+ width: Math.round(candidate.node.geometry.width),
2570
+ height: Math.round(candidate.node.geometry.height)
2571
+ },
2572
+ ancestorTestIDs: candidate.ancestorTestIDs.slice(0, 5)
2573
+ }))
2574
+ };
2575
+ }
2576
+ const picked = candidates[idx];
2577
+ if (!picked) return { matched: 0, total };
2578
+ const node = picked.node;
2579
+ const tap = await bridge.send({
2580
+ type: "call",
2581
+ path: "SootSim.bridges.test.resolveTapTarget",
2582
+ args: [node.nodeId]
2575
2583
  });
2584
+ if (!isTapTargetInfo(tap)) return { matched: 0, total };
2585
+ return payloadFromResolved(
2586
+ {
2587
+ x: tap.cx,
2588
+ y: tap.cy,
2589
+ nodeId: tap.target.nodeId,
2590
+ testID: tap.target.testID,
2591
+ text: tap.target.text ?? tap.target.accessibilityLabel,
2592
+ type: tap.target.type
2593
+ },
2594
+ node,
2595
+ { total, idx }
2596
+ );
2597
+ }
2598
+ function isTapNode(value) {
2599
+ return value !== null && typeof value === "object" && typeof Reflect.get(value, "nodeId") === "number";
2600
+ }
2601
+ function isTapTargetInfo(value) {
2602
+ return value !== null && typeof value === "object" && typeof Reflect.get(value, "cx") === "number" && typeof Reflect.get(value, "cy") === "number" && isTapNode(Reflect.get(value, "target"));
2576
2603
  }
2577
2604
  async function tapBest(bridge, query, opts = {}) {
2578
2605
  return tapResolvedTarget(bridge, {
@@ -2594,17 +2621,19 @@ async function tapBest(bridge, query, opts = {}) {
2594
2621
  }
2595
2622
  };
2596
2623
  }
2597
- const byText = await resolveTargetCoords(bridge, { mode: "text", value: query });
2598
- if (byText) {
2624
+ const byText = await resolveTextTapTarget(bridge, query);
2625
+ if ("ambiguous" in byText) {
2626
+ return {
2627
+ ambiguous: true,
2628
+ error: `multiple text matches for "${query}"; use tap-text with --nth, --within or --near`
2629
+ };
2630
+ }
2631
+ if ("cx" in byText && typeof byText.cx === "number") {
2599
2632
  return {
2600
- ...payloadFromResolved(byText, byText),
2633
+ ...byText,
2601
2634
  strategy: "text",
2602
2635
  node: {
2603
- nodeId: byText.nodeId ?? null,
2604
- id: byText.testID ?? null,
2605
- testID: byText.testID ?? null,
2606
- type: byText.type ?? null,
2607
- text: byText.text ?? query
2636
+ ...byText.target
2608
2637
  }
2609
2638
  };
2610
2639
  }
@@ -4160,7 +4189,7 @@ var swiftHost;
4160
4189
  var loadSwiftHost = () => swiftHost ??= import("peachsim/vite-swift");
4161
4190
  var KOTLIN_ROUTE_PREFIX = "/__peach/kotlin";
4162
4191
  var kotlinHost;
4163
- var loadKotlinHost = () => kotlinHost ??= import("peachsim/vite-kotlin");
4192
+ var loadKotlinHost = () => kotlinHost ??= import("peachsim/kotlin-host");
4164
4193
  function rnxPlugin(options = {}) {
4165
4194
  const prefix2 = options.prefix || "/__soot";
4166
4195
  return {
@@ -4243,34 +4272,17 @@ function rnxPlugin(options = {}) {
4243
4272
  const pathname = (req.url ?? "").split("?")[0];
4244
4273
  if (!pathname.startsWith(KOTLIN_ROUTE_PREFIX)) return next();
4245
4274
  loadKotlinHost().then(async (host) => {
4246
- if (host.serveKotlinArtifact(pathname, res)) return;
4247
- if (pathname !== "/__peach/kotlin-package") return next();
4248
4275
  if (!kotlinModuleRoot) {
4249
- const moduleRoot = import_path3.default.join(
4250
- import_path3.default.dirname(
4251
- (0, import_node_module.createRequire)(import_path3.default.join(server.config.root, "package.json")).resolve(
4252
- "peachsim/package.json"
4253
- )
4254
- ),
4255
- "kotlin"
4256
- );
4257
- kotlinModuleRoot = moduleRoot;
4258
- server.watcher.add(import_path3.default.join(moduleRoot, "Sources"));
4259
- }
4260
- try {
4261
- const artifact = await host.kotlinArtifact(
4262
- server.config.root,
4263
- kotlinModuleRoot,
4264
- devOrigin()
4265
- );
4266
- res.setHeader("access-control-allow-origin", "*");
4267
- res.setHeader("content-type", "application/json");
4268
- res.end(JSON.stringify({ ...artifact, hmrPath }));
4269
- } catch (error) {
4270
- res.statusCode = 500;
4271
- res.setHeader("access-control-allow-origin", "*");
4272
- res.end(error instanceof Error ? error.message : String(error));
4276
+ kotlinModuleRoot = host.peachKotlinModuleRoot(server.config.root);
4277
+ server.watcher.add(import_path3.default.join(kotlinModuleRoot, "Sources"));
4273
4278
  }
4279
+ const served = await host.serveKotlinRequest(pathname, res, {
4280
+ root: server.config.root,
4281
+ moduleRoot: kotlinModuleRoot,
4282
+ origin: devOrigin(),
4283
+ hmrPath
4284
+ });
4285
+ if (!served) next();
4274
4286
  }).catch(next);
4275
4287
  });
4276
4288
  server.watcher.on("change", (file) => {
@@ -4482,6 +4494,110 @@ var import_fs5 = __toESM(require("fs"), 1);
4482
4494
  var import_module2 = require("module");
4483
4495
  var import_node_crypto5 = require("node:crypto");
4484
4496
  var import_path4 = __toESM(require("path"), 1);
4497
+
4498
+ // src/native-firebase-config.ts
4499
+ var import_node_fs5 = __toESM(require("node:fs"), 1);
4500
+ var import_node_module2 = require("node:module");
4501
+ var import_node_path6 = __toESM(require("node:path"), 1);
4502
+
4503
+ // src/plist.ts
4504
+ var import_plist = __toESM(require("@expo/plist"), 1);
4505
+ function resolvePlistParser(value) {
4506
+ if (!isRecord(value)) return null;
4507
+ const direct = value.parse;
4508
+ if (typeof direct === "function") {
4509
+ return (source) => Reflect.apply(direct, value, [source]);
4510
+ }
4511
+ const nested = value.default;
4512
+ if (!isRecord(nested)) return null;
4513
+ const nestedParse = nested.parse;
4514
+ return typeof nestedParse === "function" ? (source) => Reflect.apply(nestedParse, nested, [source]) : null;
4515
+ }
4516
+ var parsePlist = resolvePlistParser(import_plist.default);
4517
+ function parsePlistSource(source) {
4518
+ return parsePlist?.(source) ?? null;
4519
+ }
4520
+
4521
+ // src/native-firebase-config.ts
4522
+ function readNativeFirebaseConfig(projectRoot) {
4523
+ const result = {};
4524
+ if (![
4525
+ "app.json",
4526
+ "app.config.js",
4527
+ "app.config.ts",
4528
+ "app.config.cjs",
4529
+ "app.config.mjs"
4530
+ ].some((name2) => import_node_fs5.default.existsSync(import_node_path6.default.join(projectRoot, name2))))
4531
+ return result;
4532
+ const require2 = (0, import_node_module2.createRequire)(import_node_path6.default.join(projectRoot, "package.json"));
4533
+ const configModule = require2("@expo/config");
4534
+ if (!isRecord(configModule) || typeof configModule.getConfig !== "function") {
4535
+ throw new Error("Expo native configuration is unavailable.");
4536
+ }
4537
+ const resolved = Reflect.apply(configModule.getConfig, configModule, [
4538
+ projectRoot,
4539
+ { skipSDKVersionRequirement: true }
4540
+ ]);
4541
+ const config = isRecord(resolved) && isRecord(resolved.exp) ? resolved.exp : {};
4542
+ for (const platform of ["ios", "android"]) {
4543
+ const native = config[platform];
4544
+ if (!isRecord(native) || typeof native.googleServicesFile !== "string") continue;
4545
+ try {
4546
+ const source = import_node_fs5.default.readFileSync(
4547
+ import_node_path6.default.resolve(projectRoot, native.googleServicesFile),
4548
+ "utf8"
4549
+ );
4550
+ const parsed = platform === "ios" ? parsePlistSource(source) : JSON.parse(source);
4551
+ if (!isRecord(parsed)) throw new Error("Invalid Google services file.");
4552
+ if (platform === "ios") {
4553
+ result[platform] = {
4554
+ apiKey: parsed.API_KEY,
4555
+ appId: parsed.GOOGLE_APP_ID,
4556
+ projectId: parsed.PROJECT_ID,
4557
+ databaseURL: parsed.DATABASE_URL,
4558
+ gaTrackingId: parsed.TRACKING_ID,
4559
+ storageBucket: parsed.STORAGE_BUCKET,
4560
+ messagingSenderId: parsed.GCM_SENDER_ID,
4561
+ clientId: parsed.CLIENT_ID,
4562
+ androidClientID: parsed.ANDROID_CLIENT_ID,
4563
+ deepLinkUrlScheme: parsed.REVERSED_CLIENT_ID
4564
+ };
4565
+ } else {
4566
+ const project = isRecord(parsed.project_info) ? parsed.project_info : {};
4567
+ const clients = Array.isArray(parsed.client) ? parsed.client : [];
4568
+ const client = clients.find((value) => {
4569
+ if (!isRecord(value) || !isRecord(value.client_info)) return false;
4570
+ const android = value.client_info.android_client_info;
4571
+ return isRecord(android) && android.package_name === native.package;
4572
+ });
4573
+ if (!isRecord(client) || !isRecord(client.client_info))
4574
+ throw new Error("No Google services client matches the Android package.");
4575
+ const apiKeys = Array.isArray(client.api_key) ? client.api_key : [];
4576
+ const apiKey = apiKeys.find(
4577
+ (value) => isRecord(value) && typeof value.current_key === "string"
4578
+ );
4579
+ const oauthClients = Array.isArray(client.oauth_client) ? client.oauth_client : [];
4580
+ const oauth = oauthClients.find(
4581
+ (value) => isRecord(value) && value.client_type === 3
4582
+ );
4583
+ result[platform] = {
4584
+ apiKey: isRecord(apiKey) ? apiKey.current_key : void 0,
4585
+ appId: client.client_info.mobilesdk_app_id,
4586
+ projectId: project.project_id,
4587
+ databaseURL: project.firebase_url,
4588
+ storageBucket: project.storage_bucket,
4589
+ messagingSenderId: project.project_number,
4590
+ clientId: isRecord(oauth) ? oauth.client_id : void 0
4591
+ };
4592
+ }
4593
+ } catch (error) {
4594
+ result[platform] = { error: error instanceof Error ? error.message : String(error) };
4595
+ }
4596
+ }
4597
+ return result;
4598
+ }
4599
+
4600
+ // src/metro-plugin.ts
4485
4601
  var prefix = "/__soot";
4486
4602
  function readCallableMetroExport(moduleValue, label) {
4487
4603
  const callable = typeof moduleValue === "function" ? moduleValue : typeof moduleValue === "object" && moduleValue !== null ? Reflect.get(moduleValue, "default") : void 0;
@@ -4602,6 +4718,22 @@ function withRNX(config) {
4602
4718
  });
4603
4719
  return;
4604
4720
  }
4721
+ if ((req.url || "").split("?")[0] === "/__peach-native-firebase-config") {
4722
+ try {
4723
+ res.setHeader("Content-Type", "application/json");
4724
+ res.setHeader("Cache-Control", "no-store");
4725
+ res.setHeader("Access-Control-Allow-Origin", "*");
4726
+ res.end(JSON.stringify(readNativeFirebaseConfig(cfgProjectRoot)));
4727
+ } catch (error) {
4728
+ res.statusCode = 400;
4729
+ res.end(
4730
+ JSON.stringify({
4731
+ error: error instanceof Error ? error.message : String(error)
4732
+ })
4733
+ );
4734
+ }
4735
+ return;
4736
+ }
4605
4737
  const url = req.url || "";
4606
4738
  const pathname = url.split("?")[0];
4607
4739
  const runtimeRoot = resolveRuntimeRootForRequestHost(req.headers?.host);
@@ -4670,13 +4802,13 @@ function resolveMetroPort(server) {
4670
4802
  var metro_plugin_default = withRNX;
4671
4803
 
4672
4804
  // src/jump-to-source-babel.ts
4673
- function normalizeSourcePath(path8) {
4674
- const queryStart = path8.indexOf("?");
4675
- const clean = queryStart >= 0 ? path8.slice(0, queryStart) : path8;
4805
+ function normalizeSourcePath(path9) {
4806
+ const queryStart = path9.indexOf("?");
4807
+ const clean = queryStart >= 0 ? path9.slice(0, queryStart) : path9;
4676
4808
  return clean.replace(/\\/g, "/");
4677
4809
  }
4678
- function matchesPath(path8, matcher) {
4679
- return typeof matcher === "string" ? path8.includes(matcher) : matcher.test(path8);
4810
+ function matchesPath(path9, matcher) {
4811
+ return typeof matcher === "string" ? path9.includes(matcher) : matcher.test(path9);
4680
4812
  }
4681
4813
  function shouldStampFile(filename, options) {
4682
4814
  const normalized = normalizeSourcePath(filename);
@@ -4709,11 +4841,11 @@ function sootsimJumpToSourceBabelPlugin(api, options = {}) {
4709
4841
  return {
4710
4842
  name: "sootsim-jump-to-source",
4711
4843
  visitor: {
4712
- JSXOpeningElement(path8, state) {
4844
+ JSXOpeningElement(path9, state) {
4713
4845
  if (options.enabled === false) return;
4714
4846
  const filename = state.file?.opts?.filename;
4715
4847
  if (!filename || !shouldStampFile(filename, options)) return;
4716
- const node = path8.node;
4848
+ const node = path9.node;
4717
4849
  const tagName = getJSXName(node.name);
4718
4850
  if (!tagName || tagName === "Fragment" || tagName.endsWith(".Fragment")) return;
4719
4851
  if (hasAttribute(node, attrName)) return;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.628 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.630 | (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.628 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.630 | (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;