peachsim 0.1.1635 → 0.1.1640
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 +5 -1
- package/cli/app-config.ts +14 -5
- package/cli/app-stubs.ts +62 -13
- package/cli/bridge-flow-runner.ts +2 -2
- package/cli/cloud-client.ts +60 -40
- package/cli/commands/doctor.ts +13 -5
- package/cli/commands/flow.ts +1 -1
- package/cli/commands/inspect/shared.ts +9 -4
- package/cli/commands/inspect.ts +65 -47
- package/cli/commands/preview.ts +1 -1
- package/cli/commands/record.ts +97 -47
- package/cli/commands/swift.ts +9 -3
- package/cli/commands/upload.ts +48 -42
- package/cli/drivers/playwright-sim-host.ts +65 -42
- package/cli/main.ts +1 -1
- package/cli/outbound-endpoints.ts +7 -5
- package/cli/pause-aware-timeout.ts +4 -0
- package/cli/ws-bridge.ts +19 -10
- package/detox/attempt-events.cjs +62 -5
- package/detox/attempt-events.d.cts +7 -0
- package/detox/expectations.ts +7 -3
- package/detox/index.ts +121 -47
- package/detox/motion-change.cjs +170 -21
- package/detox/motion-change.d.cts +32 -0
- package/detox/navigation-lifecycle.ts +35 -3
- package/dist-lib/agent-daemon-client.cjs +1 -1
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/attached-projects.cjs +1 -1
- package/dist-lib/auth/shared-session.cjs +1 -1
- package/dist-lib/backend-origin.cjs +8 -7
- 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 +4038 -1983
- 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 +1 -1
- package/dist-lib/cloud.mjs +1 -1
- package/dist-lib/config.cjs +1 -1
- package/dist-lib/config.mjs +1 -1
- package/dist-lib/detox/index.cjs +509 -86
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/expo-swift-bundle.cjs +3551 -1574
- package/dist-lib/expo-swift-metadata.cjs +9 -7
- package/dist-lib/expo-swift-metadata.mjs +9 -7
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +43633 -2635
- 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 +2748 -128
- 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 +1 -1
- package/dist-lib/menu.mjs +1 -1
- 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 +1 -1
- package/dist-lib/metro-production-bundle.mjs +1 -1
- package/dist-lib/metro.cjs +2547 -83
- package/dist-lib/native-app-identity-project.cjs +237 -50
- package/dist-lib/native-app-identity-project.mjs +237 -50
- package/dist-lib/native-app-identity.cjs +29 -30
- package/dist-lib/native-app-identity.mjs +29 -30
- package/dist-lib/native-firebase-metadata.cjs +1 -1
- package/dist-lib/native-firebase-metadata.mjs +1 -1
- 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 +1 -1
- package/dist-lib/react-native-host-modules.cjs +2 -2
- package/dist-lib/react-native-host-modules.mjs +2 -2
- package/dist-lib/scripts/dev-server-scanner.cjs +8 -6
- package/dist-lib/sdk.cjs +1 -1
- package/dist-lib/sdk.mjs +1 -1
- package/dist-lib/skills.cjs +65403 -22739
- package/dist-lib/swift-compile.cjs +93 -32
- package/dist-lib/swift-compile.mjs +93 -32
- package/dist-lib/swift-package.cjs +1 -1
- package/dist-lib/swift-package.mjs +1 -1
- package/dist-lib/swift-submit.cjs +7 -5
- package/dist-lib/swift-submit.mjs +7 -5
- package/dist-lib/swift-toolchain.cjs +316 -41
- package/dist-lib/swift.cjs +1 -1
- package/dist-lib/vite-swift.cjs +7335 -5432
- package/dist-lib/vite.cjs +2857 -12
- package/dist-lib/worklet-executable.cjs +14 -2
- package/dist-lib/worklet-executable.mjs +14 -2
- package/package.json +1 -1
- package/src/backend-origin.ts +13 -6
- package/src/expo-swift-host.ts +5 -2
- package/src/expoKotlinContract.ts +4 -7
- package/src/expoSwiftContract.ts +4 -6
- package/src/host/staged-host-files.ts +9 -0
- package/src/native-app-identity-project.ts +1 -4
- package/src/native-app-identity.ts +1 -4
- package/src/nativeAppIconContract.ts +1 -4
- package/src/output-lock-stdio.ts +36 -0
- package/src/react-native-bridge-modules.ts +21 -6
- package/src/react-native-host-modules.ts +4 -1
- package/src/settings-bundle.ts +1 -4
- package/src/swift-compile.ts +91 -33
- package/src/swift-distribution.lock.json +22 -12
- package/src/swift-distribution.ts +103 -4
- package/src/swift-host.ts +89 -26
- package/src/swift-icu.ts +16 -10
- package/src/swift-objc-compile.ts +355 -0
- package/src/swift-objc-sdk.ts +689 -18
- package/src/swift-objc-standin.ts +126 -14
- package/src/swift-package-manifest.ts +39 -1
- package/src/swift-packages.ts +35 -21
- package/src/swift-standin-emit.ts +40 -7
- package/src/swift-standins.ts +404 -138
- package/src/swift-submit.ts +3 -5
- package/src/swift-toolchain.ts +378 -46
- package/src/worker-system-chunks.ts +11 -12
- package/src/worklet-executable.ts +19 -1
- package/src/xcode-project.ts +270 -30
package/README.md
CHANGED
|
@@ -599,7 +599,11 @@ foreground or background state. Both retain its browser session and app state.
|
|
|
599
599
|
`sourceApp` does not add fields to React Native's `{ url }` event. A
|
|
600
600
|
stopped guest rejects URL delivery. Explicit `newInstance: true`, deletion,
|
|
601
601
|
and video recording create a new browser context; cold launches still use the
|
|
602
|
-
supplied shell URL.
|
|
602
|
+
supplied shell URL. Process termination and `newInstance: true` preserve
|
|
603
|
+
cookies, local storage and IndexedDB for the next launch. `delete: true` and
|
|
604
|
+
driver cleanup clear that retained storage. Relaunch creates a fresh runtime,
|
|
605
|
+
so input focus and other in-memory component state do not survive termination.
|
|
606
|
+
When synchronization is enabled, URL delivery waits for
|
|
603
607
|
existing work before dispatch and for navigation and painting afterward.
|
|
604
608
|
|
|
605
609
|
Element taps finish viewport measurement before resolving the selected node's
|
package/cli/app-config.ts
CHANGED
|
@@ -89,12 +89,21 @@ async function loadConfigFile(
|
|
|
89
89
|
}
|
|
90
90
|
const wire = WIRE_SLOTS.filter((slot) => loaded[slot] !== undefined)
|
|
91
91
|
if (wire.length > 0) {
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
92
|
+
const slots: readonly string[] = WIRE_SLOTS
|
|
93
|
+
const kept = Object.entries(loaded)
|
|
94
|
+
.filter(([key, value]) => !slots.includes(key) && value !== undefined)
|
|
95
|
+
.map(([key]) => key)
|
|
96
|
+
const migration = wireSlotMigration(
|
|
97
|
+
project.appDir,
|
|
98
|
+
project.configPath,
|
|
99
|
+
{
|
|
100
|
+
modules: { ...loaded.turboModules, ...loaded.nativeModules },
|
|
101
|
+
views: { ...loaded.nativeViews },
|
|
102
|
+
},
|
|
103
|
+
kept,
|
|
104
|
+
)
|
|
96
105
|
throw new Error(
|
|
97
|
-
`${wire.join(', ')} moved under packages, keyed by the package whose native side they answer.
|
|
106
|
+
`${wire.join(', ')} moved under packages, keyed by the package whose native side they answer. ${kept.length ? 'remove them and add' : 'replace the config with'}:\n${migration}`,
|
|
98
107
|
)
|
|
99
108
|
}
|
|
100
109
|
return resolveConfigReplacementFiles(loaded, project.configPath)
|
package/cli/app-stubs.ts
CHANGED
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
type PeachConfig,
|
|
26
26
|
} from '../src/config'
|
|
27
27
|
import { isRecord } from '../src/is-record'
|
|
28
|
+
import { rnxPublicBrand } from '../src/public-brand'
|
|
28
29
|
import { visitParsedProgram } from '../src/visit-parsed-program'
|
|
29
30
|
import {
|
|
30
31
|
readProjectManifest,
|
|
@@ -380,26 +381,61 @@ export function packageNativeRequests(dir: string): NativeRequest[] {
|
|
|
380
381
|
)
|
|
381
382
|
}
|
|
382
383
|
|
|
383
|
-
// the
|
|
384
|
-
// name
|
|
385
|
-
//
|
|
386
|
-
|
|
387
|
-
export function wireSlotMigration(
|
|
384
|
+
// the native names each installed dependency's own JavaScript asks for, keyed
|
|
385
|
+
// `kind:name`. links to source outside node_modules are the app's own packages
|
|
386
|
+
// and are scanned as app code instead.
|
|
387
|
+
function dependencyNativeOwners(
|
|
388
388
|
appDir: string,
|
|
389
|
-
|
|
390
|
-
): string {
|
|
389
|
+
manifest: ProjectManifest | null,
|
|
390
|
+
): Map<string, string> {
|
|
391
391
|
const owners = new Map<string, string>()
|
|
392
|
-
const manifest = readProjectManifest(appDir)
|
|
393
392
|
for (const name of Object.keys({
|
|
394
393
|
...manifest?.devDependencies,
|
|
395
394
|
...manifest?.dependencies,
|
|
396
395
|
})) {
|
|
397
396
|
const dir = installedPackageDir(appDir, name)
|
|
398
|
-
if (!dir) continue
|
|
397
|
+
if (!dir || !dir.split('/').includes('node_modules')) continue
|
|
399
398
|
for (const request of packageNativeRequests(dir))
|
|
400
399
|
if (!owners.has(`${request.kind}:${request.name}`))
|
|
401
400
|
owners.set(`${request.kind}:${request.name}`, name)
|
|
402
401
|
}
|
|
402
|
+
return owners
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// a complete config module in the form the app's config file already uses, so
|
|
406
|
+
// the printed text pastes over it
|
|
407
|
+
function configModule(configPath: string | null, packages: string[]): string {
|
|
408
|
+
const body = [' packages: {', ...packages, ' },']
|
|
409
|
+
if (
|
|
410
|
+
configPath &&
|
|
411
|
+
(configPath.endsWith('.cjs') ||
|
|
412
|
+
(configPath.endsWith('.js') &&
|
|
413
|
+
/\bmodule\.exports\b/.test(readSmallFile(configPath) ?? '')))
|
|
414
|
+
)
|
|
415
|
+
return ['module.exports = {', ...body, '}'].join('\n')
|
|
416
|
+
return [
|
|
417
|
+
`import { defineConfig } from '${rnxPublicBrand.packageName}/config'`,
|
|
418
|
+
'',
|
|
419
|
+
'export default defineConfig({',
|
|
420
|
+
...body,
|
|
421
|
+
'})',
|
|
422
|
+
].join('\n')
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// what replaces a config's old top-level wire slots: each name keyed by the
|
|
426
|
+
// package whose JavaScript asks for it. a config holding only those slots gets
|
|
427
|
+
// a complete module; one with other settings (`kept`) gets just the packages
|
|
428
|
+
// entries, since a whole module would drop them. `packages` takes only 'noop'
|
|
429
|
+
// or a file path, so any other old value is named for a hand move instead of
|
|
430
|
+
// guessed.
|
|
431
|
+
export function wireSlotMigration(
|
|
432
|
+
appDir: string,
|
|
433
|
+
configPath: string | null,
|
|
434
|
+
wire: { modules: Record<string, unknown>; views: Record<string, unknown> },
|
|
435
|
+
kept: readonly string[] = [],
|
|
436
|
+
): string {
|
|
437
|
+
const manifest = readProjectManifest(appDir)
|
|
438
|
+
const owners = dependencyNativeOwners(appDir, manifest)
|
|
403
439
|
if (manifest)
|
|
404
440
|
for (const request of scanLocalNative(
|
|
405
441
|
appDir,
|
|
@@ -408,6 +444,7 @@ export function wireSlotMigration(
|
|
|
408
444
|
undefined,
|
|
409
445
|
new Set(),
|
|
410
446
|
true,
|
|
447
|
+
owners,
|
|
411
448
|
))
|
|
412
449
|
if (!owners.has(`${request.kind}:${request.name}`))
|
|
413
450
|
owners.set(`${request.kind}:${request.name}`, request.package)
|
|
@@ -453,9 +490,17 @@ export function wireSlotMigration(
|
|
|
453
490
|
].filter(Boolean)
|
|
454
491
|
return ` [${JSON.stringify(owner)}]: { ${parts.join(', ')} },`
|
|
455
492
|
})
|
|
493
|
+
if (lines.length && kept.length)
|
|
494
|
+
return [
|
|
495
|
+
...manual.map((line) => `// ${line}`),
|
|
496
|
+
`// keep ${kept.join(', ')}; merge these into packages`,
|
|
497
|
+
' packages: {',
|
|
498
|
+
...lines,
|
|
499
|
+
' },',
|
|
500
|
+
].join('\n')
|
|
456
501
|
return [
|
|
457
|
-
...(
|
|
458
|
-
...
|
|
502
|
+
...manual.map((line) => `// ${line}`),
|
|
503
|
+
...(lines.length ? [configModule(configPath, lines)] : []),
|
|
459
504
|
].join('\n')
|
|
460
505
|
}
|
|
461
506
|
|
|
@@ -519,6 +564,7 @@ export function scanLocalNative(
|
|
|
519
564
|
platform: ScanResult['scoreSummary']['platform'] | undefined,
|
|
520
565
|
knownPackages: ReadonlySet<string> = new Set(),
|
|
521
566
|
includeLegacy = false,
|
|
567
|
+
providers?: ReadonlyMap<string, string>,
|
|
522
568
|
): LocalNative[] {
|
|
523
569
|
const expoModuleDirs: string[] = []
|
|
524
570
|
const excludedModuleDirs: string[] = []
|
|
@@ -571,11 +617,14 @@ export function scanLocalNative(
|
|
|
571
617
|
return result
|
|
572
618
|
}
|
|
573
619
|
const found = new Map<string, LocalNative>()
|
|
620
|
+
// a name an installed dependency asks for belongs to that dependency, whoever
|
|
621
|
+
// in the app spells it out
|
|
574
622
|
const add = (kind: LocalNative['kind'], name: string, file: string, line: number) => {
|
|
575
|
-
const packageName = owner(dirname(file))
|
|
576
|
-
if (knownPackages.has(packageName)) return
|
|
577
623
|
const key = `${kind}:${name}`
|
|
578
624
|
if (found.has(key)) return
|
|
625
|
+
providers ??= dependencyNativeOwners(appDir, pkg)
|
|
626
|
+
const packageName = providers.get(key) ?? owner(dirname(file))
|
|
627
|
+
if (knownPackages.has(packageName)) return
|
|
579
628
|
const moduleDir = expoModuleDirs.find((dir) => file.startsWith(`${dir}/`)) ?? null
|
|
580
629
|
found.set(key, {
|
|
581
630
|
kind,
|
|
@@ -3103,11 +3103,11 @@ export class SootSimBridgeFlowRunner {
|
|
|
3103
3103
|
}
|
|
3104
3104
|
|
|
3105
3105
|
async startProfile() {
|
|
3106
|
-
const result = await this.evaluate<any>(`(() => {
|
|
3106
|
+
const result = await this.evaluate<any>(`(async () => {
|
|
3107
3107
|
if (!window.__sootsimShellPerf) {
|
|
3108
3108
|
return { error: "shell frame profile unavailable (__sootsimShellPerf missing on the page)" }
|
|
3109
3109
|
}
|
|
3110
|
-
window.__sootsimShellPerf.start()
|
|
3110
|
+
await window.__sootsimShellPerf.start()
|
|
3111
3111
|
return { started: true }
|
|
3112
3112
|
})()`)
|
|
3113
3113
|
if (result?.error) {
|
package/cli/cloud-client.ts
CHANGED
|
@@ -27,6 +27,10 @@ import {
|
|
|
27
27
|
import { rewriteGuestImportMeta } from '../../peach-engine/src/guest-import-meta.ts'
|
|
28
28
|
import { loadMetroFingerprintRegistry } from '../../peach-engine/src/metro-fingerprint-registry-client.ts'
|
|
29
29
|
import { inferMetroModuleIdentity } from '../../peach-engine/src/metro-fingerprint.ts'
|
|
30
|
+
import {
|
|
31
|
+
isNativeJavaScriptBundle,
|
|
32
|
+
isRolldownBundle,
|
|
33
|
+
} from '../../peach-engine/src/preview/bundle-layout'
|
|
30
34
|
import { isLoopbackHost } from '../src/backend-origin.ts'
|
|
31
35
|
import {
|
|
32
36
|
isRnxCloudCommandType,
|
|
@@ -172,7 +176,7 @@ function resolveCloudOrigin(value?: string): string {
|
|
|
172
176
|
function readImmutableBundleSource(bundlePath: string, command: string): string {
|
|
173
177
|
const filepath = resolve(bundlePath)
|
|
174
178
|
if (extname(filepath) !== '.js') {
|
|
175
|
-
throw new Error(`${command} requires one local
|
|
179
|
+
throw new Error(`${command} requires one local .js bundle`)
|
|
176
180
|
}
|
|
177
181
|
const descriptor = openSync(filepath, 'r')
|
|
178
182
|
try {
|
|
@@ -200,9 +204,6 @@ function readImmutableBundleSource(bundlePath: string, command: string): string
|
|
|
200
204
|
} catch {
|
|
201
205
|
throw new Error(`${command} bundle must be valid UTF-8 JavaScript`)
|
|
202
206
|
}
|
|
203
|
-
if (source.includes('\0') || !source.includes('__d(') || !source.includes('__r(')) {
|
|
204
|
-
throw new Error(`${command} requires a Metro JavaScript bundle`)
|
|
205
|
-
}
|
|
206
207
|
return source
|
|
207
208
|
} finally {
|
|
208
209
|
closeSync(descriptor)
|
|
@@ -231,9 +232,6 @@ async function fetchBundleSource(bundleUrl: string, command: string): Promise<st
|
|
|
231
232
|
if (text.length === 0) {
|
|
232
233
|
throw new Error(`${command} bundle is empty`)
|
|
233
234
|
}
|
|
234
|
-
if (text.includes('\0') || !text.includes('__d(') || !text.includes('__r(')) {
|
|
235
|
-
throw new Error(`${command} requires a Metro JavaScript bundle`)
|
|
236
|
-
}
|
|
237
235
|
return text
|
|
238
236
|
}
|
|
239
237
|
|
|
@@ -260,6 +258,9 @@ async function loadBundleSource(bundleInput: string, command: string): Promise<s
|
|
|
260
258
|
const source = isRemoteBundleInput(bundleInput)
|
|
261
259
|
? await fetchBundleSource(bundleInput, command)
|
|
262
260
|
: readImmutableBundleSource(bundleInput, command)
|
|
261
|
+
if (!isNativeJavaScriptBundle(source)) {
|
|
262
|
+
throw new Error(`${command} requires a supported React Native JavaScript bundle`)
|
|
263
|
+
}
|
|
263
264
|
// validate the actual runtime script form without executing guest code or
|
|
264
265
|
// allocating a host.
|
|
265
266
|
try {
|
|
@@ -728,25 +729,44 @@ export async function produceCloudArtifact(
|
|
|
728
729
|
maxBytes: number = MAX_RNX_CLOUD_BUNDLE_BYTES,
|
|
729
730
|
assetsDirectory?: string,
|
|
730
731
|
): Promise<CloudArtifactProduction> {
|
|
731
|
-
|
|
732
|
+
const prepared = await embedBundleAssets(
|
|
732
733
|
bundlePath,
|
|
733
|
-
maxBytes,
|
|
734
734
|
REMOTE_COMMAND,
|
|
735
735
|
assetsDirectory ? 'require-assets' : 'allow-placeholders',
|
|
736
736
|
assetsDirectory,
|
|
737
737
|
)
|
|
738
|
+
return produceArtifact(prepared, maxBytes, REMOTE_COMMAND)
|
|
738
739
|
}
|
|
739
740
|
|
|
740
741
|
/** prepares a standalone preview whose bytes must represent every Metro asset. */
|
|
741
|
-
export async function producePreviewArtifact(
|
|
742
|
-
bundlePath
|
|
743
|
-
)
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
)
|
|
742
|
+
export async function producePreviewArtifact(bundlePath: string) {
|
|
743
|
+
const prepared = await embedBundleAssets(bundlePath, PREVIEW_COMMAND, 'require-assets')
|
|
744
|
+
if (!isRolldownBundle(prepared.source)) {
|
|
745
|
+
return {
|
|
746
|
+
...(await produceArtifact(prepared, Number.POSITIVE_INFINITY, PREVIEW_COMMAND)),
|
|
747
|
+
minified: true,
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
const bytes = Buffer.from(prepared.code, 'utf8')
|
|
751
|
+
const sha256 = createHash('sha256').update(bytes).digest('hex')
|
|
752
|
+
return {
|
|
753
|
+
bytes,
|
|
754
|
+
sha256,
|
|
755
|
+
minified: false,
|
|
756
|
+
receipt: {
|
|
757
|
+
source: {
|
|
758
|
+
sha256: createHash('sha256').update(prepared.source).digest('hex'),
|
|
759
|
+
bytes: Buffer.byteLength(prepared.source, 'utf8'),
|
|
760
|
+
},
|
|
761
|
+
artifact: { sha256, bytes: bytes.byteLength },
|
|
762
|
+
},
|
|
763
|
+
warnings:
|
|
764
|
+
prepared.search.unconfirmed && prepared.dest
|
|
765
|
+
? [
|
|
766
|
+
`peach preview could not authenticate asset hashes in ${prepared.dest.directory}; confirm these are the assets this bundle was built against.`,
|
|
767
|
+
]
|
|
768
|
+
: [],
|
|
769
|
+
}
|
|
750
770
|
}
|
|
751
771
|
|
|
752
772
|
export interface CloudBoxArtifactProduction {
|
|
@@ -890,18 +910,10 @@ async function embedBundleAssets(
|
|
|
890
910
|
}
|
|
891
911
|
|
|
892
912
|
async function produceArtifact(
|
|
893
|
-
|
|
913
|
+
prepared: Awaited<ReturnType<typeof embedBundleAssets>>,
|
|
894
914
|
maxBytes: number,
|
|
895
915
|
command: string,
|
|
896
|
-
assetPolicy: 'allow-placeholders' | 'require-assets',
|
|
897
|
-
assetsDirectory?: string,
|
|
898
916
|
): Promise<CloudArtifactProduction> {
|
|
899
|
-
const prepared = await embedBundleAssets(
|
|
900
|
-
bundlePath,
|
|
901
|
-
command,
|
|
902
|
-
assetPolicy,
|
|
903
|
-
assetsDirectory,
|
|
904
|
-
)
|
|
905
917
|
const artifact = await buildRnxCloudArtifact(prepared.code, {
|
|
906
918
|
inferModuleIdentity: (source) => inferCloudModuleIdentity(source, command),
|
|
907
919
|
})
|
|
@@ -1453,12 +1465,17 @@ function cloudCommand(command: {
|
|
|
1453
1465
|
class CloudBridge implements WsBridge {
|
|
1454
1466
|
readonly plane = 'cloud' as const
|
|
1455
1467
|
|
|
1456
|
-
constructor(
|
|
1468
|
+
constructor(
|
|
1469
|
+
private readonly session: CloudSimSession,
|
|
1470
|
+
private readonly explicitTimeoutMs?: number,
|
|
1471
|
+
) {}
|
|
1457
1472
|
|
|
1458
1473
|
async send(
|
|
1459
1474
|
command: { type: string; [key: string]: unknown },
|
|
1460
|
-
options?: { timeoutMs?: number },
|
|
1475
|
+
options?: { timeoutMs?: number; defaultTimeoutMs?: number },
|
|
1461
1476
|
): Promise<unknown> {
|
|
1477
|
+
const timeoutMs =
|
|
1478
|
+
options?.timeoutMs ?? this.explicitTimeoutMs ?? options?.defaultTimeoutMs
|
|
1462
1479
|
const body: CloudCommandEnvelope = {
|
|
1463
1480
|
id: randomUUID(),
|
|
1464
1481
|
claimId: this.session.claimId,
|
|
@@ -1475,9 +1492,7 @@ class CloudBridge implements WsBridge {
|
|
|
1475
1492
|
'content-type': 'application/json',
|
|
1476
1493
|
},
|
|
1477
1494
|
body: JSON.stringify(body),
|
|
1478
|
-
...(
|
|
1479
|
-
? { signal: AbortSignal.timeout(options.timeoutMs) }
|
|
1480
|
-
: {}),
|
|
1495
|
+
...(timeoutMs ? { signal: AbortSignal.timeout(timeoutMs) } : {}),
|
|
1481
1496
|
},
|
|
1482
1497
|
)
|
|
1483
1498
|
} catch (error) {
|
|
@@ -1538,12 +1553,17 @@ class CloudBridge implements WsBridge {
|
|
|
1538
1553
|
class CloudBoxBridge implements WsBridge {
|
|
1539
1554
|
readonly plane = 'cloud' as const
|
|
1540
1555
|
|
|
1541
|
-
constructor(
|
|
1556
|
+
constructor(
|
|
1557
|
+
private readonly session: CloudBoxSession,
|
|
1558
|
+
private readonly explicitTimeoutMs?: number,
|
|
1559
|
+
) {}
|
|
1542
1560
|
|
|
1543
1561
|
async send(
|
|
1544
1562
|
command: { type: string; [key: string]: unknown },
|
|
1545
|
-
options?: { timeoutMs?: number },
|
|
1563
|
+
options?: { timeoutMs?: number; defaultTimeoutMs?: number },
|
|
1546
1564
|
): Promise<unknown> {
|
|
1565
|
+
const timeoutMs =
|
|
1566
|
+
options?.timeoutMs ?? this.explicitTimeoutMs ?? options?.defaultTimeoutMs
|
|
1547
1567
|
const id = randomUUID()
|
|
1548
1568
|
// the wire id is this request's, so the command carries none. `simId` names
|
|
1549
1569
|
// a local sim among several and a box has exactly one.
|
|
@@ -1562,9 +1582,7 @@ class CloudBoxBridge implements WsBridge {
|
|
|
1562
1582
|
'content-type': 'application/json',
|
|
1563
1583
|
},
|
|
1564
1584
|
body: JSON.stringify({ id, command: payload }),
|
|
1565
|
-
...(
|
|
1566
|
-
? { signal: AbortSignal.timeout(options.timeoutMs) }
|
|
1567
|
-
: {}),
|
|
1585
|
+
...(timeoutMs ? { signal: AbortSignal.timeout(timeoutMs) } : {}),
|
|
1568
1586
|
},
|
|
1569
1587
|
)
|
|
1570
1588
|
} catch (error) {
|
|
@@ -1619,13 +1637,15 @@ class CloudBoxBridge implements WsBridge {
|
|
|
1619
1637
|
}
|
|
1620
1638
|
|
|
1621
1639
|
export function createCloudBridgeForParsed(
|
|
1622
|
-
parsed: Pick<ParsedBridgeCliArgs, 'simId' | 'simIdSource'
|
|
1640
|
+
parsed: Pick<ParsedBridgeCliArgs, 'simId' | 'simIdSource'> &
|
|
1641
|
+
Partial<Pick<ParsedBridgeCliArgs, 'explicitTimeout' | 'commandTimeoutMs'>>,
|
|
1623
1642
|
): WsBridge | null {
|
|
1624
1643
|
const session = resolveCloudSessionForParsed(parsed)
|
|
1625
1644
|
if (!session) return null
|
|
1645
|
+
const timeoutMs = parsed.explicitTimeout ? parsed.commandTimeoutMs : undefined
|
|
1626
1646
|
return session.service === 'box'
|
|
1627
|
-
? new CloudBoxBridge(session)
|
|
1628
|
-
: new CloudBridge(session)
|
|
1647
|
+
? new CloudBoxBridge(session, timeoutMs)
|
|
1648
|
+
: new CloudBridge(session, timeoutMs)
|
|
1629
1649
|
}
|
|
1630
1650
|
|
|
1631
1651
|
// deletes the box, which ends the simulator it holds. a cloud session box is
|
package/cli/commands/doctor.ts
CHANGED
|
@@ -476,7 +476,10 @@ function packageRow(
|
|
|
476
476
|
return {
|
|
477
477
|
rank: RANK.unknown,
|
|
478
478
|
glyph: accent('?'),
|
|
479
|
-
|
|
479
|
+
// fonts and assets, or names built at runtime, leave nothing to stub by name
|
|
480
|
+
detail: unknownNative.requests.length
|
|
481
|
+
? 'native code Peach does not know; stub it below'
|
|
482
|
+
: 'native code; names no native module',
|
|
480
483
|
tone: muted,
|
|
481
484
|
}
|
|
482
485
|
// a stub answers ahead of Peach's own support for the package
|
|
@@ -560,19 +563,24 @@ function printNativeGuidance(
|
|
|
560
563
|
)
|
|
561
564
|
}
|
|
562
565
|
// the open names grouped by the package they belong to, the key `packages` takes
|
|
563
|
-
const open = new Map<string, NativeRequest
|
|
566
|
+
const open = new Map<string, Map<string, NativeRequest>>()
|
|
564
567
|
for (const [owner, requests] of [
|
|
565
568
|
...unknownNative.map((item) => [item.name, item.requests] as const),
|
|
566
569
|
...local.map((item) => [item.package, [item]] as const),
|
|
567
570
|
]) {
|
|
568
|
-
const
|
|
569
|
-
|
|
571
|
+
for (const request of requests) {
|
|
572
|
+
if (request.answered) continue
|
|
573
|
+
const names = open.get(owner) ?? new Map<string, NativeRequest>()
|
|
574
|
+
names.set(`${request.kind}:${request.name}`, request)
|
|
575
|
+
open.set(owner, names)
|
|
576
|
+
}
|
|
570
577
|
}
|
|
571
578
|
if (open.size) {
|
|
572
579
|
console.log(`\n stub them in peach.config.ts, by package:\n`)
|
|
573
580
|
console.log(muted(` export default defineConfig({`))
|
|
574
581
|
console.log(muted(` packages: {`))
|
|
575
|
-
for (const [owner,
|
|
582
|
+
for (const [owner, requestsByName] of open) {
|
|
583
|
+
const requests = [...requestsByName.values()]
|
|
576
584
|
console.log(muted(` '${owner}': {`))
|
|
577
585
|
for (const kind of ['module', 'view'] as const) {
|
|
578
586
|
const names = requests.filter((request) => request.kind === kind)
|
package/cli/commands/flow.ts
CHANGED
|
@@ -973,7 +973,7 @@ flow extension:
|
|
|
973
973
|
// canvas during scrub for smooth motion while the real app
|
|
974
974
|
// catches up.
|
|
975
975
|
if (videoPath) uploadArgs.push('--video', videoPath)
|
|
976
|
-
if (scenePath) uploadArgs.push('--scene', scenePath)
|
|
976
|
+
if (scenePath) uploadArgs.push('--scene', scenePath, '--live-bundle')
|
|
977
977
|
if (previewScreenshotPath) {
|
|
978
978
|
uploadArgs.push('--screenshot', previewScreenshotPath)
|
|
979
979
|
}
|
|
@@ -92,10 +92,15 @@ export async function maybeWaitForStartedScreenTransitions(
|
|
|
92
92
|
) {
|
|
93
93
|
if (bridge.plane === 'cloud') return
|
|
94
94
|
try {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
95
|
+
// the sim bounds this wait itself, so the round trip gets a short budget of
|
|
96
|
+
// its own; under the command budget a wedged sim cost every read twice
|
|
97
|
+
const result: SootSimScreenTransitionWaitResult | undefined = await bridge.send(
|
|
98
|
+
{
|
|
99
|
+
type: 'call',
|
|
100
|
+
path: '__sootsimTest.waitForScreenTransitions',
|
|
101
|
+
args: [SCREEN_TRANSITION_PREFLIGHT],
|
|
102
|
+
},
|
|
103
|
+
{ timeoutMs: SCREEN_TRANSITION_PREFLIGHT.timeoutMs + 1200 },
|
|
99
104
|
)
|
|
100
105
|
if (!opts.verbose || !result?.started) return
|
|
101
106
|
if (result.timedOut) {
|