Skip to content

chore(deps): bump the fluttersdk group with 6 updates - #22

Open
dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/pub/fluttersdk-f72d0c9998
Open

dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/pub/fluttersdk-f72d0c9998

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github Oct 5, 2026

Copy link
Copy Markdown
Contributor

Bumps the fluttersdk group with 6 updates:

Package From To
fluttersdk_dusk 0.0.16 0.0.17
fluttersdk_telescope 0.0.7 0.0.9
magic 0.0.22 0.0.26
magic_deeplink 0.1.4 0.1.5
magic_devtools 0.0.7 0.0.8
magic_notifications 0.3.5 0.3.7

Updates fluttersdk_dusk from 0.0.16 to 0.0.17

Release notes

Sourced from fluttersdk_dusk's releases.

0.0.17

Fixed

  • dusk:perf_campaign keeps the credentials it reads out of the processes it starts. Hooks got the whole inherited environment spread under DUSK_PERF_*, and the preparation processes inherited it too, so a server hooks.before_campaign backgrounded kept the password the campaign reads through ${env.*} for as long as it lived. The loader now names every variable a ${env.NAME} read (PerfLoadResult.envNames, PerfSetupLoadResult.envNames, PerfCampaign.secretEnvNames), and hooks and preparation processes run with includeParentEnvironment: false on the invoking environment minus those names. The filter is by name, not by value: CI=1 survives a secret that reads 1. artisan start and stop run in-process with no environment seam, so the flutter run, the Chrome and the Android adb force-stop they spawn still inherit the dispatcher's environment; the campaign doc says so. (lib/src/perf/scenario.dart, lib/src/perf/scenario_loader.dart, lib/src/perf/campaign.dart, lib/src/commands/dusk_perf_campaign_command.dart, doc/commands/dusk-perf-campaign.md)

  • perf_run's restart wait names the app's last answer. Since the boot wait became pollDuskBoot, a read could start after the pause that spent the budget, with no time left, and its TimeoutException replaced the app's own last error in the failure; no read starts once the budget is spent now. The campaign also survives a stale .err it cannot delete (reported, like a write it cannot make) and a flutter-dev.log with malformed UTF-8 (decoded leniently), both of which escaped the per-scenario catch and skipped after_campaign and the envelope. (lib/src/perf/perf_run_driver.dart, lib/src/commands/dusk_perf_campaign_command.dart)

  • dusk:perf_campaign --json prints its envelope however the campaign ends. A stop in the preparation (a hook, flutter pub get, an Android step) returned before the envelope, so a --json caller read nothing; a before_scenario stop listed only the scenarios tried. The envelope now lists every selected scenario, those never tried as status: "not_run" with attempts: 0, carries the stop sentence as stopped, and names anything that failed after the scenarios in errors. An artisan stop that fails after the campaign now exits 1 (it kept exit 0), and an .err that cannot be written inside the per-scenario catch is reported through the output instead of escaping it and ending the campaign before the app stop. (lib/src/commands/dusk_perf_campaign_command.dart, doc/commands/dusk-perf-campaign.md)

  • dusk:perf_campaign's after_start waits for the Router as long as its guards wait. The Router wait before after_start gave up at perf_run's 10 s, so a cold start that mounted its login screen at 15 s failed before a when guard written for 60 s ever polled. PerfSetupRunner.awaitRouter takes an optional budget (perf_run keeps its 10 s, kPerfRouterBudget), and the campaign passes the largest timeout_ms among the after_start guards, the 60 s guard default when there is none, never less than 10 s. (lib/src/perf/perf_setup_runner.dart, lib/src/commands/dusk_perf_campaign_command.dart, doc/commands/dusk-perf-campaign.md)

  • A secret shorter than 4 characters is a load problem. Every output is masked for every secret wherever its text appears, so a secret 1 or 80 masked that number in every log line, run file and envelope. loadPerfScenarios and loadPerfSetup now refuse a non-empty ${env.*} value or secret: true param under 4 characters (kPerfMinSecretLength), naming the variable or the param and its length, never the value. (lib/src/perf/scenario.dart, lib/src/perf/scenario_loader.dart, doc/commands/dusk-perf-run.md)

  • ext.dusk.navigate_back leaves a hidden shell branch alone. It popped the first poppable Navigator in tree order, offstage ones included, so in a go_router StatefulShellRoute (every branch kept alive, the inactive ones under Offstage and a disabled TickerMode) a page stacked on a hidden branch was popped while the visible branch stayed, and the answer said popped: true. A subtree under Offstage(offstage: true), TickerMode(enabled: false) or Visibility(visible: false) is no longer walked. (lib/src/extensions/ext_navigation.dart)

  • A perf_run setup navigate lands on its own path, and a late landing is on the record. The late-landing poll accepted a path under the route, so a dropped navigate to /monitors while a web hot restart had left the app on /monitors/<id> passed as landed; it now needs the exact path, query ignored. A unit whose setup relied on a late landing carries setupLandedLate: true in its repeats[] entry. (lib/src/commands/dusk_perf_run_command.dart)

  • perf_run's post-idle route check compares paths. It compared whole URIs, so a page that normalises its query after the first fetch (/monitors to /monitors?page=1) failed setup as moved. (lib/src/commands/dusk_perf_run_command.dart)

  • perf_trace cuts its frames by perf_end's rule. Under a clock mismatch (frames carry a timestamp and none lands in the window) perf_end keeps every frame while the trace dropped them all, so the two artifacts disagreed silently. The trace now shares perf_end's window cut, keeps the frames, and says so with otherData.sessionClockMismatch: true. buildPerfReport forwards sessionClockMismatch the way analysePerf takes it. (lib/src/extensions/ext_perf.dart, lib/src/extensions/ext_perf_trace.dart, lib/src/utils/perf_insights.dart)

  • The CDP emulation tools say how long their override lasts. dusk_resize_viewport and dusk_device_profile promised a resize, and neither can pass hold: each call opens its own CDP session and closes it on return, and Chrome drops the Emulation.* override with it. Both descriptors, both CLI descriptions and dusk:device's output now say the override lasts only as long as the call's CDP session (only dusk:device's window size stays), and dusk:resize reports "override sent" rather than "Viewport set to", pointing at --hold. (lib/src/dusk_artisan_provider.dart, lib/src/commands/dusk_resize_command.dart, lib/src/commands/dusk_device_command.dart)

  • ext.dusk.navigate_back pops a page stacked inside a shell. It popped the first Navigator in the tree, which in a go_router app is the root one, and that holds the shell alone: for a detail page pushed inside a ShellRoute (MagicRouter layouts, .stacked()) the pop did nothing, the answer was still navigatedBack: true, and get_routes's uri kept naming the detail page because it was still mounted. It pops the outermost Navigator that can pop instead, so a page pushed on the root is left before a shell's. uri itself was not stale: it follows the Router's report within the frames the handler awaits. The answer gains an additive popped, false when no Navigator could pop, so a no-op no longer reads as a pop. (lib/src/extensions/ext_navigation.dart, lib/src/dusk_artisan_provider.dart, doc/mcp/tool-reference.md, skills/fluttersdk-dusk/references/mcp-tools.md)

  • The semantics pass re-acquires after perf_end, not inside the window. perf_run called ext.dusk.semantics_hold action=acquire before perf_end, so the acquire's frame, a rebuild of the whole semantics tree, was a frame of the session, and perf_end read env.semanticsEnabled after it: every semanticsOff repeat reported true. The session now closes first and the acquire follows, and it still runs when a step or perf_end throws. (lib/src/commands/dusk_perf_run_command.dart, lib/src/extensions/ext_semantics_hold.dart, doc/commands/dusk-perf-run.md, doc/reference/semantics-hold.md)

  • The semantics pass no longer reports a release that left semantics on as measured. On Flutter web the engine turns semantics on at the first semantics tree a real app sends and never turns it off, so once dusk had snapshotted the app, releasing dusk's handle left the tree on: a measured uptizm list scroll showed the SEMANTICS block on 58 of 60 semanticsOff frames, as many as the attribution series, under semanticsPass: "measured". The release answer gains heldByPlatform (platformDispatcher.semanticsEnabled), and a release that answers semanticsEnabled: true ends the pass: semanticsPass: "unsupported", no semanticsOff series, and a semanticsPassReason naming the platform and what holds semantics on (the web engine on chrome, an accessibility service on a device, or another SemanticsHandle). (lib/src/extensions/ext_semantics_hold.dart, lib/src/commands/dusk_perf_run_command.dart, lib/src/dusk_artisan_provider.dart, doc/commands/dusk-perf-run.md, doc/reference/semantics-hold.md, doc/mcp/tool-reference.md)

  • perf_run navigates once the app can route. ext.dusk.boot_id answers as soon as DuskPlugin.install() has run, which a host that installs dusk before its own boot (magic_devtools' documented order) reaches before runApp mounts its Router, so the first setup navigate after every hot restart answered navigated: false and every Chrome scenario that navigates failed at its first unit. A setup navigate now waits, up to 10 s, for get_routes to report a mounted Router, and holds a navigated: false against the Router for up to 3 s before failing: a Router that has only just mounted is still applying its first location when the navigate reads it (measured: false as it mounted, true 300 ms later). The route is never dispatched twice. (lib/src/commands/dusk_perf_run_command.dart, doc/commands/dusk-perf-run.md)

  • perf_run's post-idle route check can see a redirect. It compared get_routes's location before and after network idle, which a Router-based app answers as "" on both sides; it compares uri now, and the setup diagnostics quote it (route none (no Router mounted) when there is none). (lib/src/commands/dusk_perf_run_command.dart)

  • perf_end describes its own window. A list scroll on the Pixel 8 emulator drew 24 frames and the summary held 31: timings the engine had parked before perf_begin arrive after the buffer is cleared, and the idle frame perf_end draws to flush the tail is a frame of its own. Frames are now kept by vsyncStartUs inside the session window (a record without one is kept), the denominator stays the liveness advance read before the flush, and coverage.framesOutsideSession counts what was read and left out. vsyncStartUs against FlutterTimeline.now is unverified off the web; a disagreement shows as a large framesOutsideSession. (lib/src/extensions/ext_perf.dart, lib/src/utils/perf_insights.dart, doc/commands/dusk-perf-end.md)

  • perf_trace counts the frames it leaves out. A frame outside the session window was dropped silently; otherData.framesOutsideWindow now says how many. (lib/src/extensions/ext_perf_trace.dart, doc/reference/perf-trace.md)

  • ext.dusk.find_by_label finds labels in a running app. It walked rootPipelineOwner alone, so a view mounted under a child pipeline owner (the test harness, a multi-view app) matched nothing; it now walks every owner as observe and snap do. (lib/src/extensions/ext_wait_find.dart)

  • dusk:resize says what it can keep. Chrome drops Emulation.setDeviceMetricsOverride when the DevTools session that sent it detaches, whatever its parameters (measured against Chrome: 390 px inside the session, the page's own width right after), so the command set a viewport and exited, and the success line said it was set. A new --hold keeps the session open until Ctrl-C or until Chrome exits; without it the command now warns that the override ends with it. (lib/src/commands/dusk_resize_command.dart, doc/commands/index.md, doc/commands/dusk-screenshot.md)

  • perf_run setup failures say which screen the app was on. A wait_for_text that timed out, or a setup gesture that matched nothing, named only the text, so a navigate the router had dropped read the same as a slow page. Every setup failure but a restart's now ends with a Diagnostics: line: the route ext.dusk.get_routes answers, the last setup navigate's payload and the three newest ext.dusk.exceptions entries. A setup navigate answering navigated: false fails at once with its payload instead of being ignored, and after each one the runner waits for network idle and reads the route again, failing when the app has moved (an auth redirect after the first fetch). (lib/src/commands/dusk_perf_run_command.dart, doc/commands/dusk-perf-run.md)

  • perf_run waits up to 3 s for a target. A target the screen had not built yet failed the run as "matched nothing" on the first lookup; it is now looked up every 100 ms for up to 3 s (30 lookups at most) first. (lib/src/commands/dusk_perf_run_command.dart)

  • perf_run resolves outside the window what it can. Every target was resolved inside the measured window, so each lookup (and a wheel's hover) was session cost. A target only wait steps precede is now resolved before perf_begin, a wheel's hover point with it (PerfStepVerb.movesTargets); a target an earlier step can create or move, such as an option in the overlay a tap opens, still resolves just before its step. Each repeat in the run file gains resolves: [{step, verb, phase: beforeBegin|inWindow, resolveMs}], so the in-window cost is visible. (lib/src/commands/dusk_perf_run_command.dart, lib/src/perf/scenario.dart, doc/commands/dusk-perf-run.md)

  • The run file keeps the renderer the app reported. env.renderer was always overwritten with the run log scrape, which reads unknown on web, so the canvaskit or skwasm answer from rendererReader never reached the file. The app's answer now wins, and the scrape is used only when the app answers unknown. (lib/src/commands/dusk_perf_run_command.dart, doc/commands/dusk-perf-run.md)

  • perf_run setup waits survive a slow web restart. wait_for_text now waits in 5 s slices until its budget is spent: DWDS abandons any service extension call at 10 s, so one 15 s in-app wait failed as a -32603 whenever the app took longer than 10 s to show the text.

  • The actionability gate no longer refuses every ref once the soft keyboard has opened. The stable check measured the live rect against the rect the snapshot minted, so any reflow since the snapshot read as motion: on Android, the keyboard that fill itself opens failed the next fill and tap with not stable (rect changed by 21.0px) and kept failing until a re-snap, however still the page was. Both samples are now live, one frame apart, as the check was documented to do. The off-viewport check now measures against the view minus its viewInsets, and scrolls a target in when its center is outside that area: a submit button laid out under the keyboard used to pass as on-screen and then fail with obscured by other widget (top=_RenderInkFeatures), the Scaffold's own Material in the inset. With no Scrollable to bring it up, such a target is refused as off-viewport. Check order and reason substrings are unchanged. (lib/src/utils/actionability_gate.dart, doc/reference/actionability-gate.md)

  • A scenario wheel can scroll like a wheel. ticks: N sends N events of dx/dy, one frame apart, at the one point the step resolved. A single 1200 px event jumped the uptizm monitor list in one frame and the session measured five.

  • perf_end no longer loses the frames a session drew last. The web engine hands frame timings over only from inside a later frame, 100 ms after the previous hand-over, so the tail of a session stayed parked and a list scroll reported 1 of 5 frames. perf_end now waits past that interval and draws one idle frame (after the liveness verdict, so a hidden page still refuses) before it reads.

Added

  • hooks.after_campaign: a campaign's teardown. before_campaign could start services and nothing stopped them (a server bound to 0.0.0.0 outlived every campaign). after_campaign runs once after the final app stop, however the campaign ended, a failed before_campaign included (it may have started half of what the teardown stops), and not when the command refused its input or selected nothing. It gets the hook environment plus DUSK_PERF_STATUS=ok|failed; a failure exits 1, is listed in the envelope's errors and appended to campaign-<label>.err, and never hides the scenario results. (lib/src/perf/campaign.dart, lib/src/commands/dusk_perf_campaign_command.dart, doc/commands/dusk-perf-campaign.md)

  • ext.dusk.navigate answers exactPath. Its verdict stays a prefix match, the documented intent (a navigate to /monitors that shows /monitors/7, a default child, navigated), and the payload now says whether the Router's path is the route's own, query aside: exactPath: true for the route itself, false for a page under it and beside navigated: false. Additive; dusk:perf_run's setup keeps its own exact check. (lib/src/extensions/ext_navigation.dart, lib/src/dusk_artisan_provider.dart, doc/mcp/tool-reference.md, skills/fluttersdk-dusk/)

  • The run file's setup echoes its when guards. PerfSetupStep.toJson wrote a guarded step as if it always ran, so nothing in a run file said a guard stood in front of it. Each guard is now echoed once, marking where its group begins, as when: {text, unless_text, timeout_ms}, on the first step it governs (PerfSetupStep.toJson(opens:), PerfSetupGuard.toJson); an enclosing guard entered on the same step is its parent, and a bare verb there becomes {verb: null, when}. Unguarded steps are unchanged, and dusk:perf_compare reads no setup. (lib/src/perf/scenario.dart, doc/commands/dusk-perf-run.md)

  • dusk:perf_campaign: a whole perf campaign in one command. dusk:perf_campaign <campaign.yaml> --platform=<chrome|android|ios> filters the campaign's scenarios by platform and --only (nothing selected exits 1 before any hook or process runs), runs hooks.before_campaign through /bin/sh -c with DUSK_PERF_PLATFORM, DUSK_PERF_LABEL and DUSK_PERF_OUT, flutter pub get, and on Android boots android.avd, waits for sys.boot_completed, adb reverses each port and installs and grants a profile APK. Each scenario then gets up to retries + 1 attempts, each from a cold start: hooks.before_scenario (plus DUSK_PERF_SCENARIO), artisan stop, a wait until the old pid is gone and its ports are free, artisan start, a poll of ext.dusk.boot_id, after_start once a Router is mounted, and dusk:perf_run in-process. A failed attempt, whatever it threw, is recorded in <out>/<scenario>-<label>.err (with flutter-dev.log when the start failed) and the campaign goes on; the app is stopped at the end; one line per scenario (or a --json envelope) and exit 1 when any failed. Every line, .err, run file and envelope is masked for the campaign's secrets: the run file perf_run wrote is masked again for the after_start ones, a diagnostic exception message is masked before it is cut to 200 characters, and the --json envelopes of both commands are tree-masked and printed once, so a numeric secret cannot break their JSON. A flutter, adb or hook shell that cannot start stops the campaign with <exe> could not start, a --cdp-port that is not a port exits 1 before anything runs, an android.grant entry or Gradle applicationId that is not [A-Za-z0-9_.]+ is refused before it reaches the device shell, and a scenario that passed on a retry prints ok (attempt N, see <err>). dusk_perf_run takes variant. CLI only, no MCP tool. (lib/src/commands/dusk_perf_campaign_command.dart, lib/src/dusk_artisan_provider.dart, doc/commands/dusk-perf-campaign.md)

  • dusk:perf_run loads fragments and variants, runs when guards and masks secrets. Both the scenario and --against load through loadPerfScenarios, so include, ${...} and variants work in a run. --variant=<key> picks one variant (required when the file declares variants, the error listing the keys; refused when it does not; applied to --against too), and the run file carries a top-level variant when one was picked. An include's when guard polls ext.dusk.find --text every 250 ms up to timeout_ms, unless_text first: text runs the group, unless_text skips it, a timeout skips it without unless_text and fails the run naming both texts with one. Every line the command prints and every string in the run files it writes is masked for the loaded secrets, raw and JSON-encoded (PerfRedactor, RedactingOutput). The setup and step execution moved into the public PerfSetupRunner (run, awaitRouter, diagnose) and PerfActions, which the campaign command reuses; existing scenarios run exactly as before. (lib/src/commands/dusk_perf_run_command.dart, lib/src/perf/perf_actions.dart, lib/src/perf/perf_setup_runner.dart, lib/src/perf/perf_redaction.dart, doc/commands/dusk-perf-run.md)

  • Scenario fragments, interpolation, secrets and variants. loadPerfScenarios(path, env:) loads a scenario file and answers (scenarios, secrets): a setup entry - include: <path> with with: and when: flattens a fragment (params, when, steps, nested up to 8 deep, cycles refused) into setup, each flattened entry keeping its origin (fragments/login.yaml steps[1]) and the when guard as a PerfSetupGuard; every scalar is interpolated once (${param}, ${env.NAME}, $$); a value from the environment or a secret: true param may only be the text of a fill or type, is marked PerfStep.secret, written as *** by toJson and masked in every parse problem; variants: {<key>: {viewport, platforms, repeat, steps}} yields <name>-<key> scenarios validated one by one. loadPerfSetup reads a bare entry list (a campaign's after_start:) the same way. PerfScenario.parse refuses include and variants, and reads $$ as $. (lib/src/perf/scenario.dart, lib/src/perf/scenario_loader.dart, doc/commands/dusk-perf-run.md)

  • rendererReader, a seventh cross-package pointer, fills env.renderer in the perf_end report. Its default is dusk's own answer: skwasm or canvaskit on web, unknown elsewhere, where the launch log scrape of dusk:perf_run covers native. A host may reassign it. Exported from the barrel. (lib/src/utils/perf_readers.dart, lib/src/extensions/ext_perf.dart, lib/dusk.dart)

  • perfInteractionAt(int us), exported. The interaction whose window [startUs, closedAtUs ?? now] holds a recorded time, the newest when windows overlap; for a host that recorded a time without recording the gesture behind it. (lib/src/utils/perf_interaction.dart, lib/dusk.dart)

  • dusk:perf_insight / dusk_perf_insight / ext.dusk.perf_insight: drill into one insight of the last perf_end report. Takes id (and an optional token naming the report's session) and returns Title / Summary / Detail / EstimatedSavings / NextStep, where detail is the raw rows behind the insight: the worst frames with their self-time blocks, the frame-number gaps, the frames where one block weighed most, or what a coverage gap left out. Ids are assigned before the report cuts its list, so an insight counted in omitted.insights is still drillable. An unknown id, a stale token or a refused session answers an error naming what to read instead; an unknown id points at perf_end's insights[]. (lib/src/extensions/ext_perf.dart, lib/src/commands/dusk_perf_insight_command.dart, lib/src/dusk_artisan_provider.dart, doc/commands/dusk-perf-insight.md)

  • perf_begin gains mode: attribution|timing. timing touches no debugProfile* flag and no collection flag, and its report carries frame timings only: the profiling that makes attribution possible inflates every duration it wraps, so milliseconds are only comparable between timing sessions. phases with timing is rejected, as is an unknown mode, before anything is touched. (lib/src/extensions/ext_perf.dart, lib/src/commands/dusk_perf_begin_command.dart)

  • perfInsightContributors, a fifth cross-package pointer, and a pure buildPerfReport, both exported from the barrel. The host (magic_devtools) appends rules that need to know what a wind or magic counter means; a contributor that throws or returns a malformed insight becomes one warn insight (contributorErrors) and never costs the report. buildPerfReport(framePerf, extras, wind, env: ...) is the function the extension calls, so a host's conformance test builds the same report from the same maps. (lib/src/utils/perf_insights.dart, lib/src/utils/perf_readers.dart, lib/dusk.dart)

  • A closable interaction per dusk gesture, carried in the zone. While a perf session is open, every verb that dispatches into the app (tap, dblclick, triple_click, right_click, hover, drag, type, clear, fill, press_key, focus, blur, scroll, select_option, set_checkbox, navigate, navigate_back, dismiss_modals, reset_overlays) runs its dispatch inside runZoned(zoneValues: {#fluttersdk_interaction: handle}), so the Timers, Futures and subscriptions its callbacks start can name the gesture that caused them. PerfInteraction {id, verb, target, startUs, closedAtUs} closes at settle (no frame scheduled for 300 ms, or 5 s, or the session closing), and a closed handle reads as absent: a socket opened during a tap keeps its zone forever and would otherwise be attributed to it for good. Frame-zone work (builds, initState refetches, post-frame callbacks), which the zone cannot reach, joins through activeInteraction(). A verb dispatched inside another joins its interaction, so fill is one. Outside a session nothing changes: no handle, no zone, no timer; the actionability gate and gesture dispatch are untouched. PerfInteraction and activeInteraction are exported from the barrel. (lib/src/utils/perf_interaction.dart, the gesture extensions, lib/dusk.dart, doc/reference/perf-trace.md)

  • ext.dusk.perf_trace: the closed session's timeline as Chrome Trace Event JSON, which ui.perfetto.dev and chrome://tracing open as is. Interactions and frames (placed at vsyncStartUs) are X slices; host rows from the new perfTimelineReader pointer map by kind, a span with an id to an async b/e pair, one without to X, an instant to i, a counter to C. An X slice that would straddle another on its track moves to an overflow lane (frames (2)) rather than being trimmed. Only what started inside the session window is exported; a malformed host row is counted in otherData.skippedRows. Takes an optional token; answers an error before any session closed, for a stale token, while a newer session is open (its perf_begin cleared the buffers the trace reads), and when a host reader throws. dusk:perf_trace / dusk_perf_trace write it to a file. (lib/src/extensions/ext_perf_trace.dart, lib/src/utils/perf_readers.dart, lib/src/extensions/register_dusk_extensions.dart, doc/reference/perf-trace.md)

  • ext.dusk.perf_end accepts full: 'true', which lifts every cut: block rankings, counter breakdowns, route transitions and insights carry every row and omitted reads all zeros. For a runner that writes the report to a file; the default stays bounded to about 6 KB. buildPerfReport gains the same full flag. (lib/src/extensions/ext_perf.dart, lib/src/utils/perf_insights.dart, doc/commands/dusk-perf-end.md)

... (truncated)

Changelog

Sourced from fluttersdk_dusk's changelog.

[0.0.17] - 2026-09-29

Fixed

  • dusk:perf_campaign keeps the credentials it reads out of the processes it starts. Hooks got the whole inherited environment spread under DUSK_PERF_*, and the preparation processes inherited it too, so a server hooks.before_campaign backgrounded kept the password the campaign reads through ${env.*} for as long as it lived. The loader now names every variable a ${env.NAME} read (PerfLoadResult.envNames, PerfSetupLoadResult.envNames, PerfCampaign.secretEnvNames), and hooks and preparation processes run with includeParentEnvironment: false on the invoking environment minus those names. The filter is by name, not by value: CI=1 survives a secret that reads 1. artisan start and stop run in-process with no environment seam, so the flutter run, the Chrome and the Android adb force-stop they spawn still inherit the dispatcher's environment; the campaign doc says so. (lib/src/perf/scenario.dart, lib/src/perf/scenario_loader.dart, lib/src/perf/campaign.dart, lib/src/commands/dusk_perf_campaign_command.dart, doc/commands/dusk-perf-campaign.md)

  • perf_run's restart wait names the app's last answer. Since the boot wait became pollDuskBoot, a read could start after the pause that spent the budget, with no time left, and its TimeoutException replaced the app's own last error in the failure; no read starts once the budget is spent now. The campaign also survives a stale .err it cannot delete (reported, like a write it cannot make) and a flutter-dev.log with malformed UTF-8 (decoded leniently), both of which escaped the per-scenario catch and skipped after_campaign and the envelope. (lib/src/perf/perf_run_driver.dart, lib/src/commands/dusk_perf_campaign_command.dart)

  • dusk:perf_campaign --json prints its envelope however the campaign ends. A stop in the preparation (a hook, flutter pub get, an Android step) returned before the envelope, so a --json caller read nothing; a before_scenario stop listed only the scenarios tried. The envelope now lists every selected scenario, those never tried as status: "not_run" with attempts: 0, carries the stop sentence as stopped, and names anything that failed after the scenarios in errors. An artisan stop that fails after the campaign now exits 1 (it kept exit 0), and an .err that cannot be written inside the per-scenario catch is reported through the output instead of escaping it and ending the campaign before the app stop. (lib/src/commands/dusk_perf_campaign_command.dart, doc/commands/dusk-perf-campaign.md)

  • dusk:perf_campaign's after_start waits for the Router as long as its guards wait. The Router wait before after_start gave up at perf_run's 10 s, so a cold start that mounted its login screen at 15 s failed before a when guard written for 60 s ever polled. PerfSetupRunner.awaitRouter takes an optional budget (perf_run keeps its 10 s, kPerfRouterBudget), and the campaign passes the largest timeout_ms among the after_start guards, the 60 s guard default when there is none, never less than 10 s. (lib/src/perf/perf_setup_runner.dart, lib/src/commands/dusk_perf_campaign_command.dart, doc/commands/dusk-perf-campaign.md)

  • A secret shorter than 4 characters is a load problem. Every output is masked for every secret wherever its text appears, so a secret 1 or 80 masked that number in every log line, run file and envelope. loadPerfScenarios and loadPerfSetup now refuse a non-empty ${env.*} value or secret: true param under 4 characters (kPerfMinSecretLength), naming the variable or the param and its length, never the value. (lib/src/perf/scenario.dart, lib/src/perf/scenario_loader.dart, doc/commands/dusk-perf-run.md)

  • ext.dusk.navigate_back leaves a hidden shell branch alone. It popped the first poppable Navigator in tree order, offstage ones included, so in a go_router StatefulShellRoute (every branch kept alive, the inactive ones under Offstage and a disabled TickerMode) a page stacked on a hidden branch was popped while the visible branch stayed, and the answer said popped: true. A subtree under Offstage(offstage: true), TickerMode(enabled: false) or Visibility(visible: false) is no longer walked. (lib/src/extensions/ext_navigation.dart)

  • A perf_run setup navigate lands on its own path, and a late landing is on the record. The late-landing poll accepted a path under the route, so a dropped navigate to /monitors while a web hot restart had left the app on /monitors/<id> passed as landed; it now needs the exact path, query ignored. A unit whose setup relied on a late landing carries setupLandedLate: true in its repeats[] entry. (lib/src/commands/dusk_perf_run_command.dart)

  • perf_run's post-idle route check compares paths. It compared whole URIs, so a page that normalises its query after the first fetch (/monitors to /monitors?page=1) failed setup as moved. (lib/src/commands/dusk_perf_run_command.dart)

  • perf_trace cuts its frames by perf_end's rule. Under a clock mismatch (frames carry a timestamp and none lands in the window) perf_end keeps every frame while the trace dropped them all, so the two artifacts disagreed silently. The trace now shares perf_end's window cut, keeps the frames, and says so with otherData.sessionClockMismatch: true. buildPerfReport forwards sessionClockMismatch the way analysePerf takes it. (lib/src/extensions/ext_perf.dart, lib/src/extensions/ext_perf_trace.dart, lib/src/utils/perf_insights.dart)

  • The CDP emulation tools say how long their override lasts. dusk_resize_viewport and dusk_device_profile promised a resize, and neither can pass hold: each call opens its own CDP session and closes it on return, and Chrome drops the Emulation.* override with it. Both descriptors, both CLI descriptions and dusk:device's output now say the override lasts only as long as the call's CDP session (only dusk:device's window size stays), and dusk:resize reports "override sent" rather than "Viewport set to", pointing at --hold. (lib/src/dusk_artisan_provider.dart, lib/src/commands/dusk_resize_command.dart, lib/src/commands/dusk_device_command.dart)

  • ext.dusk.navigate_back pops a page stacked inside a shell. It popped the first Navigator in the tree, which in a go_router app is the root one, and that holds the shell alone: for a detail page pushed inside a ShellRoute (MagicRouter layouts, .stacked()) the pop did nothing, the answer was still navigatedBack: true, and get_routes's uri kept naming the detail page because it was still mounted. It pops the outermost Navigator that can pop instead, so a page pushed on the root is left before a shell's. uri itself was not stale: it follows the Router's report within the frames the handler awaits. The answer gains an additive popped, false when no Navigator could pop, so a no-op no longer reads as a pop. (lib/src/extensions/ext_navigation.dart, lib/src/dusk_artisan_provider.dart, doc/mcp/tool-reference.md, skills/fluttersdk-dusk/references/mcp-tools.md)

  • The semantics pass re-acquires after perf_end, not inside the window. perf_run called ext.dusk.semantics_hold action=acquire before perf_end, so the acquire's frame, a rebuild of the whole semantics tree, was a frame of the session, and perf_end read env.semanticsEnabled after it: every semanticsOff repeat reported true. The session now closes first and the acquire follows, and it still runs when a step or perf_end throws. (lib/src/commands/dusk_perf_run_command.dart, lib/src/extensions/ext_semantics_hold.dart, doc/commands/dusk-perf-run.md, doc/reference/semantics-hold.md)

  • The semantics pass no longer reports a release that left semantics on as measured. On Flutter web the engine turns semantics on at the first semantics tree a real app sends and never turns it off, so once dusk had snapshotted the app, releasing dusk's handle left the tree on: a measured uptizm list scroll showed the SEMANTICS block on 58 of 60 semanticsOff frames, as many as the attribution series, under semanticsPass: "measured". The release answer gains heldByPlatform (platformDispatcher.semanticsEnabled), and a release that answers semanticsEnabled: true ends the pass: semanticsPass: "unsupported", no semanticsOff series, and a semanticsPassReason naming the platform and what holds semantics on (the web engine on chrome, an accessibility service on a device, or another SemanticsHandle). (lib/src/extensions/ext_semantics_hold.dart, lib/src/commands/dusk_perf_run_command.dart, lib/src/dusk_artisan_provider.dart, doc/commands/dusk-perf-run.md, doc/reference/semantics-hold.md, doc/mcp/tool-reference.md)

  • perf_run navigates once the app can route. ext.dusk.boot_id answers as soon as DuskPlugin.install() has run, which a host that installs dusk before its own boot (magic_devtools' documented order) reaches before runApp mounts its Router, so the first setup navigate after every hot restart answered navigated: false and every Chrome scenario that navigates failed at its first unit. A setup navigate now waits, up to 10 s, for get_routes to report a mounted Router, and holds a navigated: false against the Router for up to 3 s before failing: a Router that has only just mounted is still applying its first location when the navigate reads it (measured: false as it mounted, true 300 ms later). The route is never dispatched twice. (lib/src/commands/dusk_perf_run_command.dart, doc/commands/dusk-perf-run.md)

  • perf_run's post-idle route check can see a redirect. It compared get_routes's location before and after network idle, which a Router-based app answers as "" on both sides; it compares uri now, and the setup diagnostics quote it (route none (no Router mounted) when there is none). (lib/src/commands/dusk_perf_run_command.dart)

  • perf_end describes its own window. A list scroll on the Pixel 8 emulator drew 24 frames and the summary held 31: timings the engine had parked before perf_begin arrive after the buffer is cleared, and the idle frame perf_end draws to flush the tail is a frame of its own. Frames are now kept by vsyncStartUs inside the session window (a record without one is kept), the denominator stays the liveness advance read before the flush, and coverage.framesOutsideSession counts what was read and left out. vsyncStartUs against FlutterTimeline.now is unverified off the web; a disagreement shows as a large framesOutsideSession. (lib/src/extensions/ext_perf.dart, lib/src/utils/perf_insights.dart, doc/commands/dusk-perf-end.md)

  • perf_trace counts the frames it leaves out. A frame outside the session window was dropped silently; otherData.framesOutsideWindow now says how many. (lib/src/extensions/ext_perf_trace.dart, doc/reference/perf-trace.md)

  • ext.dusk.find_by_label finds labels in a running app. It walked rootPipelineOwner alone, so a view mounted under a child pipeline owner (the test harness, a multi-view app) matched nothing; it now walks every owner as observe and snap do. (lib/src/extensions/ext_wait_find.dart)

  • dusk:resize says what it can keep. Chrome drops Emulation.setDeviceMetricsOverride when the DevTools session that sent it detaches, whatever its parameters (measured against Chrome: 390 px inside the session, the page's own width right after), so the command set a viewport and exited, and the success line said it was set. A new --hold keeps the session open until Ctrl-C or until Chrome exits; without it the command now warns that the override ends with it. (lib/src/commands/dusk_resize_command.dart, doc/commands/index.md, doc/commands/dusk-screenshot.md)

  • perf_run setup failures say which screen the app was on. A wait_for_text that timed out, or a setup gesture that matched nothing, named only the text, so a navigate the router had dropped read the same as a slow page. Every setup failure but a restart's now ends with a Diagnostics: line: the route ext.dusk.get_routes answers, the last setup navigate's payload and the three newest ext.dusk.exceptions entries. A setup navigate answering navigated: false fails at once with its payload instead of being ignored, and after each one the runner waits for network idle and reads the route again, failing when the app has moved (an auth redirect after the first fetch). (lib/src/commands/dusk_perf_run_command.dart, doc/commands/dusk-perf-run.md)

  • perf_run waits up to 3 s for a target. A target the screen had not built yet failed the run as "matched nothing" on the first lookup; it is now looked up every 100 ms for up to 3 s (30 lookups at most) first. (lib/src/commands/dusk_perf_run_command.dart)

  • perf_run resolves outside the window what it can. Every target was resolved inside the measured window, so each lookup (and a wheel's hover) was session cost. A target only wait steps precede is now resolved before perf_begin, a wheel's hover point with it (PerfStepVerb.movesTargets); a target an earlier step can create or move, such as an option in the overlay a tap opens, still resolves just before its step. Each repeat in the run file gains resolves: [{step, verb, phase: beforeBegin|inWindow, resolveMs}], so the in-window cost is visible. (lib/src/commands/dusk_perf_run_command.dart, lib/src/perf/scenario.dart, doc/commands/dusk-perf-run.md)

  • The run file keeps the renderer the app reported. env.renderer was always overwritten with the run log scrape, which reads unknown on web, so the canvaskit or skwasm answer from rendererReader never reached the file. The app's answer now wins, and the scrape is used only when the app answers unknown. (lib/src/commands/dusk_perf_run_command.dart, doc/commands/dusk-perf-run.md)

  • perf_run setup waits survive a slow web restart. wait_for_text now waits in 5 s slices until its budget is spent: DWDS abandons any service extension call at 10 s, so one 15 s in-app wait failed as a -32603 whenever the app took longer than 10 s to show the text.

  • The actionability gate no longer refuses every ref once the soft keyboard has opened. The stable check measured the live rect against the rect the snapshot minted, so any reflow since the snapshot read as motion: on Android, the keyboard that fill itself opens failed the next fill and tap with not stable (rect changed by 21.0px) and kept failing until a re-snap, however still the page was. Both samples are now live, one frame apart, as the check was documented to do. The off-viewport check now measures against the view minus its viewInsets, and scrolls a target in when its center is outside that area: a submit button laid out under the keyboard used to pass as on-screen and then fail with obscured by other widget (top=_RenderInkFeatures), the Scaffold's own Material in the inset. With no Scrollable to bring it up, such a target is refused as off-viewport. Check order and reason substrings are unchanged. (lib/src/utils/actionability_gate.dart, doc/reference/actionability-gate.md)

  • A scenario wheel can scroll like a wheel. ticks: N sends N events of dx/dy, one frame apart, at the one point the step resolved. A single 1200 px event jumped the uptizm monitor list in one frame and the session measured five.

  • perf_end no longer loses the frames a session drew last. The web engine hands frame timings over only from inside a later frame, 100 ms after the previous hand-over, so the tail of a session stayed parked and a list scroll reported 1 of 5 frames. perf_end now waits past that interval and draws one idle frame (after the liveness verdict, so a hidden page still refuses) before it reads.

Added

  • hooks.after_campaign: a campaign's teardown. before_campaign could start services and nothing stopped them (a server bound to 0.0.0.0 outlived every campaign). after_campaign runs once after the final app stop, however the campaign ended, a failed before_campaign included (it may have started half of what the teardown stops), and not when the command refused its input or selected nothing. It gets the hook environment plus DUSK_PERF_STATUS=ok|failed; a failure exits 1, is listed in the envelope's errors and appended to campaign-<label>.err, and never hides the scenario results. (lib/src/perf/campaign.dart, lib/src/commands/dusk_perf_campaign_command.dart, doc/commands/dusk-perf-campaign.md)

  • ext.dusk.navigate answers exactPath. Its verdict stays a prefix match, the documented intent (a navigate to /monitors that shows /monitors/7, a default child, navigated), and the payload now says whether the Router's path is the route's own, query aside: exactPath: true for the route itself, false for a page under it and beside navigated: false. Additive; dusk:perf_run's setup keeps its own exact check. (lib/src/extensions/ext_navigation.dart, lib/src/dusk_artisan_provider.dart, doc/mcp/tool-reference.md, skills/fluttersdk-dusk/)

  • The run file's setup echoes its when guards. PerfSetupStep.toJson wrote a guarded step as if it always ran, so nothing in a run file said a guard stood in front of it. Each guard is now echoed once, marking where its group begins, as when: {text, unless_text, timeout_ms}, on the first step it governs (PerfSetupStep.toJson(opens:), PerfSetupGuard.toJson); an enclosing guard entered on the same step is its parent, and a bare verb there becomes {verb: null, when}. Unguarded steps are unchanged, and dusk:perf_compare reads no setup. (lib/src/perf/scenario.dart, doc/commands/dusk-perf-run.md)

  • dusk:perf_campaign: a whole perf campaign in one command. dusk:perf_campaign <campaign.yaml> --platform=<chrome|android|ios> filters the campaign's scenarios by platform and --only (nothing selected exits 1 before any hook or process runs), runs hooks.before_campaign through /bin/sh -c with DUSK_PERF_PLATFORM, DUSK_PERF_LABEL and DUSK_PERF_OUT, flutter pub get, and on Android boots android.avd, waits for sys.boot_completed, adb reverses each port and installs and grants a profile APK. Each scenario then gets up to retries + 1 attempts, each from a cold start: hooks.before_scenario (plus DUSK_PERF_SCENARIO), artisan stop, a wait until the old pid is gone and its ports are free, artisan start, a poll of ext.dusk.boot_id, after_start once a Router is mounted, and dusk:perf_run in-process. A failed attempt, whatever it threw, is recorded in <out>/<scenario>-<label>.err (with flutter-dev.log when the start failed) and the campaign goes on; the app is stopped at the end; one line per scenario (or a --json envelope) and exit 1 when any failed. Every line, .err, run file and envelope is masked for the campaign's secrets: the run file perf_run wrote is masked again for the after_start ones, a diagnostic exception message is masked before it is cut to 200 characters, and the --json envelopes of both commands are tree-masked and printed once, so a numeric secret cannot break their JSON. A flutter, adb or hook shell that cannot start stops the campaign with <exe> could not start, a --cdp-port that is not a port exits 1 before anything runs, an android.grant entry or Gradle applicationId that is not [A-Za-z0-9_.]+ is refused before it reaches the device shell, and a scenario that passed on a retry prints ok (attempt N, see <err>). dusk_perf_run takes variant. CLI only, no MCP tool. (lib/src/commands/dusk_perf_campaign_command.dart, lib/src/dusk_artisan_provider.dart, doc/commands/dusk-perf-campaign.md)

  • dusk:perf_run loads fragments and variants, runs when guards and masks secrets. Both the scenario and --against load through loadPerfScenarios, so include, ${...} and variants work in a run. --variant=<key> picks one variant (required when the file declares variants, the error listing the keys; refused when it does not; applied to --against too), and the run file carries a top-level variant when one was picked. An include's when guard polls ext.dusk.find --text every 250 ms up to timeout_ms, unless_text first: text runs the group, unless_text skips it, a timeout skips it without unless_text and fails the run naming both texts with one. Every line the command prints and every string in the run files it writes is masked for the loaded secrets, raw and JSON-encoded (PerfRedactor, RedactingOutput). The setup and step execution moved into the public PerfSetupRunner (run, awaitRouter, diagnose) and PerfActions, which the campaign command reuses; existing scenarios run exactly as before. (lib/src/commands/dusk_perf_run_command.dart, lib/src/perf/perf_actions.dart, lib/src/perf/perf_setup_runner.dart, lib/src/perf/perf_redaction.dart, doc/commands/dusk-perf-run.md)

  • Scenario fragments, interpolation, secrets and variants. loadPerfScenarios(path, env:) loads a scenario file and answers (scenarios, secrets): a setup entry - include: <path> with with: and when: flattens a fragment (params, when, steps, nested up to 8 deep, cycles refused) into setup, each flattened entry keeping its origin (fragments/login.yaml steps[1]) and the when guard as a PerfSetupGuard; every scalar is interpolated once (${param}, ${env.NAME}, $$); a value from the environment or a secret: true param may only be the text of a fill or type, is marked PerfStep.secret, written as *** by toJson and masked in every parse problem; variants: {<key>: {viewport, platforms, repeat, steps}} yields <name>-<key> scenarios validated one by one. loadPerfSetup reads a bare entry list (a campaign's after_start:) the same way. PerfScenario.parse refuses include and variants, and reads $$ as $. (lib/src/perf/scenario.dart, lib/src/perf/scenario_loader.dart, doc/commands/dusk-perf-run.md)

  • rendererReader, a seventh cross-package pointer, fills env.renderer in the perf_end report. Its default is dusk's own answer: skwasm or canvaskit on web, unknown elsewhere, where the launch log scrape of dusk:perf_run covers native. A host may reassign it. Exported from the barrel. (lib/src/utils/perf_readers.dart, lib/src/extensions/ext_perf.dart, lib/dusk.dart)

  • perfInteractionAt(int us), exported. The interaction whose window [startUs, closedAtUs ?? now] holds a recorded time, the newest when windows overlap; for a host that recorded a time without recording the gesture behind it. (lib/src/utils/perf_interaction.dart, lib/dusk.dart)

  • dusk:perf_insight / dusk_perf_insight / ext.dusk.perf_insight: drill into one insight of the last perf_end report. Takes id (and an optional token naming the report's session) and returns Title / Summary / Detail / EstimatedSavings / NextStep, where detail is the raw rows behind the insight: the worst frames with their self-time blocks, the frame-number gaps, the frames where one block weighed most, or what a coverage gap left out. Ids are assigned before the report cuts its list, so an insight counted in omitted.insights is still drillable. An unknown id, a stale token or a refused session answers an error naming what to read instead; an unknown id points at perf_end's insights[]. (lib/src/extensions/ext_perf.dart, lib/src/commands/dusk_perf_insight_command.dart, lib/src/dusk_artisan_provider.dart, doc/commands/dusk-perf-insight.md)

  • perf_begin gains mode: attribution|timing. timing touches no debugProfile* flag and no collection flag, and its report carries frame timings only: the profiling that makes attribution possible inflates every duration it wraps, so milliseconds are only comparable between timing sessions. phases with timing is rejected, as is an unknown mode, before anything is touched. (lib/src/extensions/ext_perf.dart, lib/src/commands/dusk_perf_begin_command.dart)

  • perfInsightContributors, a fifth cross-package pointer, and a pure buildPerfReport, both exported from the barrel. The host (magic_devtools) appends rules that need to know what a wind or magic counter means; a contributor that throws or returns a malformed insight becomes one warn insight (contributorErrors) and never costs ...

    Description has been truncated

Bumps the fluttersdk group with 6 updates:

| Package | From | To |
| --- | --- | --- |
| [fluttersdk_dusk](https://github.com/fluttersdk/dusk) | `0.0.16` | `0.0.17` |
| [fluttersdk_telescope](https://github.com/fluttersdk/telescope) | `0.0.7` | `0.0.9` |
| [magic](https://github.com/fluttersdk/magic) | `0.0.22` | `0.0.26` |
| [magic_deeplink](https://github.com/fluttersdk/magic_deeplink) | `0.1.4` | `0.1.5` |
| [magic_devtools](https://github.com/fluttersdk/magic_devtools) | `0.0.7` | `0.0.8` |
| [magic_notifications](https://github.com/fluttersdk/magic_notifications) | `0.3.5` | `0.3.7` |


Updates `fluttersdk_dusk` from 0.0.16 to 0.0.17
- [Release notes](https://github.com/fluttersdk/dusk/releases)
- [Changelog](https://github.com/fluttersdk/dusk/blob/master/CHANGELOG.md)
- [Commits](fluttersdk/dusk@0.0.16...0.0.17)

Updates `fluttersdk_telescope` from 0.0.7 to 0.0.9
- [Release notes](https://github.com/fluttersdk/telescope/releases)
- [Changelog](https://github.com/fluttersdk/telescope/blob/master/CHANGELOG.md)
- [Commits](fluttersdk/telescope@0.0.7...0.0.9)

Updates `magic` from 0.0.22 to 0.0.26
- [Release notes](https://github.com/fluttersdk/magic/releases)
- [Changelog](https://github.com/fluttersdk/magic/blob/master/CHANGELOG.md)
- [Commits](fluttersdk/magic@0.0.22...0.0.26)

Updates `magic_deeplink` from 0.1.4 to 0.1.5
- [Release notes](https://github.com/fluttersdk/magic_deeplink/releases)
- [Changelog](https://github.com/fluttersdk/magic_deeplink/blob/master/CHANGELOG.md)
- [Commits](fluttersdk/magic_deeplink@0.1.4...0.1.5)

Updates `magic_devtools` from 0.0.7 to 0.0.8
- [Changelog](https://github.com/fluttersdk/magic_devtools/blob/master/CHANGELOG.md)
- [Commits](fluttersdk/magic_devtools@0.0.7...0.0.8)

Updates `magic_notifications` from 0.3.5 to 0.3.7
- [Release notes](https://github.com/fluttersdk/magic_notifications/releases)
- [Changelog](https://github.com/fluttersdk/magic_notifications/blob/master/CHANGELOG.md)
- [Commits](fluttersdk/magic_notifications@0.3.5...0.3.7)

---
updated-dependencies:
- dependency-name: fluttersdk_dusk
  dependency-version: 0.0.17
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: fluttersdk
- dependency-name: fluttersdk_telescope
  dependency-version: 0.0.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: fluttersdk
- dependency-name: magic
  dependency-version: 0.0.26
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: fluttersdk
- dependency-name: magic_deeplink
  dependency-version: 0.1.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: fluttersdk
- dependency-name: magic_devtools
  dependency-version: 0.0.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: fluttersdk
- dependency-name: magic_notifications
  dependency-version: 0.3.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: fluttersdk
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot dependabot Bot added dependencies Pull requests that update a dependency file pub Dart pub.dev package updates labels Oct 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file pub Dart pub.dev package updates

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant