peachsim 0.1.2180 → 0.1.2182

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/cli/commands/shell-url.ts +28 -22
  2. package/dist-lib/agent-daemon-client.cjs +1 -1
  3. package/dist-lib/agent-events.cjs +1 -1
  4. package/dist-lib/attached-projects.cjs +1 -1
  5. package/dist-lib/auth/shared-session.cjs +1 -1
  6. package/dist-lib/backend-origin.cjs +1 -1
  7. package/dist-lib/beta.cjs +1 -1
  8. package/dist-lib/beta.mjs +1 -1
  9. package/dist-lib/bridge-constants.cjs +1 -1
  10. package/dist-lib/bridge-contract-input.cjs +1 -1
  11. package/dist-lib/bridge-contract-input.mjs +1 -1
  12. package/dist-lib/bridge-contract.cjs +1 -1
  13. package/dist-lib/bridge-contract.mjs +1 -1
  14. package/dist-lib/bundle-channel.cjs +141 -70
  15. package/dist-lib/capture-contract.cjs +1 -1
  16. package/dist-lib/capture-contract.mjs +1 -1
  17. package/dist-lib/cloud-contract.cjs +1 -1
  18. package/dist-lib/cloud-contract.mjs +1 -1
  19. package/dist-lib/cloud-test-args.cjs +1 -1
  20. package/dist-lib/cloud-test-args.mjs +1 -1
  21. package/dist-lib/cloud.cjs +1 -1
  22. package/dist-lib/cloud.mjs +1 -1
  23. package/dist-lib/config.cjs +1 -1
  24. package/dist-lib/config.mjs +1 -1
  25. package/dist-lib/detox/index.cjs +1 -1
  26. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  27. package/dist-lib/expo-swift-bundle.cjs +68 -36
  28. package/dist-lib/expo-swift-metadata.cjs +1 -1
  29. package/dist-lib/expo-swift-metadata.mjs +1 -1
  30. package/dist-lib/home-paths.cjs +1 -1
  31. package/dist-lib/host/bridge-host.cjs +106 -59
  32. package/dist-lib/host/fetch-proxy-handler.cjs +3 -3
  33. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  34. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  35. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  36. package/dist-lib/host/websocket-proxy.cjs +1 -1
  37. package/dist-lib/index.cjs +3 -3
  38. package/dist-lib/jump-to-source-babel.cjs +1 -1
  39. package/dist-lib/jump-to-source-native.cjs +1 -1
  40. package/dist-lib/kotlin-host.cjs +1 -1
  41. package/dist-lib/menu.cjs +1 -1
  42. package/dist-lib/menu.mjs +1 -1
  43. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  44. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  45. package/dist-lib/metro-production-bundle.cjs +1 -1
  46. package/dist-lib/metro-production-bundle.mjs +1 -1
  47. package/dist-lib/metro.cjs +3 -3
  48. package/dist-lib/native-app-identity-project.cjs +2 -2
  49. package/dist-lib/native-app-identity-project.mjs +2 -2
  50. package/dist-lib/native-app-identity.cjs +1 -1
  51. package/dist-lib/native-app-identity.mjs +1 -1
  52. package/dist-lib/native-firebase-metadata.cjs +1 -1
  53. package/dist-lib/native-firebase-metadata.mjs +1 -1
  54. package/dist-lib/pdfium-raster.cjs +1 -1
  55. package/dist-lib/pdfium-raster.mjs +1 -1
  56. package/dist-lib/plist.cjs +1 -1
  57. package/dist-lib/plist.mjs +1 -1
  58. package/dist-lib/profiles.cjs +1 -1
  59. package/dist-lib/public-brand.cjs +1 -1
  60. package/dist-lib/public-origins-lib-esm.json +24 -24
  61. package/dist-lib/public-origins-lib.json +53 -53
  62. package/dist-lib/react-native-host-modules.cjs +1 -1
  63. package/dist-lib/react-native-host-modules.mjs +1 -1
  64. package/dist-lib/reap-browser.cjs +1 -1
  65. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  66. package/dist-lib/sdk.cjs +1 -1
  67. package/dist-lib/sdk.mjs +1 -1
  68. package/dist-lib/skills.cjs +131 -67
  69. package/dist-lib/swift-compile.cjs +13 -4
  70. package/dist-lib/swift-compile.mjs +13 -4
  71. package/dist-lib/swift-package.cjs +1 -1
  72. package/dist-lib/swift-package.mjs +1 -1
  73. package/dist-lib/swift-submit.cjs +2 -2
  74. package/dist-lib/swift-submit.mjs +2 -2
  75. package/dist-lib/swift-toolchain.cjs +1 -1
  76. package/dist-lib/swift.cjs +1 -1
  77. package/dist-lib/vite-swift.cjs +15 -6
  78. package/dist-lib/vite.cjs +3 -3
  79. package/dist-lib/worklet-executable.cjs +1 -1
  80. package/dist-lib/worklet-executable.mjs +1 -1
  81. package/dist-types/packages/peach-engine/src/render-worker/map-command-channel.d.ts +14 -0
  82. package/package.json +1 -1
  83. package/src/expo-native-workspace.ts +17 -20
  84. package/src/expo-swift-bundle.ts +6 -1
  85. package/src/expo-swift-host.ts +35 -6
  86. package/src/expoSwiftContract.ts +3 -0
  87. package/src/native-bundles.ts +30 -10
  88. package/src/one-sdk/source.ts +10 -4
  89. package/src/one-sdk-bundle.ts +56 -24
  90. package/src/swift-compile.ts +3 -1
  91. package/src/swift-submit.ts +2 -1
  92. package/src/wasm-optimize.ts +11 -1
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.2180 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.2182 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = '', __sootsim_import_meta_filename = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; __sootsim_import_meta_filename = __filename; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -14977,6 +14977,13 @@ var BINARYEN_RELEASES = /* @__PURE__ */ new Map([
14977
14977
  ]
14978
14978
  ]);
14979
14979
  async function runWasmOpt(file, output2, skipPasses, binary) {
14980
+ const admittedCores = windowAdmission()?.cores ?? Number(process.env.TM_EXEC_CPU_CORES);
14981
+ const requestedCores = Number(process.env.BINARYEN_CORES);
14982
+ const environment = { ...process.env, WASM_OPT_PATH: binary };
14983
+ if (Number.isInteger(admittedCores) && admittedCores > 0)
14984
+ environment.BINARYEN_CORES = String(
14985
+ Number.isInteger(requestedCores) && requestedCores > 0 ? Math.min(admittedCores, requestedCores) : admittedCores
14986
+ );
14980
14987
  const result = await (0, import_wasm_opt.wasmOpt)(
14981
14988
  [
14982
14989
  "-Oz",
@@ -14986,7 +14993,7 @@ async function runWasmOpt(file, output2, skipPasses, binary) {
14986
14993
  "-o",
14987
14994
  output2
14988
14995
  ],
14989
- { env: { ...process.env, WASM_OPT_PATH: binary } }
14996
+ { env: environment }
14990
14997
  );
14991
14998
  if (result.status !== 0)
14992
14999
  throw new Error(`wasm-opt exited ${result.status}: ${result.stderr}`);
@@ -17039,7 +17046,7 @@ var darwinRecordsRegion = (quoted) => quoted.length ? ["-Wl,--start-lib", ...quo
17039
17046
  var REACTOR_FLAG = '"-Xclang-linker", "-mexec-model=reactor",\n';
17040
17047
  var MULTI_MANIFEST_VERSION = 68;
17041
17048
  var SWIFT_HEADER_PRODUCTS_TARGET = "PeachHeaderProducts";
17042
- var compilerSourceDigest = true ? "f4c68ce2ea46dd5a9205990046616e5dcc4a3b3b527378ee285d9e25865e5851" : swiftCompilerSourceDigest(__sootsim_import_meta_dirname);
17049
+ var compilerSourceDigest = true ? "73960368f0b83cf042511655d857d5f64204fa539e2c984423a12c08657b068e" : swiftCompilerSourceDigest(__sootsim_import_meta_dirname);
17043
17050
  function swiftArtifactHash(input, release, moduleRoot) {
17044
17051
  const hash = (0, import_node_crypto13.createHash)("sha256");
17045
17052
  hash.update(`swift-compile
@@ -20343,7 +20350,7 @@ function readCompiledArtifact(directory, hash) {
20343
20350
  return null;
20344
20351
  const bytes = (0, import_node_fs27.readFileSync)(import_node_path27.default.join(directory, "app.wasm"));
20345
20352
  const gzip2 = (0, import_node_fs27.readFileSync)(import_node_path27.default.join(directory, "icu.gz"));
20346
- if ((0, import_node_crypto13.createHash)("sha256").update(bytes).digest("hex") !== receipt.wasm || (0, import_node_crypto13.createHash)("sha256").update(gzip2).digest("hex") !== receipt.icu || bytes.length < 8 || bytes.readUInt32LE(0) !== 1836278016 || bytes.readUInt32LE(4) !== 1)
20353
+ if ((0, import_node_crypto13.createHash)("sha256").update(bytes).digest("hex") !== receipt.wasm || (0, import_node_crypto13.createHash)("sha256").update(gzip2).digest("hex") !== receipt.icu || bytes.length <= 8 || bytes.readUInt32LE(0) !== 1836278016 || bytes.readUInt32LE(4) !== 1)
20347
20354
  return null;
20348
20355
  return { hash, bytes, icu: { sha256: receipt.icu, gzip: gzip2 } };
20349
20356
  } catch (error) {
@@ -21405,6 +21412,8 @@ function compileSwiftProjectOnce(input, options) {
21405
21412
  bytes: (0, import_node_fs27.readFileSync)(executable),
21406
21413
  icu: await icu
21407
21414
  };
21415
+ if (artifact.bytes.length <= 8)
21416
+ throw new Error("swift-compile: deferred linker produced no executable");
21408
21417
  if (!options.project) saveCompiledArtifact(root, artifact);
21409
21418
  saveSharedArtifact(sharedRoot, artifact);
21410
21419
  report("compiled artifact verified");
@@ -29589,7 +29598,7 @@ var MAX_SWIFT_COMPILE_METAL_TOTAL_BYTES = 8 * 1024 * 1024;
29589
29598
  var MAX_SWIFT_COMPILE_IMAGES = 256;
29590
29599
  var MAX_SWIFT_COMPILE_IMAGE_BYTES = 4 * 1024 * 1024;
29591
29600
  var MAX_SWIFT_COMPILE_IMAGE_TOTAL_BYTES = 16 * 1024 * 1024;
29592
- var SWIFT_HOST_ABI = 69;
29601
+ var SWIFT_HOST_ABI = 70;
29593
29602
  var POLL_INTERVAL_MS = 1500;
29594
29603
  function swiftNativeEntry(native, file) {
29595
29604
  return {
@@ -30440,7 +30449,7 @@ function sourceClosureDigest(srcDir, entrypoints) {
30440
30449
  return hash.digest("hex");
30441
30450
  }
30442
30451
  var standinSourceDigest = (srcDir = __sootsim_import_meta_dirname) => sourceClosureDigest(srcDir, STANDIN_DIGEST_ENTRYPOINTS);
30443
- var STANDIN_SOURCE_DIGEST = true ? "f2fd734797f09885af5cbd2e5fd5b69c0958bd1f06e2a2e9454cbae6de386412" : standinSourceDigest();
30452
+ var STANDIN_SOURCE_DIGEST = true ? "cc396d180b261e922288f645807b4a704fa92410ffc902bfbaac72b81af6413a" : standinSourceDigest();
30444
30453
 
30445
30454
  // src/swift-standins.ts
30446
30455
  init_define_SWIFT_COVERAGE_SOURCE_DIGESTS();
package/dist-lib/vite.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.2180 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.2182 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = '', __sootsim_import_meta_filename = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; __sootsim_import_meta_filename = __filename; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1909,7 +1909,7 @@ function readIosSdkTree(root) {
1909
1909
 
1910
1910
  // src/swift-standin-digest.ts
1911
1911
  init_define_SWIFT_COVERAGE_SOURCE_DIGESTS();
1912
- var STANDIN_SOURCE_DIGEST = true ? "f2fd734797f09885af5cbd2e5fd5b69c0958bd1f06e2a2e9454cbae6de386412" : standinSourceDigest();
1912
+ var STANDIN_SOURCE_DIGEST = true ? "cc396d180b261e922288f645807b4a704fa92410ffc902bfbaac72b81af6413a" : standinSourceDigest();
1913
1913
 
1914
1914
  // src/expo-native-workspace.ts
1915
1915
  init_define_SWIFT_COVERAGE_SOURCE_DIGESTS();
@@ -5388,7 +5388,7 @@ var FOUNDATION_IMPLICIT_MODULES = [
5388
5388
  var FOUNDATION_IMPLICIT_FLAGS = FOUNDATION_IMPLICIT_MODULES.map(
5389
5389
  (module2) => `, "-Xfrontend", "-import-module", "-Xfrontend", "${module2}"`
5390
5390
  ).join("");
5391
- var compilerSourceDigest = true ? "f4c68ce2ea46dd5a9205990046616e5dcc4a3b3b527378ee285d9e25865e5851" : swiftCompilerSourceDigest2(__sootsim_import_meta_dirname);
5391
+ var compilerSourceDigest = true ? "73960368f0b83cf042511655d857d5f64204fa539e2c984423a12c08657b068e" : swiftCompilerSourceDigest2(__sootsim_import_meta_dirname);
5392
5392
  var CORE_FOUNDATION_HANDLE_IMPORTS = [
5393
5393
  "CFAllocator",
5394
5394
  "CFArray",
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.2180 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.2182 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
  let __sootsim_import_meta_url = '', __sootsim_import_meta_dirname = '', __sootsim_import_meta_filename = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; __sootsim_import_meta_dirname = __dirname; __sootsim_import_meta_filename = __filename; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! peach v0.1.2180 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
1
+ /*! peach v0.1.2182 | (c) 2026 Tamagui LLC | Proprietary; see LICENSE */
2
2
 
3
3
  // src/worklet-executable.ts
4
4
  import { parse } from "acorn";
@@ -9,16 +9,22 @@ export type MapStyle = string | {
9
9
  sources: Record<string, unknown>;
10
10
  layers: Array<Record<string, unknown>>;
11
11
  };
12
+ export type MapScaleLocale = {
13
+ languageTag: string;
14
+ measurementSystem: 'metric' | 'us' | 'uk';
15
+ };
12
16
  export type MapMountOptions = {
13
17
  style?: MapStyle;
14
18
  nativeAppearance?: 'light' | 'dark';
15
19
  apple?: NativeSurfaceIdentity & {
16
20
  token: string;
17
21
  evaluationId: string;
22
+ locale: MapScaleLocale;
18
23
  };
19
24
  mapType?: 'satellite' | 'hybrid';
20
25
  controls?: {
21
26
  kind: 'compass' | 'scale' | 'userLocation';
27
+ top?: number;
22
28
  }[];
23
29
  projection?: MapProjection;
24
30
  center?: LngLat;
@@ -149,6 +155,14 @@ export type MapCommand = {
149
155
  kind: 'markers';
150
156
  mapId: string;
151
157
  markers: MapMarker[];
158
+ } | {
159
+ kind: 'locale';
160
+ mapId: string;
161
+ locale: MapScaleLocale;
162
+ } | {
163
+ kind: 'controls';
164
+ mapId: string;
165
+ controls: NonNullable<MapMountOptions['controls']>;
152
166
  } | {
153
167
  kind: 'overlays';
154
168
  mapId: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "peachsim",
3
- "version": "0.1.2180",
3
+ "version": "0.1.2182",
4
4
  "description": "Vite and Metro plugins, testing drivers, and SDK exports for peach.",
5
5
  "author": "Tamagui LLC",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -877,6 +877,7 @@ async function prepareWorkspace(
877
877
  // a pinned bun installs into its own prefix: npm exec keeps one cached copy per spec, so a copy
878
878
  // installed without its postinstall (hosts that set ignore-scripts) would stay broken
879
879
  const bunPrefix = path.join(cacheDir(), 'package-managers', manager)
880
+ const bunExecutable = path.join(bunPrefix, 'node_modules/.bin/bun')
880
881
  const install =
881
882
  manager === 'pnpm' || manager.startsWith('pnpm@')
882
883
  ? [
@@ -893,11 +894,7 @@ async function prepareWorkspace(
893
894
  : manager === 'bun' || manager.startsWith('bun@')
894
895
  ? manager === 'bun'
895
896
  ? ['bun', 'install', '--frozen-lockfile']
896
- : [
897
- path.join(bunPrefix, 'node_modules/.bin/bun'),
898
- 'install',
899
- '--frozen-lockfile',
900
- ]
897
+ : [bunExecutable, 'install', '--frozen-lockfile']
901
898
  : // npm ci would delete the cloned tree before installing it again
902
899
  ['npm', 'install', '--no-audit', '--no-fund']
903
900
  // dependencies such as react-native-skia fetch their native binaries in a lifecycle
@@ -907,22 +904,22 @@ async function prepareWorkspace(
907
904
  const command = [...prefix, ...args]
908
905
  await run(command[0]!, command.slice(1), cwd, env)
909
906
  }
907
+ if (manager.startsWith('bun@') && (!projectReady || !fs.existsSync(bunExecutable)))
908
+ await execute(
909
+ [
910
+ 'npm',
911
+ 'install',
912
+ '--prefix',
913
+ bunPrefix,
914
+ // the bun package ships a placeholder binary its postinstall replaces
915
+ '--ignore-scripts=false',
916
+ '--no-audit',
917
+ '--no-fund',
918
+ manager,
919
+ ],
920
+ work,
921
+ )
910
922
  if (!projectReady) {
911
- if (manager.startsWith('bun@'))
912
- await execute(
913
- [
914
- 'npm',
915
- 'install',
916
- '--prefix',
917
- bunPrefix,
918
- // the bun package ships a placeholder binary its postinstall replaces
919
- '--ignore-scripts=false',
920
- '--no-audit',
921
- '--no-fund',
922
- manager,
923
- ],
924
- work,
925
- )
926
923
  // npm install keeps the cloned tree but can otherwise rewrite a stale lockfile.
927
924
  if (install[0] === 'npm' && fs.existsSync(path.join(work, 'package-lock.json'))) {
928
925
  await execute(
@@ -2,7 +2,10 @@
2
2
  // its immutable native assets beside the javascript bundle.
3
3
  import { compileExpoSwiftModules } from './expo-swift-host'
4
4
  import { swiftIcuBundleAsset } from './swift-icu'
5
- import type { ExpoSwiftCompilationResult } from './expoSwiftContract'
5
+ import type {
6
+ ExpoSwiftCompilationResult,
7
+ ExpoSwiftPreparationOptions,
8
+ } from './expoSwiftContract'
6
9
 
7
10
  export interface ExpoSwiftBundle {
8
11
  metadata: ExpoSwiftCompilationResult
@@ -11,6 +14,7 @@ export interface ExpoSwiftBundle {
11
14
 
12
15
  export async function buildExpoSwiftBundle(
13
16
  appRoot: string,
17
+ oneSDK?: ExpoSwiftPreparationOptions['oneSDK'],
14
18
  ): Promise<ExpoSwiftBundle | null> {
15
19
  const assets: ExpoSwiftBundle['assets'] = []
16
20
  const metadata = await compileExpoSwiftModules(
@@ -25,6 +29,7 @@ export async function buildExpoSwiftBundle(
25
29
  })
26
30
  if ('gzip' in artifact.icu) assets.push(await swiftIcuBundleAsset(artifact.icu))
27
31
  },
32
+ { oneSDK },
28
33
  )
29
34
  if (!metadata) return null
30
35
 
@@ -1999,7 +1999,16 @@ function targetSettings(units: ExpoSwiftSourceUnit[]) {
1999
1999
  return settings
2000
2000
  }
2001
2001
 
2002
- function expoEntry(units: ExpoSwiftSourceUnit[]): string {
2002
+ function oneSDKStamp(oneSDK: ExpoSwiftPreparationOptions['oneSDK']): string {
2003
+ if (!oneSDK) return ''
2004
+ const hash = createHash('sha256')
2005
+ for (const files of [oneSDK.targets, oneSDK.entry])
2006
+ for (const [name, source] of [...files].sort(([a], [b]) => a.localeCompare(b)))
2007
+ hash.update(`${name}\0${source}\0`)
2008
+ return hash.digest('hex')
2009
+ }
2010
+
2011
+ function expoEntry(units: ExpoSwiftSourceUnit[], oneSDK = false): string {
2003
2012
  // an Objective-C bridge module registers with no Swift of its own
2004
2013
  const namespaces = [...new Set(units.map((unit) => unit.namespace))]
2005
2014
  .filter((namespace) => namespace !== 'PeachExpoApp')
@@ -2015,7 +2024,11 @@ function expoEntry(units: ExpoSwiftSourceUnit[]): string {
2015
2024
  subscribers.length + handlers.length > 0
2016
2025
  ? `; PeachExpoRuntime.registerDelegates(subscribers: [${subscribers.join(', ')}], handlers: [${handlers.join(', ')}])`
2017
2026
  : ''
2018
- return `import SwiftUI\ninternal import ExpoModulesCore\n${bridgeModules.length ? 'import React\n' : ''}${namespaces.map((namespace) => `import ${namespace}`).join('\n')}\n\n@main struct PeachExpoApp: PeachPackage {\n init() { MainActor.assumeIsolated { PeachExpoRuntime.register([${units.flatMap((unit) => unit.classes).join(', ')}])${delegates}${bridgeModules.length ? `; RCTModuleHost.register([${bridgeModules.join(', ')}])` : ''} } }\n func view(props: JSON) -> PeachExpoNativeView { PeachExpoNativeView(props: props) }\n}\n\n@_expose(wasm, "peach_expo_call")\n@_cdecl("peach_expo_call")\n@MainActor public func peachExpoCall(_ payload: UnsafePointer<UInt8>?, _ length: Int32) -> UnsafeMutablePointer<UInt8>? {\n PeachExpoRuntime.dispatch(payload, length)\n}\n`
2027
+ // a One SDK host component marks its view; every other view is an Expo view
2028
+ const view = oneSDK
2029
+ ? '@ViewBuilder func view(props: JSON) -> some View {\n if props["oneSDKHost"] != nil { OneSDKUIKitHost(props: props) } else { PeachExpoNativeView(props: props) }\n }'
2030
+ : 'func view(props: JSON) -> PeachExpoNativeView { PeachExpoNativeView(props: props) }'
2031
+ return `import SwiftUI\ninternal import ExpoModulesCore\n${bridgeModules.length ? 'import React\n' : ''}${oneSDK ? 'import One\nimport OneSDKBindings\n' : ''}${namespaces.map((namespace) => `import ${namespace}`).join('\n')}\n\n@main struct PeachExpoApp: PeachPackage {\n init() { MainActor.assumeIsolated { PeachExpoRuntime.register([${units.flatMap((unit) => unit.classes).join(', ')}])${delegates}${bridgeModules.length ? `; RCTModuleHost.register([${bridgeModules.join(', ')}])` : ''} } }\n ${view}\n}\n\n@_expose(wasm, "peach_expo_call")\n@_cdecl("peach_expo_call")\n@MainActor public func peachExpoCall(_ payload: UnsafePointer<UInt8>?, _ length: Int32) -> UnsafeMutablePointer<UInt8>? {\n PeachExpoRuntime.dispatch(payload, length)\n}\n`
2019
2032
  }
2020
2033
 
2021
2034
  // a compile outlives its client: a dropped open leaves the work running so the
@@ -2153,7 +2166,7 @@ export async function compileExpoSwiftModules(
2153
2166
  installSwiftInputTrace()
2154
2167
  let key: string
2155
2168
  try {
2156
- key = `${await discoveryKey(root, options.installedWorkspace, true, options.iosTarget, options.moduleRoot)}\0${options.only?.join(',') ?? ''}`
2169
+ key = `${await discoveryKey(root, options.installedWorkspace, true, options.iosTarget, options.moduleRoot)}\0${options.only?.join(',') ?? ''}\0${oneSDKStamp(options.oneSDK)}`
2157
2170
  } catch {
2158
2171
  return compileExpoSwiftModulesTraced(root, base, publishArtifact, options)
2159
2172
  }
@@ -2406,11 +2419,23 @@ async function compileExpoSwiftModulesTraced(
2406
2419
  throw new Error(`conflicting Expo Swift binary source ${name}`)
2407
2420
  files[name] = source
2408
2421
  }
2409
- files[EXPO_SWIFT_ENTRY] = expoEntry(linked)
2422
+ const settings = targetSettings(linked)
2423
+ if (options.oneSDK) {
2424
+ for (const [name, source] of options.oneSDK.targets) files[name] = source
2425
+ for (const [name, source] of options.oneSDK.entry)
2426
+ files[`Sources/PeachExpoApp/${name}`] = source
2427
+ settings.set('OneSDKBindings', {
2428
+ languageMode: 5,
2429
+ mainActorIsolation: false,
2430
+ swiftFlags: ['-suppress-warnings'],
2431
+ dependencies: [{ kind: 'target', name: 'One' }],
2432
+ })
2433
+ }
2434
+ files[EXPO_SWIFT_ENTRY] = expoEntry(linked, options.oneSDK !== undefined)
2410
2435
  files['Package.swift'] = expoManifest(
2411
2436
  files,
2412
2437
  linked.map((unit) => generations.get(unit) ?? '').join(','),
2413
- targetSettings(linked),
2438
+ settings,
2414
2439
  )
2415
2440
  const provider = linked.some((unit) => unit.reactNativeProvider)
2416
2441
  ? reactProvider
@@ -2648,7 +2673,11 @@ async function compileExpoSwiftModulesTraced(
2648
2673
  stamps.set(unit, stamp)
2649
2674
  candidates.push(unit)
2650
2675
  }
2651
- const preparedKey = expoSwiftPreparedKey(base, units, reactProviderStamp)
2676
+ const preparedKey = expoSwiftPreparedKey(
2677
+ base,
2678
+ units,
2679
+ reactProviderStamp + oneSDKStamp(options.oneSDK),
2680
+ )
2652
2681
  if (!publishArtifact) {
2653
2682
  const prepared = preparedExpoSwift.get(preparedKey)
2654
2683
  if (prepared) {
@@ -5,6 +5,7 @@ import {
5
5
  import { isRecord } from './is-record'
6
6
  import { isNativeAppIdentity, type NativeAppIdentity } from './native-app-identity'
7
7
  import { isSwiftResourcePath } from './swiftContract'
8
+ import type { OneSDKLinkSources } from './one-sdk-bundle'
8
9
  import type {
9
10
  SwiftNativeTarget,
10
11
  SwiftHeaderAliases,
@@ -19,6 +20,8 @@ export interface ExpoSwiftPreparationOptions {
19
20
  moduleRoot?: string
20
21
  // compile only these Swift modules and the modules they import
21
22
  only?: readonly string[]
23
+ // the app's One SDK, linked into the same executable: one process per app
24
+ oneSDK?: Pick<OneSDKLinkSources, 'targets' | 'entry'>
22
25
  }
23
26
 
24
27
  export interface ExpoSwiftSourceOptions {
@@ -47,17 +47,37 @@ export async function buildNativeBundles(
47
47
  configuration: 'Debug' | 'Release',
48
48
  ): Promise<NativeBundles> {
49
49
  return getRuntimePlatformAdapter(platform).select<() => Promise<NativeBundles>>({
50
- ios: async () => ({
51
- oneSDK: await (await import('./one-sdk-bundle')).buildOneSDKBundle(projectRoot),
52
- expoSwift: await (
50
+ ios: async () => {
51
+ const { appOneSDKLinkSources, buildOneSDKBundle } = await import('./one-sdk-bundle')
52
+ // on iOS an app is one process: its One SDK links into the Expo executable
53
+ // when the app has one, and its process serves both
54
+ const oneSDK = await appOneSDKLinkSources(projectRoot)
55
+ const expoSwift = await (
53
56
  await import('./expo-swift-bundle')
54
- ).buildExpoSwiftBundle(projectRoot),
55
- nitro: await (
56
- await import('./nitro-bundle')
57
- ).buildNitroBundle(projectRoot, configuration),
58
- turbo: await (await import('./turbo-bundle')).buildTurboBundle(projectRoot),
59
- expoKotlin: null,
60
- }),
57
+ ).buildExpoSwiftBundle(projectRoot, oneSDK ?? undefined)
58
+ const linked = oneSDK && expoSwift?.metadata.modules[0]
59
+ return {
60
+ oneSDK: linked
61
+ ? {
62
+ metadata: {
63
+ url: linked.url,
64
+ hash: linked.hash,
65
+ icuUrl: linked.icuUrl,
66
+ rootId: `${linked.rootId.split('/')[0]}/OneSDK`,
67
+ bundleFiles: linked.bundleFiles ?? {},
68
+ modules: oneSDK.modules,
69
+ },
70
+ assets: [],
71
+ }
72
+ : await buildOneSDKBundle(projectRoot),
73
+ expoSwift,
74
+ nitro: await (
75
+ await import('./nitro-bundle')
76
+ ).buildNitroBundle(projectRoot, configuration),
77
+ turbo: await (await import('./turbo-bundle')).buildTurboBundle(projectRoot),
78
+ expoKotlin: null,
79
+ }
80
+ },
61
81
  android: async () => ({
62
82
  oneSDK: null,
63
83
  expoSwift: null,
@@ -30,12 +30,13 @@ extension OneNativeComposable {
30
30
  }
31
31
  `
32
32
 
33
- export const oneSDKApp = String.raw`import SwiftUI
33
+ // the view a One SDK host component mounts, in whichever executable links the
34
+ // app's One SDK
35
+ export const oneSDKHost = String.raw`import SwiftUI
34
36
  @_spi(Peach) import UIKit
35
37
  import One
36
- import OneSDKBindings
37
38
 
38
- private struct UIKitHost: UIViewRepresentable {
39
+ struct OneSDKUIKitHost: UIViewRepresentable {
39
40
  let props: JSON
40
41
  func makeUIView(context: Context) -> OneNativeUIKitHostView { OneNativeUIKitHostView() }
41
42
  func updateUIView(_ view: OneNativeUIKitHostView, context: Context) {
@@ -47,10 +48,15 @@ private struct UIKitHost: UIViewRepresentable {
47
48
  }
48
49
  static func dismantleUIView(_ view: OneNativeUIKitHostView, coordinator: Coordinator) { view.reset() }
49
50
  }
51
+ `
52
+
53
+ export const oneSDKApp = String.raw`import SwiftUI
54
+ import One
55
+ import OneSDKBindings
50
56
 
51
57
  @main
52
58
  struct SDKGuest: PeachPackage {
53
- func view(props: JSON) -> some View { UIKitHost(props: props) }
59
+ func view(props: JSON) -> some View { OneSDKUIKitHost(props: props) }
54
60
  }
55
61
  `
56
62
 
@@ -4,7 +4,7 @@ import path from 'node:path'
4
4
  import { prepareExpoNativeProject } from './expo-native-workspace'
5
5
  import { compiledModuleAssets, compiledModuleRequestHandler } from './nitro-bundle'
6
6
  import { selectOneSDK } from './one-sdk-preparation'
7
- import { oneSDKApp, oneSDKModule, oneSDKTransport } from './one-sdk/source'
7
+ import { oneSDKApp, oneSDKHost, oneSDKModule, oneSDKTransport } from './one-sdk/source'
8
8
  import { ONE_SDK_SOURCE_DIRECTORY, readOneSDKSourceManifest } from './oneSdkContract'
9
9
  import { syncSwiftTree } from './swift-compile'
10
10
  import { resolveSwiftModule } from './swift-distribution'
@@ -24,12 +24,19 @@ export async function compileOneSDKApp(
24
24
  return compilePreparedOneSDKApp(appRoot, prepared.app, origin, moduleRootOverride)
25
25
  }
26
26
 
27
- export async function compilePreparedOneSDKApp(
27
+ // the One and OneSDKBindings targets an executable links for the app's One SDK
28
+ // imports, the host view and transport its executable target adds, and the
29
+ // module names the bundle registers
30
+ export interface OneSDKLinkSources {
31
+ targets: Map<string, string>
32
+ entry: Map<string, string>
33
+ modules: string[]
34
+ }
35
+
36
+ export async function oneSDKLinkSources(
28
37
  appRoot: string,
29
38
  preparedRoot: string,
30
- origin: string,
31
- moduleRootOverride?: string,
32
- ) {
39
+ ): Promise<OneSDKLinkSources> {
33
40
  const selected = await selectOneSDK(appRoot)
34
41
  if (!selected) throw new Error('prepared app has no One SDK imports')
35
42
  const { one, frameworks } = selected
@@ -47,6 +54,47 @@ export async function compilePreparedOneSDKApp(
47
54
  throw new Error(
48
55
  'prepared One SDK sources differ from the installed facades or app imports',
49
56
  )
57
+ const targets = new Map<string, string>()
58
+ targets.set('Sources/One/One.swift', oneSDKModule)
59
+ for (const source of [
60
+ 'SDK/OneSDKRuntime.swift',
61
+ 'OneNativeUIKitHostView.swift',
62
+ 'OneNativeContent.swift',
63
+ 'OneNativeAccessibility.swift',
64
+ 'SDKViews/OneNativeViewsTable.swift',
65
+ ])
66
+ targets.set(
67
+ `Sources/One/${path.basename(source)}`,
68
+ fs.readFileSync(path.join(one, 'ios', source), 'utf8'),
69
+ )
70
+ for (const name of generated.files)
71
+ targets.set(
72
+ `Sources/OneSDKBindings/${name}`,
73
+ fs.readFileSync(path.join(sourceRoot, name), 'utf8'),
74
+ )
75
+ return {
76
+ targets,
77
+ entry: new Map([
78
+ ['OneSDKHost.swift', oneSDKHost],
79
+ ['OneSDKTransport.swift', oneSDKTransport],
80
+ ]),
81
+ modules: frameworks.map((name) => `${name}_${generated.generation}`),
82
+ }
83
+ }
84
+
85
+ // the app's One SDK sources, when it imports One's SDK
86
+ export async function appOneSDKLinkSources(appRoot: string) {
87
+ if (!(await selectOneSDK(appRoot))) return null
88
+ return oneSDKLinkSources(appRoot, (await prepareExpoNativeProject(appRoot)).app)
89
+ }
90
+
91
+ export async function compilePreparedOneSDKApp(
92
+ appRoot: string,
93
+ preparedRoot: string,
94
+ origin: string,
95
+ moduleRootOverride?: string,
96
+ ) {
97
+ const sources = await oneSDKLinkSources(appRoot, preparedRoot)
50
98
  const moduleRoot = moduleRootOverride ?? (await resolveSwiftModule()).root
51
99
  const packagePath = path.join(
52
100
  swiftCacheDir(),
@@ -66,25 +114,9 @@ let package = Package(name: "OneSDKGuest", targets: [
66
114
  ])
67
115
  `,
68
116
  )
69
- files.set('Sources/One/One.swift', oneSDKModule)
70
- for (const source of [
71
- 'SDK/OneSDKRuntime.swift',
72
- 'OneNativeUIKitHostView.swift',
73
- 'OneNativeContent.swift',
74
- 'OneNativeAccessibility.swift',
75
- 'SDKViews/OneNativeViewsTable.swift',
76
- ])
77
- files.set(
78
- `Sources/One/${path.basename(source)}`,
79
- fs.readFileSync(path.join(one, 'ios', source), 'utf8'),
80
- )
81
- for (const name of generated.files)
82
- files.set(
83
- `Sources/OneSDKBindings/${name}`,
84
- fs.readFileSync(path.join(sourceRoot, name), 'utf8'),
85
- )
117
+ for (const [name, source] of sources.targets) files.set(name, source)
118
+ for (const [name, source] of sources.entry) files.set(`Sources/App/${name}`, source)
86
119
  files.set('Sources/App/App.swift', oneSDKApp)
87
- files.set('Sources/App/Transport.swift', oneSDKTransport)
88
120
  fs.mkdirSync(packagePath, { recursive: true })
89
121
  syncSwiftTree(
90
122
  packagePath,
@@ -100,7 +132,7 @@ let package = Package(name: "OneSDKGuest", targets: [
100
132
  icuUrl: icuUrl(origin, artifact),
101
133
  rootId: `one-sdk-${artifact.hash.slice(0, 16)}`,
102
134
  bundleFiles: artifact.package.bundleFiles.files,
103
- modules: frameworks.map((name) => `${name}_${generated.generation}`),
135
+ modules: sources.modules,
104
136
  }
105
137
  return { metadata, artifact }
106
138
  }
@@ -6053,7 +6053,7 @@ function readCompiledArtifact(
6053
6053
  if (
6054
6054
  createHash('sha256').update(bytes).digest('hex') !== receipt.wasm ||
6055
6055
  createHash('sha256').update(gzip).digest('hex') !== receipt.icu ||
6056
- bytes.length < 8 ||
6056
+ bytes.length <= 8 ||
6057
6057
  bytes.readUInt32LE(0) !== 0x6d736100 ||
6058
6058
  bytes.readUInt32LE(4) !== 1
6059
6059
  )
@@ -7423,6 +7423,8 @@ function compileSwiftProjectOnce(
7423
7423
  bytes: readFileSync(executable),
7424
7424
  icu: await icu,
7425
7425
  }
7426
+ if (artifact.bytes.length <= 8)
7427
+ throw new Error('swift-compile: deferred linker produced no executable')
7426
7428
  if (!options.project) saveCompiledArtifact(root, artifact)
7427
7429
  saveSharedArtifact(sharedRoot, artifact)
7428
7430
  report('compiled artifact verified')
@@ -146,7 +146,8 @@ export const MAX_SWIFT_COMPILE_IMAGE_TOTAL_BYTES = 16 * 1024 * 1024
146
146
  // 68: fetch headers replies carry `setCookies`, the response's Set-Cookie list.
147
147
  // 69: `peach.icu_item_v1` replaces `icu_data_v1`: the host fills icu's table of
148
148
  // contents and places each item's pack the first time icu opens it.
149
- export const SWIFT_HOST_ABI = 69
149
+ // 70: full Objective-C forwarding uses exact typed IMPs (`peach.imp_forwarder_mint`).
150
+ export const SWIFT_HOST_ABI = 70
150
151
 
151
152
  export interface SwiftImageUpload {
152
153
  /** the raster file picked for the asset (has the extension). */
@@ -2,6 +2,7 @@ import { existsSync } from 'node:fs'
2
2
  import { join } from 'node:path'
3
3
  import { installVerifiedTarball } from '@contrast/runtime-delivery'
4
4
  import { BINARYEN_VERSION, wasmOpt } from 'wasm-opt'
5
+ import { windowAdmission } from './admitted-mac-build'
5
6
  import { rnxHomeDir } from './home-paths'
6
7
 
7
8
  // packaged native executables, size-optimized. clang's own
@@ -60,6 +61,15 @@ async function runWasmOpt(
60
61
  skipPasses: readonly string[],
61
62
  binary: string,
62
63
  ): Promise<void> {
64
+ const admittedCores = windowAdmission()?.cores ?? Number(process.env.TM_EXEC_CPU_CORES)
65
+ const requestedCores = Number(process.env.BINARYEN_CORES)
66
+ const environment: NodeJS.ProcessEnv = { ...process.env, WASM_OPT_PATH: binary }
67
+ if (Number.isInteger(admittedCores) && admittedCores > 0)
68
+ environment.BINARYEN_CORES = String(
69
+ Number.isInteger(requestedCores) && requestedCores > 0
70
+ ? Math.min(admittedCores, requestedCores)
71
+ : admittedCores,
72
+ )
63
73
  const result = await wasmOpt(
64
74
  [
65
75
  '-Oz',
@@ -69,7 +79,7 @@ async function runWasmOpt(
69
79
  '-o',
70
80
  output,
71
81
  ],
72
- { env: { ...process.env, WASM_OPT_PATH: binary } },
82
+ { env: environment },
73
83
  )
74
84
  if (result.status !== 0)
75
85
  throw new Error(`wasm-opt exited ${result.status}: ${result.stderr}`)