Skip to content

0.0.16

Choose a tag to compare

@github-actions github-actions released this 18 Sep 23:07
· 8 commits to master since this release
f7cc3cb

Fixed

  • A pattern injection that matches nothing now fails the install instead of reporting Success over a file it never touched. ConfigEditor.insertCodeAfterPattern and insertCodeBeforePattern ended in a bare if (match != null) { write } with no else, so a miss wrote nothing and returned normally. InstallTransaction had no way to tell an applied injection from a skipped one, added the target to _helperWrittenTargets either way, and plugin:install printed Success. Both helpers return bool now and the transaction returns Error naming the target file.

    The distinction that had to survive: an idempotent skip, where the code is already present, still answers true. Collapsing the two would make a re-run of plugin:install fail on work it had already done.

    Measured in a consumer app. injectProvider's regex required the providers-list entry to read exactly (app) => XServiceProvider(app),, and that app's lib/config/app.dart writes (MagicApp app) =>. The type annotation is optional in Dart and both spellings are in use across real apps, so the install matched nothing, registered no provider, booted no plugin, and reported Success. plugin_installer.dart's own docblock described this as "helper behaviour" and told plugin authors to document the requirement upstream; it is a defect rather than a contract.

  • A pattern injection is checked BEFORE anything is staged, so a miss costs nothing. Returning the Error from inside the stage loop was not enough: helper-backed ops write through dart:io during staging and sit outside the .tmp rollback, and the install record plugin:uninstall reads is only written in phase 6. So a miss found mid-loop stranded an orphan import, and possibly a pubspec entry, with nothing recorded to reverse them. _assertPatternsResolvable runs over the same files first and names every offending op rather than the first. An idempotent re-run still commits, because the preflight treats "the code is already there" as resolvable.

  • InjectBeforePattern and InjectAfterPattern take an optional fallbackPattern, tried only when the primary matches nothing. Without it, converting the silent no-op into a hard failure widened the blast radius rather than narrowing it: a host whose 'providers': [] or configFactories: [] is empty matched nothing before and installed quietly, and would now fail outright. The fallback anchors on the opening bracket, which is the same two-step make:command already does by hand (make_command_command.dart:215-228). One regex with an alternation cannot express it, because firstMatch scans by position and the opening bracket always precedes the last entry, so every injection would land at the top of a populated list.

  • injectConfigFactory's regex no longer requires the entry to end in Config,. A host is free to name its own factory, and () => appSettings, is a legal entry the old pattern refused. Widening injectProvider alone would have left this one silently broken and then loudly broken.

  • Both composite injections now match from their list's own key forward, so the insertion point cannot land in a different list. Dropping the Config, requirement removed the only thing tying injectConfigFactory's primary to configFactories: () => \w+, before a ] describes any zero-argument closure that is last in any list, and firstMatch scans by position, so an earlier list anywhere in lib/main.dart won and the factory was appended to THAT list. An empty configFactories: [] was the same defect wearing a second face, and the worse one: the primary matched a later list's last entry, so the fallback that exists for the empty case never ran. 'providers': [ and configFactories: [ now open both patterns and ] is excluded from the gap, which keeps the scan inside the list the anchor opened and turns an empty list back into the non-match the fallback is waiting for. injectProvider takes the same anchor, where \w+ServiceProvider(app), had been an accidental guard rather than a deliberate one.

  • The append lookahead skips a trailing or own-line // comment between the last entry and the closing bracket. A comment trailing the entry on the same line ((app) => AppServiceProvider(app), // core) defeated it, and so did a comment on its own line below, which is the shape a scaffold placeholder takes (// add plugin providers here). Either one sent the injection to the fallback, which prepends: correct Dart, wrong position. A /* block */ comment in that position is still a miss and still prepends, unchanged from before. It cannot skip a real entry, because every line it consumes has to be whitespace or a // comment through to the newline, so the append still pins to the LAST entry.

  • The providers key may be written with either quote. Anchoring the primary on 'providers' made its quoting load-bearing for the first time, so a host writing "providers": [ had BOTH patterns miss and the preflight fail an install that worked before the anchor. prefer_single_quotes makes that shape uncommon rather than impossible, and a character class costs one character in each pattern.

  • injectProvider matches both spellings of a providers-list entry. The parameter's type is optional in the pattern now, so (app) => XServiceProvider(app), and (MagicApp app) => XServiceProvider(app), both match. The lookahead that anchors the append to the LAST entry before ] is asserted separately, because widening a pattern is the easy way to lose it. That assertion is now on where the match ENDS rather than on what it spans, since an anchored pattern necessarily carries every earlier entry in its match text while match.end stays the byte the code is written at. (lib/src/helpers/config_editor.dart, lib/src/installer/install_operation.dart, lib/src/installer/install_transaction.dart, lib/src/installer/plugin_installer.dart, test/helpers/config_editor_match_report_test.dart, test/installer/inject_provider_shapes_test.dart, doc/plugins/installer-dsl.md, skills/fluttersdk-artisan/references/cli-commands.md)

Added

  • server.json, the manifest that lists this package on the official MCP registry. The ecosystem was absent from every MCP directory, so an agent looking for a Dart CLI and MCP substrate had no way to find it. The entry carries repository and websiteUrl and deliberately no packages block: registryType documents npm, pypi, oci, nuget and mcpb with no pub equivalent, and both packages and remotes are optional on ServerDetail, which requires only name, description and version. This is a sixth hand-maintained copy of the package version and the only one nothing guarded, so test/server_json_version_test.dart asserts it against pubspec.yaml in the same spirit as test/mcp/mcp_server_version_test.dart, and additionally pins the schema's 100 character description cap. Excluded from the pub archive for the same reason codecov.yml is. (server.json, .pubignore, test/server_json_version_test.dart)