peachsim 0.1.1640 → 0.1.1644

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 (125) hide show
  1. package/README.md +5 -0
  2. package/cli/app-bundle-resources.ts +57 -0
  3. package/cli/app-stubs.ts +38 -13
  4. package/cli/cloud-session.ts +16 -0
  5. package/cli/commands/assert.ts +17 -4
  6. package/cli/commands/box.ts +33 -19
  7. package/cli/commands/control.ts +12 -0
  8. package/cli/commands/doctor.ts +24 -15
  9. package/cli/commands/inspect/actions.ts +3 -2
  10. package/cli/commands/inspect/core.ts +0 -8
  11. package/cli/commands/inspect.ts +0 -14
  12. package/cli/commands/platform.ts +2 -1
  13. package/cli/commands/record.ts +64 -28
  14. package/cli/commands/swift.ts +10 -2
  15. package/cli/commands/three-mode.ts +4 -38
  16. package/cli/commands/upload.ts +8 -8
  17. package/cli/drivers/playwright.ts +39 -0
  18. package/cli/flow-live-status.ts +0 -2
  19. package/cli/main.ts +1 -1
  20. package/cli/outbound-endpoints.ts +4 -2
  21. package/cli/ws-bridge.ts +0 -6
  22. package/detox/attempt-events.cjs +41 -5
  23. package/detox/index.ts +155 -109
  24. package/detox/jest-environment.cjs +5 -1
  25. package/detox/motion-change.cjs +7 -5
  26. package/detox/motion-change.d.cts +1 -0
  27. package/detox/motion-leg-starts.cjs +20 -0
  28. package/detox/motion-leg-starts.d.cts +8 -0
  29. package/dist-lib/agent-daemon-client.cjs +1 -1
  30. package/dist-lib/agent-events.cjs +1 -1
  31. package/dist-lib/attached-projects.cjs +1 -1
  32. package/dist-lib/auth/shared-session.cjs +1 -1
  33. package/dist-lib/backend-origin.cjs +12 -3
  34. package/dist-lib/beta.cjs +1 -1
  35. package/dist-lib/beta.mjs +1 -1
  36. package/dist-lib/bridge-constants.cjs +1 -1
  37. package/dist-lib/bridge-contract-input.cjs +1 -1
  38. package/dist-lib/bridge-contract-input.mjs +1 -1
  39. package/dist-lib/bridge-contract.cjs +1 -1
  40. package/dist-lib/bridge-contract.mjs +1 -1
  41. package/dist-lib/bundle-channel.cjs +2671 -1833
  42. package/dist-lib/capture-contract.cjs +1 -1
  43. package/dist-lib/capture-contract.mjs +1 -1
  44. package/dist-lib/cloud-contract.cjs +1 -1
  45. package/dist-lib/cloud-contract.mjs +1 -1
  46. package/dist-lib/cloud-test-args.cjs +1 -1
  47. package/dist-lib/cloud-test-args.mjs +1 -1
  48. package/dist-lib/cloud.cjs +1 -1
  49. package/dist-lib/cloud.mjs +1 -1
  50. package/dist-lib/config.cjs +1 -1
  51. package/dist-lib/config.mjs +1 -1
  52. package/dist-lib/detox/index.cjs +578 -130
  53. package/dist-lib/detox/jest-environment.cjs +5 -1
  54. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  55. package/dist-lib/expo-swift-bundle.cjs +2374 -1554
  56. package/dist-lib/expo-swift-metadata.cjs +1 -1
  57. package/dist-lib/expo-swift-metadata.mjs +1 -1
  58. package/dist-lib/home-paths.cjs +1 -1
  59. package/dist-lib/host/bridge-host.cjs +2968 -2141
  60. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  61. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  62. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  63. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  64. package/dist-lib/host/websocket-proxy.cjs +1 -1
  65. package/dist-lib/index.cjs +132 -57
  66. package/dist-lib/jump-to-source-babel.cjs +1 -1
  67. package/dist-lib/jump-to-source-native.cjs +1 -1
  68. package/dist-lib/kotlin-host.cjs +1 -1
  69. package/dist-lib/menu.cjs +14 -6
  70. package/dist-lib/menu.mjs +14 -6
  71. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  72. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  73. package/dist-lib/metro-production-bundle.cjs +1 -1
  74. package/dist-lib/metro-production-bundle.mjs +1 -1
  75. package/dist-lib/metro.cjs +120 -53
  76. package/dist-lib/native-app-identity-project.cjs +96 -44
  77. package/dist-lib/native-app-identity-project.mjs +95 -44
  78. package/dist-lib/native-app-identity.cjs +1 -1
  79. package/dist-lib/native-app-identity.mjs +1 -1
  80. package/dist-lib/native-firebase-metadata.cjs +1 -1
  81. package/dist-lib/native-firebase-metadata.mjs +1 -1
  82. package/dist-lib/plist.cjs +1 -1
  83. package/dist-lib/plist.mjs +1 -1
  84. package/dist-lib/profiles.cjs +1 -1
  85. package/dist-lib/public-brand.cjs +1 -1
  86. package/dist-lib/react-native-host-modules.cjs +1 -1
  87. package/dist-lib/react-native-host-modules.mjs +1 -1
  88. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  89. package/dist-lib/sdk.cjs +5 -3
  90. package/dist-lib/sdk.mjs +5 -3
  91. package/dist-lib/skills.cjs +3932 -2868
  92. package/dist-lib/swift-compile.cjs +587 -374
  93. package/dist-lib/swift-compile.mjs +595 -369
  94. package/dist-lib/swift-package.cjs +1 -1
  95. package/dist-lib/swift-package.mjs +1 -1
  96. package/dist-lib/swift-submit.cjs +8 -3
  97. package/dist-lib/swift-submit.mjs +8 -3
  98. package/dist-lib/swift-toolchain.cjs +1 -1
  99. package/dist-lib/swift.cjs +1 -1
  100. package/dist-lib/vite-swift.cjs +2263 -1467
  101. package/dist-lib/vite.cjs +1 -1
  102. package/dist-lib/worklet-executable.cjs +1 -1
  103. package/dist-lib/worklet-executable.mjs +1 -1
  104. package/package.json +1 -1
  105. package/src/appBundleResourcesContract.ts +51 -0
  106. package/src/backend-origin.ts +11 -1
  107. package/src/host/bridge-host.ts +17 -11
  108. package/src/host/staged-host-files.ts +28 -4
  109. package/src/launchMetadataContract.ts +1 -0
  110. package/src/menu.ts +12 -4
  111. package/src/native-app-identity-project.ts +47 -38
  112. package/src/react-native-bridge-modules.ts +89 -76
  113. package/src/runtime-delivery.ts +6 -7
  114. package/src/swift-compile.ts +150 -112
  115. package/src/swift-compiler-digest.ts +5 -3
  116. package/src/swift-distribution.lock.json +9 -8
  117. package/src/swift-host.ts +6 -4
  118. package/src/swift-objc-compile.ts +416 -194
  119. package/src/swift-objc-metadata.ts +204 -0
  120. package/src/swift-objc-sdk.ts +195 -2
  121. package/src/swift-package-manifest.ts +6 -3
  122. package/src/swift-root-lock.ts +302 -0
  123. package/src/swift-submit.ts +15 -2
  124. package/src/swiftContract.ts +12 -0
  125. package/src/xcode-project.ts +77 -7
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.1640 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.1644 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.1640 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.1644 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.1640 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.1644 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
 
3
3
  // src/host/fetch-proxy-overrides.ts
4
4
  var FETCH_PROXY_NATIVE_USER_AGENT_HEADER = "x-sootsim-native-user-agent";
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.1640 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.1644 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.1640 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.1644 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.1640 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.1644 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -5532,15 +5532,28 @@ function packageNativeRequests(dir) {
5532
5532
  }
5533
5533
  function dependencyNativeOwners(appDir, manifest) {
5534
5534
  const owners = /* @__PURE__ */ new Map();
5535
- for (const name2 of Object.keys({
5535
+ const pending = Object.keys({
5536
5536
  ...manifest?.devDependencies,
5537
5537
  ...manifest?.dependencies
5538
- })) {
5539
- const dir = installedPackageDir(appDir, name2);
5540
- if (!dir || !dir.split("/").includes("node_modules")) continue;
5541
- for (const request of packageNativeRequests(dir))
5542
- if (!owners.has(`${request.kind}:${request.name}`))
5543
- owners.set(`${request.kind}:${request.name}`, name2);
5538
+ }).map((name2) => ({ from: appDir, name: name2 }));
5539
+ const visited = /* @__PURE__ */ new Set();
5540
+ for (let index = 0; index < pending.length; index++) {
5541
+ const { from, name: name2 } = pending[index];
5542
+ const dir = installedPackageDir(from, name2);
5543
+ if (!dir || visited.has(dir)) continue;
5544
+ visited.add(dir);
5545
+ const dependency = readProjectManifest(dir);
5546
+ const optional = readPackageJson(dir)?.optionalDependencies;
5547
+ if (dir.split("/").includes("node_modules") && (from === appDir || hasNativeCode(dir, "ios"))) {
5548
+ for (const request of packageNativeRequests(dir))
5549
+ if (!owners.has(`${request.kind}:${request.name}`))
5550
+ owners.set(`${request.kind}:${request.name}`, dependency?.name ?? name2);
5551
+ }
5552
+ for (const child of Object.keys({
5553
+ ...dependency?.dependencies,
5554
+ ...isRecord(optional) ? optional : {}
5555
+ }))
5556
+ pending.push({ from: dir, name: child });
5544
5557
  }
5545
5558
  return owners;
5546
5559
  }
@@ -5672,7 +5685,9 @@ function scanLocalNative(appDir, pkg, answered, platform, knownPackages = /* @__
5672
5685
  expoModuleDirs.map((dir) => [
5673
5686
  dir,
5674
5687
  scanRepository(dir).directories.flatMap(
5675
- (sub) => listDir(sub).filter((name2) => NATIVE_SOURCE_FILE.test(name2)).map((name2) => (0, import_node_path16.join)(sub, name2))
5688
+ (sub) => listDir(sub).filter(
5689
+ (name2) => NATIVE_SOURCE_FILE.test(name2) && (!platform || (platform === "ios" ? name2.endsWith(".swift") : name2.endsWith(".kt")))
5690
+ ).map((name2) => (0, import_node_path16.join)(sub, name2))
5676
5691
  )
5677
5692
  ])
5678
5693
  );
@@ -5701,7 +5716,9 @@ function scanLocalNative(appDir, pkg, answered, platform, knownPackages = /* @__
5701
5716
  path: `${(0, import_node_path16.relative)(appDir, file)}:${line}`,
5702
5717
  expoModule: moduleDir ? (0, import_node_path16.relative)(appDir, moduleDir) || "." : null,
5703
5718
  swift: moduleDir ? (nativeFilesByModule.get(moduleDir) ?? []).some(
5704
- (path20) => path20.endsWith(".swift")
5719
+ (path20) => path20.endsWith(".swift") && [...(readSmallFile(path20) ?? "").matchAll(/\bName\(\s*"(\w+)"\s*\)/g)].some(
5720
+ (match) => match[1] === name2
5721
+ )
5705
5722
  ) : false,
5706
5723
  answered: answered[kind].has(name2)
5707
5724
  });
@@ -7269,6 +7286,8 @@ function createCloudSession(input) {
7269
7286
  return session;
7270
7287
  }
7271
7288
  function createCloudBoxSession(input) {
7289
+ if (input.appFontsWireSha256 !== void 0 && !/^[a-f0-9]{64}$/.test(input.appFontsWireSha256))
7290
+ throw new Error("cloud box session has an invalid app-font wire digest");
7272
7291
  const session = {
7273
7292
  version: CLOUD_SESSION_VERSION,
7274
7293
  service: "box",
@@ -7277,7 +7296,8 @@ function createCloudBoxSession(input) {
7277
7296
  apiOrigin: normalizeCloudOrigin(input.apiOrigin.trim()),
7278
7297
  ...input.platform ? { platform: input.platform } : {},
7279
7298
  ...input.device?.trim() ? { device: input.device.trim() } : {},
7280
- ...input.artifact ? { artifact: input.artifact } : {}
7299
+ ...input.artifact ? { artifact: input.artifact } : {},
7300
+ ...input.appFontsWireSha256 !== void 0 ? { appFontsWireSha256: input.appFontsWireSha256 } : {}
7281
7301
  };
7282
7302
  if (!session.boxId || !session.boxToken) {
7283
7303
  throw new Error("cloud box session requires a box, an origin, and a box token");
@@ -7318,13 +7338,16 @@ function parseCloudSession(encoded) {
7318
7338
  try {
7319
7339
  if (parsed.service === "box") {
7320
7340
  if (!boxId || !boxToken) throw new Error("unreadable cloud box session");
7341
+ if (parsed.appFontsWireSha256 !== void 0 && typeof parsed.appFontsWireSha256 !== "string")
7342
+ throw new Error("unreadable app-font wire digest");
7321
7343
  return createCloudBoxSession({
7322
7344
  boxId,
7323
7345
  boxToken,
7324
7346
  apiOrigin,
7325
7347
  platform,
7326
7348
  device,
7327
- artifact
7349
+ artifact,
7350
+ appFontsWireSha256: parsed.appFontsWireSha256
7328
7351
  });
7329
7352
  }
7330
7353
  const simId = parsed.simId;
@@ -10381,9 +10404,10 @@ async function tapByText(bridge, query, options = {}, opts = {}) {
10381
10404
  });
10382
10405
  }
10383
10406
  async function resolveTextTapTarget(bridge, query, options = {}) {
10407
+ const needle = options.exact ? query : query.trim().toLowerCase();
10408
+ if (!needle) return { matched: 0, total: 0 };
10384
10409
  const { tree } = await inspectTree(bridge, 50);
10385
10410
  if (!Array.isArray(tree)) return { matched: 0, total: 0 };
10386
- const needle = options.exact ? query : query.toLowerCase();
10387
10411
  let candidates = collectTextCandidates(tree).filter(
10388
10412
  ({ node: node2, ancestorTestIDs, role }) => {
10389
10413
  const text2 = nodeText(node2);
@@ -10407,7 +10431,7 @@ async function resolveTextTapTarget(bridge, query, options = {}) {
10407
10431
  if (actionable.length > 0) candidates = actionable;
10408
10432
  if (!options.exact) {
10409
10433
  const whole = candidates.filter(
10410
- ({ node: node2 }) => nodeText(node2).trim().toLowerCase() === needle.trim()
10434
+ ({ node: node2 }) => nodeText(node2).trim().toLowerCase() === needle
10411
10435
  );
10412
10436
  if (whole.length > 0) candidates = whole;
10413
10437
  }
@@ -10534,6 +10558,7 @@ var LAUNCH_METADATA_KEYS = [
10534
10558
  "appFonts",
10535
10559
  "appSplash",
10536
10560
  "appCatalog",
10561
+ "appBundleResources",
10537
10562
  "nativeAppIdentity",
10538
10563
  "nativeFirebase",
10539
10564
  "appManifest",
@@ -15908,11 +15933,14 @@ function targetSettings(ref, configuration, builtins, options = {}) {
15908
15933
  if (!config) return [];
15909
15934
  const name2 = str(config.name) ?? configuration;
15910
15935
  const base = str(config.baseConfigurationReference);
15936
+ const anchor = str(config.baseConfigurationReferenceAnchor);
15937
+ const relative3 = str(config.baseConfigurationReferenceRelativePath);
15911
15938
  const own = isDict(config.buildSettings) ? config.buildSettings : {};
15912
- const baseFile = base ? project.resolvePath(base) : null;
15913
- if (options.requireSources !== false && base && (!baseFile || !import_node_fs13.default.existsSync(baseFile)))
15939
+ const directory = anchor ? project.resolvePath(anchor) : null;
15940
+ const baseFile = base ? project.resolvePath(base) : directory && relative3 ? import_node_path13.default.resolve(directory, relative3) : null;
15941
+ if (options.requireSources !== false && (base || anchor || relative3) && (!baseFile || !import_node_fs13.default.existsSync(baseFile)))
15914
15942
  throw new Error(
15915
- `Unable to open base configuration reference file '${baseFile ?? base}'`
15943
+ `Unable to open base configuration reference file '${baseFile ?? base ?? relative3 ?? anchor}'`
15916
15944
  );
15917
15945
  const read = [];
15918
15946
  const fromFile = baseFile ? readXcconfig(baseFile, read) : {};
@@ -15959,6 +15987,9 @@ function targetSettings(ref, configuration, builtins, options = {}) {
15959
15987
  return applyOperator(get("PRODUCT_NAME"), "c99extidentifier");
15960
15988
  case "EXECUTABLE_NAME":
15961
15989
  return get("PRODUCT_NAME");
15990
+ // the native default of CoreBuildSystem.xcspec
15991
+ case "EXCLUDED_RECURSIVE_SEARCH_PATH_SUBDIRECTORIES":
15992
+ return "*.nib *.lproj *.framework *.gch *.xcode* *.xcassets *.icon (*) .DS_Store CVS .svn .git .hg *.pbproj *.pbxproj";
15962
15993
  case "AppIdentifierPrefix":
15963
15994
  case "TeamIdentifierPrefix": {
15964
15995
  const team = get("DEVELOPMENT_TEAM");
@@ -15981,6 +16012,45 @@ function settingList(value) {
15981
16012
  items2.push((quoted ?? plain).replace(/\\(.)/g, "$1"));
15982
16013
  return items2;
15983
16014
  }
16015
+ function expandSearchPath(directory, rules) {
16016
+ if (import_node_path13.default.basename(directory) !== "**") return [directory];
16017
+ const expanded = [];
16018
+ const seen = /* @__PURE__ */ new Set();
16019
+ const queue = [import_node_path13.default.dirname(directory)];
16020
+ for (let at = 0; at < queue.length; at++) {
16021
+ const current = queue[at];
16022
+ let real;
16023
+ try {
16024
+ real = import_node_fs13.default.realpathSync(current);
16025
+ } catch {
16026
+ continue;
16027
+ }
16028
+ if (!import_node_fs13.default.statSync(real).isDirectory() || seen.has(real)) continue;
16029
+ seen.add(real);
16030
+ expanded.push(current);
16031
+ for (const entry of import_node_fs13.default.readdirSync(real, { withFileTypes: true }).sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0)) {
16032
+ if (entry.isSymbolicLink() ? !rules.followLinks : !entry.isDirectory()) continue;
16033
+ if (rules.excluded.some((pattern) => pattern.test(entry.name)) && !rules.included.some((pattern) => pattern.test(entry.name)))
16034
+ continue;
16035
+ queue.push(import_node_path13.default.join(current, entry.name));
16036
+ }
16037
+ }
16038
+ return expanded;
16039
+ }
16040
+ function recursiveSearchRules(settings) {
16041
+ const glob = (pattern) => new RegExp(
16042
+ `^${pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replaceAll("*", ".*").replaceAll("?", ".")}$`
16043
+ );
16044
+ return {
16045
+ excluded: settingList(settings("EXCLUDED_RECURSIVE_SEARCH_PATH_SUBDIRECTORIES")).map(
16046
+ glob
16047
+ ),
16048
+ included: settingList(settings("INCLUDED_RECURSIVE_SEARCH_PATH_SUBDIRECTORIES")).map(
16049
+ glob
16050
+ ),
16051
+ followLinks: settings("RECURSIVE_SEARCH_PATHS_FOLLOW_SYMLINKS") !== "NO"
16052
+ };
16053
+ }
15984
16054
  function compilationConditions(settings) {
15985
16055
  const flags = settingList(settings("OTHER_SWIFT_FLAGS"));
15986
16056
  const defined = flags.flatMap(
@@ -16343,6 +16413,7 @@ function readXcodeAppTarget(input, targetName, configurationName, options = {})
16343
16413
  for (const file of read) xcconfigs.add(file);
16344
16414
  const swiftVersion = Number.parseFloat(settings2("SWIFT_VERSION") || "5");
16345
16415
  const members = targetMembers(ref, settings2, options, keys, xcconfigs);
16416
+ const searchRules = recursiveSearchRules(settings2);
16346
16417
  const implementations = members.allSources.filter(
16347
16418
  (file) => /\.(c|cc|cpp|cxx|m|mm)$/.test(file)
16348
16419
  );
@@ -16385,10 +16456,10 @@ function readXcodeAppTarget(input, targetName, configurationName, options = {})
16385
16456
  headerSearchPaths: [
16386
16457
  ...settingList(settings2("HEADER_SEARCH_PATHS")),
16387
16458
  ...settingList(settings2("USER_HEADER_SEARCH_PATHS"))
16388
- ].map(absolute),
16389
- frameworkSearchPaths: settingList(settings2("FRAMEWORK_SEARCH_PATHS")).map(
16390
- absolute
16391
- ),
16459
+ ].flatMap((raw) => expandSearchPath(absolute(raw), searchRules)),
16460
+ frameworkSearchPaths: settingList(
16461
+ settings2("FRAMEWORK_SEARCH_PATHS")
16462
+ ).flatMap((raw) => expandSearchPath(absolute(raw), searchRules)),
16392
16463
  flags: [
16393
16464
  ...settings2("CLANG_ENABLE_OBJC_ARC") === "YES" ? ["-fobjc-arc"] : [],
16394
16465
  ...settings2("CLANG_ENABLE_OBJC_WEAK") === "YES" ? ["-fobjc-weak"] : [],
@@ -16761,42 +16832,8 @@ function readAndroidNativeAppIdentity(projectRoot, androidMergedManifest) {
16761
16832
  );
16762
16833
  }
16763
16834
  function readIosNativeAppIdentity(projectRoot, parseInfoPlist, iosTarget) {
16764
- const iosRoot = import_node_path14.default.join(projectRoot, "ios");
16765
- if (!import_node_fs14.default.existsSync(iosRoot)) return null;
16766
- const projects = import_node_fs14.default.readdirSync(iosRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.endsWith(".xcodeproj"));
16767
- if (projects.length === 0) return null;
16768
- if (projects.length > 1 && !iosTarget) {
16769
- throw new NativeAppTargetSelectionError(
16770
- `native app identity requires a selected Xcode project; set withPeach's iosTarget to an app target in one of: ${projects.map((entry) => entry.name).join(", ")}`
16771
- );
16772
- }
16773
- let project = projects[0];
16774
- if (iosTarget && projects.length > 1) {
16775
- const matches = projects.filter(
16776
- (entry) => listXcodeAppTargets(import_node_path14.default.join(iosRoot, entry.name), {
16777
- requireSources: false
16778
- }).includes(iosTarget)
16779
- );
16780
- if (matches.length === 0) {
16781
- throw new Error(`native app identity has no application target ${iosTarget}`);
16782
- }
16783
- if (matches.length > 1) {
16784
- throw new Error(
16785
- `native app identity target ${iosTarget} is ambiguous across ${matches.map((entry) => entry.name).join(", ")}`
16786
- );
16787
- }
16788
- project = matches[0];
16789
- }
16790
- const xcodeproj = import_node_path14.default.join(iosRoot, project.name);
16791
- const appTargets = listXcodeAppTargets(xcodeproj, { requireSources: false });
16792
- if (!iosTarget && appTargets.length > 1) {
16793
- throw new NativeAppTargetSelectionError(
16794
- `native app identity requires a selected app target; set withPeach's iosTarget to one of: ${appTargets.join(", ")}`
16795
- );
16796
- }
16797
- const app = readXcodeAppTarget(xcodeproj, iosTarget ?? appTargets[0], "Release", {
16798
- requireSources: false
16799
- });
16835
+ const app = readIosAppTarget(projectRoot, iosTarget);
16836
+ if (!app) return null;
16800
16837
  const identity = readNativeAppIdentityFromXcodeTarget(app, parseInfoPlist);
16801
16838
  const projectManifestPath = import_node_path14.default.join(projectRoot, "package.json");
16802
16839
  const projectRequire = (0, import_node_module3.createRequire)(projectManifestPath);
@@ -16849,6 +16886,44 @@ function readIosNativeAppIdentity(projectRoot, parseInfoPlist, iosTarget) {
16849
16886
  }
16850
16887
  return { ...identity, reanimatedStaticFeatureFlags: flags };
16851
16888
  }
16889
+ function readIosAppTarget(projectRoot, iosTarget) {
16890
+ const iosRoot = import_node_path14.default.join(projectRoot, "ios");
16891
+ if (!import_node_fs14.default.existsSync(iosRoot)) return null;
16892
+ const projects = import_node_fs14.default.readdirSync(iosRoot, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.endsWith(".xcodeproj"));
16893
+ if (projects.length === 0) return null;
16894
+ if (projects.length > 1 && !iosTarget) {
16895
+ throw new NativeAppTargetSelectionError(
16896
+ `native app identity requires a selected Xcode project; set withPeach's iosTarget to an app target in one of: ${projects.map((entry) => entry.name).join(", ")}`
16897
+ );
16898
+ }
16899
+ let project = projects[0];
16900
+ if (iosTarget && projects.length > 1) {
16901
+ const matches = projects.filter(
16902
+ (entry) => listXcodeAppTargets(import_node_path14.default.join(iosRoot, entry.name), {
16903
+ requireSources: false
16904
+ }).includes(iosTarget)
16905
+ );
16906
+ if (matches.length === 0) {
16907
+ throw new Error(`native app identity has no application target ${iosTarget}`);
16908
+ }
16909
+ if (matches.length > 1) {
16910
+ throw new Error(
16911
+ `native app identity target ${iosTarget} is ambiguous across ${matches.map((entry) => entry.name).join(", ")}`
16912
+ );
16913
+ }
16914
+ project = matches[0];
16915
+ }
16916
+ const xcodeproj = import_node_path14.default.join(iosRoot, project.name);
16917
+ const appTargets = listXcodeAppTargets(xcodeproj, { requireSources: false });
16918
+ if (!iosTarget && appTargets.length > 1) {
16919
+ throw new NativeAppTargetSelectionError(
16920
+ `native app identity requires a selected app target; set withPeach's iosTarget to one of: ${appTargets.join(", ")}`
16921
+ );
16922
+ }
16923
+ return readXcodeAppTarget(xcodeproj, iosTarget ?? appTargets[0], "Release", {
16924
+ requireSources: false
16925
+ });
16926
+ }
16852
16927
 
16853
16928
  // src/native-firebase-config.ts
16854
16929
  var import_node_child_process6 = require("node:child_process");
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.1640 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.1644 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.1640 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.1644 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.1640 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.1644 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
package/dist-lib/menu.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.1640 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.1644 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -90,7 +90,13 @@ var SOOTSIM_RENDER_RESOLUTION_LABELS = {
90
90
  medium: "Medium",
91
91
  low: "Low"
92
92
  };
93
- var SOOTSIM_SCREEN_EFFECTS = ["none", "wireframe", "blueprint", "panel", "green"];
93
+ var SOOTSIM_SCREEN_EFFECTS = [
94
+ "none",
95
+ "wireframe",
96
+ "blueprint",
97
+ "panel",
98
+ "green"
99
+ ];
94
100
  function isSootSimScreenEffect(value) {
95
101
  return value === "none" || value === "wireframe" || value === "blueprint" || value === "panel" || value === "green";
96
102
  }
@@ -275,9 +281,9 @@ function deviceMenu(state, deviceChoices) {
275
281
  },
276
282
  { type: "separator" },
277
283
  { label: "Trigger Screenshot", action: "screenshot" },
278
- // pose controls for the current device: open/close on foldables,
279
- // rotate left/right wherever a turn changes geometry, the way ios
280
- // simulator holds them (rotate left is cmd+left, rotate right
284
+ // pose controls for the current device: open/close, book and laptop on
285
+ // foldables, rotate left/right wherever a turn changes geometry, the way
286
+ // ios simulator holds them (rotate left is cmd+left, rotate right
281
287
  // cmd+right). truthiness, not !== null: a partial state must hide
282
288
  // these, never show duo items for the wrong device.
283
289
  ...state.devicePosture || state.canRotate ? [
@@ -286,7 +292,9 @@ function deviceMenu(state, deviceChoices) {
286
292
  {
287
293
  label: state.devicePosture === "open" ? "Close iPhone Duo" : "Open iPhone Duo",
288
294
  action: "toggle-duo-posture"
289
- }
295
+ },
296
+ { label: "Book Pose", action: "fold-duo-book" },
297
+ { label: "Laptop Pose", action: "fold-duo-laptop" }
290
298
  ] : [],
291
299
  ...state.canRotate ? [
292
300
  {
package/dist-lib/menu.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.1640 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.1644 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
 
3
3
  // src/public-brand.ts
4
4
  var name = "Peach";
@@ -43,7 +43,13 @@ var SOOTSIM_RENDER_RESOLUTION_LABELS = {
43
43
  medium: "Medium",
44
44
  low: "Low"
45
45
  };
46
- var SOOTSIM_SCREEN_EFFECTS = ["none", "wireframe", "blueprint", "panel", "green"];
46
+ var SOOTSIM_SCREEN_EFFECTS = [
47
+ "none",
48
+ "wireframe",
49
+ "blueprint",
50
+ "panel",
51
+ "green"
52
+ ];
47
53
  function isSootSimScreenEffect(value) {
48
54
  return value === "none" || value === "wireframe" || value === "blueprint" || value === "panel" || value === "green";
49
55
  }
@@ -228,9 +234,9 @@ function deviceMenu(state, deviceChoices) {
228
234
  },
229
235
  { type: "separator" },
230
236
  { label: "Trigger Screenshot", action: "screenshot" },
231
- // pose controls for the current device: open/close on foldables,
232
- // rotate left/right wherever a turn changes geometry, the way ios
233
- // simulator holds them (rotate left is cmd+left, rotate right
237
+ // pose controls for the current device: open/close, book and laptop on
238
+ // foldables, rotate left/right wherever a turn changes geometry, the way
239
+ // ios simulator holds them (rotate left is cmd+left, rotate right
234
240
  // cmd+right). truthiness, not !== null: a partial state must hide
235
241
  // these, never show duo items for the wrong device.
236
242
  ...state.devicePosture || state.canRotate ? [
@@ -239,7 +245,9 @@ function deviceMenu(state, deviceChoices) {
239
245
  {
240
246
  label: state.devicePosture === "open" ? "Close iPhone Duo" : "Open iPhone Duo",
241
247
  action: "toggle-duo-posture"
242
- }
248
+ },
249
+ { label: "Book Pose", action: "fold-duo-book" },
250
+ { label: "Laptop Pose", action: "fold-duo-laptop" }
243
251
  ] : [],
244
252
  ...state.canRotate ? [
245
253
  {
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.1640 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.1644 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.1640 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.1644 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
 
3
3
  // src/is-record.ts
4
4
  function isRecord(value) {
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.1640 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.1644 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.1640 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.1644 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  var __defProp = Object.defineProperty;
3
3
  var __getOwnPropNames = Object.getOwnPropertyNames;
4
4
  var __esm = (fn, res) => function __init() {