From 85fed52ce284a310270928f39ff5ad4f000613f7 Mon Sep 17 00:00:00 2001 From: Anilcan Cakir Date: Mon, 6 Apr 2026 01:12:26 +0300 Subject: [PATCH 1/2] chore(release): 1.0.0-alpha.7 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Broadcasting module (Echo facade, BroadcastManager, ReverbBroadcastDriver), router observers, network driver plugin hook, custom log drivers. Updates: facade count 16→17, test count 453→858, CHANGELOG PR refs, docs/skills/README synced with Echo facade and broadcasting docs. --- .github/copilot-instructions.md | 5 ++-- CHANGELOG.md | 10 ++++---- CLAUDE.md | 9 ++++--- README.md | 7 +++--- example/pubspec.lock | 2 +- example/pubspec.yaml | 2 +- pubspec.yaml | 2 +- skills/magic-framework/SKILL.md | 2 +- .../magic-framework/references/facades-api.md | 24 ++++++++++++++++++ .../references/testing-patterns.md | 25 +++++++++++++++++++ 10 files changed, 70 insertions(+), 18 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 53f5f60..71b033c 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -14,8 +14,9 @@ lib/ ├── config/ # Default configs └── src/ ├── foundation/ # MagicApp (IoC), Magic (bootstrap), ConfigRepository, Env - ├── facades/ # 16 facades: Auth, Cache, Config, Crypt, DB, Event, Gate, Http, Lang, Launch, Log, Pick, Route, Schema, Storage, Vault + ├── facades/ # 17 facades: Auth, Cache, Config, Crypt, DB, Echo, Event, Gate, Http, Lang, Launch, Log, Pick, Route, Schema, Storage, Vault ├── auth/ # AuthManager, guards, events + ├── broadcasting/ # BroadcastManager, Echo facade, Reverb/Null drivers ├── cache/ # CacheManager, drivers (memory, file) ├── database/ # Eloquent ORM, QueryBuilder, migrations, seeders, factories ├── http/ # MagicController, middleware pipeline, Kernel @@ -48,7 +49,7 @@ lib/ | Facade call before `Magic.init()` | Always `await Magic.init()` in `main()` first | | Missing `Auth.manager.setUserFactory()` | Must call in boot phase | | Forgetting test reset | `MagicApp.reset()` + `Magic.flush()` in every `setUp()` | -| `EncryptionServiceProvider` / `LaunchServiceProvider` | NOT auto-registered -- add explicitly | +| `BroadcastServiceProvider` / `EncryptionServiceProvider` / `LaunchServiceProvider` | NOT auto-registered -- add explicitly | | `ValidatesRequests` import | Lives in `concerns/`, not `http/` | | `Event.register()` takes factories | `List`, not listener instances | diff --git a/CHANGELOG.md b/CHANGELOG.md index f3fc621..c438cc9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,13 +2,13 @@ All notable changes to this project will be documented in this file. -## [Unreleased] +## [1.0.0-alpha.7] - 2026-04-06 ### ✨ Features -- **Broadcasting**: `Echo` facade, `BroadcastManager`, `ReverbBroadcastDriver` (Pusher-compatible WebSocket with reconnection, dedup, heartbeat), `NullBroadcastDriver`, `BroadcastInterceptor` pipeline, `FakeBroadcastManager`, `BroadcastServiceProvider`. Laravel Echo equivalent for real-time channels. (#37) -- **Router Observers**: `MagicRouter.instance.addObserver()` enables NavigatorObserver integration for analytics/monitoring (Sentry, Firebase Analytics, custom observers). Observers are passed to GoRouter automatically. (#31) -- **Network Driver Plugin Hook**: `DioNetworkDriver.configureDriver()` exposes the underlying Dio instance for SDK integrations (sentry_dio, certificate pinning, custom adapters). (#32) -- **Custom Log Drivers**: `LogManager.extend()` enables custom LoggerDriver registration (Sentry, file, Slack). Config-driven resolution with built-in override support. (#33) +- **Broadcasting**: `Echo` facade, `BroadcastManager`, `ReverbBroadcastDriver` (Pusher-compatible WebSocket with reconnection, dedup, heartbeat), `NullBroadcastDriver`, `BroadcastInterceptor` pipeline, `FakeBroadcastManager`, `BroadcastServiceProvider`. Laravel Echo equivalent for real-time channels. (#38) +- **Router Observers**: `MagicRouter.instance.addObserver()` enables NavigatorObserver integration for analytics/monitoring (Sentry, Firebase Analytics, custom observers). Observers are passed to GoRouter automatically. (#34) +- **Network Driver Plugin Hook**: `DioNetworkDriver.configureDriver()` exposes the underlying Dio instance for SDK integrations (sentry_dio, certificate pinning, custom adapters). (#35) +- **Custom Log Drivers**: `LogManager.extend()` enables custom LoggerDriver registration (Sentry, file, Slack). Config-driven resolution with built-in override support. (#36) ## [1.0.0-alpha.6] - 2026-04-05 diff --git a/CLAUDE.md b/CLAUDE.md index caea240..213d6df 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -24,11 +24,12 @@ Laravel-inspired Flutter framework with Facades, Eloquent ORM, Service Providers ``` lib/ ├── magic.dart # Barrel export (public API) -├── config/ # Default configs (app, auth, cache, database, view) +├── config/ # Default configs (app, auth, broadcasting, cache, database, view) └── src/ ├── foundation/ # MagicApp (IoC), Magic (bootstrap), ConfigRepository, Env - ├── facades/ # 16 facades: Auth, Cache, Config, Crypt, DB, Event, Gate, Http, Lang, Launch, Log, Pick, Route, Schema, Storage, Vault + ├── facades/ # 17 facades: Auth, Cache, Config, Crypt, DB, Echo, Event, Gate, Http, Lang, Launch, Log, Pick, Route, Schema, Storage, Vault ├── auth/ # AuthManager, guards (Bearer, BasicAuth, ApiKey), events + ├── broadcasting/ # BroadcastManager, Echo facade, Reverb/Null drivers, interceptors ├── cache/ # CacheManager, drivers (memory, file) ├── database/ # Eloquent ORM, QueryBuilder, migrations, seeders, factories ├── encryption/ # EncryptionServiceProvider (NOT auto-registered) @@ -71,7 +72,7 @@ This project follows strict **Test-Driven Development**. Every feature, fix, or **Rules:** - No production code without a failing test first -- Run `flutter test` after every change — all 453+ tests must stay green +- Run `flutter test` after every change — all 858+ tests must stay green - Run `dart analyze` after every change — zero warnings, zero errors - Run `dart format .` before committing — zero formatting issues - `dart pub publish --dry-run` must pass before any release @@ -93,7 +94,7 @@ This project follows strict **Test-Driven Development**. Every feature, fix, or | Facade call before `Magic.init()` | Always `await Magic.init()` in `main()` first | | Missing `Auth.manager.setUserFactory()` | Must call in boot phase — auth won't work without it | | Forgetting test reset | `MagicApp.reset()` + `Magic.flush()` in every `setUp()` | -| `EncryptionServiceProvider` / `LaunchServiceProvider` | NOT auto-registered — add explicitly to config providers | +| `BroadcastServiceProvider` / `EncryptionServiceProvider` / `LaunchServiceProvider` | NOT auto-registered — add explicitly to config providers | | `configFactories` vs `configs` param | Use `configFactories` for configs needing `Env.get()` | | `Event.register()` takes factories | `List`, not listener instances | | `routerConfig` before init | Only accessible after `Magic.init()` completes | diff --git a/README.md b/README.md index 430525d..161da2f 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ final user = await User.find(1); | | Feature | Description | |:--|:--------|:------------| | 🏗️ | **IoC Container** | Service Container with singleton, bind, and instance registration | -| 🎭 | **16 Facades** | `Auth`, `Http`, `Cache`, `DB`, `Event`, `Gate`, `Log`, `Route`, `Lang`, `Storage`, `Vault`, `Crypt` and more | +| 🎭 | **17 Facades** | `Auth`, `Http`, `Cache`, `DB`, `Echo`, `Event`, `Gate`, `Log`, `Route`, `Lang`, `Storage`, `Vault`, `Crypt` and more | | 🗄️ | **Eloquent ORM** | Models, QueryBuilder, migrations, seeders, factories — hybrid API + SQLite persistence | | 🛣️ | **Routing** | GoRouter integration with middleware, named routes, and context-free navigation | | 🔐 | **Authentication** | Token-based auth with guards (Bearer, BasicAuth, ApiKey), session restore, auto-refresh | @@ -423,7 +423,7 @@ void main() { } ``` -All facades support `fake()` / `unfake()`: `Http`, `Auth`, `Cache`, `Vault`, `Log`. Each fake records operations and exposes assertion helpers. +All facades support `fake()` / `unfake()`: `Http`, `Auth`, `Cache`, `Vault`, `Log`, `Echo`. Each fake records operations and exposes assertion helpers. ## Architecture @@ -433,7 +433,7 @@ Magic.init() → Env.load() → configFactories → providers register() → pro ``` lib/ -├── config/ # Configuration files (app, auth, cache, database) +├── config/ # Configuration files (app, auth, broadcasting, cache, database) ├── app/ │ ├── controllers/ # Request handlers (MagicController) │ ├── models/ # Eloquent models @@ -462,6 +462,7 @@ Full docs at **[magic.fluttersdk.com](https://magic.fluttersdk.com)**. | [HTTP Client](https://magic.fluttersdk.com/basics/http-client) | Network requests | | [Middleware](https://magic.fluttersdk.com/basics/middleware) | Request pipeline | | [Forms](https://magic.fluttersdk.com/basics/forms) | Form handling and validation | +| [Broadcasting](https://magic.fluttersdk.com/digging-deeper/broadcasting) | Real-time WebSocket channels | ## AI Agent Integration diff --git a/example/pubspec.lock b/example/pubspec.lock index 8074922..84e9137 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -507,7 +507,7 @@ packages: path: ".." relative: true source: path - version: "1.0.0-alpha.6" + version: "1.0.0-alpha.7" magic_cli: dependency: transitive description: diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 40d88fb..bf4aba2 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -36,7 +36,7 @@ dependencies: cupertino_icons: ^1.0.9 magic: path: .. - version: 1.0.0-alpha.6 + version: 1.0.0-alpha.7 dev_dependencies: flutter_test: diff --git a/pubspec.yaml b/pubspec.yaml index 7fbec24..7b8858c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: magic description: "A Laravel-inspired Flutter framework with Eloquent ORM, routing, and MVC architecture." -version: 1.0.0-alpha.6 +version: 1.0.0-alpha.7 homepage: https://magic.fluttersdk.com repository: https://github.com/fluttersdk/magic issue_tracker: https://github.com/fluttersdk/magic/issues diff --git a/skills/magic-framework/SKILL.md b/skills/magic-framework/SKILL.md index 67d0872..a220ec1 100644 --- a/skills/magic-framework/SKILL.md +++ b/skills/magic-framework/SKILL.md @@ -617,7 +617,7 @@ Official plugins extending Magic Framework. Each has its own package, service pr | File | Content | Load When | |------|---------|-----------| | `references/bootstrap-lifecycle.md` | Magic.init 7-step sequence, IoC API, ServiceProvider register/boot, Env/Config, Kernel, MagicApplication | Setting up app bootstrap, creating providers, or configuring environment | -| `references/facades-api.md` | All 16 facades with method signatures and return types | Looking up any facade method signature or return type | +| `references/facades-api.md` | All 17 facades with method signatures and return types | Looking up any facade method signature or return type | | `references/eloquent-orm.md` | Model definition, attributes, casts, relations, `InteractsWithPersistence`, QueryBuilder, migrations, Blueprint | Working with models, database queries, or migrations | | `references/controllers-views.md` | MagicController, MagicStateMixin, RxStatus, MagicView, MagicStatefulView, MagicStatefulViewState, MagicResponsiveView, MagicBuilder, MagicCan/MagicCannot | Building controllers or views, reactive state, authorization widgets | | `references/forms-validation.md` | MagicFormData, MagicForm, rules(), FormValidator, ValidatesRequests, built-in rules (Required, Email, Min, Max, Confirmed, Same, Accepted), process(), processingListenable | Building forms, adding validation, handling server-side errors | diff --git a/skills/magic-framework/references/facades-api.md b/skills/magic-framework/references/facades-api.md index fffb921..6a1bd22 100644 --- a/skills/magic-framework/references/facades-api.md +++ b/skills/magic-framework/references/facades-api.md @@ -188,6 +188,30 @@ await DB.transaction(() async { --- +## Echo + +Proxies to `BroadcastManager` for real-time WebSocket communication. `BroadcastServiceProvider` must be registered explicitly. + +| Signature | Return Type | Notes | +|-----------|-------------|-------| +| `Echo.channel(String name)` | `BroadcastChannel` | Subscribe to a public channel. | +| `Echo.private(String name)` | `BroadcastChannel` | Subscribe to a private channel (auth required). | +| `Echo.join(String name)` | `BroadcastPresenceChannel` | Join a presence channel (auth + member tracking). | +| `Echo.listen(String channel, String event, Function(BroadcastEvent) callback)` | `BroadcastChannel` | Shorthand: subscribe + listen in one call. | +| `Echo.leave(String name)` | `void` | Unsubscribe from a channel. | +| `Echo.connect()` | `Future` | Establish the WebSocket connection. | +| `Echo.disconnect()` | `Future` | Close connection and release resources. | +| `Echo.connection` | `BroadcastDriver` | Resolved default driver instance. | +| `Echo.socketId` | `String?` | Server-assigned socket ID; `null` when disconnected. | +| `Echo.connectionState` | `Stream` | Stream of connection lifecycle state changes. | +| `Echo.onReconnect` | `Stream` | Emits once each successful reconnect. | +| `Echo.addInterceptor(BroadcastInterceptor interceptor)` | `void` | Register an interceptor on the default connection. | +| `Echo.manager` | `BroadcastManager` | Direct manager access for `extend()`. | +| `Echo.fake()` | `FakeBroadcastManager` | Swap to in-memory fake for testing. | +| `Echo.unfake()` | `void` | Restore real manager binding. | + +--- + ## Event Dispatches via `EventDispatcher.instance`. The `Event` facade only exposes `dispatch()` — listener registration is done directly on the dispatcher. diff --git a/skills/magic-framework/references/testing-patterns.md b/skills/magic-framework/references/testing-patterns.md index b22e0d6..da34a20 100644 --- a/skills/magic-framework/references/testing-patterns.md +++ b/skills/magic-framework/references/testing-patterns.md @@ -621,6 +621,7 @@ setUp(() { cacheFake = Cache.fake(); // In-memory cache, records operations vaultFake = Vault.fake(); // In-memory secure storage, no platform channels logFake = Log.fake(); // Captures log entries, no console output + echoFake = Echo.fake(); // In-memory broadcasting, no WebSocket }); tearDown(() { @@ -628,6 +629,7 @@ tearDown(() { Cache.unfake(); Vault.unfake(); Log.unfake(); + Echo.unfake(); }); ``` @@ -723,6 +725,29 @@ test('second test', () { }); ``` +### Echo.fake() + +```dart +final fake = Echo.fake(); + +Echo.channel('orders'); +Echo.private('user.1'); + +// Assertions +fake.assertConnected(); +fake.assertDisconnected(); +fake.assertSubscribed('orders'); +fake.assertNotSubscribed('chat'); +fake.assertInterceptorAdded(); + +// Low-level driver access +expect(fake.driver.subscribedChannels, contains('orders')); +expect(fake.driver.subscribedChannels, contains('private-user.1')); + +// Reset recorded state +fake.reset(); +``` + ## Middleware Testing Middleware is tested by verifying whether it calls `next()` or halts the pipeline. From b0aad2e9d6afc2a9e781e0ddf372526259b789b0 Mon Sep 17 00:00:00 2001 From: Anilcan Cakir Date: Mon, 6 Apr 2026 01:16:52 +0300 Subject: [PATCH 2/2] fix(docs): address PR #39 review feedback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add missing `late FakeBroadcastManager echoFake` declaration - Fix Echo.fake() example: connect before assertConnected, disconnect before assertDisconnected - Fix Echo.listen callback type: `void Function(BroadcastEvent)` not `Function(BroadcastEvent)` - Reword "All facades" → "The following facades" for fake/unfake list --- README.md | 2 +- skills/magic-framework/references/facades-api.md | 2 +- skills/magic-framework/references/testing-patterns.md | 9 +++++++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 161da2f..1efc646 100644 --- a/README.md +++ b/README.md @@ -423,7 +423,7 @@ void main() { } ``` -All facades support `fake()` / `unfake()`: `Http`, `Auth`, `Cache`, `Vault`, `Log`, `Echo`. Each fake records operations and exposes assertion helpers. +The following facades support `fake()` / `unfake()`: `Http`, `Auth`, `Cache`, `Vault`, `Log`, `Echo`. Each fake records operations and exposes assertion helpers. ## Architecture diff --git a/skills/magic-framework/references/facades-api.md b/skills/magic-framework/references/facades-api.md index 6a1bd22..d8e1051 100644 --- a/skills/magic-framework/references/facades-api.md +++ b/skills/magic-framework/references/facades-api.md @@ -197,7 +197,7 @@ Proxies to `BroadcastManager` for real-time WebSocket communication. `BroadcastS | `Echo.channel(String name)` | `BroadcastChannel` | Subscribe to a public channel. | | `Echo.private(String name)` | `BroadcastChannel` | Subscribe to a private channel (auth required). | | `Echo.join(String name)` | `BroadcastPresenceChannel` | Join a presence channel (auth + member tracking). | -| `Echo.listen(String channel, String event, Function(BroadcastEvent) callback)` | `BroadcastChannel` | Shorthand: subscribe + listen in one call. | +| `Echo.listen(String channel, String event, void Function(BroadcastEvent) callback)` | `BroadcastChannel` | Shorthand: subscribe + listen in one call. | | `Echo.leave(String name)` | `void` | Unsubscribe from a channel. | | `Echo.connect()` | `Future` | Establish the WebSocket connection. | | `Echo.disconnect()` | `Future` | Close connection and release resources. | diff --git a/skills/magic-framework/references/testing-patterns.md b/skills/magic-framework/references/testing-patterns.md index da34a20..73d1722 100644 --- a/skills/magic-framework/references/testing-patterns.md +++ b/skills/magic-framework/references/testing-patterns.md @@ -612,6 +612,7 @@ late FakeAuthManager authFake; late FakeCacheManager cacheFake; late FakeVaultService vaultFake; late FakeLogManager logFake; +late FakeBroadcastManager echoFake; setUp(() { MagicApp.reset(); @@ -730,12 +731,13 @@ test('second test', () { ```dart final fake = Echo.fake(); +await Echo.connect(); + Echo.channel('orders'); Echo.private('user.1'); -// Assertions +// Assertions while connected fake.assertConnected(); -fake.assertDisconnected(); fake.assertSubscribed('orders'); fake.assertNotSubscribed('chat'); fake.assertInterceptorAdded(); @@ -744,6 +746,9 @@ fake.assertInterceptorAdded(); expect(fake.driver.subscribedChannels, contains('orders')); expect(fake.driver.subscribedChannels, contains('private-user.1')); +await Echo.disconnect(); +fake.assertDisconnected(); + // Reset recorded state fake.reset(); ```