@astur-mobile/core 0.5.0-beta.3 → 0.5.0-beta.5

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.
@@ -0,0 +1,630 @@
1
+ import { AsturError } from '../errors.js';
2
+ /**
3
+ * Reads a running Flutter app's UI through the Dart VM service.
4
+ *
5
+ * On Android, Flutter does not publish its widget tree to the OS accessibility
6
+ * layer, so the UIAutomator path sees an opaque FlutterView. Instead we evaluate
7
+ * a one-shot Dart walk of the element tree on-device, which returns every node's
8
+ * `Semantics(identifier)` (the same ids the app exposes for iOS), its text, and
9
+ * its GLOBAL bounds in physical pixels via `renderObject.localToGlobal()`.
10
+ *
11
+ * Evaluating source requires the Dart expression compiler, which is attached
12
+ * only when the app is launched through the Flutter tool (`flutter run`/`attach`)
13
+ * — see {@link FlutterProcess}.
14
+ */
15
+ // Field separator (SOH) between columns; rows are newline-separated. Neither
16
+ // character can appear in widget ids/text/types, so parsing is unambiguous.
17
+ const SEP = '';
18
+ // Single-line Dart expression (the debug expression compiler rejects multi-line
19
+ // bodies). Walks the element tree depth-first and emits one row per node that
20
+ // carries an identifier, text, or label and has laid-out bounds.
21
+ //
22
+ // It skips subtrees that aren't actually painted so the tree matches what's on
23
+ // screen. This matters most for IndexedStack (the common "keep every tab
24
+ // mounted" pattern): it wraps every inactive child in an Offstage/Visibility so
25
+ // only the selected screen is shown. Skipping Offstage / Visibility(false) /
26
+ // Opacity(0) subtrees drops those inactive screens (and e.g. a closed drawer's
27
+ // backdrop) — otherwise every screen's widgets show up at once, overlapping and
28
+ // mostly not interactable. Only nodes actually on screen are emitted.
29
+ const EXTRACT_EXPR = '(){' +
30
+ 'final sb=StringBuffer();' +
31
+ 'final view=WidgetsBinding.instance.platformDispatcher.views.first;' +
32
+ 'final dpr=view.devicePixelRatio;final sw=view.physicalSize.width;final sh=view.physicalSize.height;' +
33
+ // `cnt` assigns each emitted node a sequential id; `parent` threads the nearest
34
+ // emitted ancestor's id down the walk so the flat output can be rebuilt into a
35
+ // tree. Levels that don't emit (no id/text/label) are transparent — their
36
+ // children attach to the same ancestor — so geometry that reads a card's
37
+ // subtree (e.g. counters under a tap-lab card) matches the native nested tree.
38
+ 'int cnt=0;' +
39
+ 'void visit(Element el,int parent){' +
40
+ 'final wd=el.widget;' +
41
+ 'if(wd is Offstage && wd.offstage)return;' +
42
+ 'if(wd is Visibility && !wd.visible)return;' +
43
+ 'if(wd is Opacity && wd.opacity==0.0)return;' +
44
+ 'final ro=el.renderObject;' +
45
+ 'String? sid;String? lbl;String? txt;' +
46
+ 'int self=parent;' +
47
+ 'if(wd is Semantics){final p=wd.properties;if(p.identifier!=null)sid=p.identifier;if(p.label!=null)lbl=p.label;}' +
48
+ 'if(wd is Text && wd.data!=null)txt=wd.data;' +
49
+ 'if((sid!=null||txt!=null||lbl!=null) && ro is RenderBox && ro.attached && ro.hasSize){' +
50
+ 'final o=ro.localToGlobal(Offset.zero);' +
51
+ 'if(o.dx.isFinite && o.dy.isFinite){' +
52
+ 'final x=(o.dx*dpr).round();final y=(o.dy*dpr).round();final w=(ro.size.width*dpr).round();final h=(ro.size.height*dpr).round();' +
53
+ 'final on=(w>0 && h>0 && x+w>0 && y+h>0 && x<sw && y<sh);' +
54
+ 'if(on){' +
55
+ 'self=++cnt;' +
56
+ // An id-bearing node that directly wraps a text field (AsturId/Semantics around
57
+ // a TextField -> EditableText) is reported as "TextField" so the inspector
58
+ // recognises it as fillable. The search stops at nested id-bearing Semantics so
59
+ // a container (e.g. screen-login) that merely *contains* inputs isn't flagged.
60
+ 'String kind=wd.runtimeType.toString();String val="";' +
61
+ // Walk into an id-bearing node to (a) classify it as a text field and (b) read
62
+ // its value. A Material TextField exposes both the typed text (controller.text)
63
+ // and, when empty, its placeholder (decoration.hintText) — mirroring Android's
64
+ // native behaviour where an empty EditText reports its hint as text, so
65
+ // toHaveValue works the same on both builds. EditableText is the universal
66
+ // fallback (every field type embeds one) for the typed value.
67
+ 'if(sid!=null){bool ed=false;void ck(Element c){if(ed)return;final cw=c.widget;if(cw is Semantics && cw.properties.identifier!=null)return;if(cw is TextField){final t=cw.controller?.text;final hint=cw.decoration?.hintText;val=(t!=null && t.isNotEmpty)?t:(hint??"");ed=true;return;}if(cw is EditableText){val=cw.controller.text;ed=true;return;}c.visitChildren(ck);}el.visitChildren(ck);if(ed)kind="TextField";}' +
68
+ 'sb.write(self.toString());sb.writeCharCode(1);sb.write(parent.toString());sb.writeCharCode(1);' +
69
+ 'sb.write(sid??"");sb.writeCharCode(1);sb.write(txt??"");sb.writeCharCode(1);sb.write(lbl??"");sb.writeCharCode(1);' +
70
+ 'sb.write(kind);sb.writeCharCode(1);' +
71
+ 'sb.write(x.toString());sb.writeCharCode(1);sb.write(y.toString());sb.writeCharCode(1);' +
72
+ 'sb.write(w.toString());sb.writeCharCode(1);sb.write(h.toString());sb.writeCharCode(1);sb.write("1");sb.writeCharCode(1);sb.write(val);sb.writeCharCode(10);' +
73
+ '}' +
74
+ '}}' +
75
+ 'el.visitChildren((c){visit(c,self);});' +
76
+ '}' +
77
+ 'visit(WidgetsBinding.instance.rootElement!,0);' +
78
+ 'return sb.toString();' +
79
+ '}()';
80
+ // Readiness probe: reports "<idle>:<w>:<h>" where idle is 1 once a frame has
81
+ // fully completed (scheduler back to idle, root element attached) and w/h is the
82
+ // physical screen size. The physical size becomes non-zero BEFORE the first
83
+ // layout pass finishes, so gating on size alone yields render objects with zero
84
+ // bounds; waiting for an idle (completed) frame guarantees the tree is laid out.
85
+ // It only reads scalars (no tree walk), so it is safe to evaluate mid-build —
86
+ // unlike EXTRACT_EXPR, which can hit Flutter's build lock.
87
+ const READY_EXPR = '(){final r=WidgetsBinding.instance.rootElement;' +
88
+ 'final v=WidgetsBinding.instance.platformDispatcher.views.first;' +
89
+ 'final w=v.physicalSize.width.round();final h=v.physicalSize.height.round();' +
90
+ 'final idle=(r!=null && WidgetsBinding.instance.schedulerPhase.name=="idle")?1:0;' +
91
+ 'return idle.toString()+":"+w.toString()+":"+h.toString();}()';
92
+ // Clears the Flutter primary focus, which unfocuses the active text field and
93
+ // asks the engine to hide the soft keyboard. This is the deterministic way to
94
+ // dismiss the IME on the no-agent driver: unlike a Back press, it can never pop
95
+ // a route or background the app, so the widget tree stays intact afterwards.
96
+ const UNFOCUS_EXPR = '(){FocusManager.instance.primaryFocus?.unfocus();return "1";}()';
97
+ export class FlutterVmService {
98
+ ws;
99
+ nextId = 1;
100
+ pending = new Map();
101
+ isolateId;
102
+ rootLibId;
103
+ screen = { x: 0, y: 0, width: 0, height: 0 };
104
+ url;
105
+ timeoutMs;
106
+ readyTimeoutMs;
107
+ readyPollMs;
108
+ readySettleMs;
109
+ evalBudgetMs;
110
+ evalRetryDelayMs;
111
+ constructor(options) {
112
+ this.url = options.url;
113
+ this.timeoutMs = options.requestTimeoutMs ?? 20_000;
114
+ this.readyTimeoutMs = options.readyTimeoutMs ?? 15_000;
115
+ this.readyPollMs = options.readyPollMs ?? 150;
116
+ this.readySettleMs = options.readySettleMs ?? 500;
117
+ // Derived from the request timeout, never a bare constant: the budget has to
118
+ // leave room for at least one full-length request plus a retry, or it
119
+ // truncates the very first attempt and reports a timeout for a tree walk
120
+ // that was merely slow (a large widget tree can take many seconds).
121
+ this.evalBudgetMs = options.evalBudgetMs ?? this.timeoutMs + 10_000;
122
+ this.evalRetryDelayMs = options.evalRetryDelayMs ?? 250;
123
+ }
124
+ async openSocket() {
125
+ await new Promise((resolve, reject) => {
126
+ const ws = new WebSocket(this.url);
127
+ const onError = () => reject(new AsturError('FLUTTER_VM_CONNECT_FAILED', `Failed to connect to the Flutter Dart VM service at ${this.url}.`));
128
+ ws.addEventListener('open', () => { ws.removeEventListener('error', onError); resolve(); }, { once: true });
129
+ ws.addEventListener('error', onError, { once: true });
130
+ ws.addEventListener('message', (event) => this.onMessage(event));
131
+ ws.addEventListener('close', () => this.failAllPending('Flutter VM service connection closed.'));
132
+ this.ws = ws;
133
+ });
134
+ }
135
+ /**
136
+ * Attaches for network observation only.
137
+ *
138
+ * Deliberately not {@link connect}: that one binds an isolate by evaluating a
139
+ * Dart expression, which needs the expression compiler that `flutter run`
140
+ * attaches. An app launched any other way — `simctl launch` on the iOS
141
+ * simulator, for instance — has a perfectly good VM service with a working
142
+ * HTTP profiler but no compiler, so requiring evaluation rejects a session
143
+ * that can observe traffic fine.
144
+ *
145
+ * Binds on the capability actually needed instead: an isolate that registered
146
+ * the `dart:io` profiler extensions.
147
+ */
148
+ async connectForNetwork() {
149
+ await this.openSocket();
150
+ return this.resolveIsolate(undefined, 'network');
151
+ }
152
+ async connect() {
153
+ await this.openSocket();
154
+ await this.resolveIsolate();
155
+ // Wait for the first frame so the tree walk doesn't race app startup and so
156
+ // we capture a real screen size (physicalSize is 0x0 until the view lays out).
157
+ await this.waitForReady();
158
+ }
159
+ /**
160
+ * Re-binds to the isolate created by a hot restart. `flutter run`'s `R` tears
161
+ * down the running isolate and starts a *new* one. The old isolate lingers in
162
+ * `getVM` for a moment — and is still readable but FROZEN at its last frame —
163
+ * so binding to it makes tree reads return stale UI (taps reach the live app
164
+ * but the tree never reflects them) or, once it dies, an empty tree. Excluding
165
+ * the pre-restart id is what makes resets land on the live isolate.
166
+ */
167
+ async reattach() {
168
+ await this.resolveIsolate(this.isolateId);
169
+ return this.waitForReady();
170
+ }
171
+ /**
172
+ * Re-checks readiness on the already-bound isolate without re-resolving it.
173
+ * Used by the reset path to re-wait after re-foregrounding a surfaceless engine
174
+ * (a hot restart can come back with a 0x0 view), so callers can retry cheaply
175
+ * instead of paying the isolate-resolution grace window again.
176
+ */
177
+ async ensureReady() {
178
+ return this.waitForReady();
179
+ }
180
+ /**
181
+ * Binds {@link isolateId}/{@link rootLibId} to a live, evaluable isolate.
182
+ *
183
+ * When `previousIsolateId` is given (a hot-restart reattach), an isolate with a
184
+ * *different* id is strongly preferred — that's the replacement; the old one is
185
+ * the frozen/dying predecessor. We only fall back to the same id after a short
186
+ * grace window (covers the rare case where a restart reuses the id) so we don't
187
+ * grab the predecessor before its replacement has registered. Each candidate is
188
+ * validated with a trivial eval, since a dying isolate can still be listed yet
189
+ * reject evaluations.
190
+ */
191
+ async resolveIsolate(previousIsolateId, mode = 'evaluate') {
192
+ const deadline = Date.now() + this.readyTimeoutMs;
193
+ const reuseGraceUntil = Date.now() + 1_500;
194
+ let lastReason = 'no Dart isolate was reported';
195
+ const tryBind = async (candidates) => {
196
+ for (const candidate of candidates) {
197
+ const isolate = await this.call('getIsolate', { isolateId: candidate.id }, deadline).catch(() => undefined);
198
+ const pauseKind = isolate?.pauseEvent?.kind;
199
+ if (pauseKind === 'PauseExit' || pauseKind === 'Exit' || isolate?.runnable === false) {
200
+ lastReason = 'the isolate had already exited';
201
+ continue;
202
+ }
203
+ if (mode === 'network') {
204
+ // Bind on the capability this mode actually needs. An isolate that
205
+ // registered the profiler extensions can serve traffic regardless of
206
+ // whether it can compile expressions.
207
+ const extensions = isolate?.extensionRPCs ?? [];
208
+ if (!extensions.includes('ext.dart.io.getHttpProfile')) {
209
+ lastReason = 'the isolate has not registered the dart:io HTTP profiler extensions';
210
+ continue;
211
+ }
212
+ this.isolateId = candidate.id;
213
+ this.rootLibId = isolate?.rootLib?.id;
214
+ return true;
215
+ }
216
+ const rootLibId = isolate?.rootLib?.id;
217
+ if (!rootLibId) {
218
+ lastReason = 'the isolate exposed no root library for evaluation';
219
+ continue;
220
+ }
221
+ this.isolateId = candidate.id;
222
+ this.rootLibId = rootLibId;
223
+ try {
224
+ await this.evaluateString(READY_EXPR, deadline);
225
+ return true;
226
+ }
227
+ catch {
228
+ lastReason = 'the candidate isolate did not accept evaluations yet';
229
+ }
230
+ }
231
+ return false;
232
+ };
233
+ for (;;) {
234
+ // Bounded by the same deadline the loop below enforces. Without it a
235
+ // single unresponsive request blocks for the full request timeout and
236
+ // overruns the deadline it is supposed to be respecting.
237
+ const vm = await this.call('getVM', {}, deadline);
238
+ const isolates = vm.isolates ?? [];
239
+ // VM service lists isolates in creation order. Multiple frozen isolates can
240
+ // linger after repeated hot restarts and still accept read-only evaluations,
241
+ // so trying the oldest first can bind a perfectly readable but stale tree.
242
+ // Prefer the most recently created replacement.
243
+ const newestFirst = [...isolates].reverse();
244
+ const fresh = previousIsolateId
245
+ ? newestFirst.filter((iso) => iso.id !== previousIsolateId)
246
+ : newestFirst;
247
+ if (await tryBind(fresh)) {
248
+ return true;
249
+ }
250
+ // No replacement isolate yet: only reconsider the previous id after the
251
+ // grace window, in case this restart reused it.
252
+ if (previousIsolateId
253
+ && fresh.length === 0
254
+ && Date.now() >= reuseGraceUntil
255
+ && (await tryBind(newestFirst))) {
256
+ return true;
257
+ }
258
+ if (Date.now() > deadline) {
259
+ throw new AsturError('FLUTTER_VM_NO_ISOLATE', `Could not bind to the Flutter Dart isolate (${lastReason}).`);
260
+ }
261
+ await this.sleep(this.readyPollMs);
262
+ }
263
+ }
264
+ async getTree() {
265
+ if (this.screen.width === 0 || this.screen.height === 0) {
266
+ // connect() timed out before the first frame, or the view was resized to
267
+ // zero; re-establish a stable frame before reading.
268
+ await this.waitForReady();
269
+ }
270
+ const children = await this.extractChildren();
271
+ return {
272
+ type: 'FlutterView',
273
+ enabled: true,
274
+ visible: true,
275
+ bounds: this.screen,
276
+ children,
277
+ platform: 'android'
278
+ };
279
+ }
280
+ /**
281
+ * Runs the on-device tree walk and rebuilds the flat, parent-annotated output
282
+ * into a nested snapshot tree. Nesting matters because page logic reads a
283
+ * container's subtree (e.g. the counters under a tap-lab card) — a flat list
284
+ * would leave every container childless. Rows are emitted in pre-order, so a
285
+ * parent always precedes its children and the map is fully populated by the
286
+ * time we wire up links.
287
+ */
288
+ async extractChildren() {
289
+ const raw = await this.evaluateStringStable(EXTRACT_EXPR);
290
+ const rows = raw
291
+ .split('\n')
292
+ .filter((line) => line.length > 0)
293
+ .map((line) => this.parseRow(line))
294
+ .filter((row) => row !== undefined);
295
+ const byId = new Map();
296
+ for (const row of rows) {
297
+ byId.set(row.nodeId, row.node);
298
+ }
299
+ const roots = [];
300
+ for (const row of rows) {
301
+ const parent = byId.get(row.parentId);
302
+ if (parent) {
303
+ parent.children.push(row.node);
304
+ }
305
+ else {
306
+ // parentId 0 (the synthetic root) or a pruned ancestor → top level.
307
+ roots.push(row.node);
308
+ }
309
+ }
310
+ return roots;
311
+ }
312
+ /** Dismisses the soft keyboard by clearing the Flutter primary focus. */
313
+ async unfocus() {
314
+ await this.evaluateStringStable(UNFOCUS_EXPR);
315
+ }
316
+ async dispose() {
317
+ this.failAllPending('Flutter VM service disposed.');
318
+ try {
319
+ this.ws?.close();
320
+ }
321
+ catch {
322
+ // ignore
323
+ }
324
+ this.ws = undefined;
325
+ }
326
+ parseRow(line) {
327
+ const cols = line.split(SEP);
328
+ if (cols.length < 11) {
329
+ return undefined;
330
+ }
331
+ const [nodeId, parentId, id, text, label, type, x, y, w, h, vis, fieldValue] = cols;
332
+ const bounds = {
333
+ x: Number.parseInt(x, 10) || 0,
334
+ y: Number.parseInt(y, 10) || 0,
335
+ width: Number.parseInt(w, 10) || 0,
336
+ height: Number.parseInt(h, 10) || 0
337
+ };
338
+ return {
339
+ nodeId: Number.parseInt(nodeId, 10) || 0,
340
+ parentId: Number.parseInt(parentId, 10) || 0,
341
+ node: {
342
+ id: id || undefined,
343
+ text: text || undefined,
344
+ label: label || undefined,
345
+ // Text-field value (typed text, or placeholder when empty); falls back to a
346
+ // Text widget's data so plain labels still expose a value.
347
+ value: fieldValue || text || undefined,
348
+ type: type || 'FlutterWidget',
349
+ enabled: true,
350
+ visible: vis === '1',
351
+ bounds,
352
+ children: [],
353
+ platform: 'android'
354
+ }
355
+ };
356
+ }
357
+ /**
358
+ * Blocks until the app's UI has settled, so the first read returns fully
359
+ * laid-out bounds rather than zeros.
360
+ *
361
+ * Two phases:
362
+ * 1. Wait for a completed frame (scheduler idle) with a non-zero screen size —
363
+ * captures the real physical size, which is 0x0 until the view lays out.
364
+ * 2. Wait for the laid-out element set to stop changing across consecutive
365
+ * polls. This is necessary because parts of the tree (e.g. a bottom
366
+ * navigation bar) can lay out a frame or two after the first frame, so they
367
+ * report zero bounds immediately after step 1 — tapping such a node would
368
+ * hit the wrong coordinates. The fingerprint keys on which nodes are present
369
+ * and laid out, not their exact bounds, so a continuously-animating app
370
+ * still settles instead of polling until the timeout.
371
+ */
372
+ async waitForReady() {
373
+ const deadline = Date.now() + this.readyTimeoutMs;
374
+ // Phase 1 — a known screen size (physicalSize is 0x0 until the view lays out).
375
+ // Idle is not required here: a continuously-animating app never reports idle,
376
+ // and phase 2 provides the real layout guarantee.
377
+ for (;;) {
378
+ let raw = '0:0:0';
379
+ try {
380
+ raw = await this.evaluateString(READY_EXPR);
381
+ }
382
+ catch {
383
+ // Transient during isolate warm-up; retry until the deadline.
384
+ }
385
+ const [, wRaw, hRaw] = raw.split(':');
386
+ const w = Number.parseInt(wRaw, 10);
387
+ const h = Number.parseInt(hRaw, 10);
388
+ if (Number.isFinite(w) && w > 0 && Number.isFinite(h) && h > 0) {
389
+ this.screen = { x: 0, y: 0, width: w, height: h };
390
+ break;
391
+ }
392
+ if (Date.now() >= deadline) {
393
+ // Never captured a real screen size: the engine is surfaceless (a 0x0
394
+ // view after a hot restart). Report it so the reset path can re-foreground
395
+ // and retry instead of serving an unlaid-out tree.
396
+ return false;
397
+ }
398
+ await this.sleep(this.readyPollMs);
399
+ }
400
+ // Phase 2 — wait for the laid-out element set to stabilize.
401
+ let previous;
402
+ let settleStart = 0;
403
+ for (;;) {
404
+ let fingerprint = '';
405
+ try {
406
+ fingerprint = this.layoutFingerprint(await this.extractChildren());
407
+ }
408
+ catch {
409
+ // Mid-build; retry until the deadline.
410
+ }
411
+ if (fingerprint && fingerprint === previous) {
412
+ if (settleStart === 0) {
413
+ settleStart = Date.now();
414
+ }
415
+ if (Date.now() - settleStart >= this.readySettleMs) {
416
+ return true;
417
+ }
418
+ }
419
+ else {
420
+ settleStart = 0;
421
+ previous = fingerprint;
422
+ }
423
+ if (Date.now() >= deadline) {
424
+ // The view reported a real size (Phase 1 passed); the laid-out set just
425
+ // never fully settled. That is still usable, so treat it as ready.
426
+ return true;
427
+ }
428
+ await this.sleep(this.readyPollMs);
429
+ }
430
+ }
431
+ /** Identity of the laid-out, labelled nodes — present-and-sized, ignoring exact position so animations still settle. Walks the (now nested) tree. */
432
+ layoutFingerprint(roots) {
433
+ const parts = [];
434
+ const walk = (node) => {
435
+ if (node.bounds.width > 0 && node.bounds.height > 0) {
436
+ parts.push(`${node.id ?? ''}#${node.text ?? ''}#${node.label ?? ''}`);
437
+ }
438
+ for (const child of node.children) {
439
+ walk(child);
440
+ }
441
+ };
442
+ for (const root of roots) {
443
+ walk(root);
444
+ }
445
+ return parts.sort().join('|');
446
+ }
447
+ /**
448
+ * Evaluates the on-device tree walk, retrying transient build-lock failures.
449
+ *
450
+ * The walk uses `visitChildren`, which throws if it runs while Flutter is mid-build
451
+ * (app startup, route transitions, animations). Those settle within a frame or two,
452
+ * so a short bounded retry makes reads reliable without callers having to sleep.
453
+ */
454
+ async evaluateStringStable(expression) {
455
+ // Bound the whole retry sequence by wall clock, and hand the same deadline
456
+ // down to each RPC so a single hung request cannot consume the budget on
457
+ // its own. Callers poll this through `waitFor`, which only re-checks its
458
+ // own deadline *between* attempts — an unbounded evaluate here therefore
459
+ // reads as a frozen test rather than a timeout, no matter how short the
460
+ // caller's timeout is.
461
+ const deadline = Date.now() + this.evalBudgetMs;
462
+ let lastError;
463
+ let attempts = 0;
464
+ for (;;) {
465
+ attempts += 1;
466
+ try {
467
+ return await this.evaluateString(expression, deadline);
468
+ }
469
+ catch (err) {
470
+ const transientCompilationError = err instanceof AsturError
471
+ && err.code === 'FLUTTER_VM_RPC_ERROR'
472
+ && /expression compilation error/i.test(err.message);
473
+ if (!(err instanceof AsturError)
474
+ || (err.code !== 'FLUTTER_VM_EVAL_FAILED' && !transientCompilationError)) {
475
+ throw err;
476
+ }
477
+ lastError = err;
478
+ if (Date.now() + this.evalRetryDelayMs >= deadline) {
479
+ break;
480
+ }
481
+ await this.sleep(this.evalRetryDelayMs);
482
+ }
483
+ }
484
+ const expressionName = expression === EXTRACT_EXPR
485
+ ? 'Flutter tree extraction'
486
+ : expression === UNFOCUS_EXPR
487
+ ? 'Flutter keyboard unfocus'
488
+ : 'Flutter expression';
489
+ throw new AsturError('FLUTTER_VM_EVAL_FAILED', `${expressionName} failed after ${attempts} attempt(s) within ${this.evalBudgetMs} ms: ${lastError instanceof Error ? lastError.message : 'unknown evaluation error'}`, { cause: lastError });
490
+ }
491
+ sleep(ms) {
492
+ return new Promise((resolve) => setTimeout(resolve, ms));
493
+ }
494
+ /**
495
+ * Evaluates a Dart expression and returns its full String result (handles VM
496
+ * string truncation). `deadline` caps every RPC this makes, so paging a large
497
+ * tree cannot outlive the caller's retry budget.
498
+ */
499
+ async evaluateString(expression, deadline) {
500
+ const result = await this.call('evaluate', {
501
+ isolateId: this.isolateId,
502
+ targetId: this.rootLibId,
503
+ expression
504
+ }, deadline);
505
+ if (result.kind === 'Error' || result.type === '@Error') {
506
+ throw new AsturError('FLUTTER_VM_EVAL_FAILED', 'Flutter VM expression evaluation failed.', { result });
507
+ }
508
+ if (result.kind !== 'String') {
509
+ return '';
510
+ }
511
+ if (!result.valueAsStringIsTruncated) {
512
+ return result.valueAsString ?? '';
513
+ }
514
+ // Large result: page the full string in via getObject.
515
+ const objectId = result.id;
516
+ let out = '';
517
+ let offset = 0;
518
+ const count = 100_000;
519
+ // The total length is reported on the (truncated) instance.
520
+ const total = typeof result.length === 'number' ? result.length : Number.MAX_SAFE_INTEGER;
521
+ while (out.length < total) {
522
+ const obj = await this.call('getObject', { isolateId: this.isolateId, objectId, offset, count }, deadline);
523
+ const chunk = obj.valueAsString ?? '';
524
+ if (chunk.length === 0) {
525
+ break;
526
+ }
527
+ out += chunk;
528
+ offset += chunk.length;
529
+ }
530
+ return out;
531
+ }
532
+ /**
533
+ * Turns on the Dart VM's HTTP profiler for this isolate.
534
+ *
535
+ * Off by default: with it on the VM retains every request (and its body) for
536
+ * the life of the isolate, so it is enabled only when a test asks to observe,
537
+ * and the buffer is cleared between tests.
538
+ */
539
+ async enableHttpProfiling() {
540
+ await this.call('ext.dart.io.httpEnableTimelineLogging', {
541
+ isolateId: this.isolateId,
542
+ enabled: true
543
+ });
544
+ }
545
+ /** Drops everything the profiler has buffered so far. */
546
+ async clearHttpProfile() {
547
+ await this.call('ext.dart.io.clearHttpProfile', { isolateId: this.isolateId });
548
+ }
549
+ /**
550
+ * Returns the profiler's buffered requests, newest state included.
551
+ *
552
+ * The response shape is owned by the Dart SDK and has changed between
553
+ * versions, so callers must treat every field as optional rather than
554
+ * trusting a schema — see `toNetworkRecords` in the Android driver.
555
+ */
556
+ async getHttpProfile() {
557
+ return this.call('ext.dart.io.getHttpProfile', { isolateId: this.isolateId });
558
+ }
559
+ /** Full detail (including the response body) for one profiled request. */
560
+ async getHttpProfileRequest(id) {
561
+ return this.call('ext.dart.io.getHttpProfileRequest', { isolateId: this.isolateId, id });
562
+ }
563
+ /**
564
+ * Whether this isolate actually registered the dart:io HTTP profiler
565
+ * extensions. They are absent in some builds, so capability is detected at
566
+ * runtime rather than assumed from the platform.
567
+ */
568
+ async supportsHttpProfiling() {
569
+ const isolate = await this.call('getIsolate', { isolateId: this.isolateId }).catch(() => undefined);
570
+ const extensions = isolate?.extensionRPCs ?? [];
571
+ return extensions.includes('ext.dart.io.getHttpProfile');
572
+ }
573
+ call(method, params = {}, deadline) {
574
+ const ws = this.ws;
575
+ if (!ws) {
576
+ return Promise.reject(new AsturError('FLUTTER_VM_NOT_CONNECTED', 'Flutter VM service is not connected.'));
577
+ }
578
+ // Never exceed the caller's remaining budget, but always allow a small floor
579
+ // so an almost-expired deadline still issues a real request rather than a
580
+ // guaranteed-zero-timeout one.
581
+ const timeoutMs = deadline === undefined
582
+ ? this.timeoutMs
583
+ : Math.max(250, Math.min(this.timeoutMs, deadline - Date.now()));
584
+ const id = this.nextId++;
585
+ return new Promise((resolve, reject) => {
586
+ const timer = setTimeout(() => {
587
+ this.pending.delete(id);
588
+ reject(new AsturError('FLUTTER_VM_TIMEOUT', `Flutter VM service request '${method}' timed out after ${timeoutMs} ms.`));
589
+ }, timeoutMs);
590
+ this.pending.set(id, { resolve, reject, timer });
591
+ ws.send(JSON.stringify({ jsonrpc: '2.0', id, method, params }));
592
+ });
593
+ }
594
+ onMessage(event) {
595
+ let message;
596
+ try {
597
+ message = JSON.parse(typeof event.data === 'string' ? event.data : String(event.data));
598
+ }
599
+ catch {
600
+ return;
601
+ }
602
+ if (typeof message.id !== 'number') {
603
+ return;
604
+ }
605
+ const entry = this.pending.get(message.id);
606
+ if (!entry) {
607
+ return;
608
+ }
609
+ this.pending.delete(message.id);
610
+ clearTimeout(entry.timer);
611
+ if (message.error) {
612
+ const detail = message.error.data === undefined
613
+ ? ''
614
+ : `: ${typeof message.error.data === 'string'
615
+ ? message.error.data
616
+ : JSON.stringify(message.error.data)}`;
617
+ entry.reject(new AsturError('FLUTTER_VM_RPC_ERROR', `${message.error.message ?? 'Flutter VM service returned an error'}${detail}`, { data: message.error.data }));
618
+ return;
619
+ }
620
+ entry.resolve(message.result ?? {});
621
+ }
622
+ failAllPending(reason) {
623
+ for (const [, entry] of this.pending) {
624
+ clearTimeout(entry.timer);
625
+ entry.reject(new AsturError('FLUTTER_VM_DISCONNECTED', reason));
626
+ }
627
+ this.pending.clear();
628
+ }
629
+ }
630
+ //# sourceMappingURL=vmService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vmService.js","sourceRoot":"","sources":["../../src/flutter/vmService.ts"],"names":[],"mappings":"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"}