peachsim 0.1.527 → 0.1.571

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 (169) hide show
  1. package/README.md +28 -28
  2. package/cli/app-fonts.ts +1 -1
  3. package/cli/app-splash.ts +1 -1
  4. package/cli/bin.ts +1 -1
  5. package/cli/bridge-diagnostics.ts +1 -1
  6. package/cli/bridge-flow-runner.ts +72 -12
  7. package/cli/cloud-client.ts +273 -6
  8. package/cli/cloud-dispatch.ts +23 -2
  9. package/cli/cloud-session.ts +60 -4
  10. package/cli/cloud-test-result-files.ts +97 -0
  11. package/cli/cloud-test-tunnel.ts +354 -0
  12. package/cli/commands/box/bundle-plane.ts +2 -2
  13. package/cli/commands/box/checkout-plane.ts +1 -1
  14. package/cli/commands/box/rnx-command.ts +2 -2
  15. package/cli/commands/box/search-command.ts +2 -2
  16. package/cli/commands/box.ts +8 -8
  17. package/cli/commands/compat.ts +1 -1
  18. package/cli/commands/control.ts +22 -3
  19. package/cli/commands/detox.ts +1 -1
  20. package/cli/commands/device.ts +1 -1
  21. package/cli/commands/diagnose.ts +1 -1
  22. package/cli/commands/film.ts +1 -1
  23. package/cli/commands/flow.ts +60 -3
  24. package/cli/commands/inspect/actions.ts +1 -1
  25. package/cli/commands/inspect/core.ts +6 -7
  26. package/cli/commands/inspect/find.ts +2 -1
  27. package/cli/commands/inspect/resolve-target.ts +5 -3
  28. package/cli/commands/inspect/shared.ts +1 -1
  29. package/cli/commands/inspect/wait-event.ts +1 -1
  30. package/cli/commands/inspect/wait-selector.ts +15 -4
  31. package/cli/commands/inspect.ts +19 -14
  32. package/cli/commands/maestro.ts +582 -69
  33. package/cli/commands/permissions.ts +1 -1
  34. package/cli/commands/platform.ts +205 -8
  35. package/cli/commands/preview.ts +2 -2
  36. package/cli/commands/record.ts +2 -2
  37. package/cli/commands/report-issue.ts +2 -2
  38. package/cli/commands/screenshot-layers.ts +1 -1
  39. package/cli/commands/screenshot.ts +1 -1
  40. package/cli/commands/setup.ts +2 -2
  41. package/cli/commands/test.ts +600 -117
  42. package/cli/commands/three-mode.ts +1 -1
  43. package/cli/commands/upload.ts +5 -5
  44. package/cli/commands/what-happened.ts +1 -1
  45. package/cli/desktop-companion.ts +3 -3
  46. package/cli/drivers/playwright-sim-host.ts +2 -2
  47. package/cli/flow-file.ts +7 -0
  48. package/cli/flow-live-status.ts +65 -8
  49. package/cli/help-core.ts +3 -3
  50. package/cli/hidden-runtime-alias.ts +1 -1
  51. package/cli/maestro-report.ts +180 -0
  52. package/cli/main.ts +1 -1
  53. package/cli/outbound-endpoints.ts +187 -136
  54. package/cli/parse-args.ts +1 -1
  55. package/cli/registry.ts +1 -1
  56. package/cli/resolve-assets.ts +1 -1
  57. package/cli/run-registry.ts +10 -1
  58. package/cli/send-to-box.ts +4 -4
  59. package/cli/ws-bridge.ts +3 -5
  60. package/detox/expectations.ts +1 -1
  61. package/detox/gestures.ts +2 -2
  62. package/detox/index.ts +71 -29
  63. package/detox/jest-preset.cjs +5 -5
  64. package/detox/jest-setup-after-env.cjs +1 -1
  65. package/dist-lib/agent-daemon-client.cjs +2 -2
  66. package/dist-lib/agent-events.cjs +1 -1
  67. package/dist-lib/agent-identity.cjs +1 -1
  68. package/dist-lib/agent-sessions.cjs +2 -2
  69. package/dist-lib/attached-projects.cjs +2 -2
  70. package/dist-lib/auth/shared-session.cjs +1 -1
  71. package/dist-lib/backend-origin.cjs +1 -1
  72. package/dist-lib/beta.cjs +1 -1
  73. package/dist-lib/beta.mjs +1 -1
  74. package/dist-lib/bridge-constants.cjs +1 -1
  75. package/dist-lib/bridge-contract-input.cjs +1 -1
  76. package/dist-lib/bridge-contract-input.mjs +1 -1
  77. package/dist-lib/bridge-contract.cjs +1 -1
  78. package/dist-lib/bridge-contract.mjs +1 -1
  79. package/dist-lib/capture-contract.cjs +94 -3
  80. package/dist-lib/capture-contract.mjs +93 -3
  81. package/dist-lib/cli-constants.cjs +1 -1
  82. package/dist-lib/cloud-contract.cjs +1 -1
  83. package/dist-lib/cloud-contract.mjs +1 -1
  84. package/dist-lib/cloud-test-args.cjs +116 -0
  85. package/dist-lib/cloud-test-args.mjs +86 -0
  86. package/dist-lib/cloud.cjs +2 -2
  87. package/dist-lib/cloud.mjs +2 -2
  88. package/dist-lib/config.cjs +1 -1
  89. package/dist-lib/detox/index.cjs +106 -25
  90. package/dist-lib/detox/jest-preset.cjs +5 -5
  91. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  92. package/dist-lib/home-paths.cjs +1 -1
  93. package/dist-lib/host/bridge-host.cjs +150 -65
  94. package/dist-lib/host/fetch-proxy-handler.cjs +28 -2
  95. package/dist-lib/host/fetch-proxy-overrides.cjs +33 -3
  96. package/dist-lib/host/fetch-proxy-overrides.mjs +30 -2
  97. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  98. package/dist-lib/host/websocket-proxy.cjs +1 -1
  99. package/dist-lib/index.cjs +224 -41
  100. package/dist-lib/jump-to-source-babel.cjs +1 -1
  101. package/dist-lib/jump-to-source-native.cjs +1 -1
  102. package/dist-lib/menu.cjs +2 -2
  103. package/dist-lib/menu.mjs +2 -2
  104. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  105. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  106. package/dist-lib/metro-production-bundle.cjs +3 -3
  107. package/dist-lib/metro-production-bundle.mjs +3 -3
  108. package/dist-lib/metro.cjs +8 -8
  109. package/dist-lib/profiles.cjs +1 -1
  110. package/dist-lib/public-brand.cjs +2 -2
  111. package/dist-lib/react-native-host-modules.cjs +1 -1
  112. package/dist-lib/react-native-host-modules.mjs +1 -1
  113. package/dist-lib/render-mode.cjs +1 -1
  114. package/dist-lib/scripts/dev-server-scanner.cjs +31 -15
  115. package/dist-lib/sdk.cjs +3 -2
  116. package/dist-lib/sdk.mjs +3 -2
  117. package/dist-lib/skills.cjs +478 -266
  118. package/dist-lib/swift-compile.cjs +372 -0
  119. package/dist-lib/swift-compile.mjs +339 -0
  120. package/dist-lib/swift-package.cjs +78 -0
  121. package/dist-lib/swift-package.mjs +52 -0
  122. package/dist-lib/swift-submit.cjs +1 -1
  123. package/dist-lib/swift-submit.mjs +1 -1
  124. package/dist-lib/swift.cjs +583 -16
  125. package/dist-lib/vite.cjs +483 -150
  126. package/package.json +30 -9
  127. package/scripts/dev-server-scanner.ts +41 -14
  128. package/scripts/optional-demo-registry.ts +3 -3
  129. package/skills/contrast/SKILL.md +9 -9
  130. package/skills/rnx-debug/SKILL.md +14 -4
  131. package/skills/rnx-perf/SKILL.md +5 -5
  132. package/skills/rnx-setup/SKILL.md +11 -11
  133. package/skills/rnx-test/SKILL.md +14 -14
  134. package/skills/rnx-visual/SKILL.md +6 -6
  135. package/src/agent-sessions.ts +3 -3
  136. package/src/attached-projects.ts +1 -1
  137. package/src/bridge-contract-input.ts +1 -1
  138. package/src/bridge-contract.ts +23 -5
  139. package/src/capture-contract.ts +145 -3
  140. package/src/cloud-test-args.ts +100 -0
  141. package/src/config.ts +2 -2
  142. package/src/connect.ts +2 -2
  143. package/src/home-paths.ts +3 -3
  144. package/src/host/bridge-host.ts +2 -2
  145. package/src/host/fetch-proxy-handler.ts +4 -3
  146. package/src/host/fetch-proxy-overrides.ts +41 -0
  147. package/src/host/plane-host.ts +2 -2
  148. package/src/index.ts +3 -0
  149. package/src/metro-plugin.ts +4 -4
  150. package/src/metro-production-bundle.ts +2 -2
  151. package/src/native-seam-manifest.ts +6 -6
  152. package/src/public-brand.ts +1 -1
  153. package/src/rolldown-plugin-swift.ts +16 -12
  154. package/src/runtime-assets.ts +8 -6
  155. package/src/screenshots/frame-compose.ts +24 -56
  156. package/src/screenshots/org-deck.ts +1 -1
  157. package/src/screenshots/registry.ts +4 -4
  158. package/src/screenshots/schema.ts +1 -1
  159. package/src/sdk.ts +2 -2
  160. package/src/settings-bundle.ts +1 -1
  161. package/src/sim-client.ts +2 -4
  162. package/src/skills/builtin/compat-check.ts +1 -1
  163. package/src/swift-compile.ts +359 -0
  164. package/src/swift-package.ts +69 -0
  165. package/src/swift-submit.ts +3 -2
  166. package/src/vite-plugin-one.ts +1 -1
  167. package/src/vite-plugin-swift.ts +114 -113
  168. package/src/vite-plugin.ts +30 -17
  169. package/src/worklets-babel.ts +2 -2
@@ -1,41 +1,30 @@
1
- // `.swift` handling for a vite dev server: compile the project's swift sources
2
- // with the swift.org toolchain, serve the artifact at a content-hashed url,
3
- // and answer every `.swift` import with the module that mounts it.
1
+ // `.swift` handling for a vite dev server: send the project source set through
2
+ // the same project-unit compiler as `/api/swift-compile`, serve the resulting
3
+ // artifact at a content-hashed url, and answer every `.swift` import with the
4
+ // module that mounts it.
4
5
  //
5
6
  // two hosts use this. the bundler that builds the app's native bundle runs
6
7
  // resolveId/load, and the dev server runs the middleware that serves the
7
8
  // artifact. both share the build below, so the artifact is compiled once.
8
9
  //
9
- // the hash is the hash of the built wasm, so a new hash means a genuinely
10
- // different app: createSwiftEntry moves the @State store across it instead of
11
- // remounting.
10
+ // the hash covers the same source/module/toolchain inputs as the remote
11
+ // service, so a new hash means a genuinely different app: createSwiftEntry
12
+ // moves the @State store across it instead of remounting.
12
13
 
13
- import { execFile } from 'node:child_process'
14
14
  import { createHash } from 'node:crypto'
15
15
  import fs from 'node:fs'
16
16
  import os from 'node:os'
17
17
  import path from 'node:path'
18
- import { promisify } from 'node:util'
19
18
  import {
20
19
  enumerateBundleImages,
21
20
  enumerateBundleJson,
22
21
  } from '../../sootsim-swift/src/bundleAssets'
23
- import toolchain from '../../sootsim-swift/toolchain.json'
22
+ import { swiftEntryModuleEsm } from '../../sootsim-swift/src/entryModule'
23
+ import { compileSwiftProjectUnit, swiftModuleInputFiles } from './swift-compile'
24
+ import type { SwiftCompileInput } from './swift-compile'
25
+ import type { SwiftImageUpload } from './swift-submit'
24
26
  import type { Plugin, ViteDevServer } from 'vite'
25
27
 
26
- const run = promisify(execFile)
27
-
28
- // the swift.org release scripts/build.sh builds with, from the one place it is
29
- // named: the toolchain is <release>.xctoolchain and its matching wasm sdk is
30
- // <release>_wasm. xcode's swift has no wasm backend, so this toolchain is the
31
- // only one that can build the target.
32
- const TOOLCHAIN_BIN = path.join(
33
- os.homedir(),
34
- `Library/Developer/Toolchains/${toolchain.release}.xctoolchain/usr/bin`,
35
- )
36
- const WASM_SDK = `${toolchain.release}_wasm`
37
- const WASM_TRIPLE = 'wasm32-unknown-wasip1'
38
-
39
28
  const ARTIFACT_PATH = '/__rnx/swift/'
40
29
  const RESOURCE_PATH = '/__rnx/swift-res/'
41
30
  const VIRTUAL_PREFIX = '\0rnx-swift:'
@@ -140,6 +129,8 @@ const RESOURCE_CONTENT_TYPES: Record<string, string> = {
140
129
  export interface SwiftResourceTable {
141
130
  /** bundle asset name to absolute serving url. */
142
131
  table: Record<string, string>
132
+ /** the same rasters as compile-address inputs. */
133
+ images: Record<string, SwiftImageUpload>
143
134
  /** project-relative image files this load's table covers (for watching). */
144
135
  watched: string[]
145
136
  }
@@ -167,6 +158,7 @@ export function buildSwiftResourceTable(
167
158
  }
168
159
  })
169
160
  const table: Record<string, string> = {}
161
+ const images: Record<string, SwiftImageUpload> = {}
170
162
  const watched: string[] = []
171
163
  const registered = new Map<string, string>()
172
164
  for (const { name, file } of found) {
@@ -181,6 +173,10 @@ export function buildSwiftResourceTable(
181
173
  const hash = createHash('sha256').update(bytes).digest('hex').slice(0, 16)
182
174
  const ext = path.posix.extname(file).toLowerCase()
183
175
  table[name] = `${baseUrl}${RESOURCE_PATH}${hash}${ext}`
176
+ images[name] = {
177
+ file: path.posix.basename(file),
178
+ data: bytes.toString('base64'),
179
+ }
184
180
  watched.push(file)
185
181
  // a Contents.json edit (scale swap, file rename) re-resolves the same
186
182
  // way a raster edit does, so the catalog rides the watcher too.
@@ -189,7 +185,7 @@ export function buildSwiftResourceTable(
189
185
  registered.set(`${hash}${ext}`, disk)
190
186
  }
191
187
  project.resourceFiles = registered
192
- return { table, watched }
188
+ return { table, images, watched }
193
189
  }
194
190
 
195
191
  // the /res pack: every Resources/ json in the package, basename-keyed and
@@ -292,22 +288,41 @@ export function linkedModuleRoots(packagePath: string): string[] {
292
288
  return [...roots].sort()
293
289
  }
294
290
 
295
- // the stamp covers the project plus the modules it links: a running dev
296
- // server rebuilds only when this moves, so leaving the linked sources out
297
- // would keep serving the artifact built before a SwiftUI module edit for as
298
- // long as the project's own sources stayed the same. it names the same
299
- // closure the compile service's artifact hash names, but it is not the same
300
- // function: the stamp hashes mtimes, which is the cheap rebuild signal for a
301
- // watcher, while the artifact address hashes content, which is what stays
302
- // stable when a checkout restores byte-identical files. a restore rebuilds
303
- // here and reuses the stored artifact there, by design.
291
+ // the stamp covers every file that contributes to compileInput plus the
292
+ // linked module and toolchain release that the shared artifact hash names. a
293
+ // running dev server rebuilds only when this moves, so its input closure must
294
+ // stay identical to the artifact address's even though this cheap watcher
295
+ // signal hashes mtimes while that stable address hashes content. a checkout
296
+ // restoring byte-identical files rebuilds here and reuses the same address
297
+ // there, by design.
304
298
  export function sourceStamp(packagePath: string): string {
305
299
  const hash = createHash('sha256')
306
- for (const root of [packagePath, ...linkedModuleRoots(packagePath)]) {
307
- hash.update(root)
308
- for (const file of sourceFiles(root).sort()) {
309
- hash.update(path.relative(root, file))
310
- hash.update(String(fs.statSync(file).mtimeMs))
300
+ hash.update(packagePath)
301
+ const projectInputs = new Set([
302
+ path.join(packagePath, 'Package.swift'),
303
+ ...sourceFiles(packagePath),
304
+ ...buildSwiftBundleFiles(packagePath).watched.map((file) =>
305
+ path.join(packagePath, ...file.split(path.posix.sep)),
306
+ ),
307
+ ...buildSwiftResourceTable(projectFor(packagePath), '').watched.map((file) =>
308
+ path.join(packagePath, ...file.split(path.posix.sep)),
309
+ ),
310
+ ])
311
+ for (const file of [...projectInputs].sort()) {
312
+ hash.update(path.relative(packagePath, file))
313
+ hash.update(String(fs.statSync(file).mtimeMs))
314
+ }
315
+ const moduleRoot = linkedModuleRoots(packagePath).find((root) =>
316
+ fs.existsSync(path.join(root, 'toolchain.json')),
317
+ )
318
+ if (moduleRoot) {
319
+ hash.update(moduleRoot)
320
+ for (const relative of [
321
+ ...swiftModuleInputFiles(moduleRoot),
322
+ 'toolchain.json',
323
+ ].sort()) {
324
+ hash.update(relative)
325
+ hash.update(String(fs.statSync(path.join(moduleRoot, relative)).mtimeMs))
311
326
  }
312
327
  }
313
328
  return hash.digest('hex').slice(0, 16)
@@ -338,82 +353,62 @@ function artifactUrl(project: SwiftProject, artifact: SwiftArtifact): string {
338
353
  return `${serverOrigin()}${ARTIFACT_PATH}${artifact.hash}.wasm`
339
354
  }
340
355
 
341
- // one executable is the app. a package with several is ambiguous: the host
342
- // cannot know which one the page is, so it says so instead of guessing.
343
- function builtArtifact(packagePath: string): string {
344
- const binDir = path.join(packagePath, '.build', WASM_TRIPLE, 'release')
345
- const artifacts = fs.existsSync(binDir)
346
- ? fs
347
- .readdirSync(binDir)
348
- .filter((name) => name.endsWith('.wasm'))
349
- .sort()
350
- : []
351
- if (artifacts.length !== 1) {
352
- throw new Error(
353
- `swift project ${packagePath} built ${artifacts.length} wasm artifacts; exactly one executable is the app`,
354
- )
356
+ function compileInput(packagePath: string, entry: string): SwiftCompileInput {
357
+ const files: Record<string, string> = {}
358
+ for (const file of sourceFiles(packagePath)) {
359
+ const relative = path.relative(packagePath, file).split(path.sep).join(path.posix.sep)
360
+ files[relative] = fs.readFileSync(file, 'utf8')
361
+ }
362
+ const manifest = path.join(packagePath, 'Package.swift')
363
+ if (fs.existsSync(manifest)) files['Package.swift'] = fs.readFileSync(manifest, 'utf8')
364
+ const resources = buildSwiftBundleFiles(packagePath).files
365
+ const images = buildSwiftResourceTable(projectFor(packagePath), '').images
366
+ return {
367
+ files,
368
+ resources,
369
+ images,
370
+ entry,
355
371
  }
356
- return path.join(binDir, artifacts[0])
357
- }
358
-
359
- function streamText(value: unknown): string {
360
- if (typeof value === 'string') return value.trim()
361
- if (Buffer.isBuffer(value)) return value.toString('utf8').trim()
362
- return ''
363
372
  }
364
373
 
365
- async function compile(packagePath: string, stamp: string): Promise<SwiftArtifact> {
366
- const swift = path.join(TOOLCHAIN_BIN, 'swift')
367
- if (!fs.existsSync(swift)) {
374
+ async function compile(
375
+ packagePath: string,
376
+ entry: string,
377
+ stamp: string,
378
+ ): Promise<SwiftArtifact> {
379
+ const moduleRoot = linkedModuleRoots(packagePath).find((root) =>
380
+ fs.existsSync(path.join(root, 'toolchain.json')),
381
+ )
382
+ if (!moduleRoot) {
368
383
  throw new Error(
369
- `no swift.org toolchain at ${TOOLCHAIN_BIN}. install it and the ${WASM_SDK} sdk; see packages/sootsim-swift/README.md`,
370
- )
371
- }
372
- try {
373
- await run(
374
- swift,
375
- [
376
- 'build',
377
- '--package-path',
378
- packagePath,
379
- '--swift-sdk',
380
- WASM_SDK,
381
- '-c',
382
- 'release',
383
- '-Xswiftc',
384
- '-Osize',
385
- ],
386
- { maxBuffer: 64 * 1024 * 1024 },
384
+ `swift project ${packagePath} has no linked rnx SwiftUI package with toolchain.json`,
387
385
  )
388
- } catch (error) {
389
- // `swift build` prints the compiler's diagnostics on stdout and swiftpm's
390
- // own on stderr; the failure's message holds only the command line, so
391
- // both streams are what names the line that broke. stdout leads because
392
- // compiler diagnostics are what the editor needs to show.
393
- const streams =
394
- error && typeof error === 'object'
395
- ? [
396
- streamText(Reflect.get(error, 'stdout')),
397
- streamText(Reflect.get(error, 'stderr')),
398
- ].filter(Boolean)
399
- : []
400
- const detail =
401
- streams.length > 0
402
- ? streams.join('\n')
403
- : error instanceof Error
404
- ? error.message
405
- : String(error)
406
- throw new Error(`swift build failed for ${packagePath}\n${detail}`)
407
386
  }
408
- const bytes = fs.readFileSync(builtArtifact(packagePath))
409
- return {
410
- hash: createHash('sha256').update(bytes).digest('hex').slice(0, 16),
411
- bytes,
412
- stamp,
387
+ const toolchain: unknown = JSON.parse(
388
+ fs.readFileSync(path.join(moduleRoot, 'toolchain.json'), 'utf8'),
389
+ )
390
+ const release =
391
+ toolchain && typeof toolchain === 'object'
392
+ ? Reflect.get(toolchain, 'release')
393
+ : undefined
394
+ if (typeof release !== 'string') {
395
+ throw new Error(`swift toolchain manifest at ${moduleRoot} has no release`)
413
396
  }
397
+ const artifact = await compileSwiftProjectUnit(compileInput(packagePath, entry), {
398
+ buildRoot:
399
+ process.env.CONTRAST_SWIFT_BUILD_ROOT ??
400
+ path.join(os.tmpdir(), 'contrast-swift-build'),
401
+ moduleRoot,
402
+ release,
403
+ toolchainBin: path.join(
404
+ os.homedir(),
405
+ `Library/Developer/Toolchains/${release}.xctoolchain/usr/bin`,
406
+ ),
407
+ })
408
+ return { ...artifact, stamp }
414
409
  }
415
410
 
416
- function artifactFor(project: SwiftProject): Promise<SwiftArtifact> {
411
+ function artifactFor(project: SwiftProject, entry: string): Promise<SwiftArtifact> {
417
412
  const stamp = sourceStamp(project.packagePath)
418
413
  if (project.artifact?.stamp === stamp) return Promise.resolve(project.artifact)
419
414
  if (project.building) {
@@ -423,10 +418,10 @@ function artifactFor(project: SwiftProject): Promise<SwiftArtifact> {
423
418
  // would emit the older artifact and nothing would trigger another load.
424
419
  const running = project.building
425
420
  if (running.stamp === stamp) return running.promise
426
- const again = () => artifactFor(project)
421
+ const again = () => artifactFor(project, entry)
427
422
  return running.promise.then(again, again)
428
423
  }
429
- const promise = compile(project.packagePath, stamp).then(
424
+ const promise = compile(project.packagePath, entry, stamp).then(
430
425
  (artifact) => {
431
426
  project.artifact = artifact
432
427
  project.building = null
@@ -498,11 +493,13 @@ export function swiftPlugin(options: SwiftPluginOptions = {}): Plugin {
498
493
  if (!fs.existsSync(file)) throw new Error(`swift source ${file} no longer exists`)
499
494
  const packagePath = options.packagePath ?? packageFor(file)
500
495
  const project = projectFor(packagePath)
496
+ const entry = path.relative(packagePath, file).split(path.sep).join(path.posix.sep)
501
497
  // every source in the package is a dependency of this module, so a
502
498
  // bundler watching this module learns about a `.swift` edit and re-runs
503
499
  // load. that is the whole hot reload: the new hash in the emitted module
504
500
  // is what tells the mount it is looking at a different app.
505
501
  for (const source of sourceFiles(packagePath)) this.addWatchFile(source)
502
+ this.addWatchFile(path.join(packagePath, 'Package.swift'))
506
503
  // an edit to the linked SwiftUI module rebuilds the artifact without
507
504
  // touching the project's own sources, so those files gate the reload
508
505
  // too: the new hash in the emitted module is what tells the mount it
@@ -512,7 +509,7 @@ export function swiftPlugin(options: SwiftPluginOptions = {}): Plugin {
512
509
  }
513
510
  let artifact: SwiftArtifact
514
511
  try {
515
- artifact = await artifactFor(project)
512
+ artifact = await artifactFor(project, entry)
516
513
  } catch (error) {
517
514
  // a compile error must not fail this module. a module whose load threw
518
515
  // leaves the graph unwatched, so the edit that fixes the error would
@@ -530,7 +527,9 @@ export function swiftPlugin(options: SwiftPluginOptions = {}): Plugin {
530
527
  try {
531
528
  const built = buildSwiftResourceTable(project, serverOrigin())
532
529
  resources = built.table
533
- for (const watched of built.watched) this.addWatchFile(watched)
530
+ for (const watched of built.watched) {
531
+ this.addWatchFile(path.join(packagePath, ...watched.split(path.posix.sep)))
532
+ }
534
533
  } catch (error) {
535
534
  this.warn(
536
535
  `swift resources for ${packagePath}: ${error instanceof Error ? error.message : String(error)}`,
@@ -560,11 +559,13 @@ export function swiftPlugin(options: SwiftPluginOptions = {}): Plugin {
560
559
  // whether it is a view or an app root at runtime, so the transform
561
560
  // never re-derives the conformance from source. the entry is rooted by
562
561
  // its package and source path so a hot reload finds the same instance.
563
- return [
564
- `import { createSwiftEntry } from 'rnxsim/swift'`,
565
- `export default createSwiftEntry({ url: ${JSON.stringify(artifactUrl(project, artifact))}, hash: ${JSON.stringify(artifact.hash)}, rootId: ${JSON.stringify(entryRootId(packagePath, file))}, resources: ${JSON.stringify(resources)}, bundleFiles: ${JSON.stringify(bundleFiles)} })`,
566
- '',
567
- ].join('\n')
562
+ return swiftEntryModuleEsm({
563
+ url: artifactUrl(project, artifact),
564
+ hash: artifact.hash,
565
+ rootId: entryRootId(packagePath, file),
566
+ resources,
567
+ bundleFiles,
568
+ })
568
569
  },
569
570
 
570
571
  configureServer(server) {
@@ -13,7 +13,7 @@ import { createRequire } from 'module'
13
13
  import path from 'path'
14
14
  import { fileURLToPath } from 'url'
15
15
  import { type Plugin, transformWithOxc } from 'vite'
16
- import { resolveExportsHiddenSubpath } from '../../compat/src/resolve-hidden-subpath.ts'
16
+ import { resolveExportsHiddenSubpath } from '../../peach-compat/src/resolve-hidden-subpath.ts'
17
17
  import {
18
18
  SOOTSIM_COMPAT_PUBLISHED_BROWSER_ENTRIES,
19
19
  SOOTSIM_COMPAT_WRAPPER_REAL_PACKAGES,
@@ -21,7 +21,7 @@ import {
21
21
  SOOTSIM_HAPTIC_FEEDBACK_TOUCHABLE_SPECIFIER,
22
22
  compatStubsForBuildResolver,
23
23
  reactNativeDeepStubsForBuildResolver,
24
- } from '../../compat/src/stub-manifest.ts'
24
+ } from '../../peach-compat/src/stub-manifest.ts'
25
25
  import { resolveSootsimBridgePort } from './bridge-constants.ts'
26
26
  import {
27
27
  SHELL_BRIDGE_IDENTITY_PATH,
@@ -50,19 +50,32 @@ const sootsimRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '
50
50
  const workspaceRoot = path.resolve(sootsimRoot, '..', '..')
51
51
  const workspaceNodeModules = path.resolve(workspaceRoot, 'node_modules')
52
52
  const workspaceTamaguiDir = path.resolve(workspaceNodeModules, '@tamagui')
53
- // stubs and the mutable React bridge live in @sootsim/compat.
54
- const compatRoot = path.resolve(sootsimRoot, '..', 'compat')
53
+ // stubs and the mutable React bridge live in @peach/compat.
54
+ const compatRoot = path.resolve(sootsimRoot, '..', 'peach-compat')
55
55
 
56
56
  // the rendering engine and its shims live in the private sootsim-engine
57
57
  // workspace package. resolve via workspace root rather than relative hops so
58
58
  // renames of either package don't break things.
59
- const engineRoot = path.resolve(workspaceRoot, 'packages/sootsim-engine')
59
+ const engineRoot = path.resolve(workspaceRoot, 'packages/peach-engine')
60
60
  const rnShimPath = path.resolve(engineRoot, 'src/react-native/index.ts')
61
61
  const reactBridgePath = path.resolve(
62
62
  workspaceRoot,
63
63
  'packages/contrast-kit/src/react-bridge.ts',
64
64
  )
65
65
  const sootsimBrowserSourceAliases = [
66
+ {
67
+ find: 'peachsim/backend-origin',
68
+ replacement: path.resolve(sootsimRoot, 'src/backend-origin.ts'),
69
+ },
70
+ {
71
+ find: 'peachsim/bridge-constants',
72
+ replacement: path.resolve(sootsimRoot, 'src/bridge-constants.ts'),
73
+ },
74
+ {
75
+ find: 'peachsim/dev-bundle-resolution',
76
+ replacement: path.resolve(sootsimRoot, 'src/dev-bundle-resolution.ts'),
77
+ },
78
+ // old specifiers for user code still on rnxsim; remove with packages/sootsim.
66
79
  {
67
80
  find: 'rnxsim/backend-origin',
68
81
  replacement: path.resolve(sootsimRoot, 'src/backend-origin.ts'),
@@ -475,13 +488,13 @@ function rnghUpstreamInternalResolvePlugin(): Plugin {
475
488
  export function recordingPluginWorkerRedirect(): Plugin {
476
489
  const workerTarget = path.resolve(
477
490
  workspaceRoot,
478
- 'packages/rnx-plugin-recording/src/worker.ts',
491
+ 'packages/peach-plugin-recording/src/worker.ts',
479
492
  )
480
493
  return {
481
494
  name: 'sootsim-recording-plugin-worker-redirect',
482
495
  enforce: 'pre',
483
496
  resolveId(source) {
484
- if (source === '@rnx/plugin-recording') {
497
+ if (source === '@peach/plugin-recording') {
485
498
  return workerTarget
486
499
  }
487
500
  return null
@@ -542,7 +555,7 @@ const ENGINE_WORKER_BOOT_CLOSURES: WorkerBootClosure[] = [
542
555
  // statically instead of require()ing the whole react-native namespace into
543
556
  // the boot file.
544
557
  { entry: 'src/render-worker/shell-worker-entry.ts', maxBytes: 3_451_000 },
545
- { entry: 'src/render-worker/compositor-worker-entry.ts', maxBytes: 1_080_000 },
558
+ { entry: 'src/render-worker/compositor-worker-entry.ts', maxBytes: 1_085_000 },
546
559
  { entry: 'src/render-worker/compositor-worker-ganesh-entry.ts', maxBytes: 1_820_000 },
547
560
  ]
548
561
 
@@ -670,7 +683,7 @@ export function keyboardControllerBindingsRedirect(): Plugin {
670
683
  //
671
684
  // per the project policy of not shimming pure-JS libs, we let upstream resolve
672
685
  // from node_modules and redirect ONLY the native seam to our compat stub
673
- // (one file at packages/compat/src/stubs/react-native-reanimated.ts that
686
+ // (one file at packages/peach-compat/src/stubs/react-native-reanimated.ts that
674
687
  // exports the right shape — default = turbomodule, named = platform fns).
675
688
  //
676
689
  // upstream's `assertWorkletsVersion` (DEV-only) imports a build-time script
@@ -1274,11 +1287,11 @@ function reactBridgePlugin(): Plugin {
1274
1287
  // skip during dep scanning / SSR — only active in browser serve mode
1275
1288
  if ((options as { scan?: boolean })?.scan || options?.ssr) return null
1276
1289
 
1277
- // only redirect imports from sootsim, sootsim-engine, compat, and react-reconciler
1290
+ // only redirect imports from peachsim, peach-engine, peach-compat, and react-reconciler
1278
1291
  const shouldRedirect =
1279
- importer.includes('/sootsim/src/') ||
1280
- importer.includes('/sootsim-engine/src/') ||
1281
- importer.includes('/compat/src/') ||
1292
+ importer.includes('/peachsim/src/') ||
1293
+ importer.includes('/peach-engine/src/') ||
1294
+ importer.includes('/peach-compat/src/') ||
1282
1295
  importer.includes('/contrast-kit/src/') ||
1283
1296
  importer.includes('react-reconciler')
1284
1297
 
@@ -1326,16 +1339,16 @@ export function workerReactBridgePlugin(): Plugin {
1326
1339
  // stub-registry needs the real React (with __CLIENT_INTERNALS) for bundle stubs
1327
1340
  if (importer.includes('stub-registry')) return null
1328
1341
 
1329
- // redirect react imports from sootsim, sootsim-engine, compat, react-reconciler,
1342
+ // redirect react imports from peachsim, peach-engine, peach-compat, react-reconciler,
1330
1343
  // and any pure-JS RN-ecosystem package the bundle pulls in. without this
1331
1344
  // redirect, packages like react-native-drawer-layout / use-latest-callback
1332
1345
  // call into a separate React instance than the renderer/bridge does, so
1333
1346
  // their useRef/useEffect refs collide with the engine's hooks state and
1334
1347
  // we hit "Maximum update depth exceeded" infinite-loop renders.
1335
1348
  const shouldRedirect =
1336
- importer.includes('/sootsim/src/') ||
1337
- importer.includes('/sootsim-engine/src/') ||
1338
- importer.includes('/compat/src/') ||
1349
+ importer.includes('/peachsim/src/') ||
1350
+ importer.includes('/peach-engine/src/') ||
1351
+ importer.includes('/peach-compat/src/') ||
1339
1352
  importer.includes('/contrast-kit/src/') ||
1340
1353
  importer.includes('react-reconciler') ||
1341
1354
  importer.includes('/node_modules/')
@@ -41,8 +41,8 @@ export const REANIMATED_WORKLETIZATION_REGEX = new RegExp(
41
41
  // content-gated so most of node_modules skips for free.
42
42
  const REANIMATED_IGNORED_PATHS_REGEX =
43
43
  /node_modules\/(react|react-dom|react-native|react-native-web)\//
44
- const SOOTSIM_ENGINE_SRC_PATH = '/packages/sootsim-engine/src/'
45
- const SOOTSIM_ENGINE_TEST_FIXTURE_PATH = '/packages/sootsim-engine/src/test-fixtures/'
44
+ const SOOTSIM_ENGINE_SRC_PATH = '/packages/peach-engine/src/'
45
+ const SOOTSIM_ENGINE_TEST_FIXTURE_PATH = '/packages/peach-engine/src/test-fixtures/'
46
46
  const REANIMATED_IMPORT_REGEX =
47
47
  /(?:from\s+['"]react-native-reanimated(?:\/[^'"]*)?['"]|require\(\s*['"]react-native-reanimated(?:\/[^'"]*)?['"]\s*\))/
48
48