peachsim 0.1.1620 → 0.1.1635
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/LICENSE +1 -1
- package/README.md +45 -6
- package/cli/app-config.ts +4 -1
- package/cli/app-fonts.ts +39 -18
- package/cli/app-stubs.ts +224 -9
- package/cli/bridge-flow-runner.ts +248 -156
- package/cli/cloud-client.ts +68 -57
- package/cli/command-registry.ts +1 -0
- package/cli/commands/box/search-command.ts +3 -2
- package/cli/commands/box.ts +10 -29
- package/cli/commands/control.ts +391 -92
- package/cli/commands/doctor.ts +17 -184
- package/cli/commands/flow.ts +79 -11
- package/cli/commands/inspect/core.ts +10 -5
- package/cli/commands/inspect/settling.ts +71 -22
- package/cli/commands/inspect/wait-ready.ts +12 -0
- package/cli/commands/inspect.ts +26 -3
- package/cli/commands/install-desktop.ts +1 -0
- package/cli/commands/maestro-compare.ts +105 -67
- package/cli/commands/maestro.ts +9 -1
- package/cli/commands/platform.ts +71 -36
- package/cli/commands/record.ts +21 -1
- package/cli/commands/screenshot.ts +23 -1
- package/cli/commands/serve.ts +7 -2
- package/cli/commands/setup.ts +19 -42
- package/cli/commands/swift.ts +5 -0
- package/cli/commands/upload.ts +51 -8
- package/cli/desktop-companion.ts +102 -17
- package/cli/dev-build-freshness.ts +37 -2
- package/cli/drivers/desktop.ts +14 -1
- package/cli/drivers/playwright-sim-host.ts +44 -8
- package/cli/drivers/playwright.ts +9 -8
- package/cli/drivers/types.ts +5 -0
- package/cli/maestro-accessibility.ts +53 -0
- package/cli/maestro-compare.ts +108 -12
- package/cli/main.ts +2 -1
- package/cli/outbound-endpoints.ts +35 -6
- package/cli/parent-pid.ts +0 -9
- package/cli/pause-aware-timeout.ts +102 -0
- package/cli/remote-simulators.ts +112 -0
- package/cli/remoteSimulatorContract.ts +21 -0
- package/cli/scene-video.ts +89 -0
- package/cli/ws-bridge.ts +23 -14
- package/detox/attempt-events.cjs +4 -0
- package/detox/expectations.ts +5 -1
- package/detox/index.ts +241 -181
- package/detox/motion-change.cjs +73 -0
- package/detox/motion-change.d.cts +21 -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 +13 -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 +14 -8
- package/dist-lib/bridge-contract.mjs +13 -7
- package/dist-lib/bundle-channel.cjs +10480 -7962
- 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 +10 -6
- package/dist-lib/config.mjs +10 -6
- package/dist-lib/detox/index.cjs +1854 -966
- package/dist-lib/dev-bundle-resolution.cjs +64 -24
- package/dist-lib/expo-swift-bundle.cjs +8715 -6271
- package/dist-lib/expo-swift-metadata.cjs +7 -4
- package/dist-lib/expo-swift-metadata.mjs +7 -4
- package/dist-lib/home-paths.cjs +32 -3
- package/dist-lib/host/bridge-host.cjs +10135 -7060
- 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 +1843 -408
- 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 +1182 -418
- package/dist-lib/native-app-identity-project.cjs +782 -23
- package/dist-lib/native-app-identity-project.mjs +781 -23
- package/dist-lib/native-app-identity.cjs +276 -9
- package/dist-lib/native-app-identity.mjs +273 -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 +8 -1
- package/dist-lib/react-native-host-modules.mjs +8 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +106 -28
- package/dist-lib/sdk.cjs +115 -34
- package/dist-lib/sdk.mjs +115 -33
- package/dist-lib/skills.cjs +15274 -9904
- package/dist-lib/swift-compile.cjs +1165 -173
- package/dist-lib/swift-compile.mjs +1212 -203
- package/dist-lib/swift-package.cjs +12 -3
- package/dist-lib/swift-package.mjs +12 -3
- package/dist-lib/swift-submit.cjs +7 -1
- package/dist-lib/swift-submit.mjs +5 -1
- package/dist-lib/swift-toolchain.cjs +133 -18
- package/dist-lib/swift.cjs +1 -1
- package/dist-lib/vite-swift.cjs +2648 -862
- package/dist-lib/vite.cjs +70 -14
- package/dist-lib/worklet-executable.cjs +25 -2
- package/dist-lib/worklet-executable.mjs +25 -2
- package/kotlin/AppSources/dev/peach/app/PeachDrawable.kt +21 -14
- package/kotlin/Sources/androidx/annotation/DrawableRes.kt +14 -0
- package/package.json +2 -2
- package/scripts/dev-server-scanner.ts +35 -14
- package/skills/peach-perf/SKILL.md +11 -5
- package/skills/peach-setup/SKILL.md +16 -19
- 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/bridge-constants.ts +11 -0
- package/src/bridge-contract.ts +22 -34
- package/src/bundleGlobalFooter.ts +26 -7
- package/src/codex-client.ts +3 -2
- package/src/config.ts +15 -0
- package/src/desktop-host-delivery.ts +5 -1
- package/src/dev-bundle-resolution.ts +60 -26
- package/src/expo-kotlin-host.ts +6 -36
- package/src/expo-manifest-body.ts +13 -0
- package/src/expo-swift-bundle.ts +17 -12
- package/src/expo-swift-host.ts +640 -192
- package/src/expo-swift-ready.ts +78 -0
- package/src/expoSwiftContract.ts +19 -0
- package/src/home-paths.ts +48 -2
- package/src/host/bridge-host.ts +12 -57
- 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 +142 -5
- package/src/native-app-identity.ts +176 -11
- package/src/native-firebase-config.ts +209 -13
- package/src/nitro-compile.ts +2 -5
- package/src/nitro-source.ts +147 -63
- package/src/process-group.ts +114 -61
- package/src/process-output.ts +0 -9
- package/src/public-brand.ts +1 -0
- package/src/react-native-bridge-modules.ts +416 -0
- package/src/react-native-host-modules.ts +7 -0
- package/src/sim-client.ts +6 -4
- package/src/swift-binary-modules.ts +2 -2
- package/src/swift-compile.ts +1007 -108
- package/src/swift-compiler-digest.ts +14 -0
- package/src/swift-distribution.lock.json +40 -12
- package/src/swift-distribution.ts +61 -9
- package/src/swift-host.ts +149 -56
- package/src/swift-icu.ts +14 -6
- package/src/swift-objc-sdk.ts +133 -10
- package/src/swift-objc-standin.ts +38 -3
- package/src/swift-package-manifest.ts +21 -6
- package/src/swift-package.ts +15 -1
- package/src/swift-packages.ts +119 -60
- package/src/swift-standin-emit.ts +57 -9
- package/src/swift-standins.ts +185 -25
- package/src/swift-submit.ts +10 -0
- package/src/swift-toolchain.ts +149 -23
- package/src/vite-plugin.ts +20 -20
- package/src/worklet-executable.ts +30 -3
- package/src/worklets-babel.ts +35 -14
- package/src/xcode-project.ts +10 -0
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
isLoopbackHost,
|
|
4
4
|
LOOPBACK_PROBE_HOSTNAMES,
|
|
5
5
|
} from './backend-origin'
|
|
6
|
+
import { parseExpoManifestBody } from './expo-manifest-body'
|
|
6
7
|
import {
|
|
7
8
|
alignLoopbackBundleUrlWithProbe,
|
|
8
9
|
METRO_BUNDLE_PATH,
|
|
@@ -159,11 +160,15 @@ type ManifestProbe =
|
|
|
159
160
|
// manifest whose response consistently takes longer than that probe.
|
|
160
161
|
export const MANIFEST_PROBE_TIMEOUT_MS = 30_000
|
|
161
162
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
163
|
+
const manifestRequests = new Map<string, Promise<Response | undefined>>()
|
|
164
|
+
|
|
165
|
+
// the scanner, its enrichment and explicit opens share the complete response.
|
|
166
|
+
// a short scan deadline must not discard work that an open is still awaiting.
|
|
167
|
+
export async function fetchDevManifest(baseUrl: string): Promise<Response | undefined> {
|
|
168
|
+
const normalizedBaseUrl = baseUrl.replace(/\/+$/, '')
|
|
169
|
+
const existing = manifestRequests.get(normalizedBaseUrl)
|
|
170
|
+
if (existing) return (await existing)?.clone()
|
|
171
|
+
|
|
167
172
|
// the deadline means STOP WAITING, not KILL THE REQUEST. `AbortSignal.timeout`
|
|
168
173
|
// does the second: it tears down a dev server that is still assembling its
|
|
169
174
|
// manifest, which fires "Cannot pipe to a closed or destroyed stream" into
|
|
@@ -173,39 +178,55 @@ async function probeExpoManifest(
|
|
|
173
178
|
const pending = fetchDevProbe(`${normalizedBaseUrl}/`, {
|
|
174
179
|
headers: { 'expo-platform': 'ios' },
|
|
175
180
|
})
|
|
176
|
-
.then(async (res) =>
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
+
.then(async (res) => {
|
|
182
|
+
const body = await res.arrayBuffer()
|
|
183
|
+
return new Response(body.byteLength ? body : null, {
|
|
184
|
+
status: res.status,
|
|
185
|
+
headers: res.headers,
|
|
186
|
+
})
|
|
187
|
+
})
|
|
181
188
|
.catch(() => undefined)
|
|
182
189
|
let probeDeadline: ReturnType<typeof setTimeout> | undefined
|
|
183
|
-
const
|
|
190
|
+
const request = Promise.race([
|
|
184
191
|
pending,
|
|
185
192
|
new Promise<'timeout'>((resolve) => {
|
|
186
193
|
probeDeadline = setTimeout(() => resolve('timeout'), MANIFEST_PROBE_TIMEOUT_MS)
|
|
187
194
|
}),
|
|
188
195
|
])
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
196
|
+
.then((answered) => {
|
|
197
|
+
if (answered === 'timeout')
|
|
198
|
+
throw new Error(
|
|
199
|
+
`Metro manifest request timed out after ${MANIFEST_PROBE_TIMEOUT_MS / 1000}s: ${normalizedBaseUrl}/. Wait for the dev server to finish starting or check its manifest endpoint.`,
|
|
200
|
+
)
|
|
201
|
+
return answered
|
|
202
|
+
})
|
|
203
|
+
.finally(() => {
|
|
204
|
+
clearTimeout(probeDeadline)
|
|
205
|
+
manifestRequests.delete(normalizedBaseUrl)
|
|
206
|
+
})
|
|
207
|
+
manifestRequests.set(normalizedBaseUrl, request)
|
|
208
|
+
return (await request)?.clone()
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async function probeExpoManifest(
|
|
212
|
+
normalizedBaseUrl: string,
|
|
213
|
+
port: number,
|
|
214
|
+
): Promise<ManifestProbe> {
|
|
215
|
+
const answered = await fetchDevManifest(normalizedBaseUrl)
|
|
195
216
|
if (answered === undefined) return { state: 'no-answer' }
|
|
196
217
|
if (answered.status === 404 || answered.status === 405) return { state: 'no-manifest' }
|
|
197
218
|
if (answered.status < 200 || answered.status >= 300)
|
|
198
219
|
throw new Error(
|
|
199
220
|
`Metro manifest request failed: HTTP ${answered.status} at ${normalizedBaseUrl}/`,
|
|
200
221
|
)
|
|
201
|
-
body = answered.
|
|
222
|
+
const body = await answered.text()
|
|
202
223
|
if (!body.trim()) return { state: 'no-answer' }
|
|
203
224
|
|
|
204
225
|
let manifest: unknown
|
|
205
226
|
try {
|
|
206
|
-
manifest =
|
|
227
|
+
manifest = parseExpoManifestBody(body)
|
|
207
228
|
} catch {
|
|
208
|
-
if (answered.
|
|
229
|
+
if (answered.headers.get('content-type')?.includes('json'))
|
|
209
230
|
throw new Error(`Metro returned invalid manifest JSON at ${normalizedBaseUrl}/`)
|
|
210
231
|
// a page rather than a manifest — Metro's landing page, or an app's own
|
|
211
232
|
// SSR html. that is a real answer: this server serves no manifest.
|
|
@@ -309,12 +330,25 @@ async function probeBaseUrlBundle(
|
|
|
309
330
|
export async function probePortBundle(port: number): Promise<ResolvedDevBundle | null> {
|
|
310
331
|
const viaScan = await resolvePortViaServerScan(port)
|
|
311
332
|
if (viaScan) return viaScan
|
|
312
|
-
const
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
333
|
+
const unresolved = buildUnresolvedTargetError(`localhost:${port}`)
|
|
334
|
+
try {
|
|
335
|
+
// a silent or failed address family must not hold back a valid sibling.
|
|
336
|
+
return await Promise.any(
|
|
337
|
+
LOOPBACK_PROBE_HOSTNAMES.map(async (hostname) => {
|
|
338
|
+
const bundle = await probeBaseUrlBundleDirect(
|
|
339
|
+
buildLoopbackProbeBaseUrl('http:', hostname, port),
|
|
340
|
+
port,
|
|
341
|
+
)
|
|
342
|
+
if (!bundle) throw unresolved
|
|
343
|
+
return bundle
|
|
344
|
+
}),
|
|
345
|
+
)
|
|
346
|
+
} catch (error) {
|
|
347
|
+
if (!(error instanceof AggregateError)) throw error
|
|
348
|
+
const failure = error.errors.find((candidate) => candidate !== unresolved)
|
|
349
|
+
if (failure !== undefined) throw failure
|
|
350
|
+
return null
|
|
351
|
+
}
|
|
318
352
|
}
|
|
319
353
|
|
|
320
354
|
export async function resolveConnectionInput(input: string): Promise<ResolvedDevBundle> {
|
package/src/expo-kotlin-host.ts
CHANGED
|
@@ -1,22 +1,15 @@
|
|
|
1
1
|
// local Expo modules on an Android device: expo's own autolinker selects the
|
|
2
2
|
// app-owned Android modules, as its gradle plugin does, and their Kotlin
|
|
3
3
|
// compiles into one Kotlin/Wasm process with the expo-modules-core DSL port.
|
|
4
|
-
import { execFile } from 'node:child_process'
|
|
5
4
|
import fs from 'node:fs'
|
|
6
|
-
import { createRequire } from 'node:module'
|
|
7
5
|
import path from 'node:path'
|
|
8
|
-
import {
|
|
9
|
-
import { RNX_FORKED_SCRIPT_ENV } from '../cli/internal-child'
|
|
6
|
+
import { resolveExpoAndroidAutolinking } from './android-autolinking'
|
|
10
7
|
import { isLoopbackHost } from './backend-origin'
|
|
11
8
|
import { isRecord } from './is-record'
|
|
12
9
|
import { peachKotlinModuleRoot } from './kotlin-module'
|
|
13
10
|
import type { ExpoKotlinCompilationResult } from './expoKotlinContract'
|
|
14
11
|
import type { IncomingMessage, ServerResponse } from 'node:http'
|
|
15
12
|
|
|
16
|
-
const run = promisify(execFile)
|
|
17
|
-
// the compiled CLI's execPath is peach itself, which runs a forked `.js` as a
|
|
18
|
-
// script only with this set; node and bun ignore it.
|
|
19
|
-
const scriptEnv = { ...process.env, [RNX_FORKED_SCRIPT_ENV]: '1' }
|
|
20
13
|
export const EXPO_KOTLIN_ROUTE = '/__peach/expo-kotlin'
|
|
21
14
|
|
|
22
15
|
export interface ExpoKotlinModules {
|
|
@@ -28,35 +21,12 @@ export interface ExpoKotlinModules {
|
|
|
28
21
|
export async function collectExpoKotlinModules(
|
|
29
22
|
appRoot: string,
|
|
30
23
|
): Promise<ExpoKotlinModules | null> {
|
|
31
|
-
|
|
32
|
-
let autolinker: string
|
|
33
|
-
try {
|
|
34
|
-
autolinker = createRequire(path.join(appRoot, 'package.json')).resolve(
|
|
35
|
-
'expo-modules-autolinking/package.json',
|
|
36
|
-
)
|
|
37
|
-
} catch (error) {
|
|
38
|
-
if (isRecord(error) && error.code === 'MODULE_NOT_FOUND') return null
|
|
39
|
-
throw error
|
|
40
|
-
}
|
|
41
|
-
const { stdout } = await run(
|
|
42
|
-
process.execPath,
|
|
43
|
-
[
|
|
44
|
-
path.join(path.dirname(autolinker), 'bin/expo-modules-autolinking.js'),
|
|
45
|
-
'resolve',
|
|
46
|
-
'--platform',
|
|
47
|
-
'android',
|
|
48
|
-
'--project-root',
|
|
49
|
-
appRoot,
|
|
50
|
-
'--json',
|
|
51
|
-
],
|
|
52
|
-
{ timeout: 30_000, maxBuffer: 8 * 1024 * 1024, env: scriptEnv },
|
|
53
|
-
)
|
|
54
|
-
const resolved: unknown = JSON.parse(stdout)
|
|
55
|
-
if (!isRecord(resolved) || !Array.isArray(resolved.modules))
|
|
56
|
-
throw new Error('invalid Expo Android resolution')
|
|
24
|
+
const modules = await resolveExpoAndroidAutolinking(appRoot)
|
|
57
25
|
const result: ExpoKotlinModules = { modules: [], sources: [] }
|
|
58
|
-
for (const entry of
|
|
59
|
-
|
|
26
|
+
for (const entry of modules) {
|
|
27
|
+
// plugin-only dependencies have no local Kotlin module project.
|
|
28
|
+
if (entry.projects === undefined) continue
|
|
29
|
+
if (!Array.isArray(entry.projects))
|
|
60
30
|
throw new Error('invalid Expo Android module entry')
|
|
61
31
|
for (const project of entry.projects) {
|
|
62
32
|
if (
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// an Expo packager serves its manifest as JSON, or as the multipart form body
|
|
2
|
+
// Expo Updates defines (older CLIs always, whatever the request accepts) whose
|
|
3
|
+
// `manifest` part carries the same JSON.
|
|
4
|
+
export function parseExpoManifestBody(body: string) {
|
|
5
|
+
const opening = /^(--[^\r\n]+)\r?\n/.exec(body)
|
|
6
|
+
if (!opening) return JSON.parse(body)
|
|
7
|
+
for (const part of body.split(opening[1])) {
|
|
8
|
+
const headerEnd = part.search(/\r?\n\r?\n/)
|
|
9
|
+
if (headerEnd < 0 || !/\bname="manifest"/.test(part.slice(0, headerEnd))) continue
|
|
10
|
+
return JSON.parse(part.slice(headerEnd).trim())
|
|
11
|
+
}
|
|
12
|
+
throw new Error('Expo manifest response has no manifest part')
|
|
13
|
+
}
|
package/src/expo-swift-bundle.ts
CHANGED
|
@@ -12,20 +12,25 @@ export async function buildExpoSwiftBundle(
|
|
|
12
12
|
appRoot: string,
|
|
13
13
|
): Promise<ExpoSwiftBundle | null> {
|
|
14
14
|
const assets: ExpoSwiftBundle['assets'] = []
|
|
15
|
-
const metadata = await compileExpoSwiftModules(
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
})
|
|
21
|
-
if ('gzip' in artifact.icu) {
|
|
15
|
+
const metadata = await compileExpoSwiftModules(
|
|
16
|
+
appRoot,
|
|
17
|
+
'',
|
|
18
|
+
async (artifact, signal) => {
|
|
19
|
+
signal.throwIfAborted()
|
|
22
20
|
assets.push({
|
|
23
|
-
path: `__peach/swift
|
|
24
|
-
bytes: artifact.
|
|
25
|
-
contentType: 'application/
|
|
21
|
+
path: `__peach/swift/${artifact.hash}.wasm`,
|
|
22
|
+
bytes: artifact.bytes,
|
|
23
|
+
contentType: 'application/wasm',
|
|
26
24
|
})
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
if ('gzip' in artifact.icu) {
|
|
26
|
+
assets.push({
|
|
27
|
+
path: `__peach/swift-icu/${artifact.icu.sha256}`,
|
|
28
|
+
bytes: artifact.icu.gzip,
|
|
29
|
+
contentType: 'application/gzip',
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
)
|
|
29
34
|
if (!metadata) return null
|
|
30
35
|
|
|
31
36
|
return { metadata, assets }
|