Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<Listener Function()>`, not listener instances |

Expand Down
10 changes: 5 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
9 changes: 5 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand All @@ -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<Listener Function()>`, not listener instances |
| `routerConfig` before init | Only accessible after `Magic.init()` completes |
Expand Down
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down Expand Up @@ -423,7 +423,7 @@ void main() {
}
```

All facades support `fake()` / `unfake()`: `Http`, `Auth`, `Cache`, `Vault`, `Log`. 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

Expand All @@ -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
Expand Down Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion skills/magic-framework/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down
24 changes: 24 additions & 0 deletions skills/magic-framework/references/facades-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -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, void Function(BroadcastEvent) callback)` | `BroadcastChannel` | Shorthand: subscribe + listen in one call. |
| `Echo.leave(String name)` | `void` | Unsubscribe from a channel. |
| `Echo.connect()` | `Future<void>` | Establish the WebSocket connection. |
| `Echo.disconnect()` | `Future<void>` | 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<BroadcastConnectionState>` | Stream of connection lifecycle state changes. |
| `Echo.onReconnect` | `Stream<void>` | 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.
Expand Down
30 changes: 30 additions & 0 deletions skills/magic-framework/references/testing-patterns.md
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,7 @@ late FakeAuthManager authFake;
late FakeCacheManager cacheFake;
late FakeVaultService vaultFake;
late FakeLogManager logFake;
late FakeBroadcastManager echoFake;

setUp(() {
MagicApp.reset();
Expand All @@ -621,13 +622,15 @@ 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(() {
Auth.unfake();
Cache.unfake();
Vault.unfake();
Log.unfake();
Echo.unfake();
});
```

Expand Down Expand Up @@ -723,6 +726,33 @@ test('second test', () {
});
```

### Echo.fake()

```dart
final fake = Echo.fake();

await Echo.connect();

Echo.channel('orders');
Echo.private('user.1');

// Assertions while connected
fake.assertConnected();
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'));

await Echo.disconnect();
fake.assertDisconnected();

// Reset recorded state
fake.reset();
```

## Middleware Testing

Middleware is tested by verifying whether it calls `next()` or halts the pipeline.
Expand Down