peachsim 0.1.1406 → 0.1.1465
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 +8 -4
- package/README.md +22 -2
- package/cli/app-fonts.ts +50 -44
- package/cli/bin.ts +5 -4
- package/cli/bridge-diagnostics.ts +5 -0
- package/cli/bridge-flow-runner.ts +21 -13
- package/cli/cloud-client.ts +4 -3
- package/cli/commands/control.ts +155 -123
- package/cli/commands/daemon-mac-app.ts +3 -3
- package/cli/commands/daemon.ts +2 -2
- package/cli/commands/do-chain.ts +9 -2
- package/cli/commands/electron.ts +4 -1
- package/cli/commands/inspect/core.ts +1 -1
- package/cli/commands/inspect/settle.ts +19 -7
- package/cli/commands/inspect/settling.ts +92 -56
- package/cli/commands/inspect/wait-idle.ts +21 -9
- package/cli/commands/inspect/wait-ready.ts +22 -8
- package/cli/commands/inspect.ts +101 -134
- package/cli/commands/setup.ts +26 -16
- package/cli/commands/upgrade.ts +53 -9
- package/cli/desktop-companion.ts +1 -1
- package/cli/drivers/playwright-sim-host.ts +32 -13
- package/cli/drivers/playwright.ts +3 -3
- package/cli/main.ts +14 -5
- package/cli/outbound-endpoints.ts +1 -9
- package/detox/index.ts +6 -1
- 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 +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 +3 -3
- package/dist-lib/bridge-contract.mjs +3 -3
- package/dist-lib/bundle-channel.cjs +322 -274
- 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 +512 -122
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/expo-swift-bundle.cjs +69 -33
- package/dist-lib/expo-swift-metadata.cjs +1 -1
- package/dist-lib/expo-swift-metadata.mjs +1 -1
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +3650 -3528
- 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 +327 -123
- 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 +10 -5
- 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 +49 -47
- package/dist-lib/native-app-identity-project.cjs +1 -1
- package/dist-lib/native-app-identity-project.mjs +1 -1
- package/dist-lib/native-app-identity.cjs +1 -1
- package/dist-lib/native-app-identity.mjs +1 -1
- 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 +1 -1
- package/dist-lib/react-native-host-modules.mjs +1 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +23 -17
- package/dist-lib/sdk.cjs +99 -61
- package/dist-lib/sdk.mjs +97 -61
- package/dist-lib/skills.cjs +3347 -3051
- package/dist-lib/swift-compile.cjs +26 -12
- package/dist-lib/swift-compile.mjs +26 -12
- package/dist-lib/swift-package.cjs +1 -1
- package/dist-lib/swift-package.mjs +1 -1
- package/dist-lib/swift-submit.cjs +2 -2
- package/dist-lib/swift-submit.mjs +2 -2
- package/dist-lib/swift-toolchain.cjs +1 -1
- package/dist-lib/swift.cjs +1 -1
- package/dist-lib/vite-swift.cjs +66 -31
- package/dist-lib/vite.cjs +1 -1
- package/dist-lib/worklet-executable.cjs +5 -1
- package/dist-lib/worklet-executable.mjs +5 -1
- package/kotlin/AppSources/android/view/Window.kt +2 -0
- package/kotlin/AppSources/dev/peach/app/PeachApp.kt +1 -1
- package/kotlin/ComposeSources/PeachComposeHost.kt +35 -9
- package/kotlin/ExpoComposeSources/dev/peach/expo/PeachExpoCompose.kt +2 -2
- package/kotlin/ExpoSources/dev/peach/expo/PeachExpoRuntime.kt +9 -6
- package/kotlin/ExpoSources/dev/peach/expo/PeachExpoTypes.kt +4 -4
- package/kotlin/ExpoSources/expo/modules/kotlin/modules/Module.kt +2 -2
- package/kotlin/ExpoSources/expo/modules/kotlin/viewevent/ViewEvent.kt +22 -8
- package/kotlin/ExpoSources/expo/modules/kotlin/views/ExpoView.kt +0 -3
- package/kotlin/ExpoSources/expo/modules/kotlin/views/ViewDefinitionBuilder.kt +10 -9
- package/kotlin/ExpoSources/expo/modules/kotlin/views/ViewProps.kt +6 -5
- package/kotlin/fixtures/import-drawing/Drawing.d.kt.ts +7 -0
- package/kotlin/fixtures/import-drawing/Drawing.kt +101 -0
- package/kotlin/fixtures/import-drawing/ImportDrawing.native.tsx +18 -0
- package/kotlin/fixtures/import-drawing/README.md +34 -0
- package/kotlin/fixtures/import-drawing/app/_layout.tsx +4 -0
- package/kotlin/fixtures/import-drawing/app/index+spa.tsx +1 -0
- package/kotlin/fixtures/import-drawing/package.json +23 -0
- package/kotlin/fixtures/import-drawing/tsconfig.json +20 -0
- package/kotlin/fixtures/import-drawing/vite.config.ts +20 -0
- package/kotlin/fixtures/import-scroll/ImportNested.native.tsx +10 -0
- package/kotlin/fixtures/import-scroll/Nested.d.kt.ts +4 -0
- package/kotlin/fixtures/import-scroll/README.md +15 -0
- package/kotlin/fixtures/import-scroll/app/nested+spa.tsx +1 -0
- package/kotlin/fixtures/import-values/ImportValues.native.tsx +26 -0
- package/kotlin/fixtures/import-values/README.md +6 -1
- package/kotlin/fixtures/import-values/Values.d.kt.ts +4 -0
- package/kotlin/fixtures/import-values/Values.kt +18 -0
- package/kotlin/tools/expo-types.main.kts +17 -5
- package/package.json +2 -1
- package/scripts/dev-server-scanner.ts +33 -23
- package/src/assetCatalogContract.ts +7 -0
- package/src/bridge-contract.ts +21 -3
- package/src/connect.ts +18 -5
- package/src/desktop-host-delivery.ts +33 -3
- package/src/expo-swift-host.ts +6 -3
- package/src/host/bridge-host.ts +2 -0
- package/src/host/fetch-proxy-transport.ts +8 -0
- package/src/kotlin-host.ts +11 -5
- package/src/native-firebase-config.ts +95 -54
- package/src/process-output.ts +16 -0
- package/src/sdk.ts +6 -1
- package/src/sim-client.ts +6 -1
- package/src/swift-compile.ts +36 -15
- package/src/swift-distribution.lock.json +8 -8
- package/src/swift-standin-emit.ts +10 -1
- package/src/swift-standins.ts +21 -7
- package/src/swift-submit.ts +6 -1
- package/src/worklet-executable.ts +7 -2
- /package/kotlin/fixtures/{kotlin-app-variants → import-scroll}/Nested.kt +0 -0
|
@@ -44,6 +44,23 @@ export class RuntimeReadinessUnsupportedError extends Error {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
// a thrown idle read carries the host elapsed time and the original failure.
|
|
48
|
+
// it stays an error so callers cannot treat it as motion observed for maxMs.
|
|
49
|
+
export class IdleObservationError extends Error {
|
|
50
|
+
readonly code = 'idle_observation_failed'
|
|
51
|
+
|
|
52
|
+
constructor(
|
|
53
|
+
readonly elapsedMs: number,
|
|
54
|
+
readonly failure: unknown,
|
|
55
|
+
) {
|
|
56
|
+
const detail = failure instanceof Error ? failure.message : String(failure)
|
|
57
|
+
super(`idle observation failed after ${elapsedMs}ms: ${detail}`, {
|
|
58
|
+
cause: failure,
|
|
59
|
+
})
|
|
60
|
+
this.name = 'IdleObservationError'
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
47
64
|
export async function waitForSootsimIdle({
|
|
48
65
|
bridge,
|
|
49
66
|
simId,
|
|
@@ -54,6 +71,9 @@ export async function waitForSootsimIdle({
|
|
|
54
71
|
paintStill = false,
|
|
55
72
|
}: WaitForSootsimIdleOptions): Promise<WaitForSootsimIdleResult> {
|
|
56
73
|
let result: any
|
|
74
|
+
// guest elapsed covers a completed read. this clock is read only when the
|
|
75
|
+
// bridge throws, so a successful settle does not report host time.
|
|
76
|
+
const observationStartedAt = Date.now()
|
|
57
77
|
try {
|
|
58
78
|
result = await bridge.send(
|
|
59
79
|
{
|
|
@@ -84,27 +104,23 @@ export async function waitForSootsimIdle({
|
|
|
84
104
|
let screenPixels = null
|
|
85
105
|
try {
|
|
86
106
|
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
// transition FIRST, bounded by the overall budget. when no
|
|
93
|
-
// transition is happening this returns in ~80ms (startWindowMs), so
|
|
94
|
-
// a plain button tap barely pays for it.
|
|
95
|
-
try {
|
|
96
|
-
const wfst = window.__sootsimTest?.waitForScreenTransitions
|
|
97
|
-
if (typeof wfst === 'function') {
|
|
107
|
+
// discover transitions while observing layout. when motion starts,
|
|
108
|
+
// observations from before its completion cannot prove the new screen.
|
|
109
|
+
let transitionWait = (async () => {
|
|
110
|
+
try {
|
|
111
|
+
const wfst = window.__sootsimTest?.waitForScreenTransitions
|
|
98
112
|
const remaining = deadline - Date.now()
|
|
99
|
-
if (remaining > 120) {
|
|
100
|
-
await wfst({
|
|
113
|
+
if (typeof wfst === 'function' && remaining > 120) {
|
|
114
|
+
const transition = await wfst({
|
|
101
115
|
timeoutMs: Math.min(remaining - 80, 4000),
|
|
102
116
|
settleMs: 64,
|
|
103
117
|
startWindowMs: 80,
|
|
104
118
|
})
|
|
119
|
+
return transition?.started === true
|
|
105
120
|
}
|
|
106
|
-
}
|
|
107
|
-
|
|
121
|
+
} catch {}
|
|
122
|
+
return false
|
|
123
|
+
})()
|
|
108
124
|
|
|
109
125
|
// a worker that cannot answer within the remaining budget is busy, not
|
|
110
126
|
// idle: its poll reads as unsettled. bounding each read by the budget
|
|
@@ -129,17 +145,51 @@ export async function waitForSootsimIdle({
|
|
|
129
145
|
let nodeCount = null
|
|
130
146
|
let renderStatsAvailable = false
|
|
131
147
|
let shellAnimating = false
|
|
148
|
+
let shellBlockers = ''
|
|
132
149
|
let webViewStarting = false
|
|
133
150
|
let webViewBlocker = ''
|
|
151
|
+
// the reads are independent, so they run concurrently: a loaded sim
|
|
152
|
+
// answers each one late, and a poll that waits for them in turn
|
|
153
|
+
// costs their sum, which fits too few polls in the budget to settle.
|
|
134
154
|
// strict also waits out shell motion: a system alert, sheet or menu
|
|
135
155
|
// animates in the shell worker while the app's own stats read still,
|
|
136
156
|
// so its presentation is complete only once the shell's list empties.
|
|
137
|
-
|
|
138
|
-
|
|
157
|
+
const motionRead = strict
|
|
158
|
+
? answer(() => window.__sootsimTest?.listAnimations?.()).catch(() => null)
|
|
159
|
+
: null
|
|
160
|
+
const statsRead = answer(() => window.__sootsimRenderHost?.queryStats?.()).catch(() => null)
|
|
161
|
+
// image preparation ends in the tenant before the compositor decodes
|
|
162
|
+
// and realizes its pixels. both owners must finish before settling.
|
|
163
|
+
// the compositor rejects a stats request it is too busy to answer.
|
|
164
|
+
const compositorRead = answer(() => window.__sootsimCompositor.getStats()).catch(() => BUSY)
|
|
165
|
+
const countsRead = Promise.resolve().then(() => window.__sootsimTest?.getRequestCounts?.()).catch(() => null)
|
|
166
|
+
// mounted-root membership: the live node count moves while the tree
|
|
167
|
+
// mounts, unmounts or recycles, in either render mode.
|
|
168
|
+
const nodeCountRead = Promise.resolve().then(() => window.__sootsimTest?.getNodeCount?.()).catch(() => null)
|
|
169
|
+
// the mounted hierarchy in render-worker mode comes from the test
|
|
170
|
+
// bridge: window.__sootsimRoot is an empty placeholder there, so a
|
|
171
|
+
// walk over it hashes every screen to the same empty string while
|
|
172
|
+
// it moves. listInspectable is the worker-backed read describe
|
|
173
|
+
// uses; a worker too busy to answer it reads as unsettled. tight
|
|
174
|
+
// budgets skip this read and settle on the cheaper signals above.
|
|
175
|
+
const hierarchyRead =
|
|
176
|
+
fullGeometry && typeof window.__sootsimTest?.listInspectable === 'function'
|
|
177
|
+
? answer(() => window.__sootsimTest.listInspectable({})).catch(() => null)
|
|
178
|
+
: null
|
|
179
|
+
if (motionRead) {
|
|
180
|
+
const motion = await motionRead
|
|
139
181
|
if (motion === BUSY) workerBusy = true
|
|
140
|
-
else
|
|
182
|
+
else if (Array.isArray(motion)) {
|
|
183
|
+
const blocking = motion.filter((anim) => anim.realm === 'shell' && anim.priority !== 'low')
|
|
184
|
+
shellAnimating = blocking.length > 0
|
|
185
|
+
// name what holds the settle so a timeout points at its owner.
|
|
186
|
+
shellBlockers = blocking
|
|
187
|
+
.slice(0, 4)
|
|
188
|
+
.map((anim) => anim.source + '/' + anim.kind + '#' + anim.id + ' at ' + anim.current + (anim.loop ? ' loop' : ''))
|
|
189
|
+
.join(', ')
|
|
190
|
+
}
|
|
141
191
|
}
|
|
142
|
-
const stats = await
|
|
192
|
+
const stats = await statsRead
|
|
143
193
|
if (stats === BUSY) {
|
|
144
194
|
workerBusy = true
|
|
145
195
|
} else if (stats) {
|
|
@@ -176,10 +226,7 @@ export async function waitForSootsimIdle({
|
|
|
176
226
|
// mounting or remounting, even with no animation flag raised.
|
|
177
227
|
appCommits = typeof stats.appCommitCount === 'number' ? stats.appCommitCount : null
|
|
178
228
|
}
|
|
179
|
-
|
|
180
|
-
// and realizes its pixels. both owners must finish before settling.
|
|
181
|
-
// the compositor rejects a stats request it is too busy to answer.
|
|
182
|
-
const compositor = await answer(() => window.__sootsimCompositor.getStats()).catch(() => BUSY)
|
|
229
|
+
const compositor = await compositorRead
|
|
183
230
|
if (compositor === BUSY) {
|
|
184
231
|
workerBusy = true
|
|
185
232
|
} else {
|
|
@@ -190,31 +237,12 @@ export async function waitForSootsimIdle({
|
|
|
190
237
|
webViewBlocker = 'compositor webView capture not settled'
|
|
191
238
|
}
|
|
192
239
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
// mounts, unmounts or recycles, in either render mode.
|
|
200
|
-
try {
|
|
201
|
-
const total = await window.__sootsimTest?.getNodeCount?.()
|
|
202
|
-
nodeCount = typeof total === 'number' ? total : null
|
|
203
|
-
} catch {}
|
|
204
|
-
// the mounted hierarchy in render-worker mode comes from the test
|
|
205
|
-
// bridge: window.__sootsimRoot is an empty placeholder there, so a
|
|
206
|
-
// walk over it hashes every screen to the same empty string while
|
|
207
|
-
// it moves. listInspectable is the worker-backed read describe
|
|
208
|
-
// uses; a worker too busy to answer it reads as unsettled. tight
|
|
209
|
-
// budgets skip this read and settle on the cheaper signals above.
|
|
210
|
-
let hierarchy = null
|
|
211
|
-
if (fullGeometry && typeof window.__sootsimTest?.listInspectable === 'function') {
|
|
212
|
-
try {
|
|
213
|
-
hierarchy = await answer(() => window.__sootsimTest.listInspectable({}))
|
|
214
|
-
} catch {
|
|
215
|
-
hierarchy = null
|
|
216
|
-
}
|
|
217
|
-
}
|
|
240
|
+
const counts = await countsRead
|
|
241
|
+
requestTotal = typeof counts?.total === 'number' ? counts.total : 0
|
|
242
|
+
requestInFlight = typeof counts?.inFlight === 'number' ? counts.inFlight : 0
|
|
243
|
+
const total = await nodeCountRead
|
|
244
|
+
nodeCount = typeof total === 'number' ? total : null
|
|
245
|
+
let hierarchy = hierarchyRead ? await hierarchyRead : null
|
|
218
246
|
if (hierarchy === BUSY) {
|
|
219
247
|
workerBusy = true
|
|
220
248
|
hierarchy = null
|
|
@@ -290,7 +318,7 @@ export async function waitForSootsimIdle({
|
|
|
290
318
|
pixelFrameToken = token
|
|
291
319
|
}
|
|
292
320
|
}
|
|
293
|
-
return { layout: nodes.join(';'), screenPixels, animating, shellAnimating, layoutDirty, pendingFetches, workerBusy, webViewStarting, webViewBlocker, renderStatsAvailable, requestTotal, requestInFlight, appCommits, nodeCount }
|
|
321
|
+
return { layout: nodes.join(';'), screenPixels, animating, shellAnimating, shellBlockers, layoutDirty, pendingFetches, workerBusy, webViewStarting, webViewBlocker, renderStatsAvailable, requestTotal, requestInFlight, appCommits, nodeCount }
|
|
294
322
|
}
|
|
295
323
|
|
|
296
324
|
// how long generic background network is allowed to keep us waiting AFTER
|
|
@@ -377,6 +405,19 @@ export async function waitForSootsimIdle({
|
|
|
377
405
|
Date.now() - visuallyStillSince >= requiredStillMs &&
|
|
378
406
|
(networkQuiet || Date.now() - visuallyStillSince >= networkQuietGraceMs)
|
|
379
407
|
) {
|
|
408
|
+
if (transitionWait) {
|
|
409
|
+
const started = await transitionWait
|
|
410
|
+
transitionWait = null
|
|
411
|
+
if (started) {
|
|
412
|
+
lastLayout = ''
|
|
413
|
+
lastRequestTotal = -1
|
|
414
|
+
lastAppCommits = null
|
|
415
|
+
lastNodeCount = null
|
|
416
|
+
stable = 0
|
|
417
|
+
visuallyStillSince = 0
|
|
418
|
+
continue
|
|
419
|
+
}
|
|
420
|
+
}
|
|
380
421
|
return { settled: true, elapsed: Date.now() - start, blockedBy: '' }
|
|
381
422
|
}
|
|
382
423
|
} else {
|
|
@@ -395,7 +436,7 @@ export async function waitForSootsimIdle({
|
|
|
395
436
|
const blockers = []
|
|
396
437
|
if (strict && !last.renderStatsAvailable) blockers.push('render stats unavailable')
|
|
397
438
|
if (last.animating) blockers.push('animations still running')
|
|
398
|
-
if (last.shellAnimating) blockers.push('shell animations still running')
|
|
439
|
+
if (last.shellAnimating) blockers.push('shell animations still running (' + last.shellBlockers + ')')
|
|
399
440
|
if (last.layoutDirty) blockers.push('layout still dirty')
|
|
400
441
|
if (last.appCommits !== null && lastAppCommits !== null && last.appCommits !== lastAppCommits) blockers.push('layout commits still landing')
|
|
401
442
|
if (last.nodeCount !== null && lastNodeCount !== null && last.nodeCount !== lastNodeCount) blockers.push('node count still changing')
|
|
@@ -426,12 +467,7 @@ export async function waitForSootsimIdle({
|
|
|
426
467
|
},
|
|
427
468
|
)
|
|
428
469
|
} catch (error) {
|
|
429
|
-
|
|
430
|
-
return {
|
|
431
|
-
settled: false,
|
|
432
|
-
elapsed: maxMs,
|
|
433
|
-
blockedBy: message,
|
|
434
|
-
}
|
|
470
|
+
throw new IdleObservationError(Math.max(0, Date.now() - observationStartedAt), error)
|
|
435
471
|
}
|
|
436
472
|
|
|
437
473
|
if (
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { rnxExit } from '../../run-rnx'
|
|
1
|
+
import { rethrowIfExit, rnxExit } from '../../run-rnx'
|
|
2
2
|
import { resolveMaxMsFlag } from './core'
|
|
3
|
-
import { waitForSootsimIdle } from './settling'
|
|
3
|
+
import { IdleObservationError, waitForSootsimIdle } from './settling'
|
|
4
4
|
import type { WsBridge } from '../../ws-bridge'
|
|
5
5
|
|
|
6
6
|
// alias for `do settle` under the discoverable `wait` verb grouping.
|
|
@@ -15,12 +15,24 @@ export async function runWaitIdleSubcommand(opts: {
|
|
|
15
15
|
const { bridge, args } = opts
|
|
16
16
|
const strict = args.includes('--strict')
|
|
17
17
|
const maxMs = resolveMaxMsFlag(args, 3000)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
18
|
+
try {
|
|
19
|
+
const { elapsed, settled, blockedBy } = await waitForSootsimIdle({
|
|
20
|
+
bridge,
|
|
21
|
+
maxMs,
|
|
22
|
+
strict,
|
|
23
|
+
})
|
|
24
|
+
if (settled) {
|
|
25
|
+
console.log(` idle in ${elapsed}ms`)
|
|
26
|
+
} else {
|
|
27
|
+
console.error(` ⚠ wait idle timed out after ${elapsed}ms (${blockedBy})`)
|
|
28
|
+
rnxExit(1)
|
|
29
|
+
}
|
|
30
|
+
} catch (error) {
|
|
31
|
+
rethrowIfExit(error)
|
|
32
|
+
if (error instanceof IdleObservationError) {
|
|
33
|
+
console.error(` ⚠ ${error.message}`)
|
|
34
|
+
rnxExit(1)
|
|
35
|
+
}
|
|
36
|
+
throw error
|
|
25
37
|
}
|
|
26
38
|
}
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
type WaitReadyStatus,
|
|
6
6
|
waitReadyReason,
|
|
7
7
|
} from './core'
|
|
8
|
-
import { waitReadyAndSettle } from './settling'
|
|
8
|
+
import { IdleObservationError, waitReadyAndSettle } from './settling'
|
|
9
9
|
import type { WsBridge } from '../../ws-bridge'
|
|
10
10
|
|
|
11
11
|
// blocks until the guest app bundle has finished loading and painted real
|
|
@@ -29,13 +29,27 @@ export async function runWaitReadySubcommand(opts: {
|
|
|
29
29
|
printReady(status, status.elapsedMs)
|
|
30
30
|
return
|
|
31
31
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
32
|
+
let status: Awaited<ReturnType<typeof waitReadyAndSettle>>['status']
|
|
33
|
+
let idle: Awaited<ReturnType<typeof waitReadyAndSettle>>['idle']
|
|
34
|
+
let elapsedMs: number
|
|
35
|
+
try {
|
|
36
|
+
const settledWait = await waitReadyAndSettle(bridge, timeoutMs, {
|
|
37
|
+
onProgress(progress) {
|
|
38
|
+
console.error(
|
|
39
|
+
` still waiting after ${progress.elapsedMs}ms — ${waitReadyReason(progress)} (nodes: ${progress.nodes}, targets: ${progress.targets}, live surfaces: ${progress.liveFrameActive ? progress.liveFrameChannels : 0}, browser surfaces: ${progress.browserSurfaceCount}, errors: ${progress.errors})`,
|
|
40
|
+
)
|
|
41
|
+
},
|
|
42
|
+
})
|
|
43
|
+
status = settledWait.status
|
|
44
|
+
idle = settledWait.idle
|
|
45
|
+
elapsedMs = settledWait.elapsedMs
|
|
46
|
+
} catch (error) {
|
|
47
|
+
if (error instanceof IdleObservationError) {
|
|
48
|
+
console.error(` ⚠ ${error.message}`)
|
|
49
|
+
rnxExit(1)
|
|
50
|
+
}
|
|
51
|
+
throw error
|
|
52
|
+
}
|
|
39
53
|
if (!idle) timedOut(status, elapsedMs)
|
|
40
54
|
if (!idle.settled) {
|
|
41
55
|
console.error(
|