peachsim 0.1.1620 → 0.1.1625
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.
- package/README.md +35 -5
- package/cli/app-fonts.ts +2 -1
- package/cli/bridge-flow-runner.ts +184 -113
- package/cli/cloud-client.ts +68 -57
- package/cli/commands/box/search-command.ts +3 -2
- package/cli/commands/box.ts +10 -29
- package/cli/commands/control.ts +108 -43
- package/cli/commands/inspect/settling.ts +57 -20
- package/cli/commands/maestro-compare.ts +11 -32
- package/cli/commands/platform.ts +71 -36
- package/cli/commands/upload.ts +5 -1
- package/cli/dev-build-freshness.ts +37 -2
- package/cli/drivers/playwright-sim-host.ts +6 -5
- package/cli/maestro-accessibility.ts +53 -0
- package/cli/maestro-compare.ts +24 -0
- package/cli/outbound-endpoints.ts +22 -5
- package/cli/parent-pid.ts +0 -9
- package/cli/remote-simulators.ts +112 -0
- package/cli/remoteSimulatorContract.ts +21 -0
- package/detox/expectations.ts +5 -1
- package/detox/index.ts +133 -98
- package/detox/motion-change.cjs +37 -0
- package/detox/motion-change.d.cts +16 -0
- package/detox/navigation-lifecycle.ts +22 -4
- package/dist-lib/agent-daemon-client.cjs +3 -2
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/attached-projects.cjs +1 -1
- package/dist-lib/auth/shared-session.cjs +3 -2
- package/dist-lib/backend-origin.cjs +1 -1
- package/dist-lib/beta.cjs +1 -1
- package/dist-lib/beta.mjs +1 -1
- package/dist-lib/bridge-constants.cjs +1 -1
- package/dist-lib/bridge-contract-input.cjs +1 -1
- package/dist-lib/bridge-contract-input.mjs +1 -1
- package/dist-lib/bridge-contract.cjs +1 -1
- package/dist-lib/bridge-contract.mjs +1 -1
- package/dist-lib/bundle-channel.cjs +1520 -934
- package/dist-lib/capture-contract.cjs +1 -1
- package/dist-lib/capture-contract.mjs +1 -1
- package/dist-lib/cloud-contract.cjs +1 -1
- package/dist-lib/cloud-contract.mjs +1 -1
- package/dist-lib/cloud-test-args.cjs +1 -1
- package/dist-lib/cloud-test-args.mjs +1 -1
- package/dist-lib/cloud.cjs +3 -2
- package/dist-lib/cloud.mjs +3 -2
- package/dist-lib/config.cjs +5 -4
- package/dist-lib/config.mjs +5 -4
- package/dist-lib/detox/index.cjs +1021 -364
- package/dist-lib/dev-bundle-resolution.cjs +51 -23
- package/dist-lib/expo-swift-bundle.cjs +1343 -801
- package/dist-lib/expo-swift-metadata.cjs +5 -4
- package/dist-lib/expo-swift-metadata.mjs +5 -4
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +2468 -1341
- package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
- package/dist-lib/host/replacement-module-handler.cjs +1 -1
- package/dist-lib/host/websocket-proxy.cjs +1 -1
- package/dist-lib/index.cjs +1262 -300
- package/dist-lib/jump-to-source-babel.cjs +1 -1
- package/dist-lib/jump-to-source-native.cjs +1 -1
- package/dist-lib/kotlin-host.cjs +1 -1
- package/dist-lib/menu.cjs +3 -2
- package/dist-lib/menu.mjs +3 -2
- package/dist-lib/metro-fingerprint-registry.cjs +1 -1
- package/dist-lib/metro-fingerprint-registry.mjs +1 -1
- package/dist-lib/metro-production-bundle.cjs +5 -4
- package/dist-lib/metro-production-bundle.mjs +5 -4
- package/dist-lib/metro.cjs +799 -398
- package/dist-lib/native-app-identity-project.cjs +728 -22
- package/dist-lib/native-app-identity-project.mjs +727 -22
- package/dist-lib/native-app-identity.cjs +224 -9
- package/dist-lib/native-app-identity.mjs +222 -8
- package/dist-lib/native-firebase-metadata.cjs +5 -4
- package/dist-lib/native-firebase-metadata.mjs +5 -4
- package/dist-lib/plist.cjs +1 -1
- package/dist-lib/plist.mjs +1 -1
- package/dist-lib/profiles.cjs +1 -1
- package/dist-lib/public-brand.cjs +3 -2
- package/dist-lib/react-native-host-modules.cjs +4 -1
- package/dist-lib/react-native-host-modules.mjs +4 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +94 -28
- package/dist-lib/sdk.cjs +89 -24
- package/dist-lib/sdk.mjs +89 -24
- package/dist-lib/skills.cjs +4391 -2350
- package/dist-lib/swift-compile.cjs +328 -142
- package/dist-lib/swift-compile.mjs +342 -147
- package/dist-lib/swift-package.cjs +1 -1
- package/dist-lib/swift-package.mjs +1 -1
- package/dist-lib/swift-submit.cjs +1 -1
- package/dist-lib/swift-submit.mjs +1 -1
- package/dist-lib/swift-toolchain.cjs +1 -1
- package/dist-lib/swift.cjs +1 -1
- package/dist-lib/vite-swift.cjs +888 -559
- package/dist-lib/vite.cjs +68 -14
- package/dist-lib/worklet-executable.cjs +25 -2
- package/dist-lib/worklet-executable.mjs +25 -2
- package/package.json +2 -2
- package/scripts/dev-server-scanner.ts +33 -13
- package/src/admitted-mac-build.ts +34 -8
- package/src/android-app-identity.ts +146 -0
- package/src/android-autolinking.ts +75 -0
- package/src/android-string-value.ts +36 -0
- package/src/android-strings.ts +5 -36
- package/src/androidAutolinkingContract.ts +40 -0
- package/src/bundleGlobalFooter.ts +26 -7
- package/src/codex-client.ts +3 -2
- package/src/dev-bundle-resolution.ts +58 -25
- package/src/expo-kotlin-host.ts +6 -36
- package/src/expo-swift-bundle.ts +17 -12
- package/src/expo-swift-host.ts +477 -173
- package/src/expoSwiftContract.ts +6 -0
- package/src/host/bridge-host.ts +7 -55
- package/src/host/staged-host-files.ts +29 -2
- package/src/installed-package-versions.ts +54 -14
- package/src/launchMetadataContract.ts +24 -0
- package/src/metro-plugin.ts +37 -42
- package/src/native-app-identity-project.ts +116 -3
- package/src/native-app-identity.ts +86 -11
- package/src/nitro-compile.ts +2 -5
- package/src/nitro-source.ts +147 -63
- package/src/process-group.ts +90 -60
- package/src/process-output.ts +0 -9
- package/src/public-brand.ts +1 -0
- package/src/react-native-host-modules.ts +3 -0
- package/src/sim-client.ts +6 -4
- package/src/swift-compile.ts +176 -28
- package/src/swift-distribution.lock.json +37 -11
- package/src/swift-distribution.ts +61 -9
- package/src/swift-host.ts +58 -27
- package/src/swift-objc-standin.ts +13 -0
- package/src/swift-standin-emit.ts +1 -1
- package/src/swift-standins.ts +17 -5
- package/src/vite-plugin.ts +8 -19
- package/src/worklet-executable.ts +30 -3
- package/src/worklets-babel.ts +35 -14
- package/src/xcode-project.ts +10 -0
|
@@ -1,11 +1,34 @@
|
|
|
1
1
|
{
|
|
2
|
-
"id": "
|
|
2
|
+
"id": "1fda1fb984f25758",
|
|
3
3
|
"release": "swift-6.4.0-RELEASE",
|
|
4
|
-
"wasmSdkId": "swift-6.4.0-RELEASE_wasm-peach-
|
|
5
|
-
"wasmSdkUrl": "https://r2.sootsim.com/swift-wasm-sdk/swift-6.4.0-RELEASE_wasm-peach-
|
|
6
|
-
"wasmSdkChecksum": "
|
|
7
|
-
"source": "
|
|
4
|
+
"wasmSdkId": "swift-6.4.0-RELEASE_wasm-peach-34b989c2",
|
|
5
|
+
"wasmSdkUrl": "https://r2.sootsim.com/swift-wasm-sdk/swift-6.4.0-RELEASE_wasm-peach-34b989c2.artifactbundle.tar.gz",
|
|
6
|
+
"wasmSdkChecksum": "1598ac9d063e07b20aa6232acb03c7147c9a94abf989d51f1e55688eb4aba854",
|
|
7
|
+
"source": "078b4f63d3734871dc1fbea898005e2ef95a73cb",
|
|
8
8
|
"targets": [
|
|
9
|
+
"System",
|
|
10
|
+
"_LocationEssentials",
|
|
11
|
+
"GeoToolbox",
|
|
12
|
+
"DataDetection",
|
|
13
|
+
"Accessibility",
|
|
14
|
+
"Contacts",
|
|
15
|
+
"CoreML",
|
|
16
|
+
"EventKit",
|
|
17
|
+
"CloudKit",
|
|
18
|
+
"IOSurface",
|
|
19
|
+
"OpenGLES",
|
|
20
|
+
"ContactsUI",
|
|
21
|
+
"CoreLocationUI",
|
|
22
|
+
"MediaPlayer",
|
|
23
|
+
"MusicKit",
|
|
24
|
+
"RealityKit",
|
|
25
|
+
"SecureElementCredential",
|
|
26
|
+
"Social",
|
|
27
|
+
"Speech",
|
|
28
|
+
"Vision",
|
|
29
|
+
"VisionKit",
|
|
30
|
+
"WorkoutKit",
|
|
31
|
+
"_PassKit_SwiftUI",
|
|
9
32
|
"ExpoModulesCore",
|
|
10
33
|
"_MapKit_SwiftUI",
|
|
11
34
|
"PeachJSON",
|
|
@@ -21,6 +44,7 @@
|
|
|
21
44
|
"PeachObjC",
|
|
22
45
|
"UIKitCore",
|
|
23
46
|
"PeachICU",
|
|
47
|
+
"PeachCMark",
|
|
24
48
|
"CoreImage",
|
|
25
49
|
"QuartzCore",
|
|
26
50
|
"CoreGraphics",
|
|
@@ -51,6 +75,7 @@
|
|
|
51
75
|
"ActivityKit",
|
|
52
76
|
"SwiftData",
|
|
53
77
|
"UIKit",
|
|
78
|
+
"React",
|
|
54
79
|
"Combine",
|
|
55
80
|
"PeachFoundationCombine",
|
|
56
81
|
"AppIntents",
|
|
@@ -132,6 +157,7 @@
|
|
|
132
157
|
"systemVersion": ["string"],
|
|
133
158
|
"colorScheme": ["string:light", "string:dark"],
|
|
134
159
|
"semanticColors": ["object"],
|
|
160
|
+
"imageAssets": ["object"],
|
|
135
161
|
"locale": ["string"],
|
|
136
162
|
"dynamicTypeSize": [
|
|
137
163
|
"string:xSmall",
|
|
@@ -153,16 +179,16 @@
|
|
|
153
179
|
},
|
|
154
180
|
"hosts": {
|
|
155
181
|
"darwin-arm64": {
|
|
156
|
-
"sha256": "
|
|
157
|
-
"size":
|
|
182
|
+
"sha256": "6a3b956b13bcc76dbf0b55533088251e2ae29badcd5c8d66394a2459e0942a32",
|
|
183
|
+
"size": 63180761
|
|
158
184
|
},
|
|
159
185
|
"linux-x64": {
|
|
160
|
-
"sha256": "
|
|
161
|
-
"size":
|
|
186
|
+
"sha256": "37f26adff0a90c0647065b3c2e70bfdae05c84832b27add00915d441f2f79359",
|
|
187
|
+
"size": 63183751
|
|
162
188
|
},
|
|
163
189
|
"linux-arm64": {
|
|
164
|
-
"sha256": "
|
|
165
|
-
"size":
|
|
190
|
+
"sha256": "e329424d19ede87f4c9257de40f88220cdaff42bd82f1ab47616394ec7f32f35",
|
|
191
|
+
"size": 63181548
|
|
166
192
|
}
|
|
167
193
|
}
|
|
168
194
|
}
|
|
@@ -27,13 +27,23 @@ export interface SwiftModule {
|
|
|
27
27
|
toolchainBin: string
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
const installs = new Map<
|
|
30
|
+
const installs = new Map<
|
|
31
|
+
string,
|
|
32
|
+
{
|
|
33
|
+
promise: Promise<string>
|
|
34
|
+
controller: AbortController
|
|
35
|
+
clients: number
|
|
36
|
+
}
|
|
37
|
+
>()
|
|
31
38
|
|
|
32
39
|
/** the installed distribution's root, downloading it on first use, or null
|
|
33
40
|
* when PEACH_SWIFT_ENDPOINT is unset. */
|
|
34
|
-
export function swiftDistributionRoot(
|
|
41
|
+
export async function swiftDistributionRoot(
|
|
42
|
+
signal?: AbortSignal,
|
|
43
|
+
): Promise<string | null> {
|
|
44
|
+
signal?.throwIfAborted()
|
|
35
45
|
const endpoint = process.env[SWIFT_ENDPOINT_ENV]?.trim().replace(/\/+$/, '')
|
|
36
|
-
if (!endpoint) return
|
|
46
|
+
if (!endpoint) return null
|
|
37
47
|
const lock = parseSwiftDistributionLock(lockJson)
|
|
38
48
|
const host = swiftDistributionHost(process.platform, process.arch)
|
|
39
49
|
if (!host) {
|
|
@@ -46,13 +56,16 @@ export function swiftDistributionRoot(): Promise<string | null> {
|
|
|
46
56
|
swiftToolchain(lock.release, lock)
|
|
47
57
|
const dir = path.join(rnxHomeDir(), 'swift', `${lock.id}-${host}`)
|
|
48
58
|
let pending = installs.get(dir)
|
|
59
|
+
if (pending?.controller.signal.aborted) pending = undefined
|
|
49
60
|
if (!pending) {
|
|
50
|
-
|
|
61
|
+
const controller = new AbortController()
|
|
62
|
+
const promise = (async () => {
|
|
51
63
|
if (!fs.existsSync(path.join(dir, SWIFT_DISTRIBUTION_MANIFEST))) {
|
|
52
64
|
process.stderr.write(
|
|
53
65
|
`peach: downloading Swift distribution ${lock.id} (${host})…\n`,
|
|
54
66
|
)
|
|
55
67
|
await installVerifiedTarball({
|
|
68
|
+
signal: controller.signal,
|
|
56
69
|
url: `${endpoint}/${swiftDistributionKey(lock.id, host)}`,
|
|
57
70
|
digest: {
|
|
58
71
|
algorithm: 'sha256',
|
|
@@ -69,11 +82,48 @@ export function swiftDistributionRoot(): Promise<string | null> {
|
|
|
69
82
|
throw new Error(`${dir} does not hold Swift distribution ${lock.id} (${host})`)
|
|
70
83
|
}
|
|
71
84
|
return dir
|
|
72
|
-
})()
|
|
73
|
-
|
|
85
|
+
})().finally(() => {
|
|
86
|
+
if (installs.get(dir)?.controller === controller) installs.delete(dir)
|
|
87
|
+
})
|
|
88
|
+
pending = { promise, controller, clients: 0 }
|
|
74
89
|
installs.set(dir, pending)
|
|
75
90
|
}
|
|
76
|
-
|
|
91
|
+
const current = pending
|
|
92
|
+
current.clients++
|
|
93
|
+
let released = false
|
|
94
|
+
const release = () => {
|
|
95
|
+
if (released) return
|
|
96
|
+
released = true
|
|
97
|
+
if (--current.clients === 0) current.controller.abort(signal?.reason)
|
|
98
|
+
}
|
|
99
|
+
let abort: (() => void) | undefined
|
|
100
|
+
try {
|
|
101
|
+
const root = await new Promise<string>((resolve, reject) => {
|
|
102
|
+
abort = () => {
|
|
103
|
+
release()
|
|
104
|
+
const reason: unknown = signal?.reason
|
|
105
|
+
const error =
|
|
106
|
+
reason instanceof Error
|
|
107
|
+
? reason
|
|
108
|
+
: new Error('Swift distribution installation canceled', { cause: reason })
|
|
109
|
+
// other callers retain ownership; the last caller waits for cleanup.
|
|
110
|
+
if (current.clients > 0) reject(error)
|
|
111
|
+
else
|
|
112
|
+
current.promise.then(
|
|
113
|
+
() => reject(error),
|
|
114
|
+
() => reject(error),
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
signal?.addEventListener('abort', abort, { once: true })
|
|
118
|
+
current.promise.then(resolve, reject)
|
|
119
|
+
if (signal?.aborted) abort()
|
|
120
|
+
})
|
|
121
|
+
signal?.throwIfAborted()
|
|
122
|
+
return root
|
|
123
|
+
} finally {
|
|
124
|
+
if (abort) signal?.removeEventListener('abort', abort)
|
|
125
|
+
release()
|
|
126
|
+
}
|
|
77
127
|
}
|
|
78
128
|
|
|
79
129
|
/** the module this process compiles app Swift against: the checkout beside a
|
|
@@ -81,7 +131,9 @@ export function swiftDistributionRoot(): Promise<string | null> {
|
|
|
81
131
|
* nothing without it, so a checkout's toolchain error surfaces to them. */
|
|
82
132
|
export async function resolveSwiftModule({
|
|
83
133
|
required = false,
|
|
84
|
-
|
|
134
|
+
signal,
|
|
135
|
+
}: { required?: boolean; signal?: AbortSignal } = {}): Promise<SwiftModule | null> {
|
|
136
|
+
signal?.throwIfAborted()
|
|
85
137
|
const checkout = path.resolve(import.meta.dirname, '../../sootsim-swift')
|
|
86
138
|
if (fs.existsSync(path.join(checkout, 'toolchain.json'))) {
|
|
87
139
|
// a checkout is implicit: without its toolchain, apps run without their
|
|
@@ -93,7 +145,7 @@ export async function resolveSwiftModule({
|
|
|
93
145
|
return null
|
|
94
146
|
}
|
|
95
147
|
}
|
|
96
|
-
const root = await swiftDistributionRoot()
|
|
148
|
+
const root = await swiftDistributionRoot(signal)
|
|
97
149
|
return root ? { root, ...swiftModuleToolchain(root) } : null
|
|
98
150
|
}
|
|
99
151
|
|
package/src/swift-host.ts
CHANGED
|
@@ -51,6 +51,7 @@ import {
|
|
|
51
51
|
standinGenerationInputs,
|
|
52
52
|
standinLibrary,
|
|
53
53
|
swiftStandins,
|
|
54
|
+
SWIFT_STANDINS_DIR,
|
|
54
55
|
type SwiftStandin,
|
|
55
56
|
} from './swift-standins'
|
|
56
57
|
import {
|
|
@@ -172,13 +173,18 @@ export interface SwiftProject {
|
|
|
172
173
|
packagePath: string
|
|
173
174
|
artifact: SwiftArtifact | null
|
|
174
175
|
// the build in flight and the sources it was started from
|
|
175
|
-
building: {
|
|
176
|
+
building: {
|
|
177
|
+
stamp: string
|
|
178
|
+
promise: Promise<SwiftArtifact>
|
|
179
|
+
signal?: AbortSignal
|
|
180
|
+
} | null
|
|
176
181
|
// two bounded generations of immutable bytes cover in-flight asset reads.
|
|
177
182
|
resourceFiles: Map<string, { bytes: Buffer; contentType: string }>
|
|
178
183
|
resourcesPrevious: Map<string, { bytes: Buffer; contentType: string }>
|
|
179
184
|
artifacts: SwiftArtifact[]
|
|
180
185
|
hosted?: SwiftHostedCompileOptions
|
|
181
186
|
signal?: AbortSignal
|
|
187
|
+
libraries?: Record<string, string>
|
|
182
188
|
}
|
|
183
189
|
|
|
184
190
|
// a project is its swiftpm package: the build, the artifact, and the dev
|
|
@@ -402,9 +408,6 @@ const XCODE_FRAMEWORKS_DIR = '__frameworks__'
|
|
|
402
408
|
// remote package checkouts compile here: swiftpm owns `.build` in the
|
|
403
409
|
// compile's package root as its scratch directory.
|
|
404
410
|
const XCODE_PACKAGES_DIR = '__packages__'
|
|
405
|
-
// each binary target's stand-in compiles as a target under its module's
|
|
406
|
-
// directory here, from sources generated on this host.
|
|
407
|
-
const XCODE_STANDINS_DIR = '__standins__'
|
|
408
411
|
const CHECKOUTS_PREFIX = `${REMOTE_CHECKOUTS.split(path.sep).join(path.posix.sep)}/`
|
|
409
412
|
|
|
410
413
|
// the compile input key of a root-relative path in an xcode layout.
|
|
@@ -485,7 +488,7 @@ function xcodeLayout(root: string): XcodeLayout | null {
|
|
|
485
488
|
for (const standin of standins)
|
|
486
489
|
targets.push({
|
|
487
490
|
name: standin.module,
|
|
488
|
-
path: path.posix.join(
|
|
491
|
+
path: path.posix.join(SWIFT_STANDINS_DIR, standin.module),
|
|
489
492
|
executable: false,
|
|
490
493
|
resources: false,
|
|
491
494
|
exclude: [],
|
|
@@ -1033,6 +1036,8 @@ export function sourceStamp(
|
|
|
1033
1036
|
const hash = createHash('sha256')
|
|
1034
1037
|
hash.update(packagePath)
|
|
1035
1038
|
hash.update(JSON.stringify(xcodeProjects.get(packagePath)?.behavior ?? {}))
|
|
1039
|
+
const libraries = projects.get(packagePath)?.libraries
|
|
1040
|
+
if (libraries) hash.update(JSON.stringify(libraries))
|
|
1036
1041
|
const projectInputs = new Set(packageFiles(packagePath))
|
|
1037
1042
|
for (const file of [...projectInputs].sort()) {
|
|
1038
1043
|
hash.update(path.relative(packagePath, file))
|
|
@@ -1092,15 +1097,19 @@ export function icuUrl(origin: string, artifact: SwiftArtifact): string {
|
|
|
1092
1097
|
// keyed by the stand-in target it links into; its own source then defines
|
|
1093
1098
|
// nothing, so the library's functions run instead of the traps. one library
|
|
1094
1099
|
// links once, through the first module its framework ships.
|
|
1095
|
-
function standinLibraries(
|
|
1100
|
+
export function standinLibraries(
|
|
1101
|
+
binaries: readonly SwiftPackageBinary[],
|
|
1102
|
+
standins: readonly SwiftStandin[],
|
|
1103
|
+
rootDir: string,
|
|
1104
|
+
): Map<string, SwiftStandin[]> {
|
|
1096
1105
|
const linked = new Map<string, SwiftStandin[]>()
|
|
1097
|
-
for (const binary of
|
|
1098
|
-
const modules =
|
|
1106
|
+
for (const binary of binaries) {
|
|
1107
|
+
const modules = standins.filter(
|
|
1099
1108
|
(standin) =>
|
|
1100
1109
|
standin.target === binary.target && `${standin.module}.c` in standin.files,
|
|
1101
1110
|
)
|
|
1102
1111
|
if (modules.length === 0) continue
|
|
1103
|
-
const library = standinLibrary(binary,
|
|
1112
|
+
const library = standinLibrary(binary, rootDir)
|
|
1104
1113
|
if (!library) continue
|
|
1105
1114
|
linked.set(library, modules)
|
|
1106
1115
|
}
|
|
@@ -1131,7 +1140,7 @@ function packageSources(
|
|
|
1131
1140
|
files['Package.swift'] = xcodeManifest(xcode)
|
|
1132
1141
|
for (const standin of xcode.standins)
|
|
1133
1142
|
for (const [file, text] of Object.entries(standin.files))
|
|
1134
|
-
files[path.posix.join(
|
|
1143
|
+
files[path.posix.join(SWIFT_STANDINS_DIR, standin.module, file)] =
|
|
1135
1144
|
linked.has(standin) && file === `${standin.module}.c`
|
|
1136
1145
|
? `// ${standin.target}'s WebAssembly library defines ${standin.module}'s functions.\n`
|
|
1137
1146
|
: text
|
|
@@ -1152,7 +1161,9 @@ interface SwiftPackageSnapshot {
|
|
|
1152
1161
|
|
|
1153
1162
|
function packageSnapshot(project: SwiftProject, entry: string): SwiftPackageSnapshot {
|
|
1154
1163
|
const layout = xcodeLayout(project.packagePath)
|
|
1155
|
-
const linked = layout
|
|
1164
|
+
const linked = layout
|
|
1165
|
+
? standinLibraries(layout.binaries, layout.standins, layout.app.rootDir)
|
|
1166
|
+
: new Map<string, SwiftStandin[]>()
|
|
1156
1167
|
const files = packageSources(project.packagePath, new Set([...linked.values()].flat()))
|
|
1157
1168
|
const native = nativeSourceDescriptor(project.packagePath)
|
|
1158
1169
|
return {
|
|
@@ -1178,14 +1189,17 @@ function packageSnapshot(project: SwiftProject, entry: string): SwiftPackageSnap
|
|
|
1178
1189
|
files,
|
|
1179
1190
|
),
|
|
1180
1191
|
native,
|
|
1181
|
-
...(linked.size > 0
|
|
1192
|
+
...(linked.size > 0 || project.libraries
|
|
1182
1193
|
? {
|
|
1183
|
-
libraries:
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1194
|
+
libraries: {
|
|
1195
|
+
...project.libraries,
|
|
1196
|
+
...Object.fromEntries(
|
|
1197
|
+
[...linked].map(([library, [first]]) => [
|
|
1198
|
+
first!.module,
|
|
1199
|
+
fs.readFileSync(library).toString('base64'),
|
|
1200
|
+
]),
|
|
1201
|
+
),
|
|
1202
|
+
},
|
|
1189
1203
|
}
|
|
1190
1204
|
: {}),
|
|
1191
1205
|
...(native
|
|
@@ -1252,7 +1266,7 @@ async function compile(
|
|
|
1252
1266
|
linkedModuleRoots(packagePath).find((root) =>
|
|
1253
1267
|
fs.existsSync(path.join(root, 'toolchain.json')),
|
|
1254
1268
|
) ??
|
|
1255
|
-
(await swiftDistributionRoot())
|
|
1269
|
+
(await swiftDistributionRoot(signal))
|
|
1256
1270
|
if (!moduleRoot)
|
|
1257
1271
|
return {
|
|
1258
1272
|
...(await compileRemote(packagePath, snapshot, undefined, signal)),
|
|
@@ -1559,8 +1573,11 @@ export function artifactFor(
|
|
|
1559
1573
|
project: SwiftProject,
|
|
1560
1574
|
entry: string,
|
|
1561
1575
|
moduleRoot?: string,
|
|
1576
|
+
signal: AbortSignal | undefined = project.signal,
|
|
1562
1577
|
): Promise<SwiftArtifact> {
|
|
1563
|
-
project.signal
|
|
1578
|
+
if (project.signal && signal !== project.signal)
|
|
1579
|
+
signal = signal ? AbortSignal.any([project.signal, signal]) : project.signal
|
|
1580
|
+
signal?.throwIfAborted()
|
|
1564
1581
|
const stamp = sourceStamp(project.packagePath, moduleRoot, Boolean(project.hosted))
|
|
1565
1582
|
if (project.artifact?.stamp === stamp) return Promise.resolve(project.artifact)
|
|
1566
1583
|
if (project.building) {
|
|
@@ -1569,13 +1586,27 @@ export function artifactFor(
|
|
|
1569
1586
|
// and ask again, which starts the build these sources need; joining it
|
|
1570
1587
|
// would emit the older artifact and nothing would trigger another load.
|
|
1571
1588
|
const running = project.building
|
|
1572
|
-
if (running.stamp === stamp) return running.promise
|
|
1573
|
-
const again = () => artifactFor(project, entry, moduleRoot)
|
|
1574
|
-
|
|
1589
|
+
if (running.stamp === stamp && running.signal === signal) return running.promise
|
|
1590
|
+
const again = () => artifactFor(project, entry, moduleRoot, signal)
|
|
1591
|
+
const queued = running.promise.then(again, again)
|
|
1592
|
+
if (!signal) return queued
|
|
1593
|
+
return new Promise<SwiftArtifact>((resolve, reject) => {
|
|
1594
|
+
const stop = () =>
|
|
1595
|
+
reject(
|
|
1596
|
+
signal.reason instanceof Error
|
|
1597
|
+
? signal.reason
|
|
1598
|
+
: new Error('Swift compilation canceled', { cause: signal.reason }),
|
|
1599
|
+
)
|
|
1600
|
+
signal.addEventListener('abort', stop, { once: true })
|
|
1601
|
+
if (signal.aborted) stop()
|
|
1602
|
+
queued
|
|
1603
|
+
.then(resolve, reject)
|
|
1604
|
+
.finally(() => signal.removeEventListener('abort', stop))
|
|
1605
|
+
})
|
|
1575
1606
|
}
|
|
1576
1607
|
const snapshot = packageSnapshot(project, entry)
|
|
1577
1608
|
if (sourceStamp(project.packagePath, moduleRoot, Boolean(project.hosted)) !== stamp) {
|
|
1578
|
-
return artifactFor(project, entry, moduleRoot)
|
|
1609
|
+
return artifactFor(project, entry, moduleRoot, signal)
|
|
1579
1610
|
}
|
|
1580
1611
|
// a save that changed only view builder literals keeps the running bytes
|
|
1581
1612
|
const compiled = project.artifact?.compiled
|
|
@@ -1600,7 +1631,7 @@ export function artifactFor(
|
|
|
1600
1631
|
moduleRoot,
|
|
1601
1632
|
project.hosted,
|
|
1602
1633
|
snapshot,
|
|
1603
|
-
|
|
1634
|
+
signal,
|
|
1604
1635
|
).then(
|
|
1605
1636
|
(artifact) => {
|
|
1606
1637
|
project.artifact = artifact
|
|
@@ -1620,9 +1651,9 @@ export function artifactFor(
|
|
|
1620
1651
|
sourceStamp(project.packagePath, moduleRoot, Boolean(project.hosted)) ===
|
|
1621
1652
|
artifact.stamp
|
|
1622
1653
|
? artifact
|
|
1623
|
-
: artifactFor(project, entry, moduleRoot),
|
|
1654
|
+
: artifactFor(project, entry, moduleRoot, signal),
|
|
1624
1655
|
)
|
|
1625
|
-
project.building = { stamp, promise: fresh }
|
|
1656
|
+
project.building = { stamp, promise: fresh, signal }
|
|
1626
1657
|
return fresh
|
|
1627
1658
|
}
|
|
1628
1659
|
|
|
@@ -174,6 +174,19 @@ export function objcView(
|
|
|
174
174
|
const slash = include.indexOf('/')
|
|
175
175
|
if (slash > 0 && /^[A-Z]/.test(include)) {
|
|
176
176
|
const name = include.slice(0, slash)
|
|
177
|
+
const umbrella = `${name}/${name}.h`
|
|
178
|
+
if (
|
|
179
|
+
Object.hasOwn(
|
|
180
|
+
IOS_SDK_HEADERS,
|
|
181
|
+
`System/Library/Frameworks/${name}.framework/Headers/${name}.h`,
|
|
182
|
+
)
|
|
183
|
+
) {
|
|
184
|
+
// retain the sdk module's owner when a vendor imports a leaf header.
|
|
185
|
+
const header = path.join(stubIncludes, include)
|
|
186
|
+
fs.mkdirSync(path.dirname(header), { recursive: true })
|
|
187
|
+
fs.writeFileSync(header, `#import <${umbrella}>\n`)
|
|
188
|
+
return true
|
|
189
|
+
}
|
|
177
190
|
const root = path.join(stubs, `${name}.framework`)
|
|
178
191
|
const header = path.join(root, 'Headers', include.slice(slash + 1))
|
|
179
192
|
fs.mkdirSync(path.dirname(header), { recursive: true })
|
|
@@ -87,7 +87,7 @@ export type StandinResult = {
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
/** bump when emitted source changes: the stand-in cache keys on it. */
|
|
90
|
-
export const STANDIN_EMIT_VERSION = '
|
|
90
|
+
export const STANDIN_EMIT_VERSION = '7'
|
|
91
91
|
|
|
92
92
|
/** the modules every stand-in source imports. */
|
|
93
93
|
export const STANDIN_IMPORTS = ['Foundation', 'UIKit']
|
package/src/swift-standins.ts
CHANGED
|
@@ -38,6 +38,8 @@ import type { SwiftPackageBinary } from './swift-packages'
|
|
|
38
38
|
const TRIPLE = 'wasm32-unknown-wasip1'
|
|
39
39
|
// builder workspaces share this persistent cache; temporary directories hold only work.
|
|
40
40
|
const cacheDir = path.join(os.homedir(), 'builders/cache/peach-swift-standins')
|
|
41
|
+
// each binary stand-in is a target under its module's directory here.
|
|
42
|
+
export const SWIFT_STANDINS_DIR = '__standins__'
|
|
41
43
|
|
|
42
44
|
export interface SwiftStandin {
|
|
43
45
|
/** the module it stands in for, which names its compile target. */
|
|
@@ -993,7 +995,7 @@ function sourceBinaryStandins(
|
|
|
993
995
|
release: toolchain.release,
|
|
994
996
|
}),
|
|
995
997
|
)
|
|
996
|
-
|
|
998
|
+
const build = spawnSync(
|
|
997
999
|
'bun',
|
|
998
1000
|
[
|
|
999
1001
|
path.join(repo, 'scripts/build/swift-standins-submit.ts'),
|
|
@@ -1004,11 +1006,20 @@ function sourceBinaryStandins(
|
|
|
1004
1006
|
'--delivery-dir',
|
|
1005
1007
|
delivery,
|
|
1006
1008
|
],
|
|
1007
|
-
{ cwd: repo,
|
|
1008
|
-
)
|
|
1009
|
-
const receipt: unknown = JSON.parse(
|
|
1010
|
-
fs.readFileSync(path.join(delivery, 'standins.json'), 'utf8'),
|
|
1009
|
+
{ cwd: repo, encoding: 'utf8', maxBuffer: 8 * 1024 * 1024 },
|
|
1011
1010
|
)
|
|
1011
|
+
if (build.stdout) process.stdout.write(build.stdout)
|
|
1012
|
+
if (build.stderr) process.stderr.write(build.stderr)
|
|
1013
|
+
if (build.error || build.status !== 0)
|
|
1014
|
+
throw new Error(
|
|
1015
|
+
`source stand-in build for ${binary.target} failed (${build.error?.message ?? build.signal ?? `exit ${build.status}`}): ${build.stderr?.trim().slice(-4000) || build.stdout?.trim().slice(-4000) || 'builder returned no diagnostic'}`,
|
|
1016
|
+
)
|
|
1017
|
+
const output = path.join(delivery, 'standins.json')
|
|
1018
|
+
if (!fs.existsSync(output))
|
|
1019
|
+
throw new Error(
|
|
1020
|
+
`source stand-in builder returned no standins.json for ${binary.target}`,
|
|
1021
|
+
)
|
|
1022
|
+
const receipt: unknown = JSON.parse(fs.readFileSync(output, 'utf8'))
|
|
1012
1023
|
if (
|
|
1013
1024
|
!receipt ||
|
|
1014
1025
|
typeof receipt !== 'object' ||
|
|
@@ -1174,6 +1185,7 @@ function binaryStandins(
|
|
|
1174
1185
|
try {
|
|
1175
1186
|
result =
|
|
1176
1187
|
process.platform === 'darwin' &&
|
|
1188
|
+
process.env.TM_EXEC_BUILD_PLATFORM !== 'mac' &&
|
|
1177
1189
|
process.env.TM_SESSION &&
|
|
1178
1190
|
process.env.CI !== 'true' &&
|
|
1179
1191
|
toolchain.moduleRoot === path.resolve(import.meta.dirname, '../../sootsim-swift') &&
|
package/src/vite-plugin.ts
CHANGED
|
@@ -17,6 +17,7 @@ import { fileURLToPath, pathToFileURL } from 'url'
|
|
|
17
17
|
import { parseSync } from 'oxc-parser'
|
|
18
18
|
import { minifySync, transformSync } from 'rolldown/utils'
|
|
19
19
|
import { type Plugin, transformWithOxc } from 'vite'
|
|
20
|
+
import { resolveOneNativeBackend } from '../../contrast-bundler/src/oneFramework.ts'
|
|
20
21
|
import { transformFlowSource } from '../../peach-compat/src/flow-strip.ts'
|
|
21
22
|
import { renderWorkerSystemChunk } from './worker-system-chunks.ts'
|
|
22
23
|
|
|
@@ -34,7 +35,7 @@ import {
|
|
|
34
35
|
import { resolveExportsHiddenSubpath } from '../../peach-compat/src/resolve-hidden-subpath.ts'
|
|
35
36
|
import {
|
|
36
37
|
SOOTSIM_COMPAT_PUBLISHED_BROWSER_ENTRIES,
|
|
37
|
-
|
|
38
|
+
PEACH_UPSTREAM_MODULES,
|
|
38
39
|
SOOTSIM_HAPTIC_FEEDBACK_TOUCHABLE_SOURCE,
|
|
39
40
|
SOOTSIM_HAPTIC_FEEDBACK_TOUCHABLE_SPECIFIER,
|
|
40
41
|
compatStubsForBuildResolver,
|
|
@@ -291,9 +292,7 @@ const compatWrapperRealPackageAliases: Array<{
|
|
|
291
292
|
find: string
|
|
292
293
|
replacement: string
|
|
293
294
|
}> = []
|
|
294
|
-
for (const { specifier, packageName } of Object.values(
|
|
295
|
-
SOOTSIM_COMPAT_WRAPPER_REAL_PACKAGES,
|
|
296
|
-
)) {
|
|
295
|
+
for (const { specifier, packageName } of Object.values(PEACH_UPSTREAM_MODULES)) {
|
|
297
296
|
let replacement = resolveExportsHiddenSubpath(packageName, (specifier) =>
|
|
298
297
|
sootsimPluginRequire.resolve(specifier),
|
|
299
298
|
)
|
|
@@ -366,7 +365,6 @@ export function sootsim(options: SootSimOptions = {}): Plugin[] {
|
|
|
366
365
|
}
|
|
367
366
|
|
|
368
367
|
return [
|
|
369
|
-
wsBridgePlugin(),
|
|
370
368
|
reactBridgePlugin(),
|
|
371
369
|
{
|
|
372
370
|
name: 'sootsim-published-browser-internals',
|
|
@@ -1319,9 +1317,9 @@ declare global {
|
|
|
1319
1317
|
// across the overlap so every connected sim stays on the same websocket.
|
|
1320
1318
|
const viteBridgeProcesses = (globalThis.__sootsimViteBridgeProcesses ??= new Map())
|
|
1321
1319
|
|
|
1322
|
-
// start WS bridge server for debug CLI connectivity.
|
|
1323
|
-
//
|
|
1324
|
-
//
|
|
1320
|
+
// start WS bridge server for debug CLI connectivity. the shell vite server
|
|
1321
|
+
// installs it explicitly and owns the one shared bridge; sootsim() never
|
|
1322
|
+
// does, so an isolated dev server cannot bind the shared default port.
|
|
1325
1323
|
export function wsBridgePlugin(options: { ownedBridgePort?: number } = {}): Plugin {
|
|
1326
1324
|
if (
|
|
1327
1325
|
options.ownedBridgePort !== undefined &&
|
|
@@ -1726,17 +1724,6 @@ function sootsimConfigPlugin(
|
|
|
1726
1724
|
'react-native/src/private/featureflags/ReactNativeFeatureFlags.js',
|
|
1727
1725
|
),
|
|
1728
1726
|
},
|
|
1729
|
-
...[
|
|
1730
|
-
'BatchedBridge/BatchedBridge',
|
|
1731
|
-
'Components/View/ReactNativeStyleAttributes',
|
|
1732
|
-
'StyleSheet/processColor',
|
|
1733
|
-
].map((module) => ({
|
|
1734
|
-
find: `@peach-internal/react-native/${module}`,
|
|
1735
|
-
replacement: path.resolve(
|
|
1736
|
-
workspaceNodeModules,
|
|
1737
|
-
`react-native/Libraries/${module}.js`,
|
|
1738
|
-
),
|
|
1739
|
-
})),
|
|
1740
1727
|
...Object.entries(rnLibraryStubs).map(([find, replacement]) => ({
|
|
1741
1728
|
find: new RegExp(
|
|
1742
1729
|
`^${find.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(?:\\.[cm]?[jt]sx?)?$`,
|
|
@@ -2227,6 +2214,8 @@ function metroNativeResolve(isAppSource: (p: string) => boolean): Plugin {
|
|
|
2227
2214
|
filter: { id: /^\./ },
|
|
2228
2215
|
handler(source, importer) {
|
|
2229
2216
|
if (!importer) return null
|
|
2217
|
+
const oneBackend = resolveOneNativeBackend(source, importer)
|
|
2218
|
+
if (oneBackend) return oneBackend
|
|
2230
2219
|
if (!importer.includes('node_modules') && !isAppSource(importer)) return null
|
|
2231
2220
|
if (platformExts.some((ext) => source.endsWith(ext))) return null
|
|
2232
2221
|
|
|
@@ -92,7 +92,7 @@ interface Scope {
|
|
|
92
92
|
|
|
93
93
|
type Binding =
|
|
94
94
|
| { kind: 'value'; init: SyntaxNode; scope: Scope }
|
|
95
|
-
// `key` is set when the parameter is a destructured
|
|
95
|
+
// `key` is set when the parameter is a destructured member of that argument.
|
|
96
96
|
| { kind: 'param'; fn: SyntaxNode; index: number; key?: string }
|
|
97
97
|
| { kind: 'opaque' }
|
|
98
98
|
|
|
@@ -147,7 +147,7 @@ function lookup(scope: Scope, name: string): Binding | undefined {
|
|
|
147
147
|
return undefined
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
-
// a destructured parameter binds each plain
|
|
150
|
+
// a destructured parameter binds each plain member to that key of its argument.
|
|
151
151
|
// rest elements, defaults and nested patterns stay opaque.
|
|
152
152
|
function declareParam(inner: Scope, fn: SyntaxNode, index: number, param: unknown): void {
|
|
153
153
|
const name = identifierName(param)
|
|
@@ -155,6 +155,17 @@ function declareParam(inner: Scope, fn: SyntaxNode, index: number, param: unknow
|
|
|
155
155
|
declare(inner, name, { kind: 'param', fn, index })
|
|
156
156
|
return
|
|
157
157
|
}
|
|
158
|
+
if (isNode(param) && param.type === 'ArrayPattern') {
|
|
159
|
+
const elements: unknown = Reflect.get(param, 'elements')
|
|
160
|
+
if (!Array.isArray(elements)) return
|
|
161
|
+
elements.forEach((element: unknown, position) => {
|
|
162
|
+
const name = identifierName(element)
|
|
163
|
+
if (name !== undefined)
|
|
164
|
+
declare(inner, name, { kind: 'param', fn, index, key: String(position) })
|
|
165
|
+
else declareOpaque(inner, patternNames(element))
|
|
166
|
+
})
|
|
167
|
+
return
|
|
168
|
+
}
|
|
158
169
|
if (!isNode(param) || param.type !== 'ObjectPattern') {
|
|
159
170
|
declareOpaque(inner, patternNames(param))
|
|
160
171
|
return
|
|
@@ -334,6 +345,21 @@ function immediateArgument(
|
|
|
334
345
|
// the value a literal holds under a static name. the last duplicate wins, as it
|
|
335
346
|
// does at run time. a spread makes the literal's keys unknown.
|
|
336
347
|
function propertyValue(object: SyntaxNode, name: string): SyntaxNode | undefined {
|
|
348
|
+
if (object.type === 'ArrayExpression') {
|
|
349
|
+
const index = Number(name)
|
|
350
|
+
const elements: unknown = Reflect.get(object, 'elements')
|
|
351
|
+
if (!Number.isSafeInteger(index) || index < 0 || String(index) !== name)
|
|
352
|
+
return undefined
|
|
353
|
+
if (!Array.isArray(elements)) return undefined
|
|
354
|
+
if (
|
|
355
|
+
elements
|
|
356
|
+
.slice(0, index + 1)
|
|
357
|
+
.some((element: unknown) => isNode(element) && element.type === 'SpreadElement')
|
|
358
|
+
)
|
|
359
|
+
return undefined
|
|
360
|
+
const value: unknown = elements[index]
|
|
361
|
+
return isNode(value) ? value : undefined
|
|
362
|
+
}
|
|
337
363
|
let value: SyntaxNode | undefined
|
|
338
364
|
for (const property of childNodes(Reflect.get(object, 'properties'))) {
|
|
339
365
|
if (property.type === 'SpreadElement') return undefined
|
|
@@ -373,7 +399,8 @@ function resolveObject(
|
|
|
373
399
|
depth: number,
|
|
374
400
|
): ResolvedObject | undefined {
|
|
375
401
|
if (depth > MAX_RESOLUTION_DEPTH) return undefined
|
|
376
|
-
if (expression.type === 'ObjectExpression'
|
|
402
|
+
if (expression.type === 'ObjectExpression' || expression.type === 'ArrayExpression')
|
|
403
|
+
return { object: expression, scope }
|
|
377
404
|
if (expression.type === 'SequenceExpression') {
|
|
378
405
|
const last = childNodes(Reflect.get(expression, 'expressions')).at(-1)
|
|
379
406
|
return last && resolveObject(last, scope, collection, depth + 1)
|