From 7e763d30f7a8a035489600e5fd46b36850e1f9cf Mon Sep 17 00:00:00 2001 From: brein Date: Mon, 4 May 2026 01:45:36 +0200 Subject: [PATCH 1/3] Apply fork changes on upstream history --- .gitignore | 24 - CHANGELOG.md | 110 ++- README.md | 566 ++++++++++-- analysis_options.yaml | 105 ++- example/README.md | 83 +- example/example_client.dart | 10 + example/example_client.html | 138 +++ example/example_client.js | 72 ++ example/example_server.dart | 43 + example/package.json | 6 + example/polling_smoke.dart | 71 ++ lib/socket_io.dart | 148 ++- lib/src/adapter.dart | 232 +++++ lib/src/client.dart | 171 ++-- lib/src/engine/connect.dart | 42 +- lib/src/engine/engine.dart | 37 +- lib/src/engine/server.dart | 350 ++++--- lib/src/engine/socket.dart | 275 +++--- lib/src/engine/transport/jsonp_transport.dart | 79 +- .../engine/transport/polling_transport.dart | 487 +++++----- lib/src/engine/transport/transports.dart | 89 +- .../engine/transport/websocket_transport.dart | 39 +- lib/src/engine/transport/xhr_transport.dart | 87 +- lib/src/extensions/duration_extensions.dart | 165 ++++ lib/src/extensions/extensions.dart | 13 + lib/src/extensions/list_extensions.dart | 197 ++++ lib/src/extensions/map_extensions.dart | 168 ++++ lib/src/extensions/packet_extensions.dart | 282 ++++++ lib/src/extensions/socket_extensions.dart | 166 ++++ lib/src/extensions/string_extensions.dart | 200 ++++ lib/src/models/adapter_broadcast_models.dart | 224 +++++ lib/src/models/adapter_room_models.dart | 108 +++ lib/src/models/callbacks_models.dart | 123 +++ .../models/client_configuration_models.dart | 484 ++++++++++ lib/src/models/client_connection_models.dart | 223 +++++ lib/src/models/connect_buffer_models.dart | 94 ++ lib/src/models/engine_handshake_models.dart | 144 +++ lib/src/models/error_models.dart | 857 ++++++++++++++++++ lib/src/models/event_data_models.dart | 166 ++++ lib/src/models/handshake_data_models.dart | 230 +++++ lib/src/models/middleware_models.dart | 230 +++++ lib/src/models/models.dart | 32 + lib/src/models/namespace_config_models.dart | 192 ++++ lib/src/models/packet_data_models.dart | 452 +++++++++ lib/src/models/packet_models.dart | 486 ++++++++++ lib/src/models/room_management_models.dart | 258 ++++++ .../models/server_configuration_models.dart | 460 ++++++++++ lib/src/models/server_options_models.dart | 352 +++++++ .../models/socket_configuration_models.dart | 753 +++++++++++++++ lib/src/models/socket_data_models.dart | 100 ++ lib/src/models/socket_error_models.dart | 214 +++++ lib/src/models/socket_flags_models.dart | 169 ++++ lib/src/models/transport_data_models.dart | 178 ++++ lib/src/models/transport_error_models.dart | 20 + lib/src/models/transport_models.dart | 856 +++++++++++++++++ lib/src/models/validation_error_models.dart | 420 +++++++++ lib/src/namespace.dart | 259 ++++-- lib/src/server.dart | 509 ++++++----- lib/src/socket.dart | 546 +++++++---- lib/src/types/common_types.dart | 17 + lib/src/util/async_utils.dart | 21 + lib/src/util/event_emitter.dart | 159 +++- lib/src/value_objects/connection_id_vo.dart | 76 ++ .../value_objects/disconnect_reason_vo.dart | 141 +++ lib/src/value_objects/error_code_vo.dart | 174 ++++ lib/src/value_objects/event_arguments_vo.dart | 245 +++++ lib/src/value_objects/event_name_vo.dart | 65 ++ lib/src/value_objects/namespace_name_vo.dart | 48 + lib/src/value_objects/packet_id_vo.dart | 49 + lib/src/value_objects/port_number_vo.dart | 50 + .../value_objects/query_parameters_vo.dart | 152 ++++ lib/src/value_objects/room_name_vo.dart | 77 ++ lib/src/value_objects/socket_state_vo.dart | 91 ++ .../value_objects/timeout_duration_vo.dart | 73 ++ lib/src/value_objects/transport_name_vo.dart | 81 ++ lib/src/value_objects/url_path_vo.dart | 47 + lib/src/value_objects/value_objects.dart | 21 + pubspec.yaml | 29 +- test/adapter_broadcast_models_test.dart | 446 +++++++++ test/integration_basic_test.dart | 252 +++++ test/models/adapter_room_models_test.dart | 179 ++++ test/models/callbacks_models_test.dart | 126 +++ .../models/client_connection_models_test.dart | 298 ++++++ test/models/connect_buffer_models_test.dart | 276 ++++++ test/models/engine_handshake_models_test.dart | 326 +++++++ test/models/event_data_models_test.dart | 166 ++++ test/models/handshake_data_models_test.dart | 317 +++++++ test/models/middleware_models_test.dart | 330 +++++++ test/models/room_management_models_test.dart | 320 +++++++ test/models/server_options_models_test.dart | 333 +++++++ test/models/socket_data_models_test.dart | 171 ++++ test/models/socket_error_models_test.dart | 250 +++++ test/models/socket_flags_models_test.dart | 400 ++++++++ test/models/transport_data_models_test.dart | 277 ++++++ test/models/validation_error_models_test.dart | 447 +++++++++ test/namespace_config_models_test.dart | 243 +++++ test/packet_data_models_test.dart | 464 ++++++++++ test/polling_transport_integration_test.dart | 170 ++++ test/socket.test.dart | 35 +- test/socket_state_vo_test.dart | 132 +++ test/typed_event_emitter_test.dart | 188 ++++ test/value_objects/connection_id_vo_test.dart | 37 + .../disconnect_reason_vo_test.dart | 113 +++ test/value_objects/error_code_vo_test.dart | 233 +++++ .../event_arguments_vo_test.dart | 355 ++++++++ test/value_objects/event_name_vo_test.dart | 54 ++ .../value_objects/namespace_name_vo_test.dart | 53 ++ test/value_objects/packet_id_vo_test.dart | 57 ++ test/value_objects/port_number_vo_test.dart | 78 ++ .../query_parameters_vo_test.dart | 379 ++++++++ test/value_objects/room_name_vo_test.dart | 42 + .../timeout_duration_vo_test.dart | 76 ++ .../value_objects/transport_name_vo_test.dart | 78 ++ test/value_objects/url_path_vo_test.dart | 59 ++ tool/check.sh | 97 ++ tool/coverage.sh | 32 + 116 files changed, 21598 insertions(+), 1501 deletions(-) delete mode 100644 .gitignore create mode 100644 example/example_client.dart create mode 100644 example/example_client.html create mode 100644 example/example_client.js create mode 100644 example/example_server.dart create mode 100644 example/package.json create mode 100644 example/polling_smoke.dart create mode 100644 lib/src/adapter.dart create mode 100644 lib/src/extensions/duration_extensions.dart create mode 100644 lib/src/extensions/extensions.dart create mode 100644 lib/src/extensions/list_extensions.dart create mode 100644 lib/src/extensions/map_extensions.dart create mode 100644 lib/src/extensions/packet_extensions.dart create mode 100644 lib/src/extensions/socket_extensions.dart create mode 100644 lib/src/extensions/string_extensions.dart create mode 100644 lib/src/models/adapter_broadcast_models.dart create mode 100644 lib/src/models/adapter_room_models.dart create mode 100644 lib/src/models/callbacks_models.dart create mode 100644 lib/src/models/client_configuration_models.dart create mode 100644 lib/src/models/client_connection_models.dart create mode 100644 lib/src/models/connect_buffer_models.dart create mode 100644 lib/src/models/engine_handshake_models.dart create mode 100644 lib/src/models/error_models.dart create mode 100644 lib/src/models/event_data_models.dart create mode 100644 lib/src/models/handshake_data_models.dart create mode 100644 lib/src/models/middleware_models.dart create mode 100644 lib/src/models/models.dart create mode 100644 lib/src/models/namespace_config_models.dart create mode 100644 lib/src/models/packet_data_models.dart create mode 100644 lib/src/models/packet_models.dart create mode 100644 lib/src/models/room_management_models.dart create mode 100644 lib/src/models/server_configuration_models.dart create mode 100644 lib/src/models/server_options_models.dart create mode 100644 lib/src/models/socket_configuration_models.dart create mode 100644 lib/src/models/socket_data_models.dart create mode 100644 lib/src/models/socket_error_models.dart create mode 100644 lib/src/models/socket_flags_models.dart create mode 100644 lib/src/models/transport_data_models.dart create mode 100644 lib/src/models/transport_error_models.dart create mode 100644 lib/src/models/transport_models.dart create mode 100644 lib/src/models/validation_error_models.dart create mode 100644 lib/src/types/common_types.dart create mode 100644 lib/src/util/async_utils.dart create mode 100644 lib/src/value_objects/connection_id_vo.dart create mode 100644 lib/src/value_objects/disconnect_reason_vo.dart create mode 100644 lib/src/value_objects/error_code_vo.dart create mode 100644 lib/src/value_objects/event_arguments_vo.dart create mode 100644 lib/src/value_objects/event_name_vo.dart create mode 100644 lib/src/value_objects/namespace_name_vo.dart create mode 100644 lib/src/value_objects/packet_id_vo.dart create mode 100644 lib/src/value_objects/port_number_vo.dart create mode 100644 lib/src/value_objects/query_parameters_vo.dart create mode 100644 lib/src/value_objects/room_name_vo.dart create mode 100644 lib/src/value_objects/socket_state_vo.dart create mode 100644 lib/src/value_objects/timeout_duration_vo.dart create mode 100644 lib/src/value_objects/transport_name_vo.dart create mode 100644 lib/src/value_objects/url_path_vo.dart create mode 100644 lib/src/value_objects/value_objects.dart create mode 100644 test/adapter_broadcast_models_test.dart create mode 100644 test/integration_basic_test.dart create mode 100644 test/models/adapter_room_models_test.dart create mode 100644 test/models/callbacks_models_test.dart create mode 100644 test/models/client_connection_models_test.dart create mode 100644 test/models/connect_buffer_models_test.dart create mode 100644 test/models/engine_handshake_models_test.dart create mode 100644 test/models/event_data_models_test.dart create mode 100644 test/models/handshake_data_models_test.dart create mode 100644 test/models/middleware_models_test.dart create mode 100644 test/models/room_management_models_test.dart create mode 100644 test/models/server_options_models_test.dart create mode 100644 test/models/socket_data_models_test.dart create mode 100644 test/models/socket_error_models_test.dart create mode 100644 test/models/socket_flags_models_test.dart create mode 100644 test/models/transport_data_models_test.dart create mode 100644 test/models/validation_error_models_test.dart create mode 100644 test/namespace_config_models_test.dart create mode 100644 test/packet_data_models_test.dart create mode 100644 test/polling_transport_integration_test.dart create mode 100644 test/socket_state_vo_test.dart create mode 100644 test/typed_event_emitter_test.dart create mode 100644 test/value_objects/connection_id_vo_test.dart create mode 100644 test/value_objects/disconnect_reason_vo_test.dart create mode 100644 test/value_objects/error_code_vo_test.dart create mode 100644 test/value_objects/event_arguments_vo_test.dart create mode 100644 test/value_objects/event_name_vo_test.dart create mode 100644 test/value_objects/namespace_name_vo_test.dart create mode 100644 test/value_objects/packet_id_vo_test.dart create mode 100644 test/value_objects/port_number_vo_test.dart create mode 100644 test/value_objects/query_parameters_vo_test.dart create mode 100644 test/value_objects/room_name_vo_test.dart create mode 100644 test/value_objects/timeout_duration_vo_test.dart create mode 100644 test/value_objects/transport_name_vo_test.dart create mode 100644 test/value_objects/url_path_vo_test.dart create mode 100755 tool/check.sh create mode 100755 tool/coverage.sh diff --git a/.gitignore b/.gitignore deleted file mode 100644 index f587070..0000000 --- a/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -# Files and directories created by pub -.dart_tool -.packages -.pub/ -build/ -packages -# Remove the following pattern if you wish to check in your lock file -pubspec.lock - -# Files created by dart2js -*.dart.js -*.part.js -*.js.deps -*.js.map -*.info.json - -# Directory created by dartdoc -doc/api/ - -# JetBrains IDEs -.idea/ -*.iml -*.ipr -*.iws diff --git a/CHANGELOG.md b/CHANGELOG.md index ef03978..cac80dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,42 +1,68 @@ -## 1.0.1 - -**Bug Fix:** - -* [#45](https://github.com/rikulo/socket.io-dart/issues/45) [BUG] Calling close on Server raises ConcurrentModificationError - -**New Feature:** - -* [#47](https://github.com/rikulo/socket.io-dart/pull/47) Added Future to start and stop server - -## 1.0.0 - -**New Feature:** - -* [#33](https://github.com/rikulo/socket.io-dart/issues/33) Null safety - - -## 0.9.4 - -**Bug Fix:** - -* [#23](https://github.com/rikulo/socket.io-dart/issues/23) [BUG] Calling close on Server raises ConcurrentModificationError - -## 0.9.3 - -**Bug Fix:** - -* [#18](https://github.com/rikulo/socket.io-dart/pull/18) make sure it is accessing the rooms map - - -## 0.9.2 - -**Bug Fix:** - -* [#17](https://github.com/rikulo/socket.io-dart/pull/17) inference error - - -## 0.9.1+1 - -**New Feature:** - -* [#16](https://github.com/rikulo/socket.io-dart/pull/16) Apply Pedantic recommendations +# Changelog + +All notable changes to this fork are documented here. +This fork tracks upstream [`rikulo/socket.io-dart`](https://github.com/rikulo/socket.io-dart) +and adds the fixes listed below. Once these are accepted upstream the +`dependency_override` in consuming projects can be removed. + +--- + +## 2.0.1 – 2026-05-03 + +### Fixed + +- **Polling transport – packet splitting** (`lib/src/engine/transport/polling_transport.dart`) + `onData()` previously called `PacketParser.decodePayload()` which cannot + parse Engine.IO v4 / Socket.IO v3+ payloads. Replaced with a custom + `_splitPackets()` splitter that locates packet boundaries on `]`, `}` or + `"` followed by a digit, then decodes each packet individually via + `PacketParser.decodePacket()`. This makes polling fully equivalent to the + WebSocket transport for all current Socket.IO clients (JS, browser, Dart). +- **Polling transport – connection close race** (`doWrite / respond`) + `respond()` was synchronous and used `unawaited(connect!.close())`, causing + premature or failed connection closes that produced *xhr poll error* on + clients. `respond` is now `async` and `await`s `connect!.close()`. +- **Polling transport – CORS headers** + Added `Access-Control-Allow-Origin`, `Access-Control-Allow-Credentials`, + `Access-Control-Allow-Methods`, and cache-control headers to every polling + response so browser and cross-origin Dart clients do not get blocked. +- **Namespace – typed connection handler** + `onConnection()` now casts the raw `dynamic` listener argument to `Socket` + before invoking the typed `EventHandler` callback. + +### Added + +- `example/polling_smoke.dart` – raw Engine.IO v4 polling smoke test that + can be run against a live server to verify the polling fix end-to-end. +- `--with-polling-smoke` flag in `tool/check.sh`. + +--- + +## 2.0.0 – 2025-10-11 + +### Changed (breaking – Dart 3 migration) + +- Minimum SDK raised to `>=3.0.0 <4.0.0`. +- Dart 3 modernisation: sealed classes, records, exhaustive switches, and + improved null-safety throughout. +- Package renamed from `tp_socket_io` to `socket_io`; all import paths + updated (`package:socket_io/socket_io.dart`). + +### Added + +- **Typed API** – value objects (`ConnectionId`, `RoomName`, `EventName`, + `NamespaceName`, `PortNumber`, …) and sealed domain models + (`SocketIOPacket`, `SocketIOError`, `TransportData`, …) alongside the + legacy untyped API for gradual migration. +- **Expanded test suite** – 773 tests across 34 files covering every value + object, model, extension, and error type. +- `doc/ARCHITECTURE.md`, `doc/TYPE_SAFE_EXAMPLES.md`, + `doc/TYPE_SAFETY_MIGRATION_GUIDE.md`, `doc/QUICK_REFERENCE.md`. + +--- + +## 1.0.1 – upstream baseline + +Upstream release from [`rikulo/socket.io-dart`](https://github.com/rikulo/socket.io-dart). +See [upstream CHANGELOG](https://github.com/rikulo/socket.io-dart/blob/master/CHANGELOG.md) +for history prior to this fork. diff --git a/README.md b/README.md index b578007..d63f85a 100644 --- a/README.md +++ b/README.md @@ -1,96 +1,538 @@ -# socket.io-dart +# socket_io -Port of awesome JavaScript Node.js library - [Socket.io v2.0.1](https://github.com/socketio/socket.io) - in Dart +[![Dart](https://img.shields.io/badge/Dart-%3E%3D3.0.0-blue.svg)](https://dart.dev) +[![License](https://img.shields.io/badge/License-Apache%202.0-green.svg)](LICENSE) +[![Fork of rikulo/socket.io-dart](https://img.shields.io/badge/fork-rikulo%2Fsocket.io--dart-orange.svg)](https://github.com/rikulo/socket.io-dart) -## Usage +> **This is a fork** of [`rikulo/socket.io-dart`](https://github.com/rikulo/socket.io-dart) with critical polling-transport fixes for Engine.IO v4 / +> Socket.IO v3+ clients that are not yet merged upstream. Once upstream accepts these changes this +> package can be replaced with the official `socket_io` from pub.dev and the +> `dependency_override` can be removed. +> +> **Upstream PR tracking:** see [`doc/UPSTREAM_PR.md`](doc/UPSTREAM_PR.md). + +A modern, type-safe Dart implementation of [Socket.IO](https://socket.io/) for real-time +bidirectional event-based communication. + +Port of the JavaScript Node.js library [Socket.IO](https://github.com/socketio/socket.io) with +modern Dart 3.0+ features including sealed classes, value objects, and comprehensive type safety. + +## Features + +✨ **Modern & Type-Safe** +- Full Dart 3.0+ support with sealed classes and pattern matching +- Comprehensive type safety with value objects and domain models +- Dual API: backward-compatible legacy API + modern typed API + +🔌 **Complete Socket.IO Implementation** +- Real-time bidirectional communication +- Multiple transport support (WebSocket, HTTP long-polling, JSONP) +- Namespace and room support for message isolation +- Acknowledgment callbacks +- Binary data support + +🏗️ **Production Ready** +- 748+ passing tests with comprehensive coverage +- Zero analysis issues +- Extensive error handling with typed error models +- Full backward compatibility + +## Installation + +### Using this fork (recommended until upstream fix is merged) + +Add a `dependency_override` in your project's `pubspec.yaml` to use this fork +from your local checkout or from GitHub: + +```yaml +# pubspec.yaml of your consuming project +dependencies: + socket_io: ^2.0.1 + +dependency_overrides: + socket_io: + git: + url: https://github.com/reinbeumer/socket.io-dart.git + ref: master +``` + +Or point directly to a local path during development: + +```yaml +dependency_overrides: + socket_io: + path: /path/to/socket.io-dart +``` + +Then run: + +```zsh +dart pub get +``` + +### Removing the override (once fix is upstream) + +When the polling fix is accepted into [`rikulo/socket.io-dart`](https://github.com/rikulo/socket.io-dart) +and a new version is published to pub.dev, simply remove the `dependency_overrides` block and bump +the version constraint to require the fixed release. + +## Quick Start + +### Server Example ```dart import 'package:socket_io/socket_io.dart'; -main() { - var io = new Server(); - var nsp = io.of('/some'); - nsp.on('connection', (client) { - print('connection /some'); - client.on('msg', (data) { - print('data from /some => $data'); - client.emit('fromServer', "ok 2"); - }); +void main() { + // Create a Socket.IO server + final io = Server(); + + // Listen for client connections + io.onConnection((socket) { + print('Client connected: ${socket.id}'); + + // Listen for messages from client + socket.on('message', (data) { + print('Received: $data'); + + // Send acknowledgment back to client + socket.emit('messageReceived', ['Message processed']); + }); + + // Handle disconnection + socket.on('disconnect', (_) { + print('Client disconnected: ${socket.id}'); }); - io.on('connection', (client) { - print('connection default namespace'); - client.on('msg', (data) { - print('data from default => $data'); - client.emit('fromServer', "ok"); - }); - }); - io.listen(3000); + }); + + // Start listening on port 3000 + io.listen(3000); + print('Socket.IO server running on port 3000'); } ``` -```js -// JS client -var socket = io('http://localhost:3000'); -socket.on('connect', function(){console.log('connect')}); -socket.on('event', function(data){console.log(data)}); -socket.on('disconnect', function(){console.log('disconnect')}); -socket.on('fromServer', function(e){console.log(e)}); +### Client Example (JavaScript) + +```javascript +const socket = io('http://localhost:3000'); + +socket.on('connect', () => { + console.log('Connected to server'); + socket.emit('message', 'Hello from client!'); +}); + +socket.on('messageReceived', (data) => { + console.log('Server response:', data); +}); + +socket.on('disconnect', () => { + console.log('Disconnected from server'); +}); ``` +### Client Example (Dart) + ```dart -// Dart client import 'package:socket_io_client/socket_io_client.dart' as IO; -IO.Socket socket = IO.io('http://localhost:3000'); -socket.on('connect', (_) { - print('connect'); - socket.emit('msg', 'test'); +void main() { + final socket = IO.io('http://localhost:3000'); + + socket.on('connect', (_) { + print('Connected to server'); + socket.emit('message', 'Hello from Dart client!'); + }); + + socket.on('messageReceived', (data) { + print('Server response: $data'); + }); + + socket.on('disconnect', (_) { + print('Disconnected from server'); + }); +} +``` + +## Core Concepts + +### Namespaces + +Namespaces allow you to create separate communication channels over a single connection: + +```dart +final io = Server(); + +// Default namespace +io.onConnection((socket) { + print('Client connected to default namespace'); +}); + +// Custom namespace +final chatNamespace = io.of('/chat'); +chatNamespace.on('connection', (socket) { + print('Client connected to /chat namespace'); + + socket.on('chatMessage', (data) { + // Broadcast to all clients in this namespace + chatNamespace.emit('newMessage', data); + }); +}); + +io.listen(3000); +``` + +### Rooms + +Rooms are arbitrary channels that sockets can join and leave: + +```dart +io.onConnection((socket) { + // Join a room + socket.join('room1'); + + // Emit to all clients in a room + io.to('room1').emit('announcement', ['Welcome to room1']); + + // Leave a room + socket.leave('room1'); + + // Broadcast to all except sender + socket.broadcast.to('room1').emit('userJoined', [socket.id]); +}); +``` + +### Acknowledgments + +Request callbacks from the receiving side: + +```dart +// Server side +socket.on('question', (data) { + socket.emit('answer', ['The answer is 42'], ack: (response) { + print('Client acknowledged: $response'); + }); +}); + +// Client side +socket.emit('question', ['What is the meaning of life?'], ack: (answer) { + print('Server answered: $answer'); }); -socket.on('event', (data) => print(data)); -socket.on('disconnect', (_) => print('disconnect')); -socket.on('fromServer', (_) => print(_)); ``` -## Multiplexing support +## Modern Typed API + +This library provides a modern, type-safe API alongside the legacy API for backward compatibility: + +### Type-Safe Event Handling + +```dart +import 'package:socket_io/socket_io.dart'; + +// Using typed models +socket.on('userLogin', (data) { + final eventData = EventData.fromDynamic(data); + if (eventData is MapEventData) { + final username = eventData.value['username']; + print('User logged in: $username'); + } +}); + +// Using typed query parameters +socket.queryParameters?.get('token'); // Type-safe access + +// Using typed handshake data +final handshake = socket.handshakeData; +print('Connected from: ${handshake?.address}'); +print('Secure connection: ${handshake?.secure}'); +``` + +### Value Objects + +The library uses value objects for type safety: + +```dart +// Connection ID (validated non-empty string) +final connectionId = ConnectionId('socket-123'); + +// Room names (validated) +final room = RoomName('chatRoom'); + +// Event names (validated, no reserved names) +final event = EventName('customEvent'); + +// Namespace names (must start with /) +final namespace = NamespaceName('/admin'); +``` + +### Sealed Classes for Pattern Matching + +```dart +// Type-safe error handling +socket.on('error', (error) { + if (error is SocketIOError) { + switch (error) { + case TransportErrorModel(): + print('Transport error: ${error.message}'); + case ConnectionErrorModel(): + print('Connection error: ${error.message}'); + case ValidationErrorModel(): + print('Validation error: ${error.message}'); + } + } +}); +``` + +## Advanced Features + +### Middleware + +Add middleware to process connections: + +```dart +final io = Server(); + +// Namespace-level middleware +final adminNamespace = io.of('/admin'); +adminNamespace.use((socket, next) { + // Verify authentication token + final token = socket.handshake?['auth']?['token']; + if (token == 'secret') { + next(null); // Allow connection + } else { + next('Authentication failed'); // Reject connection + } +}); + +adminNamespace.onConnection((socket) { + print('Authenticated admin connected'); +}); +``` + +### Custom Server Options + +```dart +final io = Server( + options: { + 'pingTimeout': 60000, + 'pingInterval': 25000, + 'transports': ['websocket', 'polling'], + 'path': '/socket.io', + }, +); +``` + +### Broadcasting + +```dart +io.onConnection((socket) { + // To all clients + io.emit('broadcast', ['Message to everyone']); + + // To all clients except sender + socket.broadcast.emit('broadcast', ['Message to others']); + + // To specific room + io.to('room1').emit('roomMessage', ['Message to room1']); + + // To multiple rooms + io.to('room1').to('room2').emit('multiRoom', ['Message']); + + // To room except sender + socket.broadcast.to('room1').emit('announcement', ['User joined']); +}); +``` + +### Binary Data + +```dart +socket.on('image', (data) { + if (data is List) { + // Handle binary data + print('Received binary data of length: ${data.length}'); + } +}); + +// Send binary data +socket.emit('imageResponse', [imageBytes]); +``` + +## Transport Layers + +The library supports multiple transport mechanisms: + +1. **WebSocket** - Full-duplex communication channel +2. **HTTP Long-polling** - Fallback for environments without WebSocket support +3. **JSONP Polling** - Legacy support for older browsers + +Transport selection is automatic based on client capabilities and can be configured: + +```dart +final io = Server( + options: { + 'transports': ['websocket', 'polling'], // Preferred order + }, +); +``` + +## Error Handling + +Comprehensive typed error handling: + +```dart +socket.on('error', (error) { + if (error is SocketIOError) { + print('Error type: ${error.type}'); + print('Message: ${error.message}'); + print('Code: ${error.code}'); + print('Context: ${error.context}'); + } +}); + +// Graceful error recovery +socket.on('connect_error', (error) { + print('Connection failed, retrying...'); +}); +``` + +## Testing + +Run tests: + +```bash +dart test +``` + +Run tests with coverage: + +```bash +dart test --coverage=coverage +dart pub global activate coverage +dart pub global run coverage:format_coverage --lcov --in=coverage --out=coverage/lcov.info --report-on=lib +``` + +## API Documentation + +For detailed API documentation, see: + +- [TYPE_SAFE_EXAMPLES.md](doc/TYPE_SAFE_EXAMPLES.md) - Comprehensive examples of the typed API +- [TYPE_SAFETY_MIGRATION_GUIDE.md](doc/TYPE_SAFETY_MIGRATION_GUIDE.md) - Migration guide from legacy to typed API +- [QUICK_REFERENCE.md](doc/QUICK_REFERENCE.md) - Quick reference guide +- [UPGRADE_TO_V3.md](doc/archive/UPGRADE_TO_V3.md) - Upgrade guide for breaking changes + +## Architecture + +This library follows modern Dart architectural patterns: + +- **Value Objects**: Type-safe primitives with validation (ConnectionId, RoomName, etc.) +- **Domain Models**: Rich models for business logic (SocketIOPacket, HandshakeData, etc.) +- **Sealed Classes**: Exhaustive pattern matching for errors and data types +- **Extension Methods**: Utility methods without cluttering core classes +- **Dual API**: Backward compatibility with modern type-safe alternatives + +For detailed architecture documentation, see [doc/ARCHITECTURE.md](doc/ARCHITECTURE.md). + +## Compatibility + +- **Dart SDK**: >= 3.0.0 < 4.0.0 +- **Socket.IO Protocol**: Compatible with Socket.IO v2.x clients +- **Platforms**: VM, Web (with appropriate transport configuration) + +## Migration from Legacy API + +If you're using the legacy untyped API, you can gradually migrate to the typed API: + +```dart +// Legacy (still supported) +socket.on('message', (data) { + final Map map = data as Map; + print(map['text']); +}); + +// Modern typed API +socket.on('message', (data) { + final eventData = EventData.fromDynamic(data); + if (eventData is MapEventData) { + print(eventData.value['text']); + } +}); +``` + +See [TYPE_SAFETY_MIGRATION_GUIDE.md](doc/TYPE_SAFETY_MIGRATION_GUIDE.md) for details. + +## Contributing + +Contributions are welcome! Please read our contributing guidelines: + +1. Fork the repository +2. Create a feature branch (`git checkout -b feature/amazing-feature`) +3. Make your changes with tests +4. Ensure all tests pass (`dart test`) +5. Ensure code is formatted (`dart format .`) +6. Ensure no analysis issues (`dart analyze`) +7. Commit your changes (`git commit -m 'Add amazing feature'`) +8. Push to the branch (`git push origin feature/amazing-feature`) +9. Open a Pull Request + +For detailed contribution guidelines, see [doc/CONTRIBUTING.md](doc/CONTRIBUTING.md). + +## Examples + +The [example](example/) directory is now intentionally small and focused: + +- `example/example_server.dart` - server example (listens on port `3005`) +- `example/example_client.js` - Node.js client example +- `example/example_client.dart` - basic Dart client example +- `example/example_client.html` - browser client example with live logs +- `example/polling_smoke.dart` - raw Engine.IO polling smoke test + +Quick run: + +```zsh +dart run example/example_server.dart +dart run example/polling_smoke.dart +``` + +Optional full quality check including polling smoke: + +```zsh +bash tool/check.sh --with-polling-smoke +``` -Same as Socket.IO, this project allows you to create several Namespaces, which will act as separate communication channels but will share the same underlying connection. +Note: this package is server-only; client application examples are provided via +Node.js (`example/example_client.js`) and browser (`example/example_client.html`). -## Room support +## Projects Using socket_io -Within each Namespace, you can define arbitrary channels, called Rooms, that sockets can join and leave. You can then broadcast to any given room, reaching every socket that has joined it. +- [Quire](https://quire.io) - A simple, collaborative, multi-level task management tool +- [KEIKAI](https://keikai.io/) - A web spreadsheet for Big Data -## Transports support - Refers to [engine.io](https://github.com/socketio/engine.io) +## Related Projects -- `polling`: XHR / JSONP polling transport. -- `websocket`: WebSocket transport. +- [socket.io-client-dart](https://github.com/rikulo/socket.io-client-dart) - Dart client for Socket.IO +- [socket_io_common](https://pub.dev/packages/socket_io_common) - Common components for Socket.IO -## Adapters support +## License -* Default socket.io in-memory adapter class. Refers to [socket.io-adapter](https://github.com/socketio/socket.io-adapter) +Apache License 2.0 - see [LICENSE](LICENSE) file for details. -## Notes to Contributors +## Acknowledgments -### Fork socket.io-dart +This is a port of the JavaScript [Socket.IO](https://socket.io/) library. Special thanks to: -If you'd like to contribute back to the core, you can [fork this repository](https://help.github.com/articles/fork-a-repo) and send us a pull request, when it is ready. +- The Socket.IO team for the original implementation +- All contributors who have helped improve this library +- The Dart team for the excellent language and tooling -If you are new to Git or GitHub, please read [this guide](https://help.github.com/) first. +## Support -## Who Uses +- **Issues**: [GitHub Issues](https://github.com/reinbeumer/socket.io-dart/issues) +- **Discussions**: Use GitHub Discussions for questions and ideas +- **Documentation**: Check the [doc](doc/) directory -* [Quire](https://quire.io) - a simple, collaborative, multi-level task management tool. -* [KEIKAI](https://keikai.io/) - a web spreadsheet for Big Data. +## Changelog -## Socket.io Dart Client +See [CHANGELOG.md](CHANGELOG.md) for version history and migration notes. -* [socket.io-client-dart](https://github.com/rikulo/socket.io-client-dart) +--- -## Contributors -* Thanks [@felangel](https://github.com/felangel) for https://github.com/rikulo/socket.io-dart/issues/7 -* Thanks [@ThinkDigitalSoftware](https://github.com/ThinkDigitalSoftware) for https://github.com/rikulo/socket.io-dart/pull/15 -* Thanks [@guilhermecaldas](https://github.com/guilhermecaldas) for https://github.com/rikulo/socket.io-dart/pull/16 -* Thanks [@jodinathan](https://github.com/jodinathan) for https://github.com/rikulo/socket.io-dart/pull/17 -* Thanks [@jodinathan](https://github.com/jodinathan) for https://github.com/rikulo/socket.io-dart/pull/18 -* Thanks [@nicobritos](https://github.com/nicobritos) for https://github.com/rikulo/socket.io-dart/pull/46 -* Thanks [@nicobritos](https://github.com/nicobritos) for https://github.com/rikulo/socket.io-dart/pull/47 \ No newline at end of file +Made with ❤️ by the Dart community diff --git a/analysis_options.yaml b/analysis_options.yaml index 80bb834..f5e631d 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,12 +1,95 @@ -# This file allows you to configure the Dart analyzer. -# -# The commented part below is just for inspiration. Read the guide here: -# https://www.dartlang.org/guides/language/analysis-options -include: package:pedantic/analysis_options.yaml +#include: package:lint/strict.yaml +include: package:lints/recommended.yaml + +linter: + rules: + always_declare_return_types: true + always_specify_types: true + + # https://www.reddit.com/r/FlutterDev/comments/1pggt66/a_lint_package_that_prevents_subtle_singleton/ + always_use_package_imports: false + prefer_relative_imports: true + + avoid_bool_literals_in_conditional_expressions: false + avoid_classes_with_only_static_members: false + avoid_final_parameters: false + avoid_function_literals_in_foreach_calls: false + avoid_implementing_value_types: false + avoid_print: true + camel_case_types: false + cascade_invocations: true + combinators_ordering: true + constant_identifier_names: false + curly_braces_in_flow_control_structures: true + directives_ordering: true + discarded_futures: true + join_return_with_assignment: false + library_private_types_in_public_api: false + missing_whitespace_between_adjacent_strings: false + no_adjacent_strings_in_list: false + non_constant_identifier_names: false + prefer_expression_function_bodies: true + prefer_final_in_for_each: true + prefer_final_locals: true + # prefer_final_parameters: true + parameter_assignments: true + prefer_foreach: true + prefer_if_elements_to_conditional_expressions: true + prefer_single_quotes: true + prefer_void_to_null: false + switch_on_type: true + type_literal_in_constant_pattern: true + unawaited_futures: true + unintended_html_in_doc_comment: false + unnecessary_await_in_return: true + unnecessary_breaks: false + unnecessary_lambdas: true + unnecessary_raw_strings: true + unnecessary_unawaited: true + use_null_aware_elements: true + use_setters_to_change_properties: false + use_string_in_part_of_directives: false + + # consistency (from `dart_flutter_team_lints`) + lines_longer_than_80_chars: false + omit_local_variable_types: false + prefer_asserts_in_initializer_lists: false + prefer_const_constructors: true + sort_pub_dependencies: false + unnecessary_library_directive: false + unnecessary_library_name: false + unnecessary_parenthesis: false + unnecessary_statements: true + use_is_even_rather_than_modulo: true + + # correctness (from `dart_flutter_team_lints`) + avoid_catching_errors: false + avoid_dynamic_calls: false + comment_references: false + conditional_uri_does_not_exist: true + only_throw_errors: true + test_types_in_equals: true + throw_in_finally: true + type_annotate_public_apis: true + unreachable_from_main: false + analyzer: -# exclude: -# - path/to/excluded/files/** -# linter: -# rules: -# # see catalog here: http://dart-lang.github.io/linter/lints/ -# - hash_and_equals + plugins: + - dart_code_linter + language: + strict-casts: false + strict-inference: true + strict-raw-types: true + exclude: + - "**/*.g.dart" + - build/** + - "test/**" + - "example/**" + - "test*.dart" + - "**/test_*.dart" + +formatter: + page_width: 120 + trailing_commas: preserve + + diff --git a/example/README.md b/example/README.md index 756fa64..1d1fa58 100644 --- a/example/README.md +++ b/example/README.md @@ -1,70 +1,39 @@ -# socket.io-dart +# Examples -Port of awesome JavaScript Node.js library - [Socket.io v2.0.1](https://github.com/socketio/socket.io) - in Dart +This folder contains a small curated set of runnable examples. -## Usage +## Kept examples -```dart -import 'package:socket_io/socket_io.dart'; +- `example_server.dart` - Socket.IO server (port `3005`) +- `example_client.js` - Node.js client example +- `example_client.dart` - short note pointing to supported client examples +- `example_client.html` - browser client with connect/send UI and live logs +- `polling_smoke.dart` - raw Engine.IO polling smoke check -main() { - var io = new Server(); - var nsp = io.of('/some'); - nsp.on('connection', (client) { - print('connection /some'); - client.on('msg', (data) { - print('data from /some => $data'); - client.emit('fromServer', "ok 2"); - }); - }); - io.on('connection', (client) { - print('connection default namespace'); - client.on('msg', (data) { - print('data from default => $data'); - client.emit('fromServer', "ok"); - }); - }); - io.listen(3000); -} -``` - -```js -// JS client -var socket = io('http://localhost:3000'); -socket.on('connect', function(){console.log('connect')}); -socket.on('event', function(data){console.log(data)}); -socket.on('disconnect', function(){console.log('disconnect')}); -socket.on('fromServer', function(e){console.log(e)}); -``` +## Quick run -```dart -// Dart client -import 'package:socket_io_client/socket_io_client.dart' as IO; +Start the server first: -IO.Socket socket = IO.io('http://localhost:3000'); -socket.on('connect', (_) { - print('connect'); - socket.emit('msg', 'test'); -}); -socket.on('event', (data) => print(data)); -socket.on('disconnect', (_) => print('disconnect')); -socket.on('fromServer', (_) => print(_)); +```zsh +dart run example/example_server.dart ``` -## Multiplexing support - -Same as Socket.IO, this project allows you to create several Namespaces, which will act as separate communication channels but will share the same underlying connection. +Run the polling smoke check: -## Room support - -Within each Namespace, you can define arbitrary channels, called Rooms, that sockets can join and leave. You can then broadcast to any given room, reaching every socket that has joined it. +```zsh +dart run example/polling_smoke.dart +``` -## Transports support - Refers to [engine.io](https://github.com/socketio/engine.io) +Run the Node.js client: -- `polling`: XHR / JSONP polling transport. -- `websocket`: WebSocket transport. +```zsh +cd example +npm install +node example_client.js +``` -## Adapters support +Open the browser client: -* Default socket.io in-memory adapter class. Refers to [socket.io-adapter](https://github.com/socketio/socket.io-adapter) +```zsh +open example/example_client.html +``` diff --git a/example/example_client.dart b/example/example_client.dart new file mode 100644 index 0000000..5ae4c01 --- /dev/null +++ b/example/example_client.dart @@ -0,0 +1,10 @@ +import 'dart:io'; + +void main() { + stdout.writeln('This package is server-only.'); + stdout.writeln('Use one of these client examples instead:'); + stdout.writeln(' - node example/example_client.js'); + stdout.writeln(' - open example/example_client.html'); + stdout.writeln('For transport smoke checks use:'); + stdout.writeln(' - dart run example/polling_smoke.dart'); +} diff --git a/example/example_client.html b/example/example_client.html new file mode 100644 index 0000000..b20152a --- /dev/null +++ b/example/example_client.html @@ -0,0 +1,138 @@ + + + + + + Socket.IO Browser Example + + + +

Socket.IO Browser Client

+

Defaults match example/example_server.dart (host 127.0.0.1, port 3005, path /socket.io).

+ +
+ + + + +
+ +
+ + +
+ +
+ + + +
+ +

+
+  
+  
+
+
+
diff --git a/example/example_client.js b/example/example_client.js
new file mode 100644
index 0000000..73d0610
--- /dev/null
+++ b/example/example_client.js
@@ -0,0 +1,72 @@
+// Node.js client for Socket.IO server
+// Install: npm i socket.io-client
+const io = require('socket.io-client');
+
+// Allow overrides via env for easier debugging
+const HOST = process.env.HOST || '127.0.0.1'; // prefer explicit IPv4 to avoid IPv6 binding issues
+const PORT = Number(process.env.PORT || 3005);
+const PATH = process.env.SOCKET_IO_PATH || '/socket.io';
+// Comma-separated transports in env, default to polling first for maximum compatibility
+const TRANSPORTS = (process.env.TRANSPORTS || 'polling,websocket')
+  .split(',')
+  .map((s) => s.trim())
+  .filter(Boolean);
+
+console.log('Starting Socket.IO client...');
+console.log(`Target: http://${HOST}:${PORT}${PATH}`);
+console.log('Transports:', TRANSPORTS);
+
+const socket = io(`http://${HOST}:${PORT}`, {
+  path: PATH,
+  transports: TRANSPORTS,
+  reconnection: false,
+  forceNew: true,
+  timeout: 5000,
+});
+
+// Helpful manager-level event logging
+socket.io.on('error', (err) => console.log('manager error:', err && err.message || err));
+socket.io.on('reconnect_attempt', (attempt) => console.log('manager reconnect_attempt:', attempt));
+socket.io.on('reconnect_error', (err) => console.log('manager reconnect_error:', err && err.message || err));
+socket.io.on('reconnect_failed', () => console.log('manager reconnect_failed'));
+
+socket.on('connect', () => {
+  console.log('✓ Connected to server!');
+  console.log('  Socket ID:', socket.id);
+  console.log('  Transport:', socket.io.engine.transport.name);
+  // Send a message to the server
+  socket.emit('message', 'Hello from Node.js client!');
+});
+
+socket.on('connect_error', (error) => {
+  console.log('✗ Connect error:', error && error.message || error);
+  console.log('  Hints:');
+  console.log(`  - Is the server listening on http://${HOST}:${PORT}${PATH}?`);
+  console.log('  - Try forcing polling only: TRANSPORTS=polling node example_client.js');
+  console.log('  - Try IPv4: HOST=127.0.0.1 (instead of localhost)');
+  console.log('  - If you changed the server path, set SOCKET_IO_PATH to match.');
+});
+
+socket.on('error', (error) => {
+  console.log('✗ Error:', error);
+});
+
+// Useful heartbeat events
+socket.on('ping', () => console.log('ping'));
+socket.on('pong', (latency) => console.log('pong', latency));
+
+socket.on('response', (data) => {
+  console.log('✓ Server response:', data);
+});
+
+socket.on('disconnect', (reason) => {
+  console.log('✗ Disconnected from server. Reason:', reason);
+});
+
+// Keep the process running a bit longer to allow observation
+const LIFETIME_MS = Number(process.env.LIFETIME_MS || 5000);
+setTimeout(() => {
+  console.log('Closing connection...');
+  socket.close();
+  process.exit(0);
+}, LIFETIME_MS);
diff --git a/example/example_server.dart b/example/example_server.dart
new file mode 100644
index 0000000..90001a4
--- /dev/null
+++ b/example/example_server.dart
@@ -0,0 +1,43 @@
+import 'package:socket_io/socket_io.dart';
+
+void main() async {
+  // Use default options
+  final Server io = Server();
+
+  // Default namespace
+  io.onConnection((final Socket socket) {
+    print('Client connected: ${socket.id}');
+
+    socket.on('message', (final SocketIOEventData data) {
+      print('Received: $data');
+      socket.emit('response', 'Message received');
+    });
+
+    socket.on('msg', (final SocketIOEventData data) {
+      print('Received msg: $data');
+      socket.emit('fromServer', 'Message received: $data');
+    });
+
+    socket.on('disconnect', (final SocketIOEventData data) {
+      print('Client disconnected: ${socket.id}');
+    });
+  });
+
+  // Custom namespace '/some'
+  final Namespace someNamespace = io.of('/some');
+  someNamespace.onConnection((final Socket socket) {
+    print('Client connected to /some: ${socket.id}');
+
+    socket.on('msg', (final SocketIOEventData data) {
+      print('Received msg in /some: $data');
+      socket.emit('fromServer', 'Message received in /some: $data');
+    });
+
+    socket.on('disconnect', (final SocketIOEventData data) {
+      print('Client disconnected from /some: ${socket.id}');
+    });
+  });
+
+  await io.listen(3005);
+  print('Server listening on port 3005');
+}
diff --git a/example/package.json b/example/package.json
new file mode 100644
index 0000000..a286a35
--- /dev/null
+++ b/example/package.json
@@ -0,0 +1,6 @@
+{
+  "dependencies": {
+    "socket.io": "4.6.2",
+    "socket.io-client": "^4.8.1"
+  }
+}
diff --git a/example/polling_smoke.dart b/example/polling_smoke.dart
new file mode 100644
index 0000000..a58c328
--- /dev/null
+++ b/example/polling_smoke.dart
@@ -0,0 +1,71 @@
+import 'dart:convert';
+import 'dart:io';
+
+const String host = 'localhost';
+const int port = 3005;
+const String path = '/socket.io/';
+
+Future main() async {
+  stdout.writeln('Running raw polling smoke check against http://$host:$port');
+
+  final bool rawOk = await _runRawPollingCheck();
+
+  if (!rawOk) {
+    stderr.writeln('Polling smoke failed: raw=$rawOk');
+    exit(1);
+  }
+
+  stdout.writeln('Polling smoke passed');
+}
+
+Future _runRawPollingCheck() async {
+  stdout.writeln('1) Raw Engine.IO polling check');
+  final HttpClient client = HttpClient();
+
+  try {
+    final HttpClientRequest openReq = await client.getUrl(Uri.parse(
+      'http://$host:$port$path?EIO=4&transport=polling&t=${DateTime.now().millisecondsSinceEpoch}',
+    ));
+    final HttpClientResponse openRes = await openReq.close();
+    if (openRes.statusCode != 200) return false;
+    final String openData = await openRes.transform(utf8.decoder).join();
+
+    final Match? sidMatch = RegExp(r'"sid":"([^"]+)"').firstMatch(openData);
+    if (sidMatch == null) return false;
+    final String sid = sidMatch.group(1)!;
+
+    final HttpClientRequest connectReq = await client.postUrl(
+      Uri.parse('http://$host:$port$path?EIO=4&transport=polling&sid=$sid'),
+    );
+    connectReq.headers.contentType = ContentType('text', 'plain', charset: 'utf-8');
+    connectReq.write('40');
+    final HttpClientResponse connectRes = await connectReq.close();
+    if (connectRes.statusCode != 200) return false;
+    await connectRes.drain();
+
+    final HttpClientRequest eventReq = await client.postUrl(
+      Uri.parse('http://$host:$port$path?EIO=4&transport=polling&sid=$sid'),
+    );
+    eventReq.headers.contentType = ContentType('text', 'plain', charset: 'utf-8');
+    eventReq.write('42["msg","hello from raw polling"]');
+    final HttpClientResponse eventRes = await eventReq.close();
+    if (eventRes.statusCode != 200) return false;
+    await eventRes.drain();
+
+    final HttpClientRequest pollReq = await client.getUrl(Uri.parse(
+      'http://$host:$port$path?EIO=4&transport=polling&sid=$sid&t=${DateTime.now().millisecondsSinceEpoch}',
+    ));
+    final HttpClientResponse pollRes = await pollReq.close();
+    if (pollRes.statusCode != 200) return false;
+    final String pollData = await pollRes.transform(utf8.decoder).join();
+
+    final bool ok = pollData.contains('fromServer') || pollData.contains('Message received');
+    stdout.writeln('   raw result: $ok');
+    return ok;
+  } catch (error) {
+    stderr.writeln('   raw check error: $error');
+    return false;
+  } finally {
+    client.close(force: true);
+  }
+}
diff --git a/lib/socket_io.dart b/lib/socket_io.dart
index d8148d5..46e8bf6 100644
--- a/lib/socket_io.dart
+++ b/lib/socket_io.dart
@@ -1,11 +1,147 @@
+/// socket_io - A modern, type-safe Socket.IO server implementation for Dart.
+///
+/// This library provides a Dart implementation of the Socket.IO protocol for
+/// real-time bidirectional event-based communication.
+///
+/// ## Features
+///
+/// * **Real-time Communication**: WebSocket with automatic fallback to HTTP long-polling
+/// * **Type Safety**: Modern Dart 3.0+ with value objects and sealed classes
+/// * **Namespaces**: Organize connections into separate communication channels
+/// * **Rooms**: Group sockets for targeted message broadcasting
+/// * **Binary Support**: Send and receive binary data efficiently
+/// * **Backward Compatible**: Dual API supports both legacy and modern patterns
+///
+/// ## Quick Start
+///
+/// ### Server Setup
+///
+/// ```dart
+/// import 'package:socket_io/socket_io.dart';
+///
+/// void main() {
+///   final io = Server();
+///
+///   io.on('connection', (socket) {
+///     print('Client connected: ${socket.id}');
+///
+///     socket.on('message', (data) {
+///       print('Received: $data');
+///       socket.emit('response', ['Message received']);
+///     });
+///
+///     socket.on('disconnect', (reason) {
+///       print('Client disconnected: $reason');
+///     });
+///   });
+///
+///   io.listen(3000);
+///   print('Socket.IO server running on port 3000');
+/// }
+/// ```
+///
+/// ### Namespaces
+///
+/// ```dart
+/// // Default namespace
+/// io.on('connection', (socket) {
+///   print('Connected to default namespace');
+/// });
+///
+/// // Custom namespace
+/// final chat = io.of('/chat');
+/// chat.on('connection', (socket) {
+///   socket.join('general');
+///   chat.to('general').emit('user-joined', [socket.id]);
+/// });
+/// ```
+///
+/// ### Rooms
+///
+/// ```dart
+/// io.on('connection', (socket) {
+///   // Join room
+///   socket.join('room1');
+///
+///   // Emit to room
+///   io.to('room1').emit('announcement', ['Welcome!']);
+///
+///   // Broadcast to room except sender
+///   socket.broadcast.to('room1').emit('message', ['User joined']);
+///
+///   // Leave room
+///   socket.leave('room1');
+/// });
+/// ```
+///
+/// ## Type-Safe API
+///
+/// This library provides a modern type-safe API using value objects and
+/// domain models:
+///
+/// ```dart
+/// // Value objects for type safety
+/// final roomName = RoomName('chatRoom');
+/// final eventName = EventName('customEvent');
+/// final timeout = TimeoutDuration.seconds(30);
+///
+/// // Typed handshake data
+/// final handshake = socket.handshakeData;
+/// print('Connected from: ${handshake?.address}');
+/// print('Secure: ${handshake?.secure}');
+///
+/// // Typed socket data
+/// socket.socketData.set('userId', 123);
+/// final userId = socket.socketData.getInt('userId');
+/// ```
+///
+/// ## Main Classes
+///
+/// * [Server] - The main Socket.IO server
+/// * [Namespace] - Separate communication channels
+/// * [Socket] - Individual client connections
+///
+/// ## Transport Classes
+///
+/// * [WebSocketTransport] - Full-duplex WebSocket transport
+/// * [PollingTransport] - HTTP long-polling fallback
+/// * [JSONPTransport] - Legacy JSONP support
+///
+/// ## See Also
+///
+/// * [socket_io_common](https://pub.dev/packages/socket_io_common) - Shared components
+/// * [socket_io_client](https://pub.dev/packages/socket_io_client) - Dart client
+/// * [Socket.IO Documentation](https://socket.io/docs/) - Official protocol docs
 library socket_io;
 
-export 'src/server.dart';
-export 'src/socket.dart';
-export 'src/engine/transport/transports.dart' show Transport, MessageHandler;
+export 'package:socket_io_common/src/engine/parser/parser.dart' show PacketParser;
+export 'package:socket_io_common/src/parser/parser.dart'
+    show ACK, BINARY_ACK, BINARY_EVENT, CONNECT, CONNECT_ERROR, DISCONNECT, Decoder, EVENT, Encoder;
+
+export 'src/client.dart';
 export 'src/engine/transport/jsonp_transport.dart' show JSONPTransport;
 export 'src/engine/transport/polling_transport.dart' show PollingTransport;
+export 'src/engine/transport/transports.dart' show MessageHandler, Transport;
 export 'src/engine/transport/websocket_transport.dart' show WebSocketTransport;
-
-export 'package:socket_io_common/src/engine/parser/parser.dart'
-    show PacketParser;
+export 'src/models/packet_models.dart';
+export 'src/models/server_options_models.dart'
+    show
+        AllowRequestCallback,
+        AttachmentOptionsModel,
+        CookieConfig,
+        CookiePathConfig,
+        DisabledCookie,
+        DisabledCookiePath,
+        EnabledCookie,
+        EnabledCookiePath,
+        HttpCompressionConfig,
+        PerMessageDeflateConfig,
+        ServerOptionsModel;
+export 'src/namespace.dart' show Namespace;
+export 'src/server.dart' show Server;
+export 'src/socket.dart' show Socket;
+export 'src/util/event_emitter.dart' show EventHandler, SocketIOEventData;
+export 'src/value_objects/event_name_vo.dart';
+export 'src/value_objects/timeout_duration_vo.dart';
+export 'src/value_objects/transport_name_vo.dart';
+export 'src/value_objects/url_path_vo.dart';
diff --git a/lib/src/adapter.dart b/lib/src/adapter.dart
new file mode 100644
index 0000000..8fb392d
--- /dev/null
+++ b/lib/src/adapter.dart
@@ -0,0 +1,232 @@
+// adapter.dart
+//
+// Purpose:
+//
+// Description:
+//
+// History:
+//    16/02/2017, Created by jumperchen
+//
+// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+import 'dart:async';
+
+import 'models/callbacks_models.dart';
+import 'namespace.dart';
+import 'socket.dart';
+import 'util/event_emitter.dart';
+
+/// Room constructor.
+///
+/// @api private
+class _Room {
+  Map sockets = {};
+  int length = 0;
+
+  /// Adds a socket to a room.
+  ///
+  /// @param {String} socket id
+  /// @api private
+  void add(final String id) {
+    if (!sockets.containsKey(id)) {
+      sockets[id] = true;
+      length++;
+    }
+  }
+
+  /// Removes a socket from a room.
+  ///
+  /// @param {String} socket id
+  /// @api private
+  void del(final String id) {
+    if (sockets.containsKey(id)) {
+      sockets.remove(id);
+      length--;
+    }
+  }
+}
+
+abstract class Adapter {
+  Map nsps = {};
+  Map rooms = {};
+  Map> sids = >{};
+
+  void add(final String id, final String room, [final ErrorableCallback? fn]);
+  void del(final String id, final String room, [final ErrorableCallback? fn]);
+  void delAll(final String id, [final ErrorableCallback? fn]);
+  void broadcast(final Map packet, [final Map? opts]);
+  void clients([final List? rooms, final ClientsStringCallback? fn]);
+  void clientRooms(final String id, [final void Function(Object?, Iterable?)? fn]);
+
+  static Adapter newInstance(final String key, final Namespace nsp) {
+    if ('default' == key) {
+      return _MemoryStoreAdapter(nsp);
+    }
+    throw UnimplementedError('not supported other adapter yet.');
+  }
+}
+
+class _MemoryStoreAdapter extends EventEmitter implements Adapter {
+  @override
+  Map nsps = {};
+  @override
+  Map rooms = {};
+
+  @override
+  Map> sids = >{};
+  late dynamic encoder;
+  late Namespace nsp;
+
+  _MemoryStoreAdapter(this.nsp) {
+    encoder = nsp.server.encoder;
+  }
+
+  /// Adds a socket to a room.
+  ///
+  /// @param {String} socket id
+  /// @param {String} room name
+  /// @param {Function} callback
+  /// @api public
+
+  @override
+  void add(final String id, final String room, [final ErrorableCallback? fn]) {
+    sids[id] = sids[id] ?? {};
+    sids[id]![room] = true;
+    rooms[room] = rooms[room] ?? _Room();
+    rooms[room]!.add(id);
+    if (fn != null) scheduleMicrotask(() => fn(null));
+  }
+
+  /// Removes a socket from a room.
+  ///
+  /// @param {String} socket id
+  /// @param {String} room name
+  /// @param {Function} callback
+  /// @api public
+  @override
+  void del(final String id, final String room, [final ErrorableCallback? fn]) {
+    final Map? rooms = sids[id];
+    if (rooms != null && rooms.containsKey(room)) {
+      rooms.remove(room);
+      this.rooms[room]?.del(id);
+      if (this.rooms[room]?.length == 0) this.rooms.remove(room);
+    }
+    if (fn != null) scheduleMicrotask(() => fn(null));
+  }
+
+  /// Removes a socket from all rooms it's joined.
+  ///
+  /// @param {String} socket id
+  /// @param {Function} callback
+  /// @api public
+  @override
+  void delAll(final String id, [final ErrorableCallback? fn]) {
+    final Map? rooms = sids[id];
+    if (rooms != null) {
+      for (final String room in rooms.keys) {
+        if (this.rooms.containsKey(room)) {
+          this.rooms[room]!.del(id);
+          if (this.rooms[room]!.length == 0) this.rooms.remove(room);
+        }
+      }
+    }
+    sids.remove(id);
+
+    if (fn != null) scheduleMicrotask(() => fn(null));
+  }
+
+  /// Broadcasts a packet.
+  ///
+  /// Options:
+  ///  - `flags` {Object} flags for this packet
+  ///  - `except` {Array} sids that should be excluded
+  ///  - `rooms` {Array} list of rooms to broadcast to
+  ///
+  /// @param {Object} packet object
+  /// @api public
+  @override
+  void broadcast(final Map packet, [Map? opts]) {
+    final Map options = opts ?? {};
+    final List rooms = (options['rooms'] as List?)?.cast() ?? [];
+    final List except = (options['except'] as List?)?.cast() ?? [];
+    final Map flags = (options['flags'] as Map?) ?? {};
+    final Map packetOpts = {
+      'preEncoded': true,
+      'volatile': flags['volatile'],
+      'compress': flags['compress']
+    };
+    final Set ids = {};
+
+    packet['nsp'] = nsp.name;
+    final dynamic encodedPackets = encoder.encode(packet);
+    if (rooms.isNotEmpty) {
+      for (int i = 0; i < rooms.length; i++) {
+        final _Room? room = this.rooms[rooms[i]];
+        if (room == null) continue;
+        final Map sockets = room.sockets;
+        for (final String id in sockets.keys) {
+          if (sockets.containsKey(id)) {
+            if (ids.contains(id) || except.contains(id)) continue;
+            final Socket? socket = nsp.connected[id];
+            if (socket != null) {
+              socket.packet(encodedPackets, packetOpts);
+              ids.add(id);
+            }
+          }
+        }
+      }
+    } else {
+      for (final String id in sids.keys) {
+        if (except.contains(id)) continue;
+        final Socket? socket = nsp.connected[id];
+        if (socket != null) socket.packet(encodedPackets, packetOpts);
+      }
+    }
+  }
+
+  /// Gets a list of clients by sid.
+  ///
+  /// @param {Array} explicit set of rooms to check.
+  /// @param {Function} callback
+  /// @api public
+  @override
+  void clients([final List? rooms, final ClientsStringCallback? fn]) {
+    final Set ids = {};
+    final List sids = [];
+
+    if (rooms != null && rooms.isNotEmpty) {
+      for (int i = 0; i < rooms.length; i++) {
+        final _Room? room = this.rooms[rooms[i]];
+        if (room == null) continue;
+        final Map sockets = room.sockets;
+        for (final String id in sockets.keys) {
+          if (sockets.containsKey(id)) {
+            if (ids.contains(id)) continue;
+            final Socket? socket = nsp.connected[id];
+            if (socket != null) {
+              sids.add(id);
+              ids.add(id);
+            }
+          }
+        }
+      }
+    } else {
+      for (final String id in this.sids.keys) {
+        final Socket? socket = nsp.connected[id];
+        if (socket != null) sids.add(id);
+      }
+    }
+
+    if (fn != null) scheduleMicrotask(() => fn(sids));
+  }
+
+  /// Gets the list of rooms a given client has joined.
+  ///
+  /// @param {String} socket id
+  /// @param {Function} callback
+  /// @api public
+  @override
+  void clientRooms(final String id, [final void Function(Object?, Iterable?)? fn]) {
+    final Map? rooms = sids[id];
+    if (fn != null) scheduleMicrotask(() => fn(null, rooms?.keys));
+  }
+}
diff --git a/lib/src/client.dart b/lib/src/client.dart
index 745ebd5..cf739c4 100644
--- a/lib/src/client.dart
+++ b/lib/src/client.dart
@@ -1,29 +1,37 @@
-/// client.dart
-///
-/// Purpose:
-///
-/// Description:
-///
-/// History:
-///    22/02/2017, Created by jumperchen
-///
-/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+// client.dart
+//
+// Purpose:
+//
+// Description:
+//
+// History:
+//    22/02/2017, Created by jumperchen
+//
+// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+import 'dart:io';
+
 import 'package:logging/logging.dart';
 
-import 'package:socket_io/src/engine/socket.dart';
-import 'package:socket_io_common/src/parser/parser.dart';
-import 'package:socket_io/src/server.dart';
+import '../socket_io.dart' show CONNECT, CONNECT_ERROR, Decoder, Encoder;
+import 'engine/socket.dart' as engine;
+import 'models/connect_buffer_models.dart';
+import 'models/packet_models.dart';
+import 'models/socket_error_models.dart';
+import 'namespace.dart';
+import 'server.dart';
+import 'socket.dart' as io;
+import 'types/common_types.dart';
 
 class Client {
   Server server;
-  Socket conn;
-  dynamic id;
-  dynamic request;
+  engine.Socket conn;
+  String id;
+  HttpRequest request;
   Encoder encoder = Encoder();
   Decoder decoder = Decoder();
-  List sockets = [];
-  Map nsps = {};
-  List connectBuffer = [];
+  List sockets = [];
+  Map nsps = {};
+  ConnectBuffer connectBuffer = ConnectBuffer();
   final Logger _logger = Logger('socket_io:Client');
 
   /// Client constructor.
@@ -41,40 +49,45 @@ class Client {
   ///
   /// @api private
   void setup() {
-    decoder.on('decoded', ondecoded);
-    conn.on('data', ondata);
-    conn.on('error', onerror);
-    conn.on('close', onclose);
+    decoder.on('decoded', (final dynamic data) => ondecoded(data as JsonMap));
+    conn
+      ..on('data', (final dynamic data) => ondata(data as Object))
+      ..on('error', (final dynamic err) => onerror(err as Object))
+      ..on('close', (final dynamic reason) => onclose(reason is String ? reason : reason.toString()));
   }
 
   /// Connects a client to a namespace.
   ///
   /// @param {String} namespace name
   /// @api private
-  void connect(String name, [query]) {
+  void connect(final String name, [final Map? query]) {
     _logger.fine('connecting to namespace $name');
     if (!server.nsps.containsKey(name)) {
-      packet({
-        'type': ERROR,
-        'nsp': name,
-        'data': 'Invalid namespace'
-      });
+      packet({'type': CONNECT_ERROR, 'nsp': name, 'data': 'Invalid namespace'});
+      return;
+    }
+
+    // Socket.IO v3: Check if already connected to this namespace
+    // If so, the client is just sending the CONNECT packet (v3 protocol)
+    // The client already received the CONNECT acknowledgment, so just ignore this
+    if (nsps.containsKey(name)) {
+      _logger.fine('already connected to namespace $name, ignoring duplicate CONNECT (client: ${conn.transport.name})');
       return;
     }
-    var nsp = server.of(name);
+
+    final Namespace nsp = server.of(name);
     if ('/' != name && !nsps.containsKey('/')) {
       connectBuffer.add(name);
       return;
     }
 
-    var self = this;
-    nsp.add(this, query, (socket) {
+    final Client self = this;
+    nsp.add(this, query, (final io.Socket socket) {
       self.sockets.add(socket);
       self.nsps[nsp.name] = socket;
 
       if ('/' == nsp.name && self.connectBuffer.isNotEmpty) {
-        self.connectBuffer.forEach(self.connect);
-        self.connectBuffer = [];
+        self.connectBuffer.processAll(self.connect);
       }
     });
   }
@@ -85,7 +98,7 @@ class Client {
   void disconnect() {
     // we don't use a for loop because the length of
     // `sockets` changes upon each iteration
-    sockets.toList().forEach((socket) {
+    sockets.toList().forEach((final io.Socket socket) {
       socket.disconnect();
     });
     sockets.clear();
@@ -96,10 +109,10 @@ class Client {
   /// Removes a socket. Called by each `Socket`.
   ///
   /// @api private
-  void remove(socket) {
-    var i = sockets.indexOf(socket);
-    if (i >= 0) {
-      var nsp = sockets[i].nsp.name;
+  void remove(final io.Socket socket) {
+    final int i = sockets.indexOf(socket);
+    if (i != -1) {
+      final String nsp = sockets[i].nsp.name;
       sockets.removeAt(i);
       nsps.remove(nsp);
     } else {
@@ -123,44 +136,54 @@ class Client {
   /// @param {Object} packet object
   /// @param {Object} options
   /// @api private
-  void packet(packet, [Map? opts]) {
-    var self = this;
-    opts ??= {};
+  void packet(final Object packet, [final Map? opts]) {
+    final Client self = this;
+    final Map options = opts ?? {};
     // this writes to the actual connection
-    void writeToEngine(encodedPackets) {
-      if (opts!['volatile'] != null && self.conn.transport.writable != true) {
+    void writeToEngine(final List encodedPackets) {
+      final bool isVolatile = options['volatile'] == true;
+      if (isVolatile && self.conn.transport.writable != true) {
         return;
       }
-      for (var i = 0; i < encodedPackets.length; i++) {
-        self.conn.write(encodedPackets[i], {'compress': opts['compress']});
+      final bool compress = options['compress'] == true;
+      for (int i = 0; i < encodedPackets.length; i++) {
+        self.conn.write(encodedPackets[i], {'compress': compress});
       }
     }
 
     if ('open' == conn.readyState) {
       _logger.fine('writing packet $packet');
-      if (opts['preEncoded'] != true) {
+      if (options['preEncoded'] != true) {
         // not broadcasting, need to encode
-        encoder.encode(packet, (encodedPackets) {
-          // encode, then write results to engine
-          writeToEngine(encodedPackets);
-        });
+        final List encodedPackets = List.from(encoder.encode(packet as Map));
+        // encode, then write results to engine
+        writeToEngine(encodedPackets);
       } else {
         // a broadcast pre-encodes a packet
-        writeToEngine(packet);
+        writeToEngine(List.from(packet as List));
       }
     } else {
       _logger.fine('ignoring packet write $packet');
     }
   }
 
+  /// Writes a typed packet to the transport (type-safe alternative).
+  ///
+  /// @param {SocketIOPacket} packet - The typed packet to send
+  /// @param {Map} opts - Optional transmission options
+  /// @api private
+  void sendPacket(final SocketIOPacket packet, [final Map? opts]) {
+    this.packet(packet.toMap(), opts);
+  }
+
   /// Called with incoming transport data.
   ///
   /// @api private
-  void ondata(data) {
+  void ondata(final Object data) {
     // try/catch is needed for protocol violations (GH-1880)
     try {
       decoder.add(data);
-    } catch (e, st) {
+    } on Object catch (e, st) {
       _logger.severe(e, st);
       onerror(e);
     }
@@ -169,13 +192,14 @@ class Client {
   /// Called when parser fully decodes a packet.
   ///
   /// @api private
-  void ondecoded(packet) {
+  void ondecoded(final Map packet) {
+    _logger.fine('decoded packet: ${packet['type']} for namespace ${packet['nsp']}');
     if (CONNECT == packet['type']) {
-      final nsp = packet['nsp'];
-      final uri = Uri.parse(nsp);
+      final String nsp = packet['nsp'] as String;
+      final Uri uri = Uri.parse(nsp);
       connect(uri.path, uri.queryParameters);
     } else {
-      var socket = nsps[packet['nsp']];
+      final io.Socket? socket = nsps[packet['nsp']];
       if (socket != null) {
         socket.onpacket(packet);
       } else {
@@ -186,20 +210,28 @@ class Client {
 
   /// Handles an error.
   ///
-  /// @param {Objcet} error object
+  /// @param {Object} error object
   /// @api private
-  void onerror(err) {
-    sockets.forEach((socket) {
+  void onerror(final Object err) {
+    for (final io.Socket socket in sockets) {
       socket.onerror(err);
-    });
+    }
     onclose('client error');
   }
 
+  /// Handles an error with type-safe wrapper (preferred).
+  ///
+  /// @param {SocketErrorModel} error - The typed error model
+  /// @api private
+  void onErrorTyped(final SocketErrorModel error) {
+    onerror(error);
+  }
+
   /// Called upon transport close.
   ///
   /// @param {String} reason
   /// @api private
-  void onclose(reason) {
+  void onclose(final String reason) {
     _logger.fine('client close with reason $reason');
 
     // ignore a potential subsequent `close` event
@@ -207,9 +239,9 @@ class Client {
 
     // `nsps` and `sockets` are cleaned up seamlessly
     if (sockets.isNotEmpty) {
-      List.from(sockets).forEach((socket) {
+      for (final io.Socket socket in sockets.toList()) {
         socket.onclose(reason);
-      });
+      }
       sockets.clear();
     }
     decoder.destroy(); // clean up decoder
@@ -219,9 +251,10 @@ class Client {
   ///
   /// @api private
   void destroy() {
-    conn.off('data', ondata);
-    conn.off('error', onerror);
-    conn.off('close', onclose);
-    decoder.off('decoded', ondecoded);
+    conn
+      ..off('data', (final Object? data) => ondata(data as Object))
+      ..off('error', (final Object? err) => onerror(err as Object))
+      ..off('close', (final Object? reason) => onclose(reason as String));
+    decoder.off('decoded', (final Object? data) => ondecoded(data as JsonMap));
   }
 }
diff --git a/lib/src/engine/connect.dart b/lib/src/engine/connect.dart
index 4ad543d..03ffd13 100644
--- a/lib/src/engine/connect.dart
+++ b/lib/src/engine/connect.dart
@@ -1,25 +1,25 @@
-/// connect.dart
-///
-/// Purpose:
-///
-/// Description:
-///
-/// History:
-///    06/03/2017, Created by jumperchen
-///
-/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
-import 'package:stream/stream.dart';
+// connect.dart
+//
+// Purpose:
+//
+// Description:
+//
+// History:
+//    06/03/2017, Created by jumperchen
+//
+// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
 import 'dart:async';
 import 'dart:io';
 
+import 'package:stream/stream.dart';
+
 class SocketConnect extends HttpConnectWrapper {
   WebSocket? _socket;
-  Completer? _done;
+  Completer? _done;
   bool? _completed;
-  SocketConnect(HttpConnect origin) : super(origin);
+  SocketConnect(super.origin);
 
-  SocketConnect.fromWebSocket(HttpConnect origin, WebSocket socket)
-      : super(origin) {
+  SocketConnect.fromWebSocket(super.origin, final WebSocket socket) {
     _socket = socket;
   }
 
@@ -27,24 +27,24 @@ class SocketConnect extends HttpConnectWrapper {
 
   WebSocket? get websocket => _socket;
 
-  Future get done {
+  Future get done {
     if (_completed == true) {
-      return Future.value('done');
+      return Future.value('done');
     }
     if (_socket != null) {
-      return _socket!.done;
+      return _socket!.done.then((final dynamic _) => 'done');
     } else {
-      _done = Completer();
+      _done = Completer();
       return _done!.future;
     }
   }
 
   /// Closes the current connection.
-  void close() {
+  Future close() async {
     if (_done != null) {
       _done!.complete('done');
     } else if (_socket != null) {
-      _socket!.close();
+      await _socket!.close();
     } else {
       _completed = true;
     }
diff --git a/lib/src/engine/engine.dart b/lib/src/engine/engine.dart
index a2672d7..094b37c 100644
--- a/lib/src/engine/engine.dart
+++ b/lib/src/engine/engine.dart
@@ -1,30 +1,31 @@
-/// engine.dart
-///
-/// Purpose:
-///
-/// Description:
-///
-/// History:
-///    16/02/2017, Created by jumperchen
-///
-/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
-import 'package:socket_io/src/engine/server.dart';
-import 'package:socket_io/src/util/event_emitter.dart';
+// engine.dart
+//
+// Purpose:
+//
+// Description:
+//
+// History:
+//    16/02/2017, Created by jumperchen
+//
+// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+import 'package:stream/stream.dart';
 
-class Engine extends EventEmitter {
-  static Engine attach(server, [Map? options]) {
-    var engine = Server(options);
-    engine.attachTo(server, options);
+import '../util/event_emitter.dart';
+import 'server.dart';
+
+abstract class Engine extends EventEmitter {
+  static Server attach(final StreamServer server, [final Map? options]) {
+    final Server engine = Server.fromMap(options)..attachTo(server, options);
     return engine;
   }
 
-  dynamic operator [](Object key) {}
+  dynamic operator [](final Object key) {}
 
   /// Associates the [key] with the given [value].
   ///
   /// If the key was already in the map, its associated value is changed.
   /// Otherwise the key-value pair is added to the map.
-  void operator []=(String key, dynamic value) {}
+  void operator []=(final String key, final dynamic value) {}
 //  init() {}
 //  upgrades() {}
 //  verify() {}
diff --git a/lib/src/engine/server.dart b/lib/src/engine/server.dart
index 4c552bb..f7ae8be 100644
--- a/lib/src/engine/server.dart
+++ b/lib/src/engine/server.dart
@@ -1,23 +1,30 @@
-/// server
-///
-/// Purpose:
-///
-/// Description:
-///
-/// History:
-///    17/02/2017, Created by jumperchen
-///
-/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+// server
+//
+// Purpose:
+//
+// Description:
+//
+// History:
+//    17/02/2017, Created by jumperchen
+//
+// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+import 'dart:async' show unawaited;
 import 'dart:convert';
 import 'dart:io' hide Socket;
+
 import 'package:logging/logging.dart';
-import 'package:socket_io/src/engine/connect.dart';
-import 'package:socket_io/src/engine/engine.dart';
-import 'package:socket_io/src/engine/socket.dart';
-import 'package:socket_io/src/engine/transport/transports.dart';
 import 'package:stream/stream.dart';
 import 'package:uuid/uuid.dart';
 
+import '../models/server_options_models.dart';
+import '../value_objects/timeout_duration_vo.dart';
+import '../value_objects/transport_name_vo.dart';
+import '../value_objects/url_path_vo.dart';
+import 'connect.dart';
+import 'engine.dart';
+import 'socket.dart';
+import 'transport/transports.dart';
+
 /// Server constructor.
 ///
 /// @param {Object} options
@@ -30,7 +37,7 @@ class ServerErrors {
   static const int FORBIDDEN = 4;
 }
 
-const Map ServerErrorMessages = {
+const Map ServerErrorMessages = {
   0: 'Transport unknown',
   1: 'Session ID unknown',
   2: 'Bad handshake method',
@@ -40,59 +47,45 @@ const Map ServerErrorMessages = {
 
 class Server extends Engine {
   static final Logger _logger = Logger('socket_io:engine.Server');
-  Map clients = {};
+  Map clients = {};
   int clientsCount = 0;
-  late int pingTimeout;
-  late int pingInterval;
-  late int upgradeTimeout;
+  late TimeoutDuration pingTimeout;
+  late TimeoutDuration pingInterval;
+  late TimeoutDuration upgradeTimeout;
   late double maxHttpBufferSize;
-  List transports = ['polling', 'websocket'];
+  late List transports;
   late bool allowUpgrades;
-  Function? allowRequest;
-  late dynamic cookie;
-  late dynamic cookiePath;
+  AllowRequestCallback? allowRequest;
+  late CookieConfig cookie;
+  late CookiePathConfig cookiePath;
   late bool cookieHttpOnly;
-  late Map perMessageDeflate;
-  late Map httpCompression;
-  dynamic initialPacket;
-  final Uuid _uuid = Uuid();
-
-  Server([Map? opts]) {
-    opts = opts ?? {};
-
-    pingTimeout = opts['pingTimeout'] ?? 60000;
-    pingInterval = opts['pingInterval'] ?? 25000;
-    upgradeTimeout = opts['upgradeTimeout'] ?? 10000;
-    maxHttpBufferSize = opts['maxHttpBufferSize'] ?? 10E7;
-    allowUpgrades = false != opts['allowUpgrades'];
-    allowRequest = opts['allowRequest'];
-    cookie = opts['cookie'] == false
-        ? false
-        : opts['cookie'] ??
-            'io'; //false != opts.cookie ? (opts.cookie || 'io') : false;
-    cookiePath = opts['cookiePath'] == false
-        ? false
-        : opts['cookiePath'] ??
-            '/'; //false != opts.cookiePath ? (opts.cookiePath || '/') : false;
-    cookieHttpOnly = opts['cookieHttpOnly'] != false;
-
-    if (!opts.containsKey('perMessageDeflate') ||
-        opts['perMessageDeflate'] == true) {
-      perMessageDeflate =
-          opts['perMessageDeflate'] is Map ? opts['perMessageDeflate'] : {};
-      if (!perMessageDeflate.containsKey('threshold')) {
-        perMessageDeflate['threshold'] = 1024;
-      }
-    }
-    httpCompression = opts['httpCompression'] ?? {};
-    if (!httpCompression.containsKey('threshold')) {
-      httpCompression['threshold'] = 1024;
-    }
-
-    initialPacket = opts['initialPacket'];
+  late PerMessageDeflateConfig perMessageDeflate;
+  late HttpCompressionConfig httpCompression;
+  Object? initialPacket;
+  late UrlPath path;
+  final Uuid _uuid = const Uuid();
+
+  Server(final ServerOptionsModel options) {
+    pingTimeout = options.pingTimeout;
+    pingInterval = options.pingInterval;
+    upgradeTimeout = options.upgradeTimeout;
+    maxHttpBufferSize = options.maxHttpBufferSize;
+    transports = List.from(options.transports);
+    allowUpgrades = options.allowUpgrades;
+    allowRequest = options.allowRequest;
+    cookie = options.cookie;
+    cookiePath = options.cookiePath;
+    cookieHttpOnly = options.cookieHttpOnly;
+    perMessageDeflate = options.perMessageDeflate;
+    httpCompression = options.httpCompression;
+    initialPacket = options.initialPacket;
+    path = options.path;
     _init();
   }
 
+  /// Create from Map (legacy support)
+  factory Server.fromMap(final Map? opts) => Server(ServerOptionsModel.fromMap(opts));
+
   /// Initialize websocket server
   ///
   /// @api private
@@ -123,8 +116,8 @@ class Server extends Engine {
   /// @return {Array}
   /// @api public
 
-  List upgrades(String transport) {
-    if (!allowUpgrades) return List.empty();
+  List upgrades(final String transport) {
+    if (!allowUpgrades) return List.empty();
     return Transports.upgradesTo(transport);
   }
 
@@ -133,33 +126,40 @@ class Server extends Engine {
   /// @param {http.IncomingMessage}
   /// @return {Boolean} whether the request is valid
   /// @api private
-
-  void verify(SocketConnect connect, bool upgrade, fn) {
+  void verify(final SocketConnect connect, final bool upgrade, final void Function(Object?, bool) fn) {
     // transport check
-    var req = connect.request;
-    var transport = req.uri.queryParameters['transport'];
-    if (!transports.contains(transport)) {
+    final HttpRequest req = connect.request;
+    final String? transport = req.uri.queryParameters['transport'];
+    final bool transportSupported =
+        transport != null && transports.any((final TransportName t) => t.value == transport);
+    if (!transportSupported) {
       _logger.fine('unknown transport "$transport"');
       return fn(ServerErrors.UNKNOWN_TRANSPORT, false);
     }
 
     // sid check
-    var sid = req.uri.queryParameters['sid'];
+    final String? sid = req.uri.queryParameters['sid'];
     if (sid != null) {
       if (!clients.containsKey(sid)) {
         return fn(ServerErrors.UNKNOWN_SID, false);
       }
-      if (!upgrade && clients[sid].transport.name != transport) {
+      final Socket? client = clients[sid];
+      if (!upgrade && client != null && client.transport.name != transport) {
         _logger.fine('bad request: unexpected transport without upgrade');
         return fn(ServerErrors.BAD_REQUEST, false);
       }
     } else {
+      if ('OPTIONS' == req.method) {
+        return fn(null, true);
+      }
+
       // handshake is GET only
       if ('GET' != req.method) {
         return fn(ServerErrors.BAD_HANDSHAKE_METHOD, false);
       }
       if (allowRequest == null) return fn(null, true);
-      return allowRequest!(req, fn);
+      allowRequest!(req, fn);
+      return;
     }
 
     fn(null, true);
@@ -168,14 +168,11 @@ class Server extends Engine {
   /// Closes all clients.
   ///
   /// @api public
-
   @override
   void close() {
     _logger.fine('closing all open clients');
-    for (var key in clients.keys.toList(growable: false)) {
-      if (clients[key] != null) {
-        clients[key].close(true);
-      }
+    for (final String key in clients.keys.toList(growable: false)) {
+      clients[key]?.close(true);
     }
 //  if (this.ws) {
 //    _logger.fine('closing webSocketServer');
@@ -189,24 +186,22 @@ class Server extends Engine {
   /// @param {http.IncomingMessage} request
   /// @param {http.ServerResponse|http.OutgoingMessage} response
   /// @api public
-
-  void handleRequest(SocketConnect connect) {
-    var req = connect.request;
+  void handleRequest(final SocketConnect connect) {
+    final HttpRequest req = connect.request;
     _logger.fine('handling ${req.method} http request ${req.uri.path}');
-//  this.prepare(req);
-//  req.res = res;
 
-    var self = this;
-    verify(connect, false, (err, success) {
+    final Server self = this;
+    verify(connect, false, (final Object? err, final bool success) {
       if (!success) {
         sendErrorMessage(req, err);
         return;
       }
-//print('sid ${req.uri.queryParameters['sid']}');
-      if (req.uri.queryParameters['sid'] != null) {
+
+      final String? sid = req.uri.queryParameters['sid'];
+      if (sid != null) {
         _logger.fine('setting new request for existing client');
-        self.clients[req.uri.queryParameters['sid']].transport
-            .onRequest(connect);
+        final Socket? client = self.clients[sid];
+        client?.transport.onRequest(connect);
       } else {
         self.handshake(req.uri.queryParameters['transport'] as String, connect);
       }
@@ -218,29 +213,30 @@ class Server extends Engine {
   /// @param {http.ServerResponse} response
   /// @param {code} error code
   /// @api private
-
-  static void sendErrorMessage(HttpRequest req, code) {
-    var res = req.response;
-    var isForbidden = !ServerErrorMessages.containsKey(code);
+  static void sendErrorMessage(final HttpRequest req, final Object? code) {
+    final HttpResponse res = req.response;
+    final bool isForbidden = !ServerErrorMessages.containsKey(code);
     if (isForbidden) {
-      res.statusCode = HttpStatus.forbidden;
-      res.headers.contentType = ContentType.json;
-      res.write(json.encode({
-        'code': ServerErrors.FORBIDDEN,
-        'message': code ?? ServerErrorMessages[ServerErrors.FORBIDDEN]
-      }));
+      res
+        ..statusCode = HttpStatus.forbidden
+        ..headers.contentType = ContentType.json
+        ..write(json.encode({
+          'code': ServerErrors.FORBIDDEN,
+          'message': code ?? ServerErrorMessages[ServerErrors.FORBIDDEN]
+        }));
+      unawaited(res.close());
       return;
     }
     if (req.headers.value('origin') != null) {
       res.headers.add('Access-Control-Allow-Credentials', 'true');
-      res.headers
-          .add('Access-Control-Allow-Origin', req.headers.value('origin')!);
+      res.headers.add('Access-Control-Allow-Origin', req.headers.value('origin')!);
     } else {
       res.headers.add('Access-Control-Allow-Origin', '*');
     }
-    res.statusCode = HttpStatus.badRequest;
-    res.write(
-        json.encode({'code': code, 'message': ServerErrorMessages[code]}));
+    res
+      ..statusCode = HttpStatus.badRequest
+      ..write(json.encode({'code': code, 'message': ServerErrorMessages[code]}));
+    unawaited(res.close());
   }
 
   /// generate a socket id.
@@ -248,28 +244,27 @@ class Server extends Engine {
   ///
   /// @param {Object} request object
   /// @api public
-  String generateId(SocketConnect connect) {
-    return _uuid.v1().replaceAll('-', '');
-  }
+  String generateId(final SocketConnect connect) => _uuid.v1().replaceAll('-', '');
 
   /// Handshakes a new client.
   ///
   /// @param {String} transport name
   /// @param {Object} request object
   /// @api private
-  void handshake(String transportName, SocketConnect connect) {
-    var id = generateId(connect);
+  void handshake(final String transportName, final SocketConnect connect) {
+    final String id = generateId(connect);
 
     _logger.fine('handshaking client $id');
-    var transport;
-    var req = connect.request;
+    late Transport transport;
+    final HttpRequest req = connect.request;
     try {
       transport = Transports.newInstance(transportName, connect);
       if ('polling' == transportName) {
-        transport.maxHttpBufferSize = maxHttpBufferSize;
-        transport.httpCompression = httpCompression;
+        transport
+          ..maxHttpBufferSize = maxHttpBufferSize
+          ..httpCompression = httpCompression.toMap();
       } else if ('websocket' == transportName) {
-        transport.perMessageDeflate = perMessageDeflate;
+        transport.perMessageDeflate = perMessageDeflate.toMap();
       }
 
       if (req.uri.hasQuery && req.uri.queryParameters.containsKey('b64')) {
@@ -281,15 +276,18 @@ class Server extends Engine {
       sendErrorMessage(req, ServerErrors.BAD_REQUEST);
       return;
     }
-    var socket = Socket(id, this, transport, connect);
-
-    if (cookie?.isNotEmpty == true) {
-      transport.on('headers', (headers) {
-        headers['Set-Cookie'] = '$cookie=${Uri.encodeComponent(id)}' +
-            (cookiePath?.isNotEmpty == true ? '; Path=$cookiePath' : '') +
-            (cookiePath?.isNotEmpty == true && cookieHttpOnly == true
-                ? '; HttpOnly'
-                : '');
+    final Socket socket = Socket(id, this, transport, connect);
+
+    if (cookie.isEnabled && cookie.name != null) {
+      transport.on('headers', (final dynamic headers) {
+        final StringBuffer cookieValue = StringBuffer('${cookie.name}=${Uri.encodeComponent(id)}');
+        if (cookiePath.isEnabled && cookiePath.path != null) {
+          cookieValue.write('; Path=${cookiePath.path}');
+          if (cookieHttpOnly) {
+            cookieValue.write('; HttpOnly');
+          }
+        }
+        headers['Set-Cookie'] = cookieValue.toString();
       });
     }
 
@@ -298,7 +296,7 @@ class Server extends Engine {
     clients[id] = socket;
     clientsCount++;
 
-    socket.once('close', (_) {
+    socket.once('close', (final _) {
       clients.remove(id);
       clientsCount--;
     });
@@ -309,23 +307,13 @@ class Server extends Engine {
   /// Handles an Engine.IO HTTP Upgrade.
   ///
   /// @api public
-  void handleUpgrade(SocketConnect connect) {
-//  this.prepare(req);
-
-    verify(connect, true, (err, success) {
+  void handleUpgrade(final SocketConnect connect) {
+    verify(connect, true, (final Object? err, final bool success) async {
       if (!success) {
-        abortConnection(connect, err);
+        await abortConnection(connect, err);
         return;
       }
-
-//  var head = new Buffer(upgradeHead.length);
-//  upgradeHead.copy(head);
-//  upgradeHead = null;
-
-      // delegate to ws
-//  self.ws.handleUpgrade(req, socket, head, function (conn) {
-      onWebSocket(connect);
-//  });
+      await onWebSocket(connect);
     });
   }
 
@@ -333,86 +321,75 @@ class Server extends Engine {
   ///
   /// @param {ws.Socket} websocket
   /// @api private
-
-  void onWebSocket(SocketConnect connect) {
-//    socket.listen((_) {},
-//        onError: () => _logger.fine('websocket error before upgrade'));
-
-//  if (!transports[req._query.transport].handlesUpgrades) {
-//    _logger.fine('transport doesnt handle upgraded requests');
-//    socket.close();
-//    return;
-//  }
+  Future onWebSocket(final SocketConnect connect) async {
     if (connect.request.connectionInfo == null) {
       _logger.fine('WebSocket connection closed: ${connect.request.uri.path}');
       return;
     }
     // get client id
-    var id = connect.request.uri.queryParameters['sid'];
-
-    // keep a reference to the ws.Socket
-//  req.websocket = socket;
+    final String? id = connect.request.uri.queryParameters['sid'];
 
     if (id != null) {
-      var client = clients[id];
+      final Socket? client = clients[id];
       if (client == null) {
         _logger.fine('upgrade attempt for closed client');
-        connect.websocket?.close();
+        await connect.websocket?.close();
       } else if (client.upgrading == true) {
         _logger.fine('transport has already been trying to upgrade');
-        connect.websocket?.close();
+        if (connect.websocket != null) {
+          await connect.websocket!.close();
+        }
       } else if (client.upgraded == true) {
         _logger.fine('transport had already been upgraded');
-        connect.websocket?.close();
+        if (connect.websocket != null) {
+          await connect.websocket!.close();
+        }
       } else {
         _logger.fine('upgrading existing transport');
-        var req = connect.request;
-        var transport = Transports.newInstance(
-            req.uri.queryParameters['transport'] as String, connect);
-        // ignore: unrelated_type_equality_checks
-        if (req.uri.queryParameters['b64'] == true) {
+        final HttpRequest req = connect.request;
+        final Transport transport = Transports.newInstance(req.uri.queryParameters['transport'] as String, connect);
+        final String? b64 = req.uri.queryParameters['b64'];
+        if (b64 == '1' || b64 == 'true') {
           transport.supportsBinary = false;
         } else {
           transport.supportsBinary = true;
         }
-        transport.perMessageDeflate = perMessageDeflate;
+        transport.perMessageDeflate = perMessageDeflate.toMap();
         client.maybeUpgrade(transport);
       }
     } else {
-      handshake(
-          connect.request.uri.queryParameters['transport'] as String, connect);
+      handshake(connect.request.uri.queryParameters['transport'] as String, connect);
     }
   }
 
   /// Captures upgrade requests for a http.Server.
   ///
   /// @param {http.Server} server
-  /// @param {Object} options
+  /// @param {AttachmentOptionsModel} options
   /// @api public
-  void attachTo(StreamServer server, Map? options) {
-    options = options ?? {};
-    var path =
-        (options['path'] ?? '/engine.io').replaceFirst(RegExp(r'\/$'), '');
+  void attachTo(final StreamServer server, final Map? options) {
+    final AttachmentOptionsModel attachOptions = AttachmentOptionsModel.fromMap(options);
+    String path = attachOptions.path.value.replaceFirst(RegExp(r'/$'), '');
 
     // normalize path
     path += '/';
 
     // cache and clean up listeners
-    server.map('$path.*', (HttpConnect connect) async {
-      var req = connect.request;
+    server.map('$path.*', (final HttpConnect connect) async {
+      final HttpRequest req = connect.request;
 
       _logger.fine('intercepting request for path "$path"');
       if (WebSocketTransformer.isUpgradeRequest(req) &&
-          transports.contains('websocket')) {
+          transports.any((final TransportName t) => t == TransportName.websocket)) {
 //          print('init websocket... ${req.uri}');
-        var socket = await WebSocketTransformer.upgrade(req);
-        var socketConnect = SocketConnect.fromWebSocket(connect, socket);
-        socketConnect.dataset['options'] = options;
+        final WebSocket socket = await WebSocketTransformer.upgrade(req);
+        final SocketConnect socketConnect = SocketConnect.fromWebSocket(connect, socket);
+        socketConnect.dataset['options'] = attachOptions.toMap();
         handleUpgrade(socketConnect);
         return socketConnect.done;
       } else {
-        var socketConnect = SocketConnect(connect);
-        socketConnect.dataset['options'] = options;
+        final SocketConnect socketConnect = SocketConnect(connect);
+        socketConnect.dataset['options'] = attachOptions.toMap();
         handleRequest(socketConnect);
         return socketConnect.done;
       }
@@ -424,21 +401,16 @@ class Server extends Engine {
   /// @param {net.Socket} socket
   /// @param {code} error code
   /// @api private
-
-  static void abortConnection(SocketConnect connect, code) {
-    var socket = connect.websocket;
+  static Future abortConnection(final SocketConnect connect, final Object? code) async {
+    final WebSocket? socket = connect.websocket;
     if (socket?.readyState == HttpStatus.ok) {
-      var message = ServerErrorMessages.containsKey(code)
-          ? ServerErrorMessages[code]
-          : code;
-      var length = utf8.encode(message).length;
-      socket!.add('HTTP/1.1 400 Bad Request\r\n'
-              'Connection: close\r\n'
-              'Content-type: text/html\r\n'
-              'Content-Length: $length\r\n'
-              '\r\n' +
-          message);
+      final String message = ServerErrorMessages.containsKey(code) ? ServerErrorMessages[code]! : code.toString();
+      final int length = utf8.encode(message).length;
+      socket!.add(
+          'HTTP/1.1 400 Bad Request\r\nConnection: close\r\nContent-type: text/html\r\nContent-Length: $length\r\n\r\n$message');
+    }
+    if (socket != null) {
+      await socket.close();
     }
-    socket?.close();
   }
 }
diff --git a/lib/src/engine/socket.dart b/lib/src/engine/socket.dart
index 34e56ad..2a652fa 100644
--- a/lib/src/engine/socket.dart
+++ b/lib/src/engine/socket.dart
@@ -1,21 +1,25 @@
-/// socket.dart
-///
-/// Purpose:
-///
-/// Description:
-///
-/// History:
-///    17/02/2017, Created by jumperchen
-///
-/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+// socket.dart
+//
+// Purpose:
+//
+// Description:
+//
+// History:
+//    17/02/2017, Created by jumperchen
+//
+// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
 import 'dart:async';
 import 'dart:convert';
 import 'dart:io';
+
 import 'package:logging/logging.dart';
-import 'package:socket_io/src/engine/connect.dart';
-import 'package:socket_io/src/engine/server.dart';
-import 'package:socket_io/src/engine/transport/transports.dart';
-import 'package:socket_io/src/util/event_emitter.dart';
+
+import '../models/callbacks_models.dart';
+import '../util/event_emitter.dart';
+import '../value_objects/transport_name_vo.dart';
+import 'connect.dart';
+import 'server.dart';
+import 'transport/transports.dart';
 
 /// Client class (abstract).
 ///
@@ -28,10 +32,10 @@ class Socket extends EventEmitter {
   bool upgrading = false;
   bool upgraded = false;
   String readyState = 'opening';
-  List writeBuffer = [];
-  List packetsFn = [];
-  List sentCallbackFn = [];
-  List cleanupFn = [];
+  List> writeBuffer = >[];
+  List packetsFn = [];
+  List sentCallbackFn = [];
+  List cleanupFn = [];
   SocketConnect connect;
   late InternetAddress remoteAddress;
   Timer? checkIntervalTimer;
@@ -60,12 +64,13 @@ class Socket extends EventEmitter {
     // sends an `open` packet
     transport.sid = id;
     sendPacket('open',
-        data: json.encode({
+        data: json.encode({
           'sid': id,
           'upgrades': getAvailableUpgrades(),
           'pingInterval': server.pingInterval,
           'pingTimeout': server.pingTimeout
-        }));
+        }),
+        options: const {'compress': false});
 
 //    if (this.server.initialPacket != null) {
 //      this.sendPacket('message', data: this.server.initialPacket);
@@ -79,8 +84,7 @@ class Socket extends EventEmitter {
   ///
   /// @param {Object} packet
   /// @api private
-
-  void onPacket(packet) {
+  void onPacket(final Map packet) {
     if ('open' == readyState) {
       // export packet event
       _logger.fine('packet');
@@ -92,7 +96,7 @@ class Socket extends EventEmitter {
       switch (packet['type']) {
         case 'ping':
           _logger.fine('got ping');
-          sendPacket('pong');
+          sendPacket('pong', options: const {'compress': false});
           emit('heartbeat');
           break;
 
@@ -101,7 +105,7 @@ class Socket extends EventEmitter {
           break;
 
         case 'message':
-          var data = packet['data'];
+          final dynamic data = packet['data'];
           emit('data', data);
           emit('message', data);
           break;
@@ -115,7 +119,7 @@ class Socket extends EventEmitter {
   ///
   /// @param {Error} error object
   /// @api private
-  void onError(err) {
+  void onError(final Object err) {
     _logger.fine('transport error');
     onClose('transport error', err);
   }
@@ -127,8 +131,7 @@ class Socket extends EventEmitter {
     if (pingTimeoutTimer != null) {
       pingTimeoutTimer!.cancel();
     }
-    pingTimeoutTimer = Timer(
-        Duration(milliseconds: server.pingInterval + server.pingTimeout), () {
+    pingTimeoutTimer = Timer((server.pingInterval + server.pingTimeout).value, () {
       onClose('ping timeout');
     });
   }
@@ -137,74 +140,57 @@ class Socket extends EventEmitter {
   ///
   /// @param {Transport} transport
   /// @api private
-  void setTransport(Transport transport) {
-    var onError = this.onError;
-    var onPacket = this.onPacket;
-    var flush = (_) => this.flush();
-    var onClose = (_) {
-      this.onClose('transport close');
-    };
+  void setTransport(final Transport transport) {
+    void onErrorHandler(final dynamic data) => onError(data);
+    void onPacketHandler(final dynamic data) => onPacket(data as Map);
+    void flushHandler(final dynamic _) => flush();
+    void onCloseHandler(final dynamic _) {
+      onClose('transport close');
+    }
 
     this.transport = transport;
-    this.transport.once('error', onError);
-    this.transport.on('packet', onPacket);
-    this.transport.on('drain', flush);
-    this.transport.once('close', onClose);
+    transport
+      ..on('error', onErrorHandler)
+      ..on('packet', onPacketHandler)
+      ..on('drain', flushHandler)
+      ..on('close', onCloseHandler);
     // this function will manage packet events (also message callbacks)
-    setupSendCallback();
-
-    cleanupFn.add(() {
-      transport.off('error', onError);
-      transport.off('packet', onPacket);
-      transport.off('drain', flush);
-      transport.off('close', onClose);
-    });
+    this.transport = transport;
   }
 
   /// Upgrades socket to the given transport
   ///
   /// @param {Transport} transport
   /// @api private
-  void maybeUpgrade(transport) {
-    _logger.fine(
-        'might upgrade socket transport from ${this.transport.name} to ${transport.name}');
+  void maybeUpgrade(final Transport transport) {
+    _logger.fine('might upgrade socket transport from ${this.transport.name} to ${transport.name}');
 
     upgrading = true;
-    var cleanupFn = {};
-    // set transport upgrade timer
-    upgradeTimeoutTimer =
-        Timer(Duration(milliseconds: server.upgradeTimeout), () {
-      _logger.fine('client did not complete upgrade - closing transport');
-      cleanupFn['cleanup']();
-      if ('open' == transport.readyState) {
-        transport.close();
-      }
-    });
+    final Map cleanupFn = {};
 
-    // we force a polling cycle to ensure a fast upgrade
-    var check = () {
+    void check() {
       if ('polling' == this.transport.name && this.transport.writable == true) {
         _logger.fine('writing a noop packet to polling for fast upgrade');
-        this.transport.send([
-          {'type': 'noop'}
+        this.transport.send(>[
+          {'type': 'noop'}
         ]);
       }
-    };
+    }
 
-    var onPacket = (packet) {
+    void onPacketHandler(final dynamic data) {
+      final Map packet = data as Map;
       if ('ping' == packet['type'] && 'probe' == packet['data']) {
-        transport.send([
-          {'type': 'pong', 'data': 'probe'}
+        transport.send(>[
+          {'type': 'pong', 'data': 'probe'}
         ]);
         emit('upgrading', transport);
         if (checkIntervalTimer != null) {
           checkIntervalTimer!.cancel();
         }
-        checkIntervalTimer =
-            Timer.periodic(Duration(milliseconds: 100), (_) => check());
+        checkIntervalTimer = Timer.periodic(const Duration(milliseconds: 100), (final _) => check());
       } else if ('upgrade' == packet['type'] && readyState != 'closed') {
         _logger.fine('got upgrade packet - upgrading');
-        cleanupFn['cleanup']();
+        cleanupFn['cleanup']!();
         this.transport.discard();
         upgraded = true;
         clearTransport();
@@ -218,27 +204,26 @@ class Socket extends EventEmitter {
           });
         }
       } else {
-        cleanupFn['cleanup']();
+        cleanupFn['cleanup']!();
         transport.close();
       }
-    };
+    }
 
-    var onError = (err) {
+    void onErrorHandler(final dynamic err) {
       _logger.fine('client did not complete upgrade - $err');
-      cleanupFn['cleanup']();
+      cleanupFn['cleanup']!();
       transport.close();
-      transport = null;
-    };
+    }
 
-    var onTransportClose = (_) {
-      onError('transport closed');
-    };
+    void onTransportClose(final dynamic _) {
+      onErrorHandler('transport closed');
+    }
 
-    var onClose = (_) {
-      onError('socket closed');
-    };
+    void onClose(final dynamic _) {
+      onErrorHandler('socket closed');
+    }
 
-    var cleanup = () {
+    void cleanup() {
       upgrading = false;
       checkIntervalTimer?.cancel();
       checkIntervalTimer = null;
@@ -246,16 +231,28 @@ class Socket extends EventEmitter {
       upgradeTimeoutTimer?.cancel();
       upgradeTimeoutTimer = null;
 
-      transport.off('packet', onPacket);
-      transport.off('close', onTransportClose);
-      transport.off('error', onError);
+      transport
+        ..off('packet', onPacketHandler)
+        ..off('close', onTransportClose)
+        ..off('error', onErrorHandler);
       off('close', onClose);
-    };
-    cleanupFn['cleanup'] = cleanup; // define it later
-    transport.on('packet', onPacket);
-    transport.once('close', onTransportClose);
-    transport.once('error', onError);
+    }
+
+    cleanupFn['cleanup'] = cleanup;
 
+    // set transport upgrade timer
+    upgradeTimeoutTimer = Timer(server.upgradeTimeout.value, () {
+      _logger.fine('client did not complete upgrade - closing transport');
+      cleanupFn['cleanup']!();
+      if ('open' == transport.readyState) {
+        transport.close();
+      }
+    });
+
+    transport
+      ..on('packet', onPacketHandler)
+      ..once('close', onTransportClose)
+      ..once('error', onErrorHandler);
     once('close', onClose);
   }
 
@@ -263,22 +260,21 @@ class Socket extends EventEmitter {
   ///
   /// @api private
   void clearTransport() {
-    var cleanup;
+    final int toCleanUp = cleanupFn.length;
 
-    var toCleanUp = cleanupFn.length;
-
-    for (var i = 0; i < toCleanUp; i++) {
-      cleanup = cleanupFn.removeAt(0);
+    for (int i = 0; i < toCleanUp; i++) {
+      final CleanupCallback cleanup = cleanupFn.removeAt(0);
       cleanup();
     }
 
     // silence further transport errors and prevent uncaught exceptions
-    transport.on('error', (_) {
-      _logger.fine('error triggered by discarded transport');
-    });
+    transport
+      ..on('error', (final _) {
+        _logger.fine('error triggered by discarded transport');
+      })
 
-    // ensure transport won't stay open
-    transport.close();
+      // ensure transport won't stay open
+      ..close();
 
     pingTimeoutTimer?.cancel();
   }
@@ -286,7 +282,7 @@ class Socket extends EventEmitter {
   /// Called upon transport considered closed.
   /// Possible reasons: `ping timeout`, `client error`, `parse error`,
   /// `transport error`, `server close`, `transport close`
-  void onClose(reason, [description]) {
+  void onClose(final String reason, [final Object? description]) {
     if ('closed' != readyState) {
       readyState = 'closed';
       pingTimeoutTimer?.cancel();
@@ -297,12 +293,12 @@ class Socket extends EventEmitter {
       // clean writeBuffer in next tick, so developers can still
       // grab the writeBuffer on 'close' event
       scheduleMicrotask(() {
-        writeBuffer = [];
+        writeBuffer = >[];
       });
-      packetsFn = [];
-      sentCallbackFn = [];
+      packetsFn = [];
+      sentCallbackFn = [];
       clearTransport();
-      emit('close', [reason, description]);
+      emit('close', [reason, description]);
     }
   }
 
@@ -311,24 +307,15 @@ class Socket extends EventEmitter {
   /// @api private
   void setupSendCallback() {
     // the message was sent successfully, execute the callback
-    var onDrain = (_) {
+    void onDrain(final _) {
       if (sentCallbackFn.isNotEmpty) {
-        var seqFn = sentCallbackFn[0];
+        final dynamic seqFn = sentCallbackFn[0];
         if (seqFn is Function) {
           _logger.fine('executing send callback');
           seqFn(transport);
         }
-
-        /// else if (Array.isArray(seqFn)) {
-        /// _logger.fine('executing batch send callback');
-        /// for (var l = seqFn.length, i = 0; i < l; i++) {
-        /// if ('function' === typeof seqFn[i]) {
-        /// seqFn[i](self.transport);
-        /// }
-        /// }
-        ///            }
       }
-    };
+    }
 
     transport.on('drain', onDrain);
 
@@ -344,8 +331,9 @@ class Socket extends EventEmitter {
   /// @param {Function} callback
   /// @return {Socket} for chaining
   /// @api public
-  void send(data, options, [callback]) => write(data, options, callback);
-  Socket write(data, options, [callback]) {
+  void send(final Object? data, final Map options, [final PacketCallback? callback]) =>
+      write(data, options, callback);
+  Socket write(final Object? data, final Map options, [final PacketCallback? callback]) {
     sendPacket('message', data: data, options: options, callback: callback);
     return this;
   }
@@ -356,14 +344,15 @@ class Socket extends EventEmitter {
   /// @param {String} optional, data
   /// @param {Object} options
   /// @api private
-  void sendPacket(type, {data, options, callback}) {
-    options = options ?? {};
-    options['compress'] = false != options['compress'];
+  void sendPacket(final String type,
+      {final Object? data, required final Map options, final PacketCallback? callback}) {
+    // ensure default for compress
+    final Map normalizedOptions = {
+      'compress': options['compress'] ?? true,
+    };
 
     if ('closing' != readyState && 'closed' != readyState) {
-//      _logger.fine('sending packet "%s" (%s)', type, data);
-
-      var packet = {'type': type, 'options': options};
+      final Map packet = {'type': type, 'options': normalizedOptions};
       if (data != null) packet['data'] = data;
 
       // exports packetCreate event
@@ -372,7 +361,9 @@ class Socket extends EventEmitter {
       writeBuffer.add(packet);
 
       // add send callback to object, if defined
-      if (callback != null) packetsFn.add(callback);
+      if (callback != null) {
+        packetsFn.add(callback);
+      }
 
       flush();
     }
@@ -382,20 +373,18 @@ class Socket extends EventEmitter {
   ///
   /// @api private
   void flush() {
-    if ('closed' != readyState &&
-        transport.writable == true &&
-        writeBuffer.isNotEmpty) {
+    if ('closed' != readyState && transport.writable == true && writeBuffer.isNotEmpty) {
       _logger.fine('flushing buffer to transport');
       emit('flush', writeBuffer);
-      server.emit('flush', [this, writeBuffer]);
-      var wbuf = writeBuffer;
-      writeBuffer = [];
+      server.emit('flush', [this, writeBuffer]);
+      final List> wbuf = List>.from(writeBuffer);
+      writeBuffer = >[];
       if (transport.supportsFraming == false) {
-        sentCallbackFn.add((_) => packetsFn.forEach((f) => f(_)));
+        sentCallbackFn.add((final Object? value) => packetsFn.forEach((final PacketCallback f) => f(value)));
       } else {
         sentCallbackFn.addAll(packetsFn);
       }
-      packetsFn = [];
+      packetsFn = [];
       transport.send(wbuf);
       emit('drain');
       server.emit('drain', this);
@@ -406,11 +395,11 @@ class Socket extends EventEmitter {
   ///
   /// @api private
   List getAvailableUpgrades() {
-    var availableUpgrades = [];
-    var allUpgrades = server.upgrades(transport.name!);
-    for (var i = 0, l = allUpgrades.length; i < l; ++i) {
-      var upg = allUpgrades[i];
-      if (server.transports.contains(upg)) {
+    final List availableUpgrades = [];
+    final List allUpgrades = server.upgrades(transport.name!);
+    for (int i = 0, l = allUpgrades.length; i < l; ++i) {
+      final String upg = allUpgrades[i];
+      if (server.transports.any((final TransportName t) => t.value == upg)) {
         availableUpgrades.add(upg);
       }
     }
@@ -423,12 +412,12 @@ class Socket extends EventEmitter {
   /// @return {Socket} for chaining
   /// @api public
 
-  void close([discard = false]) {
+  void close([final bool discard = false]) {
     if ('open' != readyState) return;
     readyState = 'closing';
 
     if (writeBuffer.isNotEmpty) {
-      once('drain', (_) => closeTransport(discard));
+      once('drain', (final _) => closeTransport(discard));
       return;
     }
 
@@ -439,7 +428,7 @@ class Socket extends EventEmitter {
   ///
   /// @param {Boolean} discard
   /// @api private
-  void closeTransport(discard) {
+  void closeTransport(final bool discard) {
     if (discard == true) transport.discard();
     transport.close(() => onClose('forced close'));
   }
diff --git a/lib/src/engine/transport/jsonp_transport.dart b/lib/src/engine/transport/jsonp_transport.dart
index 86df83e..3dfc7f6 100644
--- a/lib/src/engine/transport/jsonp_transport.dart
+++ b/lib/src/engine/transport/jsonp_transport.dart
@@ -1,25 +1,23 @@
-/// jsonp_transport.dart
-///
-/// Purpose:
-///
-/// Description:
-///
-/// History:
-///    22/02/2017, Created by jumperchen
-///
-/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+// jsonp_transport.dart
+//
+// Purpose:
+//
+// Description:
+//
+// History:
+//    22/02/2017, Created by jumperchen
+//
+// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
 import 'dart:convert';
-import 'package:socket_io/src/engine/connect.dart';
-import 'package:socket_io/src/engine/transport/polling_transport.dart';
+
+import '../connect.dart';
+import 'polling_transport.dart';
 
 class JSONPTransport extends PollingTransport {
   late String head;
   late String foot;
-  JSONPTransport(SocketConnect connect) : super(connect) {
-    head = '___eio[' +
-        (connect.request.uri.queryParameters['j'] ?? '')
-            .replaceAll(RegExp('[^0-9]'), '') +
-        '](';
+  JSONPTransport(final SocketConnect connect) : super(connect) {
+    head = '___eio[${(connect.request.uri.queryParameters['j'] ?? '').replaceAll(RegExp('[^0-9]'), '')}](';
     foot = ');';
   }
 
@@ -28,53 +26,46 @@ class JSONPTransport extends PollingTransport {
   ///
   /// @api private
   @override
-  void onData(data) {
+  void onData(final dynamic data) {
     // we leverage the qs module so that we get built-in DoS protection
     // and the fast alternative to decodeURIComponent
-    data = parse(data)['d'];
-    if (data is String) {
-      // client will send already escaped newlines as \\\\n and newlines as \\n
-      // \\n must be replaced with \n and \\\\n with \\n
-      data = data.replaceAllMapped(RegExp(r'(\\)?\\n'), (match) {
-        throw UnimplementedError('Not implemented yet');
-//        print(match);
-//        match
-//      return slashes ? match : '\n';
-      });
-      super.onData(data.replaceAll(RegExp(r'\\\\n'), '\\n'));
-    }
+    final String d = parse(data as String)['d'] ?? '';
+    // client will send already escaped newlines as \\n and newlines as \n
+    // \n must be replaced with \n and \\n with \n
+    final String normalized = d.replaceAllMapped(RegExp(r'(\\)?\\n'), (final Match match) {
+      final String? slashes = match.group(1);
+      return slashes != null ? match.group(0)! : '\n';
+    });
+    super.onData(normalized.replaceAll(RegExp(r'\\\\n'), '\\n'));
   }
 
   /// Performs the write.
   ///
   /// @api private
   @override
-  void doWrite(data, options, [callback]) {
+  Future doWrite(dynamic data, final Map? options, [final Function? callback]) async {
     // we must output valid javascript, not valid json
     // see: http://timelessrepo.com/json-isnt-a-javascript-subset
-    var js = json
-        .encode(data)
-        .replaceAll(RegExp(r'\u2028'), '\\u2028')
-        .replaceAll(RegExp(r'\u2029'), '\\u2029');
+    final String js =
+        json.encode(data).replaceAll(RegExp(r'\u2028'), '\\u2028').replaceAll(RegExp(r'\u2029'), '\\u2029');
 
     // prepare response
-    data = head + js + foot;
+    final String payload = head + js + foot;
 
-    super.doWrite(data, options, callback);
+    await super.doWrite(payload, options, callback);
   }
 
-  static Map parse(String query) {
-    var search = RegExp('([^&=]+)=?([^&]*)');
-    var result = {};
+  static Map parse(String query) {
+    final RegExp search = RegExp('([^&=]+)=?([^&]*)');
+    final Map result = {};
+    final String normalizedQuery = query.startsWith('?') ? query.substring(1) : query;
 
     // Get rid off the beginning ? in query strings.
-    if (query.startsWith('?')) query = query.substring(1);
-
     // A custom decoder.
-    String decode(String s) => Uri.decodeComponent(s.replaceAll('+', ' '));
+    String decode(final String s) => Uri.decodeComponent(s.replaceAll('+', ' '));
 
     // Go through all the matches and build the result map.
-    for (Match match in search.allMatches(query)) {
+    for (final Match match in search.allMatches(normalizedQuery)) {
       result[decode(match.group(1)!)] = decode(match.group(2)!);
     }
 
diff --git a/lib/src/engine/transport/polling_transport.dart b/lib/src/engine/transport/polling_transport.dart
index 397aad8..f774f47 100644
--- a/lib/src/engine/transport/polling_transport.dart
+++ b/lib/src/engine/transport/polling_transport.dart
@@ -1,20 +1,25 @@
-/// polling_transport.dart
-///
-/// Purpose:
-///
-/// Description:
-///
-/// History:
-///    22/02/2017, Created by jumperchen
-///
-/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+// polling_transport.dart
+//
+// Purpose:
+//
+// Description:
+//
+// History:
+//    22/02/2017, Created by jumperchen
+//
+// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
 import 'dart:async';
 import 'dart:convert';
 import 'dart:io';
+import 'dart:typed_data';
+
 import 'package:logging/logging.dart';
-import 'package:socket_io/src/engine/connect.dart';
-import 'package:socket_io_common/src/engine/parser/parser.dart';
-import 'package:socket_io/src/engine/transport/transports.dart';
+import 'package:socket_io_common/socket_io_common.dart';
+
+import '../../models/callbacks_models.dart' show VoidCallback;
+import '../../types/common_types.dart' show Headers;
+import '../connect.dart';
+import 'transports.dart';
 
 class PollingTransport extends Transport {
   @override
@@ -25,320 +30,362 @@ class PollingTransport extends Transport {
 
   static final Logger _logger = Logger('socket_io:transport.PollingTransport');
   int closeTimeout = 30 * 1000;
-  Function? shouldClose;
+  VoidCallback? shouldClose;
   SocketConnect? dataReq;
-  PollingTransport(connect) : super(connect) {
+
+  final Map _reqCleanups = {};
+  final Map _reqCloses = {};
+
+  PollingTransport(super.connect) {
     maxHttpBufferSize = null;
     httpCompression = null;
     name = 'polling';
   }
 
   @override
-  void onRequest(SocketConnect connect) {
-    var res = connect.response;
+  Future onRequest(final SocketConnect connect) async {
+    final HttpResponse res = connect.response;
 
     if ('GET' == connect.request.method) {
-      onPollRequest(connect);
+      await onPollRequest(connect);
     } else if ('POST' == connect.request.method) {
-      onDataRequest(connect);
+      await onDataRequest(connect);
+    } else if ('OPTIONS' == connect.request.method) {
+      await onOptionsRequest(connect);
     } else {
       res.statusCode = 500;
-      res.close();
+      await res.close();
     }
   }
 
-  final Map _reqCleanups = {};
-  final Map _reqCloses = {};
+  /// Handle CORS preflight requests
+  Future onOptionsRequest(final SocketConnect connect) async {
+    final HttpResponse res = connect.response;
+    final Headers headers = {};
+
+    this.headers(connect, headers).forEach((final String key, final Object value) {
+      res.headers.set(key, value);
+    });
+
+    res
+      ..statusCode = 200
+      ..write('');
+    await res.close();
+    await connect.close();
+  }
 
   /// The client sends a request awaiting for us to send data.
-  ///
-  /// @api private
-  void onPollRequest(SocketConnect connect) {
+  Future onPollRequest(final SocketConnect connect) async {
     if (this.connect != null) {
       _logger.fine('request overlap');
-      // assert: this.res, '.req and .res should be (un)set together'
       onError('overlap from client');
-      this.connect!.response.statusCode = 500;
-      this.connect!.close();
+      connect.response.statusCode = 500;
+      await connect.close();
       return;
     }
 
     _logger.fine('setting request');
-
     this.connect = connect;
 
-    var onClose = () {
-      onError('poll connection closed prematurely');
-    };
-
-    var cleanup = () {
-      _reqCloses.remove(connect);
+    void cleanup() {
       this.connect = null;
-    };
+    }
+
+    void onClose() {
+      cleanup();
+    }
 
     _reqCleanups[connect] = cleanup;
     _reqCloses[connect] = onClose;
 
     writable = true;
-    emit('drain');
+    emit('drain', null);
 
     // if we're still writable but had a pending close, trigger an empty send
     if (writable == true && shouldClose != null) {
       _logger.fine('triggering empty send to append close packet');
-      send([
-        {'type': 'noop'}
+      send(>[
+        {'type': 'noop'}
       ]);
     }
   }
 
   /// The client sends a request with data.
-  ///
-  /// @api private
-  void onDataRequest(SocketConnect connect) {
+  Future onDataRequest(final SocketConnect connect) async {
     if (dataReq != null) {
-      // assert: this.dataRes, '.dataReq and .dataRes should be (un)set together'
       onError('data request overlap from client');
       connect.response.statusCode = 500;
-      connect.close();
+      await connect.close();
       return;
     }
 
-    var isBinary = 'application/octet-stream' ==
-        connect.request.headers.value('content-type');
+    final bool isBinary = 'application/octet-stream' == connect.request.headers.value('content-type');
 
     dataReq = connect;
+    dynamic chunks = isBinary ? [] : '';
+    final PollingTransport self = this;
+    int contentLength = 0;
 
-    dynamic chunks = isBinary ? [0] : '';
-    var self = this;
-    StreamSubscription? subscription;
-    var cleanup = () {
-      chunks = isBinary ? [0] : '';
-      if (subscription != null) {
-        subscription.cancel();
-      }
+    void cleanup() {
+      chunks = isBinary ? [] : '';
       self.dataReq = null;
-    };
+    }
 
-    var onData = (List data) {
-      var contentLength;
-      if (data is String) {
-        chunks += data;
+    Future onData(final Uint8List data) async {
+      if (chunks is String) {
+        chunks += String.fromCharCodes(data);
         contentLength = utf8.encode(chunks).length;
       } else {
-        if (chunks is String) {
-          chunks += String.fromCharCodes(data);
-        } else {
-          chunks.addAll(String.fromCharCodes(data)
-              .split(',')
-              .map((s) => int.parse(s))
-              .toList());
-        }
+        chunks.addAll(data);
         contentLength = chunks.length;
       }
 
-      if (contentLength > self.maxHttpBufferSize) {
+      if (contentLength > (self.maxHttpBufferSize ?? 1000000)) {
         chunks = '';
-        connect.close();
+        await connect.close();
+        return;
       }
-    };
+    }
 
-    var onEnd = () {
+    Future onEnd() async {
       self.onData(chunks);
-
-      var headers = {'Content-Type': 'text/html', 'Content-Length': 2};
-
-      var res = connect.response;
-
-      res.statusCode = 200;
-
+      final Headers headers = {'Content-Type': 'text/html', 'Content-Length': 2};
+      final HttpResponse res = connect.response;
       res.headers.clear();
-      // text/html is required instead of text/plain to avoid an
-      // unwanted download dialog on certain user-agents (GH-43)
-      self.headers(connect, headers).forEach((key, value) {
+
+      self.headers(connect, headers).forEach((final String key, final Object value) {
         res.headers.set(key, value);
       });
-      res.write('ok');
-      connect.close();
+      res
+        ..statusCode = 200
+        ..write('ok');
+      await connect.close();
       cleanup();
-    };
+    }
+
+    connect.request.listen(onData, onDone: onEnd);
 
-    subscription = connect.request.listen(onData, onDone: onEnd);
     if (!isBinary) {
-      connect.response.headers.contentType =
-          ContentType.text; // for encoding utf-8
+      connect.response.headers.contentType = ContentType('text', 'plain', charset: 'utf-8');
     }
+
+    _reqCleanups[connect] = cleanup;
+    _reqCloses[connect] = cleanup;
   }
 
   /// Processes the incoming data payload.
-  ///
-  /// @param {String} encoded payload
-  /// @api private
   @override
-  void onData(data) {
+  void onData(final dynamic data) {
     _logger.fine('received "$data"');
     if (messageHandler != null) {
       messageHandler!.handle(this, data);
     } else {
-      var self = this;
-      var callback = (packet, [foo, bar]) {
-        if ('close' == packet['type']) {
-          _logger.fine('got xhr close packet');
-          self.onClose();
-          return false;
+      if (data is String && data.isNotEmpty) {
+        final List packets = _extractPacketStrings(data);
+
+        for (final String packetData in packets) {
+          if (packetData.isEmpty) continue;
+
+          try {
+            final Map? packet = PacketParser.decodePacket(packetData, 'utf8') as Map?;
+            if (_handleDecodedPacket(packet)) {
+              return;
+            }
+          } catch (e, st) {
+            _logger.warning('Error decoding packet "$packetData": $e\n$st');
+          }
+        }
+      } else if (data is List) {
+        try {
+          final Map? packet = PacketParser.decodePacket(data, null) as Map?;
+          if (_handleDecodedPacket(packet)) {
+            return;
+          }
+        } catch (e, st) {
+          _logger.warning('Error decoding binary packet: $e\n$st');
         }
+      }
+    }
+  }
+
+  bool _handleDecodedPacket(final Map? packet) {
+    if (packet == null) {
+      return false;
+    }
 
-        self.onPacket(packet);
-        return true;
-      };
+    if ('close' == packet['type']) {
+      _logger.fine('got polling close packet');
+      onClose();
+      return true;
+    }
+
+    onPacket(packet);
+    return false;
+  }
+
+  /// Extracts individual packets from a text polling payload.
+  ///
+  /// Engine.IO v4 uses the record-separator character for multi-packet payloads.
+  /// Some clients have also been observed to concatenate packets directly,
+  /// so we keep the fallback splitter for compatibility.
+  List _extractPacketStrings(final String payload) {
+    if (payload.contains(SEPARATOR)) {
+      return payload.split(SEPARATOR);
+    }
+
+    return _splitPackets(payload);
+  }
+
+  /// Splits concatenated packets in a fallback polling payload.
+  List _splitPackets(final String payload) {
+    final List packets = [];
+    int start = 0;
+
+    for (int i = 1; i < payload.length; i++) {
+      if (_isDigit(payload[i])) {
+        final String prevChar = payload[i - 1];
+        if (prevChar == ']' || prevChar == '}' || prevChar == '"') {
+          packets.add(payload.substring(start, i));
+          start = i;
+        }
+      }
+    }
 
-      PacketParser.decodePayload(data, callback: callback);
+    if (start < payload.length) {
+      packets.add(payload.substring(start));
     }
+
+    return packets;
+  }
+
+  bool _isDigit(final String char) {
+    if (char.isEmpty) return false;
+    final int code = char.codeUnitAt(0);
+    return code >= 48 && code <= 57;
   }
 
   /// Overrides onClose.
-  ///
-  /// @api private
   @override
   void onClose() {
     if (writable == true) {
       // close pending poll request
-      send([
-        {'type': 'noop'}
+      send(>[
+        {'type': 'noop'}
       ]);
     }
     super.onClose();
   }
 
   /// Writes a packet payload.
-  ///
-  /// @param {Object} packet
-  /// @api private
   @override
-  void send(List packets) {
+  void send(final List> packets) {
     writable = false;
 
     if (shouldClose != null) {
       _logger.fine('appending close packet to payload');
-      packets.add({'type': 'close'});
+      packets.add({'type': 'close'});
       shouldClose!();
       shouldClose = null;
     }
 
-    var self = this;
-    PacketParser.encodePayload(packets, supportsBinary: supportsBinary == true,
-        callback: (data) {
-      var compress = packets.any((packet) {
-        var opt = packet['options'];
+    PacketParser.encodePayload(packets, callback: (final dynamic data) async {
+      final bool compress = packets.any((final Map packet) {
+        final Map? opt = packet['options'] as Map?;
         return opt != null && opt['compress'] == true;
       });
-      self.write(data, {'compress': compress});
+      await write(data, {'compress': compress});
     });
   }
 
   /// Writes data as response to poll request.
-  ///
-  /// @param {String} data
-  /// @param {Object} options
-  /// @api private
-  void write(data, [options]) {
+  Future write(final dynamic data, final Map options) async {
     _logger.fine('writing "$data"');
-    doWrite(data, options, () {
-      var fn = _reqCleanups.remove(connect);
+    await doWrite(data, options, () {
+      final Function? fn = _reqCleanups.remove(connect);
       if (fn != null) fn();
     });
   }
 
   /// Performs the write.
-  ///
-  /// @api private
-  void doWrite(data, options, [callback]) {
-    var self = this;
-
-    // explicit UTF-8 is required for pages not served under utf
-    var isString = data is String;
-    var contentType =
-        isString ? 'text/plain; charset=UTF-8' : 'application/octet-stream';
-
-    final headers = {'Content-Type': contentType};
-
-    var respond = (data) {
-      headers[HttpHeaders.contentLengthHeader] =
-          data is String ? utf8.encode(data).length : data.length;
-      var res = self.connect!.response;
-
-      // If the status code is 101 (aka upgrade), then
-      // we assume the WebSocket transport has already
-      // sent the response and closed the socket
-      if (res.statusCode != 101) {
-        res.statusCode = 200;
-
-        res.headers.clear(); // remove all default headers.
-        this.headers(connect!, headers).forEach((k, v) {
-          res.headers.set(k, v);
-        });
-        try {
-          if (data is String) {
-            res.write(data);
-            connect!.close();
-          } else {
-            if (headers.containsKey(HttpHeaders.contentEncodingHeader)) {
-              res.add(data);
+  Future doWrite(final dynamic data, final Map? options, [final Function? callback]) async {
+    final PollingTransport self = this;
+
+    final bool isString = data is String;
+    final String contentType = isString ? 'text/plain; charset=UTF-8' : 'application/octet-stream';
+
+    final Headers headers = {'Content-Type': contentType};
+
+    Future respond(final dynamic data) async {
+      headers[HttpHeaders.contentLengthHeader] = data is String ? utf8.encode(data).length : data.length;
+
+      if (self.connect != null) {
+        final HttpResponse res = self.connect!.response;
+
+        if (res.statusCode != 101) {
+          res.statusCode = 200;
+          res.headers.clear();
+
+          self.headers(connect!, headers).forEach((final String k, final Object v) {
+            res.headers.set(k, v);
+          });
+
+          try {
+            if (data is String) {
+              res.write(data);
             } else {
-              res.write(String.fromCharCodes(data));
+              if (headers.containsKey(HttpHeaders.contentEncodingHeader)) {
+                res.add(data);
+              } else {
+                res.write(String.fromCharCodes(data));
+              }
             }
-            connect!.close();
+            await connect!.close();
+          } catch (e) {
+            final Function? fn = _reqCloses.remove(connect);
+            if (fn != null) fn();
+            rethrow;
           }
-        } catch (e) {
-          var fn = _reqCloses.remove(connect);
-          if (fn != null) fn();
-          rethrow;
         }
       }
-      callback();
-    };
 
-    if (httpCompression == null || options['compress'] != true) {
-      respond(data);
+      if (callback != null) callback();
+    }
+
+    if (httpCompression == null || options?['compress'] != true) {
+      await respond(data);
       return;
     }
 
-    var len = isString ? utf8.encode(data).length : data.length;
-    if (len < httpCompression?['threshold']) {
-      respond(data);
+    final int len = isString ? utf8.encode(data).length : data.length;
+    if (len < (httpCompression?['threshold'] ?? 1024)) {
+      await respond(data);
       return;
     }
 
-    var encodings =
-        connect!.request.headers.value(HttpHeaders.acceptEncodingHeader);
-    var hasGzip = encodings!.contains('gzip');
-    if (!hasGzip && !encodings.contains('deflate')) {
-      respond(data);
+    final String? encodings = connect!.request.headers.value(HttpHeaders.acceptEncodingHeader);
+    final bool hasGzip = encodings?.contains('gzip') ?? false;
+    if (!hasGzip && !(encodings?.contains('deflate') ?? false)) {
+      await respond(data);
       return;
     }
-    var encoding = hasGzip ? 'gzip' : 'deflate';
-//    this.compress(data, encoding, (err, data) {
-//      if (err != null) {
-//        self.req.response..statusCode = 500..close();
-//        callback(err);
-//        return;
-//      }
 
+    final String encoding = hasGzip ? 'gzip' : 'deflate';
     headers[HttpHeaders.contentEncodingHeader] = encoding;
-    respond(hasGzip
-        ? gzip.encode(utf8.encode(
-            data is List ? String.fromCharCodes(data as List) : data))
-        : data);
-//    });
+
+    if (hasGzip) {
+      final String dataString = data is List ? String.fromCharCodes(data as List) : data;
+      await respond(gzip.encode(utf8.encode(dataString)));
+    } else {
+      await respond(data);
+    }
   }
 
-  /// Closes the transport.
-  ///
-  /// @api private
+  /// Overrides `doClose`.
   @override
-  void doClose([dynamic Function()? fn]) {
-    _logger.fine('closing');
+  void doClose([final VoidCallback? fn]) {
+    _logger.fine('polling transport closing');
 
-    var self = this;
+    final PollingTransport self = this;
     Timer? closeTimeoutTimer;
 
     if (dataReq != null) {
@@ -346,43 +393,57 @@ class PollingTransport extends Transport {
       dataReq = null;
     }
 
-    var onClose = () {
+    void onCloseCallback() {
       if (closeTimeoutTimer != null) closeTimeoutTimer.cancel();
       if (fn != null) fn();
       self.onClose();
-    };
+    }
+
     if (writable == true) {
       _logger.fine('transport writable - closing right away');
-      send([
-        {'type': 'close'}
+      send(>[
+        {'type': 'close'}
       ]);
-      onClose();
-    } else if (discarded) {
+      onCloseCallback();
+    } else if (discarded == true) {
       _logger.fine('transport discarded - closing right away');
-      onClose();
+      onCloseCallback();
     } else {
       _logger.fine('transport not writable - buffering orderly close');
-      shouldClose = onClose;
-      closeTimeoutTimer = Timer(Duration(milliseconds: closeTimeout), onClose);
+      shouldClose = onCloseCallback;
+      closeTimeoutTimer = Timer(Duration(milliseconds: closeTimeout), onCloseCallback);
     }
   }
 
   /// Returns headers for a response.
-  ///
-  /// @param {http.IncomingMessage} request
-  /// @param {Object} extra headers
-  /// @api private
-  Map headers(SocketConnect connect, [Map? headers]) {
-    headers = headers ?? {};
+  Headers headers(final SocketConnect connect, [Headers? headers]) {
+    final Headers responseHeaders = headers ?? {};
+
+    // Add CORS headers for cross-origin requests - CRITICAL for fallback
+    final String? origin = connect.request.headers.value('origin');
+    if (origin != null) {
+      responseHeaders['Access-Control-Allow-Origin'] = origin;
+      responseHeaders['Access-Control-Allow-Credentials'] = 'true';
+    } else {
+      responseHeaders['Access-Control-Allow-Origin'] = '*';
+    }
+
+    responseHeaders['Access-Control-Allow-Methods'] = 'GET, POST, OPTIONS';
+    responseHeaders['Access-Control-Allow-Headers'] = 'Content-Type, Authorization, X-Requested-With';
+    responseHeaders['Access-Control-Max-Age'] = '86400';
 
     // prevent XSS warnings on IE
-    // https://github.com/LearnBoost/socket.io/pull/1333
-    var ua = connect.request.headers.value('user-agent');
+    final String? ua = connect.request.headers.value('user-agent');
     if (ua != null && (ua.contains(';MSIE') || ua.contains('Trident/'))) {
-      headers['X-XSS-Protection'] = '0';
+      responseHeaders['X-XSS-Protection'] = '0';
     }
 
-    emit('headers', headers);
-    return headers;
+    // Add cache control headers to prevent caching - CRITICAL for real-time
+    responseHeaders['Cache-Control'] = 'no-cache, no-store, must-revalidate';
+    responseHeaders['Pragma'] = 'no-cache';
+    responseHeaders['Expires'] = '0';
+
+    emit('headers', responseHeaders);
+    return responseHeaders;
   }
 }
diff --git a/lib/src/engine/transport/transports.dart b/lib/src/engine/transport/transports.dart
index 910e8d3..5b3d1d8 100644
--- a/lib/src/engine/transport/transports.dart
+++ b/lib/src/engine/transport/transports.dart
@@ -1,49 +1,52 @@
-/// transports.dart
-///
-/// Purpose:
-///
-/// Description:
-///
-/// History:
-///    17/02/2017, Created by jumperchen
-///
-/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+// transports.dart
+//
+// Purpose:
+//
+// Description:
+//
+// History:
+//    17/02/2017, Created by jumperchen
+//
+// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
 import 'package:logging/logging.dart';
-import 'package:socket_io/src/engine/connect.dart';
-import 'package:socket_io_common/src/engine/parser/parser.dart';
-import 'package:socket_io/src/engine/transport/jsonp_transport.dart';
-import 'package:socket_io/src/engine/transport/websocket_transport.dart';
-import 'package:socket_io/src/engine/transport/xhr_transport.dart';
-import 'package:socket_io/src/util/event_emitter.dart';
+import 'package:socket_io_common/socket_io_common.dart';
+
+import '../../models/callbacks_models.dart' show VoidCallback;
+import '../../models/transport_error_models.dart';
+import '../../util/event_emitter.dart';
+import '../connect.dart';
+import 'jsonp_transport.dart';
+import 'websocket_transport.dart';
+import 'xhr_transport.dart';
 
 class Transports {
-  static List upgradesTo(String from) {
+  static List upgradesTo(final String from) {
     if ('polling' == from) {
-      return ['websocket'];
+      return ['websocket'];
     }
-    return [];
+    return [];
   }
 
-  static Transport newInstance(String name, SocketConnect connect) {
+  static Transport newInstance(final String name, final SocketConnect connect) {
     if ('websocket' == name) {
       return WebSocketTransport(connect);
     } else if ('polling' == name) {
-      if (connect.request.uri.queryParameters.containsKey('j')) {
+      final Map? options = connect.dataset['options'] as Map?;
+      if (options != null && options.containsKey('jsonp') && options['jsonp'] == true) {
         return JSONPTransport(connect);
       } else {
         return XHRTransport(connect);
       }
-    } else {
-      throw UnsupportedError('Unknown transport $name');
     }
+    throw UnimplementedError('Transport $name is not supported');
   }
 }
 
 abstract class Transport extends EventEmitter {
   static final Logger _logger = Logger('socket_io:transport.Transport');
   double? maxHttpBufferSize;
-  Map? httpCompression;
-  Map? perMessageDeflate;
+  Map? httpCompression;
+  Map? perMessageDeflate;
   bool? supportsBinary;
   String? sid;
   String? name;
@@ -53,12 +56,11 @@ abstract class Transport extends EventEmitter {
   SocketConnect? connect;
   MessageHandler? messageHandler;
 
-  Transport(connect) {
-    var options = connect.dataset['options'];
+  Transport(final SocketConnect connect) {
+    final Map? options = connect.dataset['options'] as Map?;
     if (options != null) {
-      messageHandler = options.containsKey('messageHandlerFactory')
-          ? options['messageHandlerFactory'](this, connect)
-          : null;
+      messageHandler =
+          options.containsKey('messageHandlerFactory') ? options['messageHandlerFactory'](this, connect) : null;
     }
   }
 
@@ -66,36 +68,43 @@ abstract class Transport extends EventEmitter {
     discarded = true;
   }
 
-  void onRequest(SocketConnect connect) {
+  void onRequest(final SocketConnect connect) {
     this.connect = connect;
   }
 
-  void close([dynamic Function()? closeFn]) {
+  void close([final VoidCallback? closeFn]) {
     if ('closed' == readyState || 'closing' == readyState) return;
     readyState = 'closing';
     doClose(closeFn);
   }
 
-  void doClose([dynamic Function()? callback]);
+  void doClose([final VoidCallback? callback]);
 
-  void onError(msg, [desc]) {
+  void onError(final Object? msg, [final Object? desc]) {
     writable = false;
     if (hasListeners('error')) {
-      emit('error', {'msg': msg, 'desc': desc, 'type': 'TransportError'});
+      final TransportError error = TransportError(
+        msg: msg?.toString(),
+        desc: desc?.toString(),
+      );
+      // Emit the typed error first
+      emit('error', error);
+      // And emit a map for backward compatibility with existing listeners
+      emit('error', error.toMap());
     } else {
       _logger.fine('ignored transport error $msg ($desc)');
     }
   }
 
-  void onPacket(Map packet) {
+  void onPacket(final Map packet) {
     emit('packet', packet);
   }
 
-  void onData(data) {
+  void onData(final dynamic data) {
     if (messageHandler != null) {
       messageHandler!.handle(this, data);
     } else {
-      onPacket(PacketParser.decodePacket(data, utf8decode: true));
+      onPacket(PacketParser.decodePacket(data, 'utf8')! as Map);
     }
   }
 
@@ -104,12 +113,12 @@ abstract class Transport extends EventEmitter {
     emit('close');
   }
 
-  void send(List data);
+  void send(final List> data);
 
   bool get supportsFraming;
   bool get handlesUpgrades;
 }
 
 abstract class MessageHandler {
-  void handle(Transport transport, /*String|List*/ message);
+  void handle(final Transport transport, /*String|List*/ final dynamic message);
 }
diff --git a/lib/src/engine/transport/websocket_transport.dart b/lib/src/engine/transport/websocket_transport.dart
index 4a23115..1361125 100644
--- a/lib/src/engine/transport/websocket_transport.dart
+++ b/lib/src/engine/transport/websocket_transport.dart
@@ -11,28 +11,31 @@ import 'dart:async';
 ///
 /// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
 import 'package:logging/logging.dart';
-import 'package:socket_io_common/src/engine/parser/parser.dart';
-import 'package:socket_io/src/engine/transport/transports.dart';
+import 'package:socket_io_common/socket_io_common.dart';
+
+import '../../models/callbacks_models.dart' show VoidCallback;
+import '../connect.dart';
+import 'transports.dart';
 
 class WebSocketTransport extends Transport {
-  static final Logger _logger =
-      Logger('socket_io:transport.WebSocketTransport');
+  static final Logger _logger = Logger('socket_io:transport.WebSocketTransport');
   @override
   bool get handlesUpgrades => true;
   @override
   bool get supportsFraming => true;
-  StreamSubscription? subscription;
-  WebSocketTransport(connect) : super(connect) {
+  StreamSubscription? subscription;
+  WebSocketTransport(final SocketConnect? connect) : super(connect!) {
     name = 'websocket';
     this.connect = connect;
-    subscription =
-        connect.websocket.listen(onData, onError: onError, onDone: onClose);
+    subscription = connect.websocket?.listen(onData, onError: onError, onDone: onClose);
     writable = true;
   }
 
   @override
-  void send(List packets) {
-    var send = (data, Map packet) {
+  void send(final List> packets) {
+    if (connect != null) super.onRequest(connect!);
+
+    void send(final Object data) {
       _logger.fine('writing "$data"');
 
       // always creates a new object since ws modifies it
@@ -50,34 +53,34 @@ class WebSocketTransport extends Transport {
 
 //      this.writable = false;
       connect!.websocket?.add(data);
-    };
+    }
 
 //    function onEnd (err) {
 //      if (err) return self.onError('write error', err.stack);
 //      self.writable = true;
 //      self.emit('drain');
 //    }
-    for (var i = 0; i < packets.length; i++) {
-      var packet = packets[i];
+    for (int i = 0; i < packets.length; i++) {
+      final Map packet = packets[i];
       PacketParser.encodePacket(packet,
-          supportsBinary: supportsBinary, callback: (_) => send(_, packet));
+          supportsBinary: supportsBinary ?? false, callback: (final dynamic data) => send(data));
     }
   }
 
   @override
-  void onClose() {
+  Future onClose() async {
     super.onClose();
 
     // workaround for https://github.com/dart-lang/sdk/issues/27414
     if (subscription != null) {
-      subscription!.cancel();
+      await subscription!.cancel();
       subscription = null;
     }
   }
 
   @override
-  void doClose([fn]) {
-    connect!.websocket?.close();
+  Future doClose([final VoidCallback? fn]) async {
+    await connect!.websocket?.close();
     if (fn != null) fn();
   }
 }
diff --git a/lib/src/engine/transport/xhr_transport.dart b/lib/src/engine/transport/xhr_transport.dart
index 33dfeba..5f5f494 100644
--- a/lib/src/engine/transport/xhr_transport.dart
+++ b/lib/src/engine/transport/xhr_transport.dart
@@ -1,38 +1,44 @@
-/// xhr_transport.dart
-///
-/// Purpose:
-///
-/// Description:
-///
-/// History:
-///    22/02/2017, Created by jumperchen
-///
-/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
-import 'package:socket_io/src/engine/connect.dart';
-import 'package:socket_io/src/engine/transport/polling_transport.dart';
+// xhr_transport.dart
+//
+// Purpose:
+//
+// Description:
+//
+// History:
+//    22/02/2017, Created by jumperchen
+//
+// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+import 'dart:async';
+import 'dart:io';
+
+import '../../types/common_types.dart' show Headers;
+import '../connect.dart';
+import 'polling_transport.dart';
 
 class XHRTransport extends PollingTransport {
-  XHRTransport(SocketConnect connect) : super(connect);
+  XHRTransport(super.connect);
 
   /// Overrides `onRequest` to handle `OPTIONS`..
   ///
   /// @param {http.IncomingMessage}
   /// @api private
   @override
-  void onRequest(SocketConnect connect) {
-    var req = connect.request;
+  Future onRequest(final SocketConnect connect) async {
+    final HttpRequest req = connect.request;
     if ('OPTIONS' == req.method) {
-      var res = req.response;
-      var headers = this.headers(connect);
+      final HttpResponse res = req.response;
+      final Headers headers = this.headers(connect);
       headers['Access-Control-Allow-Headers'] = 'Content-Type';
-      headers.forEach((key, value) {
+      headers.forEach((final String key, final Object value) {
         res.headers.set(key, value);
       });
-      res.statusCode = 200;
-
-      connect.close();
+      res
+        ..statusCode = 200
+        ..write('');
+      await res.close();
+      await connect.close();
     } else {
-      super.onRequest(connect);
+      await super.onRequest(connect);
     }
   }
 
@@ -42,15 +48,36 @@ class XHRTransport extends PollingTransport {
   /// @param {Object} extra headers
   /// @api private
   @override
-  Map headers(SocketConnect connect, [Map? headers]) {
-    headers = headers ?? {};
-    var req = connect.request;
-    if (req.headers.value('origin') != null) {
-      headers['Access-Control-Allow-Credentials'] = 'true';
-      headers['Access-Control-Allow-Origin'] = req.headers.value('origin');
+  Headers headers(final SocketConnect connect, [Headers? extraHeaders]) {
+    final HttpRequest req = connect.request;
+    final Headers responseHeaders = extraHeaders ?? {};
+
+    // Add CORS headers for cross-origin requests
+    final String? origin = req.headers.value('origin');
+    if (origin != null) {
+      responseHeaders['Access-Control-Allow-Origin'] = origin;
+      responseHeaders['Access-Control-Allow-Credentials'] = 'true';
     } else {
-      headers['Access-Control-Allow-Origin'] = '*';
+      responseHeaders['Access-Control-Allow-Origin'] = '*';
+    }
+
+    responseHeaders['Access-Control-Allow-Methods'] = 'GET, POST, OPTIONS';
+    responseHeaders['Access-Control-Allow-Headers'] = 'Content-Type, Authorization, X-Requested-With';
+    responseHeaders['Access-Control-Max-Age'] = '86400';
+
+    // prevent XSS warnings on IE
+    // https://github.com/LearnBoost/socket.io/pull/1333
+    final String? ua = req.headers.value('user-agent');
+    if (ua != null && (ua.contains(';MSIE') || ua.contains('Trident/'))) {
+      responseHeaders['X-XSS-Protection'] = '0';
     }
-    return super.headers(connect, headers);
+
+    // Add cache control headers to prevent caching of polling responses
+    responseHeaders['Cache-Control'] = 'no-cache, no-store, must-revalidate';
+    responseHeaders['Pragma'] = 'no-cache';
+    responseHeaders['Expires'] = '0';
+
+    emit('headers', responseHeaders);
+    return responseHeaders;
   }
 }
diff --git a/lib/src/extensions/duration_extensions.dart b/lib/src/extensions/duration_extensions.dart
new file mode 100644
index 0000000..87c02ed
--- /dev/null
+++ b/lib/src/extensions/duration_extensions.dart
@@ -0,0 +1,165 @@
+/// duration_extensions.dart
+///
+/// Extension methods for Duration operations including timeout validation
+/// and millisecond conversions.
+///
+/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+library duration_extensions;
+
+/// Extension methods for Duration
+extension DurationExtensions on Duration {
+  /// Converts Duration to milliseconds as int
+  int get inWholeMilliseconds => inMilliseconds;
+
+  /// Checks if duration is valid for a timeout (non-negative)
+  bool get isValidTimeout => !isNegative;
+
+  /// Checks if duration is within a reasonable timeout range
+  /// (between 0 and 1 hour by default)
+  bool isWithinTimeoutRange({final Duration max = const Duration(hours: 1)}) => !isNegative && this <= max;
+
+  /// Creates a human-readable string representation
+  String toHumanReadable() {
+    if (inDays > 0) {
+      return '$inDays day${inDays == 1 ? '' : 's'}';
+    } else if (inHours > 0) {
+      return '$inHours hour${inHours == 1 ? '' : 's'}';
+    } else if (inMinutes > 0) {
+      return '$inMinutes minute${inMinutes == 1 ? '' : 's'}';
+    } else if (inSeconds > 0) {
+      return '$inSeconds second${inSeconds == 1 ? '' : 's'}';
+    } else {
+      return '${inMilliseconds}ms';
+    }
+  }
+
+  /// Creates a compact string representation (e.g., "1d", "2h", "30m", "45s", "100ms")
+  String toCompact() {
+    if (inDays > 0) {
+      return '${inDays}d';
+    } else if (inHours > 0) {
+      return '${inHours}h';
+    } else if (inMinutes > 0) {
+      return '${inMinutes}m';
+    } else if (inSeconds > 0) {
+      return '${inSeconds}s';
+    } else {
+      return '${inMilliseconds}ms';
+    }
+  }
+
+  /// Clamps duration between min and max values
+  Duration clamp(final Duration min, final Duration max) {
+    if (this < min) return min;
+    if (this > max) return max;
+    return this;
+  }
+
+  /// Multiplies duration by a factor
+  Duration operator *(final num factor) => Duration(microseconds: (inMicroseconds * factor).round());
+
+  /// Divides duration by a factor
+  Duration operator /(final num factor) => Duration(microseconds: (inMicroseconds / factor).round());
+
+  /// Checks if duration is approximately equal to another within tolerance
+  bool isApproximately(final Duration other, {final Duration tolerance = const Duration(milliseconds: 1)}) =>
+      (this - other).abs() <= tolerance;
+
+  /// Returns the absolute value of the duration
+  Duration abs() => isNegative ? -this : this;
+}
+
+/// Extension methods for int representing milliseconds
+extension IntToDuration on int {
+  /// Converts milliseconds (as int) to Duration
+  Duration get milliseconds => Duration(milliseconds: this);
+
+  /// Converts seconds (as int) to Duration
+  Duration get seconds => Duration(seconds: this);
+
+  /// Converts minutes (as int) to Duration
+  Duration get minutes => Duration(minutes: this);
+
+  /// Converts hours (as int) to Duration
+  Duration get hours => Duration(hours: this);
+
+  /// Converts days (as int) to Duration
+  Duration get days => Duration(days: this);
+
+  /// Checks if millisecond value is a valid timeout (non-negative)
+  bool get isValidTimeoutMs => this >= 0;
+
+  /// Checks if millisecond value is within reasonable timeout range
+  bool get isReasonableTimeoutMs => this >= 0 && this <= const Duration(hours: 1).inMilliseconds;
+}
+
+/// Extension methods for double representing milliseconds
+extension DoubleToDuration on double {
+  /// Converts milliseconds (as double) to Duration
+  Duration get milliseconds => Duration(microseconds: (this * 1000).round());
+
+  /// Converts seconds (as double) to Duration
+  Duration get seconds => Duration(microseconds: (this * 1000000).round());
+
+  /// Converts minutes (as double) to Duration
+  Duration get minutes => Duration(microseconds: (this * 60000000).round());
+
+  /// Converts hours (as double) to Duration
+  Duration get hours => Duration(microseconds: (this * 3600000000).round());
+
+  /// Converts days (as double) to Duration
+  Duration get days => Duration(microseconds: (this * 86400000000).round());
+}
+
+/// Utility class for common timeout durations
+class TimeoutDurations {
+  TimeoutDurations._();
+
+  /// No timeout (Duration.zero)
+  static const Duration none = Duration.zero;
+
+  /// Very short timeout (100ms)
+  static const Duration veryShort = Duration(milliseconds: 100);
+
+  /// Short timeout (1 second)
+  static const Duration short = Duration(seconds: 1);
+
+  /// Medium timeout (5 seconds)
+  static const Duration medium = Duration(seconds: 5);
+
+  /// Default timeout (30 seconds)
+  static const Duration defaultTimeout = Duration(seconds: 30);
+
+  /// Long timeout (1 minute)
+  static const Duration long = Duration(minutes: 1);
+
+  /// Very long timeout (5 minutes)
+  static const Duration veryLong = Duration(minutes: 5);
+
+  /// Extremely long timeout (30 minutes)
+  static const Duration extreme = Duration(minutes: 30);
+
+  /// Maximum reasonable timeout (1 hour)
+  static const Duration maximum = Duration(hours: 1);
+
+  /// Connection timeout (typically 20 seconds)
+  static const Duration connection = Duration(seconds: 20);
+
+  /// Handshake timeout (typically 10 seconds)
+  static const Duration handshake = Duration(seconds: 10);
+
+  /// Keep-alive interval (typically 25 seconds)
+  static const Duration keepAlive = Duration(seconds: 25);
+
+  /// Ping interval (typically 25 seconds)
+  static const Duration ping = Duration(seconds: 25);
+
+  /// Ping timeout (typically 60 seconds)
+  static const Duration pingTimeout = Duration(seconds: 60);
+
+  /// Reconnection delay (typically 1 second)
+  static const Duration reconnection = Duration(seconds: 1);
+
+  /// HTTP request timeout (typically 30 seconds)
+  static const Duration httpRequest = Duration(seconds: 30);
+}
diff --git a/lib/src/extensions/extensions.dart b/lib/src/extensions/extensions.dart
new file mode 100644
index 0000000..42809b6
--- /dev/null
+++ b/lib/src/extensions/extensions.dart
@@ -0,0 +1,13 @@
+/// extensions.dart
+///
+/// Barrel file for all extension methods in socket_io
+///
+/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+library extensions;
+
+export 'duration_extensions.dart';
+export 'list_extensions.dart';
+export 'map_extensions.dart';
+export 'packet_extensions.dart';
+export 'socket_extensions.dart';
+export 'string_extensions.dart';
diff --git a/lib/src/extensions/list_extensions.dart b/lib/src/extensions/list_extensions.dart
new file mode 100644
index 0000000..419b9a6
--- /dev/null
+++ b/lib/src/extensions/list_extensions.dart
@@ -0,0 +1,197 @@
+/// list_extensions.dart
+///
+/// Extension methods for List operations to provide safe access
+/// and common list manipulation patterns.
+///
+/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+library list_extensions;
+
+/// Extension methods for List providing safe access and operations
+extension ListSafeAccess on List {
+  /// Gets an element at index or returns null if out of bounds
+  T? getOrNull(final int index) {
+    if (index < 0 || index >= length) return null;
+    return this[index];
+  }
+
+  /// Gets an element at index or returns default value if out of bounds
+  T getOrDefault(final int index, final T defaultValue) => getOrNull(index) ?? defaultValue;
+
+  /// Gets the first element or null if list is empty
+  T? get firstOrNull => isEmpty ? null : first;
+
+  /// Gets the last element or null if list is empty
+  T? get lastOrNull => isEmpty ? null : last;
+
+  /// Gets the first element matching predicate or null
+  T? firstWhereOrNull(final bool Function(T element) test) {
+    for (final T element in this) {
+      if (test(element)) return element;
+    }
+    return null;
+  }
+
+  /// Gets the last element matching predicate or null
+  T? lastWhereOrNull(final bool Function(T element) test) {
+    for (int i = length - 1; i >= 0; i--) {
+      if (test(this[i])) return this[i];
+    }
+    return null;
+  }
+
+  /// Splits list into chunks of specified size
+  List> chunk(final int size) {
+    if (size <= 0) throw ArgumentError('Chunk size must be positive');
+
+    final List> chunks = >[];
+    for (int i = 0; i < length; i += size) {
+      final int end = (i + size < length) ? i + size : length;
+      chunks.add(sublist(i, end));
+    }
+    return chunks;
+  }
+
+  /// Groups elements by a key
+  Map> groupBy(final K Function(T element) keySelector) {
+    final Map> groups = >{};
+    for (final T element in this) {
+      final K key = keySelector(element);
+      groups.putIfAbsent(key, () => []).add(element);
+    }
+    return groups;
+  }
+
+  /// Removes duplicates from the list
+  List distinct() => toSet().toList();
+
+  /// Removes duplicates based on a key selector
+  List distinctBy(final K Function(T element) keySelector) {
+    final Set seen = {};
+    final List result = [];
+
+    for (final T element in this) {
+      final K key = keySelector(element);
+      if (seen.add(key)) {
+        result.add(element);
+      }
+    }
+    return result;
+  }
+
+  /// Partitions list into two lists based on a predicate
+  ({List matched, List unmatched}) partition(final bool Function(T element) test) {
+    final List matched = [];
+    final List unmatched = [];
+
+    for (final T element in this) {
+      if (test(element)) {
+        matched.add(element);
+      } else {
+        unmatched.add(element);
+      }
+    }
+
+    return (matched: matched, unmatched: unmatched);
+  }
+
+  /// Intersperses a separator between elements
+  List intersperse(final T separator) {
+    if (isEmpty) return [];
+    if (length == 1) return [first];
+
+    final List result = [];
+    for (int i = 0; i < length; i++) {
+      result.add(this[i]);
+      if (i < length - 1) {
+        result.add(separator);
+      }
+    }
+    return result;
+  }
+
+  /// Takes elements while predicate is true
+  List takeWhile(final bool Function(T element) test) {
+    final List result = [];
+    for (final T element in this) {
+      if (!test(element)) break;
+      result.add(element);
+    }
+    return result;
+  }
+
+  /// Skips elements while predicate is true
+  List skipWhile(final bool Function(T element) test) {
+    int index = 0;
+    while (index < length && test(this[index])) {
+      index++;
+    }
+    return sublist(index);
+  }
+
+  /// Zips two lists together
+  List<({T first, U second})> zip(final List other) {
+    final int minLength = length < other.length ? length : other.length;
+    final List<({T first, U second})> result = <({T first, U second})>[];
+
+    for (int i = 0; i < minLength; i++) {
+      result.add((first: this[i], second: other[i]));
+    }
+
+    return result;
+  }
+
+  /// Flattens a list of lists (only works when T is also a List)
+  List flatten() {
+    if (this is! List>) {
+      throw UnsupportedError('flatten only works on List>');
+    }
+
+    final List result = [];
+    for (final dynamic element in this) {
+      if (element is List) {
+        result.addAll(element);
+      }
+    }
+    return result;
+  }
+
+  /// Counts elements matching a predicate
+  int count(final bool Function(T element) test) {
+    int counter = 0;
+    for (final T element in this) {
+      if (test(element)) counter++;
+    }
+    return counter;
+  }
+
+  /// Checks if list contains any element matching predicate
+  bool anyMatch(final bool Function(T element) test) => firstWhereOrNull(test) != null;
+
+  /// Checks if all elements match predicate (returns true for empty list)
+  bool allMatch(final bool Function(T element) test) {
+    for (final T element in this) {
+      if (!test(element)) return false;
+    }
+    return true;
+  }
+
+  /// Checks if no elements match predicate
+  bool noneMatch(final bool Function(T element) test) => !anyMatch(test);
+}
+
+/// Extension methods for List with type checking
+extension DynamicListTypeChecking on List {
+  /// Checks if all elements are of a specific type
+  bool allOfType() => every((final dynamic element) => element is T);
+
+  /// Filters elements of a specific type
+  List whereType() => [...where((final dynamic element) => element is T).cast()];
+
+  /// Safely casts to List if all elements are of type T
+  List? tryCast() {
+    if (allOfType()) {
+      return List.from(this);
+    }
+    return null;
+  }
+}
diff --git a/lib/src/extensions/map_extensions.dart b/lib/src/extensions/map_extensions.dart
new file mode 100644
index 0000000..cc271cc
--- /dev/null
+++ b/lib/src/extensions/map_extensions.dart
@@ -0,0 +1,168 @@
+/// map_extensions.dart
+///
+/// Extension methods for Map operations to provide type-safe getters
+/// and common map manipulation patterns.
+///
+/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+library map_extensions;
+
+/// Extension methods for Map providing type-safe getters
+extension MapTypeSafeGetters on Map {
+  /// Gets a String value or returns null if not found or wrong type
+  String? getStringOrNull(final String key) {
+    final dynamic value = this[key];
+    return value is String ? value : null;
+  }
+
+  /// Gets a String value or returns default if not found or wrong type
+  String getString(final String key, final String defaultValue) => getStringOrNull(key) ?? defaultValue;
+
+  /// Gets an int value or returns null if not found or wrong type
+  int? getIntOrNull(final String key) {
+    final dynamic value = this[key];
+    if (value is int) return value;
+    if (value is num) return value.toInt();
+    return null;
+  }
+
+  /// Gets an int value or returns default if not found or wrong type
+  int getInt(final String key, final int defaultValue) => getIntOrNull(key) ?? defaultValue;
+
+  /// Gets a double value or returns null if not found or wrong type
+  double? getDoubleOrNull(final String key) {
+    final dynamic value = this[key];
+    if (value is double) return value;
+    if (value is num) return value.toDouble();
+    return null;
+  }
+
+  /// Gets a double value or returns default if not found or wrong type
+  double getDouble(final String key, final double defaultValue) => getDoubleOrNull(key) ?? defaultValue;
+
+  /// Gets a bool value or returns null if not found or wrong type
+  bool? getBoolOrNull(final String key) {
+    final dynamic value = this[key];
+    return value is bool ? value : null;
+  }
+
+  /// Gets a bool value or returns default if not found or wrong type
+  bool getBool(final String key, final bool defaultValue) => getBoolOrNull(key) ?? defaultValue;
+
+  /// Gets a List value or returns null if not found or wrong type
+  List? getListOrNull(final String key) {
+    final dynamic value = this[key];
+    if (value is List) return value;
+    if (value is List) {
+      try {
+        return List.from(value);
+      } on Object {
+        return null;
+      }
+    }
+    return null;
+  }
+
+  /// Gets a List value or returns default if not found or wrong type
+  List getList(final String key, final List defaultValue) => getListOrNull(key) ?? defaultValue;
+
+  /// Gets a Map value or returns null if not found or wrong type
+  Map? getMapOrNull(final String key) {
+    final dynamic value = this[key];
+    if (value is Map) return value;
+    if (value is Map) {
+      try {
+        return Map.from(value);
+      } on Object {
+        return null;
+      }
+    }
+    return null;
+  }
+
+  /// Gets a Map value or returns default if not found or wrong type
+  Map getMap(final String key, final Map defaultValue) => getMapOrNull(key) ?? defaultValue;
+
+  /// Gets a nested value using a path (e.g., 'user.profile.name')
+  dynamic getNestedValue(final String path, [final dynamic defaultValue]) {
+    final List keys = path.split('.');
+    dynamic current = this;
+
+    for (final String key in keys) {
+      if (current is Map) {
+        current = current[key];
+        if (current == null) return defaultValue;
+      } else {
+        return defaultValue;
+      }
+    }
+
+    return current ?? defaultValue;
+  }
+
+  /// Gets a nested String value using a path
+  String? getNestedString(final String path) {
+    final dynamic value = getNestedValue(path);
+    return value is String ? value : null;
+  }
+
+  /// Gets a nested int value using a path
+  int? getNestedInt(final String path) {
+    final dynamic value = getNestedValue(path);
+    if (value is int) return value;
+    if (value is num) return value.toInt();
+    return null;
+  }
+}
+
+/// Extension methods for general Map operations
+extension MapOperations on Map {
+  /// Creates a new map with only the specified keys
+  Map pick(final List keys) => Map.fromEntries(
+        keys.where(containsKey).map((final K key) => MapEntry(key, this[key] as V)),
+      );
+
+  /// Creates a new map excluding the specified keys
+  Map omit(final List keys) => Map.fromEntries(
+        entries.where((final MapEntry entry) => !keys.contains(entry.key)),
+      );
+
+  /// Merges another map into this one, with the other map's values taking precedence
+  Map merge(final Map other) => {...this, ...other};
+
+  /// Deep merges another map (only works with nested Map)
+  Map deepMerge(final Map other) {
+    if (this is! Map) {
+      throw UnsupportedError('deepMerge only works with Map');
+    }
+
+    final Map result = Map.from(this as Map);
+
+    other.forEach((final String key, final dynamic value) {
+      if (value is Map && result[key] is Map) {
+        result[key] = (result[key] as Map).deepMerge(value);
+      } else {
+        result[key] = value;
+      }
+    });
+
+    return result;
+  }
+
+  /// Returns a new map with null values removed
+  Map compact() => Map.fromEntries(
+        entries.where((final MapEntry entry) => entry.value != null),
+      );
+
+  /// Transforms values in the map
+  Map mapValues(final R Function(V value) transform) =>
+      map((final K key, final V value) => MapEntry(key, transform(value)));
+
+  /// Transforms keys in the map
+  Map mapKeys(final R Function(K key) transform) =>
+      map((final K key, final V value) => MapEntry(transform(key), value));
+
+  /// Filters entries in the map
+  Map filterEntries(final bool Function(K key, V value) predicate) => Map.fromEntries(
+        entries.where((final MapEntry entry) => predicate(entry.key, entry.value)),
+      );
+}
diff --git a/lib/src/extensions/packet_extensions.dart b/lib/src/extensions/packet_extensions.dart
new file mode 100644
index 0000000..aadc410
--- /dev/null
+++ b/lib/src/extensions/packet_extensions.dart
@@ -0,0 +1,282 @@
+/// packet_extensions.dart
+///
+/// Extension methods for Socket.IO packet operations
+///
+/// Provides type checking, validation, and convenience methods
+/// for working with Socket.IO packets.
+///
+/// Copyright (C) 2024 Potix Corporation. All Rights Reserved.
+library packet_extensions;
+
+import 'package:socket_io_common/socket_io_common.dart';
+
+import '../models/packet_models.dart';
+
+/// Extension methods for SocketIOPacket
+///
+/// Provides convenient type checking and validation methods
+extension PacketExtensions on SocketIOPacket {
+  /// Check if this packet is a CONNECT packet
+  bool get isConnect => type == CONNECT;
+
+  /// Check if this packet is a DISCONNECT packet
+  bool get isDisconnect => type == DISCONNECT;
+
+  /// Check if this packet is an EVENT packet
+  bool get isEvent => type == EVENT || type == BINARY_EVENT;
+
+  /// Check if this packet is an ACK packet
+  bool get isAck => type == ACK || type == BINARY_ACK;
+
+  /// Check if this packet is a CONNECT_ERROR packet
+  bool get isConnectError => type == CONNECT_ERROR;
+
+  /// Check if this packet contains binary data
+  bool get isBinary => type == BINARY_EVENT || type == BINARY_ACK;
+
+  /// Check if this packet requires acknowledgment
+  bool get requiresAck => id != null && (isEvent || isConnect);
+
+  /// Check if this packet is an acknowledgment response
+  bool get isAckResponse => isAck;
+
+  /// Check if packet is for the default namespace
+  bool get isDefaultNamespace => namespace == null || namespace == '/';
+
+  /// Check if packet is for a custom namespace
+  bool get isCustomNamespace => !isDefaultNamespace;
+
+  /// Get the packet type as a human-readable string
+  String get typeName {
+    switch (type) {
+      case CONNECT:
+        return 'CONNECT';
+      case DISCONNECT:
+        return 'DISCONNECT';
+      case EVENT:
+        return 'EVENT';
+      case ACK:
+        return 'ACK';
+      case CONNECT_ERROR:
+        return 'CONNECT_ERROR';
+      case BINARY_EVENT:
+        return 'BINARY_EVENT';
+      case BINARY_ACK:
+        return 'BINARY_ACK';
+      default:
+        return 'UNKNOWN($type)';
+    }
+  }
+
+  /// Check if packet has data payload
+  bool get hasData => data != null;
+
+  /// Check if packet has an ID
+  bool get hasId => id != null;
+
+  /// Get namespace or default
+  String get effectiveNamespace => namespace ?? '/';
+
+  /// Create a human-readable description of this packet
+  String get description {
+    final StringBuffer buffer = StringBuffer(typeName);
+
+    if (isCustomNamespace) {
+      buffer.write(' [${namespace!}]');
+    }
+
+    if (hasId) {
+      buffer.write(' #$id');
+    }
+
+    if (hasData) {
+      final String dataStr = data.toString();
+      final String truncated = dataStr.length > 50 ? '${dataStr.substring(0, 47)}...' : dataStr;
+      buffer.write(' data: $truncated');
+    }
+
+    return buffer.toString();
+  }
+}
+
+/// Extension methods for ConnectPacket
+extension ConnectPacketExtensions on ConnectPacket {
+  /// Check if connect packet has session ID in data
+  bool get hasSessionId {
+    final Object? currentData = data;
+    if (currentData is Map) {
+      return currentData.containsKey('sid');
+    }
+    return false;
+  }
+
+  /// Get session ID from connect data if available
+  String? get sessionId {
+    final Object? currentData = data;
+    if (currentData is Map) {
+      return currentData['sid'] as String?;
+    }
+    return null;
+  }
+}
+
+/// Extension methods for EventPacket
+extension EventPacketExtensions on EventPacket {
+  /// Get the event name from the packet
+  String? get eventName {
+    final Object? currentData = data;
+    if (currentData is List && currentData.isNotEmpty) {
+      return currentData.first as String?;
+    }
+    return null;
+  }
+
+  /// Get event arguments (excluding the event name)
+  List get eventArgs {
+    final Object? currentData = data;
+    if (currentData is List && currentData.length > 1) {
+      return currentData.sublist(1);
+    }
+    return [];
+  }
+
+  /// Get the number of arguments
+  int get argCount => eventArgs.length;
+
+  /// Check if event has arguments
+  bool get hasArgs => argCount > 0;
+
+  /// Check if this event needs acknowledgment
+  bool get needsAck => hasId;
+}
+
+/// Extension methods for AckPacket
+extension AckPacketExtensions on AckPacket {
+  /// Get the number of acknowledgment values
+  int get valueCount {
+    final Object? currentData = data;
+    if (currentData is List) {
+      return currentData.length;
+    }
+    return 0;
+  }
+
+  /// Check if ACK has values
+  bool get hasValues => valueCount > 0;
+
+  /// Get first ACK value if available
+  Object? get firstValue {
+    final Object? currentData = data;
+    if (currentData is List && currentData.isNotEmpty) {
+      return currentData.first;
+    }
+    return null;
+  }
+}
+
+/// Extension methods for ConnectErrorPacket
+extension ConnectErrorPacketExtensions on ConnectErrorPacket {
+  /// Get error message from packet
+  String get errorMessage {
+    final Object? currentData = data;
+    if (currentData is Map) {
+      return currentData['message']?.toString() ?? 'Unknown error';
+    }
+    return currentData?.toString() ?? 'Unknown error';
+  }
+
+  /// Check if error has an error code
+  bool get hasErrorCode {
+    final Object? currentData = data;
+    if (currentData is Map) {
+      return currentData.containsKey('code');
+    }
+    return false;
+  }
+
+  /// Get error code if available
+  Object? get errorCode {
+    final Object? currentData = data;
+    if (currentData is Map) {
+      return currentData['code'];
+    }
+    return null;
+  }
+}
+
+/// Extension methods for packet validation
+extension PacketValidation on SocketIOPacket {
+  /// Validate that packet has required fields
+  bool get isValid {
+    // Basic validation
+    if (type < CONNECT || type > BINARY_ACK) {
+      return false;
+    }
+
+    // Type-specific validation
+    switch (type) {
+      case CONNECT:
+      case CONNECT_ERROR:
+        // Connect packets should have namespace
+        return true;
+
+      case EVENT:
+      case BINARY_EVENT:
+        // Event packets must have data
+        return hasData;
+
+      case ACK:
+      case BINARY_ACK:
+        // ACK packets must have an ID
+        return hasId;
+
+      case DISCONNECT:
+        // Disconnect packets are always valid
+        return true;
+
+      default:
+        return false;
+    }
+  }
+
+  /// Get validation errors as a list
+  List getValidationErrors() {
+    final List errors = [];
+
+    if (type < CONNECT || type > BINARY_ACK) {
+      errors.add('Invalid packet type: $type');
+    }
+
+    switch (type) {
+      case EVENT:
+      case BINARY_EVENT:
+        if (!hasData) {
+          errors.add('Event packet must have data');
+        }
+        final Object? currentData = data;
+        if (currentData is List && currentData.isEmpty) {
+          errors.add('Event data must not be empty');
+        }
+        break;
+
+      case ACK:
+      case BINARY_ACK:
+        if (!hasId) {
+          errors.add('ACK packet must have an ID');
+        }
+        break;
+    }
+
+    return errors;
+  }
+
+  /// Check if packet can be safely serialized
+  bool get isSerializable {
+    try {
+      toMap();
+      return true;
+    } on Object {
+      return false;
+    }
+  }
+}
diff --git a/lib/src/extensions/socket_extensions.dart b/lib/src/extensions/socket_extensions.dart
new file mode 100644
index 0000000..69c3794
--- /dev/null
+++ b/lib/src/extensions/socket_extensions.dart
@@ -0,0 +1,166 @@
+/// socket_extensions.dart
+///
+/// Extension methods for Socket operations
+///
+/// Provides convenient helper methods for common socket operations,
+/// room management, and state checks.
+///
+/// Copyright (C) 2024 Potix Corporation. All Rights Reserved.
+library socket_extensions;
+
+import '../socket.dart';
+import '../value_objects/event_name_vo.dart';
+import '../value_objects/room_name_vo.dart';
+import '../value_objects/socket_state_vo.dart';
+
+/// Extension methods for Socket class
+///
+/// Provides helper methods for:
+/// - Room membership checks
+/// - State queries
+/// - Event validation
+/// - Convenience operations
+extension SocketExtensions on Socket {
+  /// Check if this socket is a member of the specified room
+  ///
+  /// Example:
+  /// ```dart
+  /// if (socket.isInRoom(RoomName('chat-room'))) {
+  ///   print('User is in chat room');
+  /// }
+  /// ```
+  bool isInRoom(final RoomName room) => roomMembership.contains(room);
+
+  /// Check if this socket is a member of the specified room (string version)
+  ///
+  /// Convenience method for backward compatibility
+  bool isInRoomString(final String roomName) => roomMembership.containsName(roomName);
+
+  /// Get all rooms this socket is currently a member of
+  ///
+  /// Returns a list of RoomName value objects
+  List getAllRooms() => roomMembership.rooms.toList();
+
+  /// Get all room names as strings
+  ///
+  /// Convenience method for backward compatibility
+  List getAllRoomNames() => roomMembership.roomNames.toList();
+
+  /// Get the number of rooms this socket is a member of
+  int get roomCount => roomMembership.length;
+
+  /// Check if socket is not in any rooms
+  bool get hasNoRooms => roomMembership.isEmpty;
+
+  /// Check if socket is in one or more rooms
+  bool get hasRooms => roomMembership.isNotEmpty;
+
+  /// Get the current state of this socket
+  SocketState get state {
+    if (connected) {
+      return SocketState.connected;
+    } else if (disconnected) {
+      return SocketState.disconnected;
+    } else {
+      return SocketState.connecting;
+    }
+  }
+
+  /// Check if socket is ready to send/receive events
+  bool get isReady => connected && !disconnected;
+
+  /// Check if socket is in a transitional state
+  bool get isInTransition => !connected && !disconnected;
+
+  /// Check if event name is valid for this socket
+  ///
+  /// Returns false if the event name is blacklisted or reserved
+  bool isEventAllowed(final EventName eventName) => !eventName.isBlacklisted;
+
+  /// Check if event name string is valid for this socket
+  ///
+  /// Convenience method for string event names
+  bool isEventAllowedString(final String eventName) {
+    try {
+      final EventName event = EventName(eventName);
+      return isEventAllowed(event);
+    } on ArgumentError {
+      return false;
+    }
+  }
+
+  /// Emit event only if socket is ready
+  ///
+  /// Returns true if event was emitted, false if socket is not ready
+  ///
+  /// Example:
+  /// ```dart
+  /// final bool sent = socket.emitIfReady('message', ['Hello']);
+  /// if (!sent) {
+  ///   print('Socket not ready, message not sent');
+  /// }
+  /// ```
+  bool emitIfReady(final String event, [final List? data]) {
+    if (!isReady) {
+      return false;
+    }
+    emit(event, data);
+    return true;
+  }
+
+  /// Check if this socket has a specific flag set
+  ///
+  /// Example:
+  /// ```dart
+  /// if (socket.hasFlag('broadcast')) {
+  ///   print('Socket is in broadcast mode');
+  /// }
+  /// ```
+  bool hasFlag(final String flagName) => flags?.containsKey(flagName) == true && flags![flagName] == true;
+
+  /// Get all active flags as a list
+  List getActiveFlags() {
+    if (flags == null) {
+      return [];
+    }
+    return flags!.entries
+        .where((final MapEntry entry) => entry.value == true)
+        .map((final MapEntry entry) => entry.key)
+        .toList();
+  }
+
+  /// Check if socket is in broadcast mode
+  bool get isBroadcasting => hasFlag('broadcast');
+
+  /// Check if socket has volatile flag set
+  bool get isVolatile => hasFlag('volatile');
+
+  /// Check if socket has compress flag set
+  bool get isCompressing => hasFlag('compress');
+
+  /// Get socket connection ID as string
+  ///
+  /// Convenience method for accessing the connection ID value
+  String get connectionIdValue => connectionId.value;
+
+  /// Get namespace name as string
+  ///
+  /// Convenience method for accessing the namespace name
+  String get namespaceName => nsp.name;
+
+  /// Check if socket is connected to the default namespace
+  bool get isDefaultNamespace => namespaceName == '/';
+
+  /// Get query parameters as a map
+  ///
+  /// Returns an empty map if no query parameters are set
+  Map getQueryMap() => queryParameters?.toMap() ?? {};
+
+  /// Get a specific query parameter value
+  ///
+  /// Returns null if the parameter doesn't exist
+  String? getQueryParameter(final String key) => queryParameters?.get(key);
+
+  /// Check if a query parameter exists
+  bool hasQueryParameter(final String key) => queryParameters?.has(key) ?? false;
+}
diff --git a/lib/src/extensions/string_extensions.dart b/lib/src/extensions/string_extensions.dart
new file mode 100644
index 0000000..07841d6
--- /dev/null
+++ b/lib/src/extensions/string_extensions.dart
@@ -0,0 +1,200 @@
+/// string_extensions.dart
+///
+/// Extension methods for String operations including validation
+/// for namespace, URL paths, and query strings.
+///
+/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+library string_extensions;
+
+/// Extension methods for String validation and manipulation
+extension StringValidation on String {
+  /// Checks if string is a valid namespace name (starts with /)
+  bool get isValidNamespace {
+    if (isEmpty) return false;
+    if (!startsWith('/')) return false;
+    // Check for invalid characters
+    final RegExp invalidChars = RegExp('[^a-zA-Z0-9/_-]');
+    return !invalidChars.hasMatch(this);
+  }
+
+  /// Checks if string is a valid URL path (starts with /)
+  bool get isValidUrlPath {
+    if (isEmpty) return false;
+    return startsWith('/');
+  }
+
+  /// Checks if string is a valid room name (non-empty, no special chars)
+  bool get isValidRoomName {
+    if (isEmpty) return false;
+    // Allow alphanumeric, underscore, hyphen, and dot
+    final RegExp validPattern = RegExp(r'^[a-zA-Z0-9_.-]+$');
+    return validPattern.hasMatch(this);
+  }
+
+  /// Checks if string is a valid event name (non-empty, not reserved)
+  bool get isValidEventName {
+    if (isEmpty) return false;
+
+    // Reserved Socket.IO event names
+    const List reserved = [
+      'connect',
+      'connect_error',
+      'disconnect',
+      'disconnecting',
+      'newListener',
+      'removeListener',
+      'error',
+    ];
+
+    return !reserved.contains(this);
+  }
+
+  /// Checks if string is a valid connection ID (non-empty)
+  bool get isValidConnectionId => isNotEmpty && length >= 3;
+
+  /// Checks if string is a valid packet ID (non-empty, alphanumeric)
+  bool get isValidPacketId {
+    if (isEmpty) return false;
+    final RegExp validPattern = RegExp(r'^[a-zA-Z0-9-_]+$');
+    return validPattern.hasMatch(this);
+  }
+}
+
+/// Extension methods for String parsing
+extension StringParsing on String {
+  /// Parses query string into Map
+  Map parseQueryString() {
+    if (isEmpty) return {};
+
+    final String query = startsWith('?') ? substring(1) : this;
+    if (query.isEmpty) return {};
+
+    final Map result = {};
+
+    for (final String pair in query.split('&')) {
+      final List parts = pair.split('=');
+      if (parts.isEmpty) continue;
+
+      final String key = Uri.decodeComponent(parts[0]);
+      final String value = parts.length > 1 ? Uri.decodeComponent(parts[1]) : '';
+
+      result[key] = value;
+    }
+
+    return result;
+  }
+
+  /// Parses namespace with optional query parameters
+  /// Returns (namespace, queryParams)
+  ({String namespace, Map queryParams}) parseNamespaceWithQuery() {
+    final Uri uri = Uri.parse(this);
+    return (
+      namespace: uri.path.isEmpty ? '/' : uri.path,
+      queryParams: uri.queryParameters,
+    );
+  }
+
+  /// Converts string to camelCase
+  String toCamelCase() {
+    if (isEmpty) return this;
+
+    final List words = split(RegExp(r'[_\s-]+'));
+    if (words.isEmpty) return this;
+
+    final StringBuffer buffer = StringBuffer(words[0].toLowerCase());
+    for (int i = 1; i < words.length; i++) {
+      if (words[i].isNotEmpty) {
+        buffer.write(words[i][0].toUpperCase());
+        if (words[i].length > 1) {
+          buffer.write(words[i].substring(1).toLowerCase());
+        }
+      }
+    }
+
+    return buffer.toString();
+  }
+
+  /// Converts string to snake_case
+  String toSnakeCase() {
+    if (isEmpty) return this;
+
+    final RegExp upperCase = RegExp('[A-Z]');
+    return replaceAllMapped(upperCase, (final Match match) {
+      final String char = match.group(0)!;
+      return match.start == 0 ? char.toLowerCase() : '_${char.toLowerCase()}';
+    });
+  }
+
+  /// Converts string to kebab-case
+  String toKebabCase() => toSnakeCase().replaceAll('_', '-');
+
+  /// Truncates string to max length with ellipsis
+  String truncate(final int maxLength, {final String ellipsis = '...'}) {
+    if (length <= maxLength) return this;
+    return '${substring(0, maxLength - ellipsis.length)}$ellipsis';
+  }
+
+  /// Capitalizes first letter
+  String capitalize() {
+    if (isEmpty) return this;
+    return '${this[0].toUpperCase()}${substring(1)}';
+  }
+
+  /// Removes all whitespace
+  String removeWhitespace() => replaceAll(RegExp(r'\s+'), '');
+
+  /// Checks if string is null or empty
+  bool get isNullOrEmpty => isEmpty;
+
+  /// Checks if string is null, empty, or only whitespace
+  bool get isNullOrBlank => trim().isEmpty;
+}
+
+/// Extension methods for nullable String
+extension NullableStringExtensions on String? {
+  /// Returns true if string is null or empty
+  bool get isNullOrEmpty => this == null || this!.isEmpty;
+
+  /// Returns true if string is null, empty, or only whitespace
+  bool get isNullOrBlank => this == null || this!.trim().isEmpty;
+
+  /// Returns the string or a default value if null or empty
+  String orDefault(final String defaultValue) => isNullOrEmpty ? defaultValue : this!;
+
+  /// Returns the string or a default value if null or blank
+  String orDefaultIfBlank(final String defaultValue) => isNullOrBlank ? defaultValue : this!;
+}
+
+/// Extension methods for String matching and comparison
+extension StringMatching on String {
+  /// Checks if string matches a pattern
+  bool matches(final RegExp pattern) => pattern.hasMatch(this);
+
+  /// Checks if string contains another string (case insensitive)
+  bool containsIgnoreCase(final String other) => toLowerCase().contains(other.toLowerCase());
+
+  /// Checks if string equals another string (case insensitive)
+  bool equalsIgnoreCase(final String other) => toLowerCase() == other.toLowerCase();
+
+  /// Checks if string starts with prefix (case insensitive)
+  bool startsWithIgnoreCase(final String prefix) => toLowerCase().startsWith(prefix.toLowerCase());
+
+  /// Checks if string ends with suffix (case insensitive)
+  bool endsWithIgnoreCase(final String suffix) => toLowerCase().endsWith(suffix.toLowerCase());
+}
+
+/// Extension methods for String encoding/escaping
+extension StringEncoding on String {
+  /// URL encodes the string
+  String urlEncode() => Uri.encodeComponent(this);
+
+  /// URL decodes the string
+  String urlDecode() => Uri.decodeComponent(this);
+
+  /// Escapes special characters for use in JSON
+  String escapeJson() => replaceAll('\\', '\\\\')
+      .replaceAll('"', '\\"')
+      .replaceAll('\n', '\\n')
+      .replaceAll('\r', '\\r')
+      .replaceAll('\t', '\\t');
+}
diff --git a/lib/src/models/adapter_broadcast_models.dart b/lib/src/models/adapter_broadcast_models.dart
new file mode 100644
index 0000000..105e535
--- /dev/null
+++ b/lib/src/models/adapter_broadcast_models.dart
@@ -0,0 +1,224 @@
+/// adapter_broadcast_models.dart
+///
+/// Models for adapter broadcast operations
+///
+/// Copyright (C) 2024 Potix Corporation. All Rights Reserved.
+library adapter_broadcast_models;
+
+import '../models/socket_flags_models.dart';
+import '../value_objects/connection_id_vo.dart';
+import '../value_objects/room_name_vo.dart';
+
+/// Options for broadcasting packets to multiple clients.
+///
+/// Provides type-safe configuration for broadcast operations including
+/// room targeting, exclusions, and transmission flags.
+class BroadcastOptions {
+  /// List of rooms to broadcast to. Empty list means broadcast to all.
+  final List rooms;
+
+  /// List of socket IDs to exclude from broadcast.
+  final List except;
+
+  /// Flags controlling transmission behavior (volatile, compress, etc.).
+  final SocketFlags flags;
+
+  /// Creates broadcast options.
+  ///
+  /// Example:
+  /// ```dart
+  /// final options = BroadcastOptions(
+  ///   rooms: [RoomName('room1'), RoomName('room2')],
+  ///   except: [ConnectionId('socket123')],
+  ///   flags: SocketFlags(volatile: true),
+  /// );
+  /// ```
+  const BroadcastOptions({
+    this.rooms = const [],
+    this.except = const [],
+    this.flags = const SocketFlags(),
+  });
+
+  /// Creates broadcast options from legacy Map format.
+  ///
+  /// Provides backward compatibility with existing code.
+  factory BroadcastOptions.fromMap(final Map map) {
+    final List? roomsList = map['rooms'] as List?;
+    final List? exceptList = map['except'] as List?;
+    final Map? flagsMap = map['flags'] as Map?;
+
+    // Convert Map to Map for SocketFlags
+    Map? flagsBoolMap;
+    if (flagsMap != null) {
+      flagsBoolMap = {};
+      for (final MapEntry entry in flagsMap.entries) {
+        flagsBoolMap[entry.key] = entry.value as bool? ?? false;
+      }
+    }
+
+    return BroadcastOptions(
+      rooms: roomsList?.map((final dynamic r) => RoomName(r.toString())).toList() ?? const [],
+      except: exceptList?.map((final dynamic e) => ConnectionId(e.toString())).toList() ?? const [],
+      flags: flagsBoolMap != null ? SocketFlags.fromMap(flagsBoolMap) : const SocketFlags(),
+    );
+  }
+
+  /// Converts to legacy Map format for backward compatibility.
+  Map toMap() => {
+        'rooms': rooms.map((final RoomName r) => r.value).toList(),
+        'except': except.map((final ConnectionId e) => e.value).toList(),
+        'flags': flags.toMap(),
+      };
+
+  /// Creates a copy with modified fields.
+  BroadcastOptions copyWith({
+    final List? rooms,
+    final List? except,
+    final SocketFlags? flags,
+  }) =>
+      BroadcastOptions(
+        rooms: rooms ?? this.rooms,
+        except: except ?? this.except,
+        flags: flags ?? this.flags,
+      );
+
+  /// Adds a room to the broadcast target list.
+  BroadcastOptions addRoom(final RoomName room) => copyWith(rooms: [...rooms, room]);
+
+  /// Adds multiple rooms to the broadcast target list.
+  BroadcastOptions addRooms(final List newRooms) => copyWith(rooms: [...rooms, ...newRooms]);
+
+  /// Excludes a socket ID from the broadcast.
+  BroadcastOptions excludeSocket(final ConnectionId socketId) => copyWith(except: [...except, socketId]);
+
+  /// Excludes multiple socket IDs from the broadcast.
+  BroadcastOptions excludeSockets(final List socketIds) =>
+      copyWith(except: [...except, ...socketIds]);
+
+  /// Sets transmission flags.
+  BroadcastOptions withFlags(final SocketFlags newFlags) => copyWith(flags: newFlags);
+
+  /// Checks if broadcasting to specific rooms.
+  bool get hasRoomFilter => rooms.isNotEmpty;
+
+  /// Checks if any sockets are excluded.
+  bool get hasExclusions => except.isNotEmpty;
+
+  /// Checks if socket ID is excluded.
+  bool isExcluded(final ConnectionId socketId) => except.contains(socketId);
+
+  /// Checks if broadcasting to all rooms.
+  bool get broadcastToAll => rooms.isEmpty;
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is BroadcastOptions &&
+          runtimeType == other.runtimeType &&
+          _listEquals(rooms, other.rooms) &&
+          _listEquals(except, other.except) &&
+          flags == other.flags;
+
+  static bool _listEquals(final List a, final List b) {
+    if (a.length != b.length) return false;
+    for (int i = 0; i < a.length; i++) {
+      if (a[i] != b[i]) return false;
+    }
+    return true;
+  }
+
+  @override
+  int get hashCode => Object.hash(Object.hashAll(rooms), Object.hashAll(except), flags);
+
+  @override
+  String toString() => 'BroadcastOptions(rooms: $rooms, except: $except, flags: $flags)';
+}
+
+/// Filter criteria for selecting rooms.
+///
+/// Provides type-safe room filtering operations.
+class RoomFilter {
+  /// Specific rooms to include in the filter.
+  final List includeRooms;
+
+  /// Specific rooms to exclude from the filter.
+  final List excludeRooms;
+
+  /// Pattern to match room names (if any).
+  final String? namePattern;
+
+  /// Creates a room filter.
+  ///
+  /// Example:
+  /// ```dart
+  /// final filter = RoomFilter(
+  ///   includeRooms: [RoomName('game1'), RoomName('game2')],
+  ///   excludeRooms: [RoomName('private')],
+  /// );
+  /// ```
+  const RoomFilter({
+    this.includeRooms = const [],
+    this.excludeRooms = const [],
+    this.namePattern,
+  });
+
+  /// Creates a filter that includes specific rooms.
+  factory RoomFilter.include(final List rooms) => RoomFilter(includeRooms: rooms);
+
+  /// Creates a filter that excludes specific rooms.
+  factory RoomFilter.exclude(final List rooms) => RoomFilter(excludeRooms: rooms);
+
+  /// Creates a filter based on a name pattern.
+  factory RoomFilter.pattern(final String pattern) => RoomFilter(namePattern: pattern);
+
+  /// Checks if a room matches this filter.
+  bool matches(final RoomName room) {
+    // Check exclusions first
+    if (excludeRooms.contains(room)) {
+      return false;
+    }
+
+    // If there are inclusions, room must be in the list
+    if (includeRooms.isNotEmpty) {
+      if (!includeRooms.contains(room)) {
+        return false;
+      }
+    }
+
+    // Check name pattern if specified
+    if (namePattern != null) {
+      final RegExp regex = RegExp(namePattern!);
+      if (!regex.hasMatch(room.value)) {
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  /// Filters a list of rooms according to this filter.
+  List filter(final List rooms) => rooms.where(matches).toList();
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is RoomFilter &&
+          runtimeType == other.runtimeType &&
+          _listEquals(includeRooms, other.includeRooms) &&
+          _listEquals(excludeRooms, other.excludeRooms) &&
+          namePattern == other.namePattern;
+
+  static bool _listEquals(final List a, final List b) {
+    if (a.length != b.length) return false;
+    for (int i = 0; i < a.length; i++) {
+      if (a[i] != b[i]) return false;
+    }
+    return true;
+  }
+
+  @override
+  int get hashCode => Object.hash(Object.hashAll(includeRooms), Object.hashAll(excludeRooms), namePattern);
+
+  @override
+  String toString() => 'RoomFilter(include: $includeRooms, exclude: $excludeRooms, pattern: $namePattern)';
+}
diff --git a/lib/src/models/adapter_room_models.dart b/lib/src/models/adapter_room_models.dart
new file mode 100644
index 0000000..d289b0b
--- /dev/null
+++ b/lib/src/models/adapter_room_models.dart
@@ -0,0 +1,108 @@
+/// adapter_room_models.dart
+///
+/// Models for adapter room management to replace dynamic maps
+///
+/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+library adapter_room_models;
+
+/// Represents a socket's membership in various rooms
+///
+/// This replaces the `Map` usage in adapter sids
+/// where each socket ID maps to a set of room names.
+class SocketRoomMembership {
+  /// Set of room names this socket is a member of
+  final Set _rooms;
+
+  /// Creates an empty membership
+  SocketRoomMembership() : _rooms = {};
+
+  /// Creates membership from existing rooms
+  SocketRoomMembership.fromRooms(final Iterable rooms) : _rooms = Set.from(rooms);
+
+  /// Adds a room to this membership
+  void addRoom(final String room) {
+    _rooms.add(room);
+  }
+
+  /// Removes a room from this membership
+  bool removeRoom(final String room) => _rooms.remove(room);
+
+  /// Checks if this socket is in a specific room
+  bool isInRoom(final String room) => _rooms.contains(room);
+
+  /// Gets all rooms this socket is in
+  Set get rooms => Set.unmodifiable(_rooms);
+
+  /// Gets the number of rooms
+  int get roomCount => _rooms.length;
+
+  /// Checks if membership is empty
+  bool get isEmpty => _rooms.isEmpty;
+
+  /// Checks if membership is not empty
+  bool get isNotEmpty => _rooms.isNotEmpty;
+
+  /// Clears all room memberships
+  void clear() {
+    _rooms.clear();
+  }
+
+  /// Creates a copy of this membership
+  SocketRoomMembership copy() => SocketRoomMembership.fromRooms(_rooms);
+
+  @override
+  String toString() => 'SocketRoomMembership(rooms: $_rooms)';
+
+  @override
+  bool operator ==(final Object other) {
+    if (identical(this, other)) return true;
+    if (other is! SocketRoomMembership) return false;
+    if (_rooms.length != other._rooms.length) return false;
+    return _rooms.every(other._rooms.contains);
+  }
+
+  @override
+  int get hashCode => Object.hashAll(_rooms);
+
+  /// Converts to a map for backward compatibility
+  ///
+  /// Returns a map where each room name maps to `true`
+  Map toCompatibilityMap() => Map.fromEntries(
+        _rooms.map((final String room) => MapEntry(room, true)),
+      );
+
+  /// Creates from a compatibility map
+  ///
+  /// Accepts the old format: `Map` where keys are room names
+  factory SocketRoomMembership.fromCompatibilityMap(final Map map) =>
+      SocketRoomMembership.fromRooms(map.keys);
+}
+
+/// Namespace data for adapter
+///
+/// Replaces dynamic values in adapter.nsps map
+class AdapterNamespaceData {
+  /// The namespace name
+  final String name;
+
+  /// Optional metadata
+  final Map metadata;
+
+  AdapterNamespaceData({
+    required this.name,
+    final Map? metadata,
+  }) : metadata = metadata ?? {};
+
+  @override
+  String toString() => 'AdapterNamespaceData(name: $name, metadata: $metadata)';
+
+  @override
+  bool operator ==(final Object other) {
+    if (identical(this, other)) return true;
+    if (other is! AdapterNamespaceData) return false;
+    return name == other.name;
+  }
+
+  @override
+  int get hashCode => name.hashCode;
+}
diff --git a/lib/src/models/callbacks_models.dart b/lib/src/models/callbacks_models.dart
new file mode 100644
index 0000000..c590fa5
--- /dev/null
+++ b/lib/src/models/callbacks_models.dart
@@ -0,0 +1,123 @@
+/// callbacks_models.dart
+///
+/// Type-safe callback definitions for Socket.IO operations
+///
+/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+library callbacks_models;
+
+import '../value_objects/connection_id_vo.dart';
+
+/// Callback for acknowledgment responses.
+///
+/// Takes a list of arguments sent back from the other side.
+typedef AckCallback = void Function(List args);
+
+/// Function that sends an acknowledgment with data.
+///
+/// This is the return type of Socket.ack() method.
+/// Takes dynamic data and sends it as an acknowledgment response.
+typedef AckFunction = void Function(dynamic data);
+
+/// Callback for error events.
+///
+/// Takes an error object (will be typed as SocketError when available).
+typedef ErrorCallback = void Function(Object error);
+
+/// Callback for connection events.
+///
+/// Takes a socket instance (will be typed when Socket class is available).
+typedef ConnectionCallback = void Function(Object socket);
+
+/// Callback for disconnect events.
+///
+/// Takes a disconnect reason string (will be typed as DisconnectReason when available).
+typedef DisconnectCallback = void Function(String reason);
+
+/// Middleware callback function.
+///
+/// Takes a socket and a next function to continue the middleware chain.
+typedef MiddlewareCallback = void Function(Object socket, MiddlewareNext next);
+
+/// Next function in middleware chain.
+///
+/// Takes an optional error to short-circuit the chain.
+typedef MiddlewareNext = void Function(Object? error);
+
+/// Callback for clients list queries.
+///
+/// Takes a list of connection IDs.
+typedef ClientsCallback = void Function(List clients);
+
+/// Callback for clients list queries (string-based, legacy).
+///
+/// Takes a list of connection ID strings.
+typedef ClientsStringCallback = void Function(List clients);
+
+/// Callback for packet operations that may receive a response value.
+///
+/// Takes an optional value parameter from packet handling.
+/// Used in engine packet processing.
+typedef PacketCallback = void Function(Object? value);
+
+/// Generic callback for packet send operations.
+///
+/// No parameters, called when packet is sent.
+typedef PacketSentCallback = void Function();
+
+/// Callback for cleanup operations.
+///
+/// No parameters, called during cleanup.
+typedef CleanupCallback = void Function();
+
+/// Callback with no parameters and no return value.
+typedef VoidCallback = void Function();
+
+/// Callback for operations that may fail with an error.
+///
+/// Takes an optional error parameter. If null, operation succeeded.
+typedef ErrorableCallback = void Function(Object? error);
+
+/// Callback for data events.
+///
+/// Takes data of any type (will be refined with EventData when integrated).
+typedef DataCallback = void Function(Object? data);
+
+/// Callback for decoded packet events.
+///
+/// Takes a decoded packet (will be typed as SocketIOPacket when integrated).
+typedef DecodedPacketCallback = void Function(Object packet);
+
+/// Callback for transport events.
+///
+/// Takes transport-specific data.
+typedef TransportCallback = void Function(Object? data);
+
+/// Callback for broadcast operations.
+///
+/// No parameters, called after broadcast completes.
+typedef BroadcastCallback = void Function();
+
+/// Callback for room join/leave operations.
+///
+/// Takes an optional error if operation failed.
+typedef RoomOperationCallback = void Function(Object? error);
+
+/// Callback for namespace operations.
+///
+/// Takes a namespace instance (will be typed when Namespace class is available).
+typedef NamespaceCallback = void Function(Object namespace);
+
+/// Callback for adapter operations.
+///
+/// Generic callback for adapter-specific operations.
+typedef AdapterCallback = void Function(Object? data);
+
+/// Callback for handshake completion.
+///
+/// Takes handshake data (will be typed as HandshakeData when available).
+typedef HandshakeCallback = void Function(Object handshake);
+
+/// Callback for event emission.
+///
+/// Takes optional error and response data.
+typedef EmitCallback = void Function(Object? error, List? response);
diff --git a/lib/src/models/client_configuration_models.dart b/lib/src/models/client_configuration_models.dart
new file mode 100644
index 0000000..8cae4f9
--- /dev/null
+++ b/lib/src/models/client_configuration_models.dart
@@ -0,0 +1,484 @@
+/// client_configuration_models.dart
+///
+/// Typed configuration models for Socket.IO client to replace Map and
+/// other dynamic types with proper model classes for better type safety and API design.
+library client_configuration_models;
+
+import 'package:meta/meta.dart';
+
+/// Configuration model for client query parameters.
+/// Replaces Map? query parameters throughout the codebase.
+@immutable
+class QueryParametersModel {
+  /// The query parameters as a map.
+  final Map parameters;
+
+  /// Creates a new query parameters model.
+  const QueryParametersModel(this.parameters);
+
+  /// Creates an empty query parameters model.
+  const QueryParametersModel.empty() : parameters = const {};
+
+  /// Creates query parameters from a legacy Map.
+  factory QueryParametersModel.fromMap(final Map? map) =>
+      QueryParametersModel(map ?? {});
+
+  /// Creates query parameters from a URI query string.
+  factory QueryParametersModel.fromQueryString(final String queryString) {
+    if (queryString.isEmpty) {
+      return const QueryParametersModel.empty();
+    }
+
+    final Map params = {};
+    final List pairs = queryString.split('&');
+
+    for (final String pair in pairs) {
+      if (pair.isEmpty) continue;
+
+      final List parts = pair.split('=');
+      if (parts.length == 2) {
+        params[Uri.decodeComponent(parts[0])] = Uri.decodeComponent(parts[1]);
+      } else if (parts.length == 1) {
+        params[Uri.decodeComponent(parts[0])] = '';
+      }
+    }
+
+    return QueryParametersModel(params);
+  }
+
+  /// Gets a parameter value by key.
+  String? operator [](final String key) => parameters[key];
+
+  /// Checks if a parameter exists.
+  bool containsKey(final String key) => parameters.containsKey(key);
+
+  /// Gets all parameter keys.
+  Iterable get keys => parameters.keys;
+
+  /// Gets all parameter values.
+  Iterable get values => parameters.values;
+
+  /// Gets all parameter entries.
+  Iterable> get entries => parameters.entries;
+
+  /// Checks if the parameters are empty.
+  bool get isEmpty => parameters.isEmpty;
+
+  /// Checks if the parameters are not empty.
+  bool get isNotEmpty => parameters.isNotEmpty;
+
+  /// Gets the number of parameters.
+  int get length => parameters.length;
+
+  /// Converts to a legacy Map for backward compatibility.
+  Map toMap() => Map.from(parameters);
+
+  /// Converts to a URI query string.
+  String toQueryString() {
+    if (parameters.isEmpty) return '';
+
+    final List pairs = [];
+    for (final MapEntry entry in parameters.entries) {
+      final String key = Uri.encodeComponent(entry.key);
+      final String value = Uri.encodeComponent(entry.value);
+      pairs.add('$key=$value');
+    }
+
+    return pairs.join('&');
+  }
+
+  /// Creates a copy with additional or updated parameters.
+  QueryParametersModel copyWith(final Map additionalParams) {
+    final Map newParams = Map.from(parameters)..addAll(additionalParams);
+    return QueryParametersModel(newParams);
+  }
+
+  /// Creates a copy without specified parameters.
+  QueryParametersModel copyWithout(final List keysToRemove) {
+    final Map newParams = Map.from(parameters);
+    keysToRemove.forEach(newParams.remove);
+    return QueryParametersModel(newParams);
+  }
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is QueryParametersModel && runtimeType == other.runtimeType && _mapsEqual(parameters, other.parameters);
+
+  @override
+  int get hashCode => parameters.hashCode;
+
+  @override
+  String toString() => 'QueryParametersModel(parameters: $parameters)';
+
+  /// Helper method to compare maps for equality.
+  bool _mapsEqual(final Map a, final Map b) {
+    if (a.length != b.length) return false;
+    for (final MapEntry entry in a.entries) {
+      if (b[entry.key] != entry.value) return false;
+    }
+    return true;
+  }
+}
+
+/// Configuration model for client connection buffer management.
+/// Replaces List connectBuffer with proper state management.
+@immutable
+class ConnectionBufferModel {
+  /// The list of pending namespace connections.
+  final List pendingConnections;
+
+  /// Whether the buffer is enabled.
+  final bool enabled;
+
+  /// Maximum buffer size to prevent memory issues.
+  final int maxSize;
+
+  /// Creates a new connection buffer model.
+  const ConnectionBufferModel({
+    this.pendingConnections = const [],
+    this.enabled = true,
+    this.maxSize = 100,
+  });
+
+  /// Creates an empty connection buffer.
+  const ConnectionBufferModel.empty() : this();
+
+  /// Creates a connection buffer with initial connections.
+  factory ConnectionBufferModel.withConnections(final List connections) =>
+      ConnectionBufferModel(pendingConnections: List.from(connections));
+
+  /// Adds a connection to the buffer.
+  ConnectionBufferModel add(final String namespace) {
+    if (!enabled || pendingConnections.length >= maxSize) {
+      return this;
+    }
+
+    if (pendingConnections.contains(namespace)) {
+      return this; // Already exists
+    }
+
+    final List newConnections = List.from(pendingConnections)..add(namespace);
+    return copyWith(pendingConnections: newConnections);
+  }
+
+  /// Removes a connection from the buffer.
+  ConnectionBufferModel remove(final String namespace) {
+    final List newConnections = List.from(pendingConnections)..remove(namespace);
+    return copyWith(pendingConnections: newConnections);
+  }
+
+  /// Clears all pending connections.
+  ConnectionBufferModel clear() => copyWith(pendingConnections: const []);
+
+  /// Checks if a namespace is in the buffer.
+  bool contains(final String namespace) => pendingConnections.contains(namespace);
+
+  /// Checks if the buffer is empty.
+  bool get isEmpty => pendingConnections.isEmpty;
+
+  /// Checks if the buffer is not empty.
+  bool get isNotEmpty => pendingConnections.isNotEmpty;
+
+  /// Gets the number of pending connections.
+  int get length => pendingConnections.length;
+
+  /// Checks if the buffer is full.
+  bool get isFull => pendingConnections.length >= maxSize;
+
+  /// Gets the pending connections as an iterable.
+  Iterable get connections => pendingConnections;
+
+  /// Converts to a legacy List for backward compatibility.
+  List toList() => List.from(pendingConnections);
+
+  /// Creates a copy with optional parameter overrides.
+  ConnectionBufferModel copyWith({
+    final List? pendingConnections,
+    final bool? enabled,
+    final int? maxSize,
+  }) =>
+      ConnectionBufferModel(
+        pendingConnections: pendingConnections ?? this.pendingConnections,
+        enabled: enabled ?? this.enabled,
+        maxSize: maxSize ?? this.maxSize,
+      );
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is ConnectionBufferModel &&
+          runtimeType == other.runtimeType &&
+          _listsEqual(pendingConnections, other.pendingConnections) &&
+          enabled == other.enabled &&
+          maxSize == other.maxSize;
+
+  @override
+  int get hashCode => pendingConnections.hashCode ^ enabled.hashCode ^ maxSize.hashCode;
+
+  @override
+  String toString() => 'ConnectionBufferModel('
+      'pendingConnections: $pendingConnections, '
+      'enabled: $enabled, '
+      'maxSize: $maxSize)';
+
+  /// Helper method to compare lists for equality.
+  bool _listsEqual(final List a, final List b) {
+    if (a.length != b.length) return false;
+    for (int i = 0; i < a.length; i++) {
+      if (a[i] != b[i]) return false;
+    }
+    return true;
+  }
+}
+
+/// Configuration model for namespace connection data.
+/// Organizes namespace connection information in a typed structure.
+@immutable
+class NamespaceConnectionModel {
+  /// The namespace name.
+  final String namespace;
+
+  /// Query parameters for this namespace connection.
+  final QueryParametersModel queryParams;
+
+  /// Whether this connection is active.
+  final bool isActive;
+
+  /// Timestamp when the connection was established.
+  final DateTime? connectedAt;
+
+  /// Additional metadata for the connection.
+  final Map metadata;
+
+  /// Creates a new namespace connection model.
+  const NamespaceConnectionModel({
+    required this.namespace,
+    this.queryParams = const QueryParametersModel.empty(),
+    this.isActive = false,
+    this.connectedAt,
+    this.metadata = const {},
+  });
+
+  /// Creates a namespace connection from legacy data.
+  factory NamespaceConnectionModel.fromLegacyData({
+    required final String namespace,
+    final Map? query,
+    final bool isActive = false,
+  }) =>
+      NamespaceConnectionModel(
+        namespace: namespace,
+        queryParams: QueryParametersModel.fromMap(query),
+        isActive: isActive,
+        connectedAt: isActive ? DateTime.now() : null,
+      );
+
+  /// Creates an active connection.
+  NamespaceConnectionModel connect() => copyWith(
+        isActive: true,
+        connectedAt: DateTime.now(),
+      );
+
+  /// Creates a disconnected connection.
+  NamespaceConnectionModel disconnect() => copyWith(
+        isActive: false,
+        connectedAt: null,
+      );
+
+  /// Updates the query parameters.
+  NamespaceConnectionModel updateQuery(final QueryParametersModel newQuery) => copyWith(queryParams: newQuery);
+
+  /// Adds metadata to the connection.
+  NamespaceConnectionModel addMetadata(final String key, final Object? value) {
+    final Map newMetadata = Map.from(metadata);
+    newMetadata[key] = value;
+    return copyWith(metadata: newMetadata);
+  }
+
+  /// Removes metadata from the connection.
+  NamespaceConnectionModel removeMetadata(final String key) {
+    final Map newMetadata = Map.from(metadata)..remove(key);
+    return copyWith(metadata: newMetadata);
+  }
+
+  /// Gets metadata value by key.
+  T? getMetadata(final String key) => metadata[key] as T?;
+
+  /// Checks if the connection has specific metadata.
+  bool hasMetadata(final String key) => metadata.containsKey(key);
+
+  /// Gets the connection duration if active.
+  Duration? get connectionDuration {
+    if (!isActive || connectedAt == null) return null;
+    return DateTime.now().difference(connectedAt!);
+  }
+
+  /// Converts to a legacy map format for backward compatibility.
+  Map toLegacyMap() => {
+        'namespace': namespace,
+        'query': queryParams.toMap(),
+        'isActive': isActive,
+        'connectedAt': connectedAt?.toIso8601String(),
+        'metadata': metadata,
+      };
+
+  /// Creates a copy with optional parameter overrides.
+  NamespaceConnectionModel copyWith({
+    final String? namespace,
+    final QueryParametersModel? queryParams,
+    final bool? isActive,
+    final DateTime? connectedAt,
+    final Map? metadata,
+  }) =>
+      NamespaceConnectionModel(
+        namespace: namespace ?? this.namespace,
+        queryParams: queryParams ?? this.queryParams,
+        isActive: isActive ?? this.isActive,
+        connectedAt: connectedAt ?? this.connectedAt,
+        metadata: metadata ?? this.metadata,
+      );
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is NamespaceConnectionModel &&
+          runtimeType == other.runtimeType &&
+          namespace == other.namespace &&
+          queryParams == other.queryParams &&
+          isActive == other.isActive &&
+          connectedAt == other.connectedAt &&
+          _mapsEqual(metadata, other.metadata);
+
+  @override
+  int get hashCode =>
+      namespace.hashCode ^ queryParams.hashCode ^ isActive.hashCode ^ connectedAt.hashCode ^ metadata.hashCode;
+
+  @override
+  String toString() => 'NamespaceConnectionModel('
+      'namespace: $namespace, '
+      'queryParams: $queryParams, '
+      'isActive: $isActive, '
+      'connectedAt: $connectedAt, '
+      'metadata: $metadata)';
+
+  /// Helper method to compare maps for equality.
+  bool _mapsEqual(final Map a, final Map b) {
+    if (a.length != b.length) return false;
+    for (final MapEntry entry in a.entries) {
+      if (b[entry.key] != entry.value) return false;
+    }
+    return true;
+  }
+}
+
+/// Configuration model for client connection options.
+/// Centralizes client configuration in a typed structure.
+@immutable
+class ClientConfigurationModel {
+  /// Default query parameters for all connections.
+  final QueryParametersModel defaultQuery;
+
+  /// Connection timeout in milliseconds.
+  final int connectionTimeout;
+
+  /// Whether to automatically reconnect.
+  final bool autoReconnect;
+
+  /// Reconnection delay in milliseconds.
+  final int reconnectionDelay;
+
+  /// Maximum number of reconnection attempts.
+  final int maxReconnectionAttempts;
+
+  /// Whether to buffer events when disconnected.
+  final bool bufferEvents;
+
+  /// Maximum size of the event buffer.
+  final int maxBufferSize;
+
+  /// Creates a new client configuration model.
+  const ClientConfigurationModel({
+    this.defaultQuery = const QueryParametersModel.empty(),
+    this.connectionTimeout = 20000,
+    this.autoReconnect = true,
+    this.reconnectionDelay = 1000,
+    this.maxReconnectionAttempts = 5,
+    this.bufferEvents = true,
+    this.maxBufferSize = 100,
+  });
+
+  /// Creates a client configuration from a legacy options map.
+  factory ClientConfigurationModel.fromMap(final Map map) => ClientConfigurationModel(
+        defaultQuery: QueryParametersModel.fromMap(map['query'] as Map?),
+        connectionTimeout: map['connectionTimeout'] as int? ?? 20000,
+        autoReconnect: map['autoReconnect'] as bool? ?? true,
+        reconnectionDelay: map['reconnectionDelay'] as int? ?? 1000,
+        maxReconnectionAttempts: map['maxReconnectionAttempts'] as int? ?? 5,
+        bufferEvents: map['bufferEvents'] as bool? ?? true,
+        maxBufferSize: map['maxBufferSize'] as int? ?? 100,
+      );
+
+  /// Converts to a map for backward compatibility.
+  Map toMap() => {
+        'query': defaultQuery.toMap(),
+        'connectionTimeout': connectionTimeout,
+        'autoReconnect': autoReconnect,
+        'reconnectionDelay': reconnectionDelay,
+        'maxReconnectionAttempts': maxReconnectionAttempts,
+        'bufferEvents': bufferEvents,
+        'maxBufferSize': maxBufferSize,
+      };
+
+  /// Creates a copy with optional parameter overrides.
+  ClientConfigurationModel copyWith({
+    final QueryParametersModel? defaultQuery,
+    final int? connectionTimeout,
+    final bool? autoReconnect,
+    final int? reconnectionDelay,
+    final int? maxReconnectionAttempts,
+    final bool? bufferEvents,
+    final int? maxBufferSize,
+  }) =>
+      ClientConfigurationModel(
+        defaultQuery: defaultQuery ?? this.defaultQuery,
+        connectionTimeout: connectionTimeout ?? this.connectionTimeout,
+        autoReconnect: autoReconnect ?? this.autoReconnect,
+        reconnectionDelay: reconnectionDelay ?? this.reconnectionDelay,
+        maxReconnectionAttempts: maxReconnectionAttempts ?? this.maxReconnectionAttempts,
+        bufferEvents: bufferEvents ?? this.bufferEvents,
+        maxBufferSize: maxBufferSize ?? this.maxBufferSize,
+      );
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is ClientConfigurationModel &&
+          runtimeType == other.runtimeType &&
+          defaultQuery == other.defaultQuery &&
+          connectionTimeout == other.connectionTimeout &&
+          autoReconnect == other.autoReconnect &&
+          reconnectionDelay == other.reconnectionDelay &&
+          maxReconnectionAttempts == other.maxReconnectionAttempts &&
+          bufferEvents == other.bufferEvents &&
+          maxBufferSize == other.maxBufferSize;
+
+  @override
+  int get hashCode =>
+      defaultQuery.hashCode ^
+      connectionTimeout.hashCode ^
+      autoReconnect.hashCode ^
+      reconnectionDelay.hashCode ^
+      maxReconnectionAttempts.hashCode ^
+      bufferEvents.hashCode ^
+      maxBufferSize.hashCode;
+
+  @override
+  String toString() => 'ClientConfigurationModel('
+      'defaultQuery: $defaultQuery, '
+      'connectionTimeout: $connectionTimeout, '
+      'autoReconnect: $autoReconnect, '
+      'reconnectionDelay: $reconnectionDelay, '
+      'maxReconnectionAttempts: $maxReconnectionAttempts, '
+      'bufferEvents: $bufferEvents, '
+      'maxBufferSize: $maxBufferSize)';
+}
diff --git a/lib/src/models/client_connection_models.dart b/lib/src/models/client_connection_models.dart
new file mode 100644
index 0000000..c81621e
--- /dev/null
+++ b/lib/src/models/client_connection_models.dart
@@ -0,0 +1,223 @@
+/// client_connection_models.dart
+///
+/// Models for client connection state and management
+///
+/// Copyright (C) 2024 Potix Corporation. All Rights Reserved.
+library client_connection_models;
+
+import '../value_objects/namespace_name_vo.dart';
+
+/// Represents the state of a client's connection to the server
+enum ClientConnectionState {
+  /// Client is establishing initial connection
+  connecting,
+
+  /// Client is fully connected and authenticated
+  connected,
+
+  /// Client is in the process of disconnecting
+  disconnecting,
+
+  /// Client is disconnected
+  disconnected,
+
+  /// Client connection encountered an error
+  error,
+}
+
+/// Extension methods for ClientConnectionState
+extension ClientConnectionStateExtensions on ClientConnectionState {
+  /// Returns true if the client can send/receive data
+  bool get isActive => this == ClientConnectionState.connected;
+
+  /// Returns true if the client is in a transitional state
+  bool get isTransitioning => this == ClientConnectionState.connecting || this == ClientConnectionState.disconnecting;
+
+  /// Returns true if the client is not connected
+  bool get isInactive => this == ClientConnectionState.disconnected || this == ClientConnectionState.error;
+
+  /// Returns a human-readable description of the state
+  String get description => switch (this) {
+        ClientConnectionState.connecting => 'Establishing connection',
+        ClientConnectionState.connected => 'Connected and active',
+        ClientConnectionState.disconnecting => 'Disconnecting',
+        ClientConnectionState.disconnected => 'Disconnected',
+        ClientConnectionState.error => 'Connection error',
+      };
+}
+
+/// Tracks the namespace connection status for a client
+class NamespaceConnectionInfo {
+  /// The namespace name
+  final NamespaceName namespace;
+
+  /// When the connection was established
+  final DateTime connectedAt;
+
+  /// Whether this is the default namespace
+  final bool isDefault;
+
+  /// Number of sockets connected to this namespace
+  final int socketCount;
+
+  const NamespaceConnectionInfo({
+    required this.namespace,
+    required this.connectedAt,
+    this.isDefault = false,
+    this.socketCount = 1,
+  });
+
+  /// Creates info for the default namespace
+  factory NamespaceConnectionInfo.defaultNamespace({
+    required final DateTime connectedAt,
+    final int socketCount = 1,
+  }) =>
+      NamespaceConnectionInfo(
+        namespace: NamespaceName.defaultNamespace,
+        connectedAt: connectedAt,
+        isDefault: true,
+        socketCount: socketCount,
+      );
+
+  /// Duration since connection was established
+  Duration get connectedDuration => DateTime.now().difference(connectedAt);
+
+  /// Creates a copy with updated values
+  NamespaceConnectionInfo copyWith({
+    final NamespaceName? namespace,
+    final DateTime? connectedAt,
+    final bool? isDefault,
+    final int? socketCount,
+  }) =>
+      NamespaceConnectionInfo(
+        namespace: namespace ?? this.namespace,
+        connectedAt: connectedAt ?? this.connectedAt,
+        isDefault: isDefault ?? this.isDefault,
+        socketCount: socketCount ?? this.socketCount,
+      );
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is NamespaceConnectionInfo &&
+          runtimeType == other.runtimeType &&
+          namespace == other.namespace &&
+          connectedAt == other.connectedAt &&
+          isDefault == other.isDefault &&
+          socketCount == other.socketCount;
+
+  @override
+  int get hashCode => namespace.hashCode ^ connectedAt.hashCode ^ isDefault.hashCode ^ socketCount.hashCode;
+
+  @override
+  String toString() =>
+      'NamespaceConnectionInfo(namespace: $namespace, connectedAt: $connectedAt, isDefault: $isDefault, socketCount: $socketCount, duration: ${connectedDuration.inSeconds}s)';
+}
+
+/// Manages the overall connection state for a client
+class ClientConnectionState2 {
+  /// Overall connection state
+  final ClientConnectionState state;
+
+  /// Map of connected namespaces
+  final Map namespaces;
+
+  /// When the client first connected
+  final DateTime? initialConnectionTime;
+
+  /// Last error that occurred, if any
+  final String? lastError;
+
+  const ClientConnectionState2({
+    required this.state,
+    this.namespaces = const {},
+    this.initialConnectionTime,
+    this.lastError,
+  });
+
+  /// Creates a new connecting state
+  factory ClientConnectionState2.connecting() => ClientConnectionState2(
+        state: ClientConnectionState.connecting,
+        initialConnectionTime: DateTime.now(),
+      );
+
+  /// Creates a new connected state
+  factory ClientConnectionState2.connected({
+    final Map? namespaces,
+  }) =>
+      ClientConnectionState2(
+        state: ClientConnectionState.connected,
+        namespaces: namespaces ?? const {},
+        initialConnectionTime: DateTime.now(),
+      );
+
+  /// Creates a new disconnected state
+  factory ClientConnectionState2.disconnected({final String? reason}) => ClientConnectionState2(
+        state: ClientConnectionState.disconnected,
+        lastError: reason,
+      );
+
+  /// Creates a new error state
+  factory ClientConnectionState2.error(final String error) => ClientConnectionState2(
+        state: ClientConnectionState.error,
+        lastError: error,
+      );
+
+  /// Returns true if connected to any namespace
+  bool get hasNamespaces => namespaces.isNotEmpty;
+
+  /// Returns true if connected to default namespace
+  bool get hasDefaultNamespace => namespaces.containsKey('/');
+
+  /// Number of connected namespaces
+  int get namespaceCount => namespaces.length;
+
+  /// Total duration since initial connection
+  Duration? get totalConnectionDuration {
+    if (initialConnectionTime == null) return null;
+    return DateTime.now().difference(initialConnectionTime!);
+  }
+
+  /// Returns true if the client can send/receive data
+  bool get isActive => state.isActive && hasNamespaces;
+
+  /// Adds a namespace to the connection state
+  ClientConnectionState2 addNamespace(
+    final String namespaceName,
+    final NamespaceConnectionInfo info,
+  ) {
+    final Map updatedNamespaces = Map.from(namespaces)
+      ..[namespaceName] = info;
+
+    return copyWith(
+      namespaces: updatedNamespaces,
+      state: ClientConnectionState.connected,
+    );
+  }
+
+  /// Removes a namespace from the connection state
+  ClientConnectionState2 removeNamespace(final String namespaceName) {
+    final Map updatedNamespaces = Map.from(namespaces)
+      ..remove(namespaceName);
+
+    return copyWith(namespaces: updatedNamespaces);
+  }
+
+  /// Creates a copy with updated values
+  ClientConnectionState2 copyWith({
+    final ClientConnectionState? state,
+    final Map? namespaces,
+    final DateTime? initialConnectionTime,
+    final String? lastError,
+  }) =>
+      ClientConnectionState2(
+        state: state ?? this.state,
+        namespaces: namespaces ?? this.namespaces,
+        initialConnectionTime: initialConnectionTime ?? this.initialConnectionTime,
+        lastError: lastError ?? this.lastError,
+      );
+
+  @override
+  String toString() =>
+      'ClientConnectionState(state: ${state.name}, namespaces: ${namespaces.length}, duration: ${totalConnectionDuration?.inSeconds ?? 0}s, error: $lastError)';
+}
diff --git a/lib/src/models/connect_buffer_models.dart b/lib/src/models/connect_buffer_models.dart
new file mode 100644
index 0000000..0aa25ae
--- /dev/null
+++ b/lib/src/models/connect_buffer_models.dart
@@ -0,0 +1,94 @@
+/// connect_buffer_models.dart
+///
+/// Model for managing pending namespace connection requests.
+///
+/// The connect buffer stores namespace names that are waiting to be connected
+/// after the default namespace ('/') connection is established.
+///
+/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+library connect_buffer_models;
+
+import '../value_objects/namespace_name_vo.dart';
+
+/// Model for managing pending namespace connections.
+///
+/// In Socket.IO, non-default namespaces cannot be connected until after
+/// the default namespace ('/') is connected. The ConnectBuffer stores
+/// these pending connection requests and processes them once the default
+/// namespace is ready.
+class ConnectBuffer {
+  final List _buffer;
+
+  /// Creates an empty connect buffer.
+  ConnectBuffer() : _buffer = [];
+
+  /// Creates a connect buffer from an existing list of namespace names.
+  ConnectBuffer.fromList(final List namespaces) : _buffer = List.from(namespaces);
+
+  /// Adds a namespace to the buffer.
+  void add(final String namespace) {
+    if (!_buffer.contains(namespace)) {
+      _buffer.add(namespace);
+    }
+  }
+
+  /// Adds a namespace using NamespaceName value object.
+  void addTyped(final NamespaceName namespace) {
+    add(namespace.value);
+  }
+
+  /// Removes a namespace from the buffer.
+  bool remove(final String namespace) => _buffer.remove(namespace);
+
+  /// Removes a namespace using NamespaceName value object.
+  bool removeTyped(final NamespaceName namespace) => remove(namespace.value);
+
+  /// Clears all pending connections.
+  void clear() => _buffer.clear();
+
+  /// Returns true if the buffer is empty.
+  bool get isEmpty => _buffer.isEmpty;
+
+  /// Returns true if the buffer is not empty.
+  bool get isNotEmpty => _buffer.isNotEmpty;
+
+  /// Returns the number of pending connections.
+  int get length => _buffer.length;
+
+  /// Returns true if the buffer contains the namespace.
+  bool contains(final String namespace) => _buffer.contains(namespace);
+
+  /// Returns true if the buffer contains the namespace.
+  bool containsTyped(final NamespaceName namespace) => contains(namespace.value);
+
+  /// Returns an immutable copy of the buffer contents.
+  List toList() => List.unmodifiable(_buffer);
+
+  /// Processes all buffered connections using the provided callback.
+  ///
+  /// After processing, the buffer is cleared.
+  void processAll(final void Function(String namespace) processor) {
+    final List snapshot = List.from(_buffer);
+    _buffer.clear();
+    snapshot.forEach(processor);
+  }
+
+  @override
+  String toString() => 'ConnectBuffer(${_buffer.length} pending: ${_buffer.join(", ")})';
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is ConnectBuffer && runtimeType == other.runtimeType && _listEquals(_buffer, other._buffer);
+
+  static bool _listEquals(final List a, final List b) {
+    if (a.length != b.length) return false;
+    for (int i = 0; i < a.length; i++) {
+      if (a[i] != b[i]) return false;
+    }
+    return true;
+  }
+
+  @override
+  int get hashCode => Object.hashAll(_buffer);
+}
diff --git a/lib/src/models/engine_handshake_models.dart b/lib/src/models/engine_handshake_models.dart
new file mode 100644
index 0000000..ac759db
--- /dev/null
+++ b/lib/src/models/engine_handshake_models.dart
@@ -0,0 +1,144 @@
+/// engine_handshake_models.dart
+///
+/// Models for Engine.IO handshake data to replace dynamic usage
+///
+/// Copyright (C) 2024 Potix Corporation. All Rights Reserved.
+library engine_handshake_models;
+
+/// Engine.IO handshake response data sent to client upon successful connection
+///
+/// Contains session ID, available transport upgrades, and ping configuration
+class EngineHandshakeData {
+  /// Session ID assigned to the socket
+  final String sid;
+
+  /// List of available transport upgrade paths (e.g., ['websocket'])
+  final List upgrades;
+
+  /// Server ping interval in milliseconds
+  final int pingInterval;
+
+  /// Server ping timeout in milliseconds
+  final int pingTimeout;
+
+  /// Maximum HTTP buffer size (optional, polling transport only)
+  final int? maxHttpBufferSize;
+
+  const EngineHandshakeData({
+    required this.sid,
+    required this.upgrades,
+    required this.pingInterval,
+    required this.pingTimeout,
+    this.maxHttpBufferSize,
+  });
+
+  /// Creates handshake data from JSON map
+  factory EngineHandshakeData.fromJson(final Map json) => EngineHandshakeData(
+        sid: json['sid'] as String,
+        upgrades: (json['upgrades'] as List).map((final dynamic e) => e.toString()).toList(),
+        pingInterval: json['pingInterval'] as int,
+        pingTimeout: json['pingTimeout'] as int,
+        maxHttpBufferSize: json['maxHttpBufferSize'] as int?,
+      );
+
+  /// Converts handshake data to JSON map
+  Map toJson() {
+    final Map json = {
+      'sid': sid,
+      'upgrades': upgrades,
+      'pingInterval': pingInterval,
+      'pingTimeout': pingTimeout,
+    };
+    if (maxHttpBufferSize != null) {
+      json['maxHttpBufferSize'] = maxHttpBufferSize;
+    }
+    return json;
+  }
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is EngineHandshakeData &&
+          runtimeType == other.runtimeType &&
+          sid == other.sid &&
+          _listEquals(upgrades, other.upgrades) &&
+          pingInterval == other.pingInterval &&
+          pingTimeout == other.pingTimeout &&
+          maxHttpBufferSize == other.maxHttpBufferSize;
+
+  @override
+  int get hashCode =>
+      sid.hashCode ^
+      upgrades.hashCode ^
+      pingInterval.hashCode ^
+      pingTimeout.hashCode ^
+      (maxHttpBufferSize?.hashCode ?? 0);
+
+  @override
+  String toString() =>
+      'EngineHandshakeData(sid: $sid, upgrades: $upgrades, pingInterval: $pingInterval, pingTimeout: $pingTimeout, maxHttpBufferSize: $maxHttpBufferSize)';
+
+  /// Helper for list equality
+  bool _listEquals(final List a, final List b) {
+    if (a.length != b.length) return false;
+    for (int i = 0; i < a.length; i++) {
+      if (a[i] != b[i]) return false;
+    }
+    return true;
+  }
+}
+
+/// Engine.IO handshake request data from client
+///
+/// Contains client configuration and preferences
+class EngineHandshakeRequest {
+  /// Transport type requested (e.g., 'websocket', 'polling')
+  final String transport;
+
+  /// Whether client supports binary data (false if b64=1 query parameter)
+  final bool supportsBinary;
+
+  /// Engine.IO protocol version (e.g., '4', '3')
+  final String? eid;
+
+  /// Optional session ID for reconnection
+  final String? sid;
+
+  const EngineHandshakeRequest({
+    required this.transport,
+    this.supportsBinary = true,
+    this.eid,
+    this.sid,
+  });
+
+  /// Creates handshake request from query parameters
+  factory EngineHandshakeRequest.fromQuery(
+    final Map queryParams,
+  ) =>
+      EngineHandshakeRequest(
+        transport: queryParams['transport'] as String? ?? 'polling',
+        supportsBinary: queryParams['b64'] != '1',
+        eid: queryParams['EIO'] as String?,
+        sid: queryParams['sid'] as String?,
+      );
+
+  /// Whether this is a reconnection request (has sid)
+  bool get isReconnection => sid != null;
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is EngineHandshakeRequest &&
+          runtimeType == other.runtimeType &&
+          transport == other.transport &&
+          supportsBinary == other.supportsBinary &&
+          eid == other.eid &&
+          sid == other.sid;
+
+  @override
+  int get hashCode => transport.hashCode ^ supportsBinary.hashCode ^ (eid?.hashCode ?? 0) ^ (sid?.hashCode ?? 0);
+
+  @override
+  String toString() =>
+      'EngineHandshakeRequest(transport: $transport, supportsBinary: $supportsBinary, eid: $eid, sid: $sid, isReconnection: $isReconnection)';
+}
diff --git a/lib/src/models/error_models.dart b/lib/src/models/error_models.dart
new file mode 100644
index 0000000..f26c253
--- /dev/null
+++ b/lib/src/models/error_models.dart
@@ -0,0 +1,857 @@
+/// error_models.dart
+///
+/// Enhanced error models for Socket.IO to replace callback-style error handling
+/// with proper typed exceptions and error states for better error management.
+library error_models;
+
+import 'package:meta/meta.dart';
+
+/// Base sealed class for all Socket.IO errors.
+///
+/// Using sealed class ensures exhaustive pattern matching and makes the set
+/// of error types closed - all subtypes are defined in this file.
+/// Provides a common interface for error handling throughout the system.
+@immutable
+sealed class SocketIOError implements Exception {
+  /// The error type identifier.
+  String get type;
+
+  /// Human-readable error message.
+  String get message;
+
+  /// Optional detailed description.
+  String? get description;
+
+  /// Error code for programmatic handling.
+  int? get code;
+
+  /// Additional error context data.
+  Map get context;
+
+  /// Timestamp when the error occurred.
+  DateTime get timestamp;
+
+  /// Converts the error to a map for serialization.
+  Map toMap();
+
+  @override
+  String toString() => '$type: $message${description != null ? ' - $description' : ''}';
+}
+
+/// Enhanced transport error model extending the existing TransportError.
+@immutable
+class TransportErrorModel extends SocketIOError {
+  @override
+  final String type;
+
+  @override
+  final String message;
+
+  @override
+  final String? description;
+
+  @override
+  final int? code;
+
+  @override
+  final Map context;
+
+  @override
+  final DateTime timestamp;
+
+  /// The transport name that encountered the error.
+  final String? transport;
+
+  /// Whether this error is recoverable.
+  final bool isRecoverable;
+
+  /// Number of retry attempts made.
+  final int retryCount;
+
+  /// Creates a new transport error model.
+  TransportErrorModel({
+    this.type = 'TransportError',
+    required this.message,
+    this.description,
+    this.code,
+    this.context = const {},
+    final DateTime? timestamp,
+    this.transport,
+    this.isRecoverable = true,
+    this.retryCount = 0,
+  }) : timestamp = timestamp ?? DateTime.fromMillisecondsSinceEpoch(0);
+
+  /// Creates a transport error from the legacy TransportError.
+  factory TransportErrorModel.fromLegacy({
+    required final String message,
+    final String? description,
+    final String type = 'TransportError',
+    final String? transport,
+  }) =>
+      TransportErrorModel(
+        type: type,
+        message: message,
+        description: description,
+        transport: transport,
+        timestamp: DateTime.now(),
+      );
+
+  /// Creates a connection timeout error.
+  factory TransportErrorModel.connectionTimeout({
+    final String? transport,
+    final int timeout = 20000,
+  }) =>
+      TransportErrorModel(
+        type: 'ConnectionTimeout',
+        message: 'Connection timeout after ${timeout}ms',
+        description: 'The transport failed to establish a connection within the specified timeout period',
+        code: 1001,
+        transport: transport,
+        isRecoverable: true,
+        timestamp: DateTime.now(),
+        context: {'timeout': timeout},
+      );
+
+  /// Creates a connection refused error.
+  factory TransportErrorModel.connectionRefused({
+    final String? transport,
+    final String? host,
+    final int? port,
+  }) =>
+      TransportErrorModel(
+        type: 'ConnectionRefused',
+        message: 'Connection refused${host != null ? ' to $host${port != null ? ':$port' : ''}' : ''}',
+        description: 'The server actively refused the connection attempt',
+        code: 1002,
+        transport: transport,
+        isRecoverable: false,
+        timestamp: DateTime.now(),
+        context: {
+          if (host != null) 'host': host,
+          if (port != null) 'port': port,
+        },
+      );
+
+  /// Creates a protocol error.
+  factory TransportErrorModel.protocolError({
+    final String? transport,
+    required final String reason,
+  }) =>
+      TransportErrorModel(
+        type: 'ProtocolError',
+        message: 'Protocol error: $reason',
+        description: 'The transport encountered a protocol-level error',
+        code: 1003,
+        transport: transport,
+        isRecoverable: false,
+        timestamp: DateTime.now(),
+        context: {'reason': reason},
+      );
+
+  /// Creates a transport unavailable error.
+  factory TransportErrorModel.transportUnavailable({
+    final String? transport,
+  }) =>
+      TransportErrorModel(
+        type: 'TransportUnavailable',
+        message: 'Transport $transport is not available',
+        description: 'The requested transport is not supported or available in this environment',
+        code: 1004,
+        transport: transport,
+        isRecoverable: false,
+        timestamp: DateTime.now(),
+      );
+
+  /// Creates a retry error after max attempts.
+  TransportErrorModel withRetry() => copyWith(retryCount: retryCount + 1);
+
+  /// Checks if max retries have been reached.
+  bool hasExceededMaxRetries(final int maxRetries) => retryCount >= maxRetries;
+
+  @override
+  Map toMap() => {
+        'type': type,
+        'message': message,
+        'description': description,
+        'code': code,
+        'context': context,
+        'timestamp': timestamp.toIso8601String(),
+        'transport': transport,
+        'isRecoverable': isRecoverable,
+        'retryCount': retryCount,
+      };
+
+  /// Creates a copy with optional parameter overrides.
+  TransportErrorModel copyWith({
+    final String? type,
+    final String? message,
+    final String? description,
+    final int? code,
+    final Map? context,
+    final DateTime? timestamp,
+    final String? transport,
+    final bool? isRecoverable,
+    final int? retryCount,
+  }) =>
+      TransportErrorModel(
+        type: type ?? this.type,
+        message: message ?? this.message,
+        description: description ?? this.description,
+        code: code ?? this.code,
+        context: context ?? this.context,
+        timestamp: timestamp ?? this.timestamp,
+        transport: transport ?? this.transport,
+        isRecoverable: isRecoverable ?? this.isRecoverable,
+        retryCount: retryCount ?? this.retryCount,
+      );
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is TransportErrorModel &&
+          runtimeType == other.runtimeType &&
+          type == other.type &&
+          message == other.message &&
+          description == other.description &&
+          code == other.code &&
+          _mapsEqual(context, other.context) &&
+          timestamp == other.timestamp &&
+          transport == other.transport &&
+          isRecoverable == other.isRecoverable &&
+          retryCount == other.retryCount;
+
+  @override
+  int get hashCode =>
+      type.hashCode ^
+      message.hashCode ^
+      description.hashCode ^
+      code.hashCode ^
+      context.hashCode ^
+      timestamp.hashCode ^
+      transport.hashCode ^
+      isRecoverable.hashCode ^
+      retryCount.hashCode;
+
+  /// Helper method to compare maps for equality.
+  bool _mapsEqual(final Map a, final Map b) {
+    if (a.length != b.length) return false;
+    for (final MapEntry entry in a.entries) {
+      if (b[entry.key] != entry.value) return false;
+    }
+    return true;
+  }
+}
+
+/// Error model for connection-related issues.
+@immutable
+class ConnectionErrorModel extends SocketIOError {
+  @override
+  final String type;
+
+  @override
+  final String message;
+
+  @override
+  final String? description;
+
+  @override
+  final int? code;
+
+  @override
+  final Map context;
+
+  @override
+  final DateTime timestamp;
+
+  /// The connection state when the error occurred.
+  final String connectionState;
+
+  /// The namespace where the error occurred.
+  final String? namespace;
+
+  /// Creates a new connection error model.
+
+  ConnectionErrorModel({
+    this.type = 'ConnectionError',
+    required this.message,
+    this.description,
+    this.code,
+    this.context = const {},
+    final DateTime? timestamp,
+    required this.connectionState,
+    this.namespace,
+  }) : timestamp = timestamp ?? DateTime.fromMillisecondsSinceEpoch(0);
+
+  /// Creates an authentication error.
+  factory ConnectionErrorModel.authenticationFailed({
+    final String? namespace,
+    final String? reason,
+  }) =>
+      ConnectionErrorModel(
+        type: 'AuthenticationError',
+        message: 'Authentication failed${namespace != null ? ' for namespace $namespace' : ''}',
+        description: reason ?? 'Invalid credentials or authentication token',
+        code: 2001,
+        connectionState: 'authenticating',
+        namespace: namespace,
+        timestamp: DateTime.now(),
+        context: {
+          if (reason != null) 'reason': reason,
+        },
+      );
+
+  /// Creates an unauthorized access error.
+  factory ConnectionErrorModel.unauthorized({
+    final String? namespace,
+    final String? resource,
+  }) =>
+      ConnectionErrorModel(
+        type: 'UnauthorizedError',
+        message: 'Unauthorized access${namespace != null ? ' to namespace $namespace' : ''}',
+        description: 'Insufficient permissions to access the requested resource',
+        code: 2002,
+        connectionState: 'rejected',
+        namespace: namespace,
+        timestamp: DateTime.now(),
+        context: {
+          if (resource != null) 'resource': resource,
+        },
+      );
+
+  /// Creates a namespace not found error.
+  factory ConnectionErrorModel.namespaceNotFound({
+    required final String namespace,
+  }) =>
+      ConnectionErrorModel(
+        type: 'NamespaceError',
+        message: 'Namespace "$namespace" not found',
+        description: 'The requested namespace does not exist on the server',
+        code: 2003,
+        connectionState: 'rejected',
+        namespace: namespace,
+        timestamp: DateTime.now(),
+      );
+
+  /// Creates a connection limit exceeded error.
+  factory ConnectionErrorModel.connectionLimitExceeded({
+    final String? namespace,
+    final int? maxConnections,
+  }) =>
+      ConnectionErrorModel(
+        type: 'ConnectionLimitError',
+        message: 'Connection limit exceeded',
+        description: 'The server has reached its maximum number of concurrent connections',
+        code: 2004,
+        connectionState: 'rejected',
+        namespace: namespace,
+        timestamp: DateTime.now(),
+        context: {
+          if (maxConnections != null) 'maxConnections': maxConnections,
+        },
+      );
+
+  @override
+  Map toMap() => {
+        'type': type,
+        'message': message,
+        'description': description,
+        'code': code,
+        'context': context,
+        'timestamp': timestamp.toIso8601String(),
+        'connectionState': connectionState,
+        'namespace': namespace,
+      };
+
+  /// Creates a copy with optional parameter overrides.
+  ConnectionErrorModel copyWith({
+    final String? type,
+    final String? message,
+    final String? description,
+    final int? code,
+    final Map? context,
+    final DateTime? timestamp,
+    final String? connectionState,
+    final String? namespace,
+  }) =>
+      ConnectionErrorModel(
+        type: type ?? this.type,
+        message: message ?? this.message,
+        description: description ?? this.description,
+        code: code ?? this.code,
+        context: context ?? this.context,
+        timestamp: timestamp ?? this.timestamp,
+        connectionState: connectionState ?? this.connectionState,
+        namespace: namespace ?? this.namespace,
+      );
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is ConnectionErrorModel &&
+          runtimeType == other.runtimeType &&
+          type == other.type &&
+          message == other.message &&
+          description == other.description &&
+          code == other.code &&
+          _mapsEqual(context, other.context) &&
+          timestamp == other.timestamp &&
+          connectionState == other.connectionState &&
+          namespace == other.namespace;
+
+  @override
+  int get hashCode =>
+      type.hashCode ^
+      message.hashCode ^
+      description.hashCode ^
+      code.hashCode ^
+      context.hashCode ^
+      timestamp.hashCode ^
+      connectionState.hashCode ^
+      namespace.hashCode;
+
+  /// Helper method to compare maps for equality.
+  bool _mapsEqual(final Map a, final Map b) {
+    if (a.length != b.length) return false;
+    for (final MapEntry entry in a.entries) {
+      if (b[entry.key] != entry.value) return false;
+    }
+    return true;
+  }
+}
+
+/// Error model for namespace-related issues.
+@immutable
+class NamespaceErrorModel extends SocketIOError {
+  @override
+  final String type;
+
+  @override
+  final String message;
+
+  @override
+  final String? description;
+
+  @override
+  final int? code;
+
+  @override
+  final Map context;
+
+  @override
+  final DateTime timestamp;
+
+  /// The namespace where the error occurred.
+  final String namespace;
+
+  /// The operation that caused the error.
+  final String? operation;
+
+  /// Creates a new namespace error model.
+  NamespaceErrorModel({
+    this.type = 'NamespaceError',
+    required this.message,
+    this.description,
+    this.code,
+    this.context = const {},
+    final DateTime? timestamp,
+    required this.namespace,
+    this.operation,
+  }) : timestamp = timestamp ?? DateTime.fromMillisecondsSinceEpoch(0);
+
+  /// Creates an invalid namespace error.
+  factory NamespaceErrorModel.invalidNamespace({
+    required final String namespace,
+    final String? reason,
+  }) =>
+      NamespaceErrorModel(
+        type: 'InvalidNamespace',
+        message: 'Invalid namespace: $namespace',
+        description: reason ?? 'The namespace name is not valid',
+        code: 3001,
+        namespace: namespace,
+        timestamp: DateTime.now(),
+        context: {
+          if (reason != null) 'reason': reason,
+        },
+      );
+
+  /// Creates a namespace operation error.
+  factory NamespaceErrorModel.operationFailed({
+    required final String namespace,
+    required final String operation,
+    final String? reason,
+  }) =>
+      NamespaceErrorModel(
+        type: 'NamespaceOperationError',
+        message: 'Operation "$operation" failed on namespace "$namespace"',
+        description: reason ?? 'The namespace operation could not be completed',
+        code: 3002,
+        namespace: namespace,
+        operation: operation,
+        timestamp: DateTime.now(),
+        context: {
+          if (reason != null) 'reason': reason,
+        },
+      );
+
+  @override
+  Map toMap() => {
+        'type': type,
+        'message': message,
+        'description': description,
+        'code': code,
+        'context': context,
+        'timestamp': timestamp.toIso8601String(),
+        'namespace': namespace,
+        'operation': operation,
+      };
+
+  /// Creates a copy with optional parameter overrides.
+  NamespaceErrorModel copyWith({
+    final String? type,
+    final String? message,
+    final String? description,
+    final int? code,
+    final Map? context,
+    final DateTime? timestamp,
+    final String? namespace,
+    final String? operation,
+  }) =>
+      NamespaceErrorModel(
+        type: type ?? this.type,
+        message: message ?? this.message,
+        description: description ?? this.description,
+        code: code ?? this.code,
+        context: context ?? this.context,
+        timestamp: timestamp ?? this.timestamp,
+        namespace: namespace ?? this.namespace,
+        operation: operation ?? this.operation,
+      );
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is NamespaceErrorModel &&
+          runtimeType == other.runtimeType &&
+          type == other.type &&
+          message == other.message &&
+          description == other.description &&
+          code == other.code &&
+          _mapsEqual(context, other.context) &&
+          timestamp == other.timestamp &&
+          namespace == other.namespace &&
+          operation == other.operation;
+
+  @override
+  int get hashCode =>
+      type.hashCode ^
+      message.hashCode ^
+      description.hashCode ^
+      code.hashCode ^
+      context.hashCode ^
+      timestamp.hashCode ^
+      namespace.hashCode ^
+      operation.hashCode;
+
+  /// Helper method to compare maps for equality.
+  bool _mapsEqual(final Map a, final Map b) {
+    if (a.length != b.length) return false;
+    for (final MapEntry entry in a.entries) {
+      if (b[entry.key] != entry.value) return false;
+    }
+    return true;
+  }
+}
+
+/// Error model for validation issues.
+@immutable
+class ValidationErrorModel extends SocketIOError {
+  @override
+  final String type;
+
+  @override
+  final String message;
+
+  @override
+  final String? description;
+
+  @override
+  final int? code;
+
+  @override
+  final Map context;
+
+  @override
+  final DateTime timestamp;
+
+  /// The field or parameter that failed validation.
+  final String? field;
+
+  /// The expected value or format.
+  final String? expected;
+
+  /// The actual value that was provided.
+  final Object? actual;
+
+  /// List of validation rules that were violated.
+  final List violations;
+
+  /// Creates a new validation error model.
+  ValidationErrorModel({
+    this.type = 'ValidationError',
+    required this.message,
+    this.description,
+    this.code,
+    this.context = const {},
+    final DateTime? timestamp,
+    this.field,
+    this.expected,
+    this.actual,
+    this.violations = const [],
+  }) : timestamp = timestamp ?? DateTime.fromMillisecondsSinceEpoch(0);
+
+  /// Creates a required field error.
+  factory ValidationErrorModel.requiredField({
+    required final String field,
+  }) =>
+      ValidationErrorModel(
+        type: 'RequiredFieldError',
+        message: 'Required field "$field" is missing',
+        description: 'A required parameter was not provided',
+        code: 4001,
+        field: field,
+        expected: 'non-null value',
+        timestamp: DateTime.now(),
+        violations: ['required'],
+      );
+
+  /// Creates an invalid format error.
+  factory ValidationErrorModel.invalidFormat({
+    required final String field,
+    required final String expected,
+    final Object? actual,
+  }) =>
+      ValidationErrorModel(
+        type: 'InvalidFormatError',
+        message: 'Field "$field" has invalid format',
+        description: 'The provided value does not match the expected format',
+        code: 4002,
+        field: field,
+        expected: expected,
+        actual: actual,
+        timestamp: DateTime.now(),
+        violations: ['format'],
+      );
+
+  /// Creates an out of range error.
+  factory ValidationErrorModel.outOfRange({
+    required final String field,
+    required final Object actual,
+    final Object? min,
+    final Object? max,
+  }) {
+    final String range = '${min ?? '∞'} - ${max ?? '∞'}';
+    return ValidationErrorModel(
+      type: 'OutOfRangeError',
+      message: 'Field "$field" is out of range',
+      description: 'The provided value is outside the acceptable range',
+      code: 4003,
+      field: field,
+      expected: 'value in range $range',
+      actual: actual,
+      timestamp: DateTime.now(),
+      violations: ['range'],
+      context: {
+        if (min != null) 'min': min,
+        if (max != null) 'max': max,
+      },
+    );
+  }
+
+  @override
+  Map toMap() => {
+        'type': type,
+        'message': message,
+        'description': description,
+        'code': code,
+        'context': context,
+        'timestamp': timestamp.toIso8601String(),
+        'field': field,
+        'expected': expected,
+        'actual': actual,
+        'violations': violations,
+      };
+
+  /// Creates a copy with optional parameter overrides.
+  ValidationErrorModel copyWith({
+    final String? type,
+    final String? message,
+    final String? description,
+    final int? code,
+    final Map? context,
+    final DateTime? timestamp,
+    final String? field,
+    final String? expected,
+    final Object? actual,
+    final List? violations,
+  }) =>
+      ValidationErrorModel(
+        type: type ?? this.type,
+        message: message ?? this.message,
+        description: description ?? this.description,
+        code: code ?? this.code,
+        context: context ?? this.context,
+        timestamp: timestamp ?? this.timestamp,
+        field: field ?? this.field,
+        expected: expected ?? this.expected,
+        actual: actual ?? this.actual,
+        violations: violations ?? this.violations,
+      );
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is ValidationErrorModel &&
+          runtimeType == other.runtimeType &&
+          type == other.type &&
+          message == other.message &&
+          description == other.description &&
+          code == other.code &&
+          _mapsEqual(context, other.context) &&
+          timestamp == other.timestamp &&
+          field == other.field &&
+          expected == other.expected &&
+          actual == other.actual &&
+          _listsEqual(violations, other.violations);
+
+  @override
+  int get hashCode =>
+      type.hashCode ^
+      message.hashCode ^
+      description.hashCode ^
+      code.hashCode ^
+      context.hashCode ^
+      timestamp.hashCode ^
+      field.hashCode ^
+      expected.hashCode ^
+      actual.hashCode ^
+      violations.hashCode;
+
+  /// Helper method to compare maps for equality.
+  bool _mapsEqual(final Map a, final Map b) {
+    if (a.length != b.length) return false;
+    for (final MapEntry entry in a.entries) {
+      if (b[entry.key] != entry.value) return false;
+    }
+    return true;
+  }
+
+  /// Helper method to compare lists for equality.
+  bool _listsEqual(final List a, final List b) {
+    if (a.length != b.length) return false;
+    for (int i = 0; i < a.length; i++) {
+      if (a[i] != b[i]) return false;
+    }
+    return true;
+  }
+}
+
+/// Factory class for creating common error instances.
+class SocketIOErrorFactory {
+  /// Creates a generic Socket.IO error.
+  static SocketIOError generic({
+    required final String message,
+    final String? description,
+    final int? code,
+    final Map context = const {},
+  }) =>
+      _GenericSocketIOError(
+        message: message,
+        description: description,
+        code: code,
+        context: context,
+      );
+
+  /// Creates an error from an exception.
+  static SocketIOError fromException(
+    final Exception exception, {
+    final String type = 'UnknownError',
+    final Map context = const {},
+  }) =>
+      _GenericSocketIOError(
+        type: type,
+        message: exception.toString(),
+        description: 'An unexpected exception occurred',
+        code: 9999,
+        context: {
+          'originalException': exception.runtimeType.toString(),
+          ...context,
+        },
+      );
+}
+
+/// Internal generic error implementation.
+@immutable
+class _GenericSocketIOError extends SocketIOError {
+  @override
+  final String type;
+
+  @override
+  final String message;
+
+  @override
+  final String? description;
+
+  @override
+  final int? code;
+
+  @override
+  final Map context;
+
+  @override
+  final DateTime timestamp;
+
+  _GenericSocketIOError({
+    this.type = 'SocketIOError',
+    required this.message,
+    this.description,
+    this.code,
+    this.context = const {},
+    final DateTime? timestamp,
+  }) : timestamp = timestamp ?? DateTime.fromMillisecondsSinceEpoch(0);
+
+  @override
+  Map toMap() => {
+        'type': type,
+        'message': message,
+        'description': description,
+        'code': code,
+        'context': context,
+        'timestamp': timestamp.toIso8601String(),
+      };
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is _GenericSocketIOError &&
+          runtimeType == other.runtimeType &&
+          type == other.type &&
+          message == other.message &&
+          description == other.description &&
+          code == other.code &&
+          _mapsEqual(context, other.context) &&
+          timestamp == other.timestamp;
+
+  @override
+  int get hashCode =>
+      type.hashCode ^ message.hashCode ^ description.hashCode ^ code.hashCode ^ context.hashCode ^ timestamp.hashCode;
+
+  /// Helper method to compare maps for equality.
+  bool _mapsEqual(final Map a, final Map b) {
+    if (a.length != b.length) return false;
+    for (final MapEntry entry in a.entries) {
+      if (b[entry.key] != entry.value) return false;
+    }
+    return true;
+  }
+}
diff --git a/lib/src/models/event_data_models.dart b/lib/src/models/event_data_models.dart
new file mode 100644
index 0000000..bb0cade
--- /dev/null
+++ b/lib/src/models/event_data_models.dart
@@ -0,0 +1,166 @@
+/// event_data_models.dart
+///
+/// Class hierarchy for type-safe event data
+///
+/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+library event_data_models;
+
+/// Base sealed class for event data types.
+///
+/// Using sealed class ensures exhaustive pattern matching and makes the set
+/// of event data types closed - all subtypes are defined in this file.
+/// This provides type-safe event data handling instead of using dynamic.
+sealed class EventData {
+  const EventData();
+}
+
+/// Event data containing a string value.
+class StringEventData extends EventData {
+  final String value;
+
+  const StringEventData(this.value);
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) || other is StringEventData && runtimeType == other.runtimeType && value == other.value;
+
+  @override
+  int get hashCode => value.hashCode;
+
+  @override
+  String toString() => value;
+}
+
+/// Event data containing a numeric value.
+class NumericEventData extends EventData {
+  final num value;
+
+  const NumericEventData(this.value);
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) || other is NumericEventData && runtimeType == other.runtimeType && value == other.value;
+
+  @override
+  int get hashCode => value.hashCode;
+
+  @override
+  String toString() => value.toString();
+}
+
+/// Event data containing a boolean value.
+class BooleanEventData extends EventData {
+  final bool value;
+
+  const BooleanEventData(this.value);
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) || other is BooleanEventData && runtimeType == other.runtimeType && value == other.value;
+
+  @override
+  int get hashCode => value.hashCode;
+
+  @override
+  String toString() => value.toString();
+}
+
+/// Event data containing a map of values.
+class MapEventData extends EventData {
+  final Map value;
+
+  const MapEventData(this.value);
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is MapEventData && runtimeType == other.runtimeType && _mapEquals(value, other.value);
+
+  static bool _mapEquals(final Map a, final Map b) {
+    if (a.length != b.length) return false;
+    for (final MapEntry entry in a.entries) {
+      if (!b.containsKey(entry.key) || b[entry.key] != entry.value) return false;
+    }
+    return true;
+  }
+
+  @override
+  int get hashCode =>
+      Object.hashAll(value.entries.map((final MapEntry e) => Object.hash(e.key, e.value)));
+
+  @override
+  String toString() => value.toString();
+}
+
+/// Event data containing a list of values.
+class ListEventData extends EventData {
+  final List value;
+
+  const ListEventData(this.value);
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is ListEventData && runtimeType == other.runtimeType && _listEquals(value, other.value);
+
+  static bool _listEquals(final List a, final List b) {
+    if (a.length != b.length) return false;
+    for (int i = 0; i < a.length; i++) {
+      if (a[i] != b[i]) return false;
+    }
+    return true;
+  }
+
+  @override
+  int get hashCode => Object.hashAll(value);
+
+  @override
+  String toString() => value.toString();
+}
+
+/// Event data representing null/no data.
+class NullEventData extends EventData {
+  const NullEventData();
+
+  @override
+  bool operator ==(final Object other) => identical(this, other) || other is NullEventData;
+
+  @override
+  int get hashCode => 0;
+
+  @override
+  String toString() => 'null';
+}
+
+/// Event data containing a generic object (fallback).
+class ObjectEventData extends EventData {
+  final Object value;
+
+  const ObjectEventData(this.value);
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) || other is ObjectEventData && runtimeType == other.runtimeType && value == other.value;
+
+  @override
+  int get hashCode => value.hashCode;
+
+  @override
+  String toString() => value.toString();
+}
+
+/// Utility to convert dynamic values to EventData.
+extension EventDataConversion on Object? {
+  EventData toEventData() {
+    final Object? self = this;
+    if (self == null) return const NullEventData();
+    if (self is String) return StringEventData(self);
+    if (self is num) return NumericEventData(self);
+    if (self is bool) return BooleanEventData(self);
+    if (self is Map) return MapEventData(self);
+    if (self is Map) return MapEventData(Map.from(self));
+    if (self is List) return ListEventData(self);
+    if (self is List) return ListEventData(List.from(self));
+    return ObjectEventData(self);
+  }
+}
diff --git a/lib/src/models/handshake_data_models.dart b/lib/src/models/handshake_data_models.dart
new file mode 100644
index 0000000..5733880
--- /dev/null
+++ b/lib/src/models/handshake_data_models.dart
@@ -0,0 +1,230 @@
+/// handshake_data_models.dart
+///
+/// Type-safe models for Socket.IO handshake data
+///
+/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+library handshake_data_models;
+
+import 'dart:io';
+
+/// Model representing Socket.IO handshake data.
+///
+/// Contains connection information established during the handshake process.
+class HandshakeDataModel {
+  /// HTTP headers from the handshake request.
+  final HttpHeaders headers;
+
+  /// Timestamp when the handshake was created.
+  final DateTime time;
+
+  /// Remote address of the client.
+  final InternetAddress address;
+
+  /// Whether the connection is cross-domain.
+  final bool xdomain;
+
+  /// Whether the connection is secure (HTTPS/WSS).
+  final bool secure;
+
+  /// Timestamp in milliseconds when the handshake was issued.
+  final int issued;
+
+  /// URL path of the connection.
+  final String url;
+
+  /// Query parameters from the connection.
+  final Map query;
+
+  /// Additional authentication data (if any).
+  final Map? auth;
+
+  const HandshakeDataModel({
+    required this.headers,
+    required this.time,
+    required this.address,
+    required this.xdomain,
+    required this.secure,
+    required this.issued,
+    required this.url,
+    required this.query,
+    this.auth,
+  });
+
+  /// Creates a HandshakeDataModel from a legacy Map.
+  factory HandshakeDataModel.fromMap(final Map map) => HandshakeDataModel(
+        headers: map['headers'] as HttpHeaders,
+        time: map['time'] is String ? DateTime.parse(map['time'] as String) : map['time'] as DateTime,
+        address: map['address'] as InternetAddress,
+        xdomain: map['xdomain'] as bool? ?? false,
+        secure: map['secure'] as bool? ?? false,
+        issued: map['issued'] as int,
+        url: map['url'] as String,
+        query: Map.from((map['query'] as Map?) ?? {}),
+        auth: map['auth'] as Map?,
+      );
+
+  /// Converts to a Map for backward compatibility.
+  Map toMap() => {
+        'headers': headers,
+        'time': time.toString(),
+        'address': address,
+        'xdomain': xdomain,
+        'secure': secure,
+        'issued': issued,
+        'url': url,
+        'query': query,
+        if (auth != null) 'auth': auth,
+      };
+
+  /// Creates a copy with optional field updates.
+  HandshakeDataModel copyWith({
+    final HttpHeaders? headers,
+    final DateTime? time,
+    final InternetAddress? address,
+    final bool? xdomain,
+    final bool? secure,
+    final int? issued,
+    final String? url,
+    final Map? query,
+    final Map? auth,
+  }) =>
+      HandshakeDataModel(
+        headers: headers ?? this.headers,
+        time: time ?? this.time,
+        address: address ?? this.address,
+        xdomain: xdomain ?? this.xdomain,
+        secure: secure ?? this.secure,
+        issued: issued ?? this.issued,
+        url: url ?? this.url,
+        query: query ?? this.query,
+        auth: auth ?? this.auth,
+      );
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is HandshakeDataModel &&
+          runtimeType == other.runtimeType &&
+          time == other.time &&
+          address == other.address &&
+          xdomain == other.xdomain &&
+          secure == other.secure &&
+          issued == other.issued &&
+          url == other.url &&
+          _mapEquals(query, other.query);
+
+  static bool _mapEquals(final Map a, final Map b) {
+    if (a.length != b.length) return false;
+    for (final MapEntry entry in a.entries) {
+      if (!b.containsKey(entry.key) || b[entry.key] != entry.value) return false;
+    }
+    return true;
+  }
+
+  @override
+  int get hashCode => Object.hash(
+        time,
+        address,
+        xdomain,
+        secure,
+        issued,
+        url,
+        Object.hashAll(query.entries.map((final MapEntry e) => Object.hash(e.key, e.value))),
+      );
+
+  @override
+  String toString() => 'HandshakeDataModel('
+      'time: $time, '
+      'address: $address, '
+      'xdomain: $xdomain, '
+      'secure: $secure, '
+      'issued: $issued, '
+      'url: $url, '
+      'query: $query'
+      ')';
+}
+
+/// Builder for constructing HandshakeDataModel instances.
+class HandshakeDataBuilder {
+  HttpHeaders? _headers;
+  DateTime? _time;
+  InternetAddress? _address;
+  bool _xdomain = false;
+  bool _secure = false;
+  int? _issued;
+  String? _url;
+  final Map _query = {};
+  Map? _auth;
+
+  HandshakeDataBuilder();
+
+  HandshakeDataBuilder headers(final HttpHeaders headers) {
+    _headers = headers;
+    return this;
+  }
+
+  HandshakeDataBuilder time(final DateTime time) {
+    _time = time;
+    return this;
+  }
+
+  HandshakeDataBuilder address(final InternetAddress address) {
+    _address = address;
+    return this;
+  }
+
+  HandshakeDataBuilder xdomain(final bool xdomain) {
+    _xdomain = xdomain;
+    return this;
+  }
+
+  HandshakeDataBuilder secure(final bool secure) {
+    _secure = secure;
+    return this;
+  }
+
+  HandshakeDataBuilder issued(final int issued) {
+    _issued = issued;
+    return this;
+  }
+
+  HandshakeDataBuilder url(final String url) {
+    _url = url;
+    return this;
+  }
+
+  HandshakeDataBuilder addQuery(final String key, final String value) {
+    _query[key] = value;
+    return this;
+  }
+
+  HandshakeDataBuilder queryMap(final Map query) {
+    _query.addAll(query);
+    return this;
+  }
+
+  HandshakeDataBuilder auth(final Map? auth) {
+    _auth = auth;
+    return this;
+  }
+
+  HandshakeDataModel build() {
+    if (_headers == null) throw StateError('headers is required');
+    if (_time == null) throw StateError('time is required');
+    if (_address == null) throw StateError('address is required');
+    if (_issued == null) throw StateError('issued is required');
+    if (_url == null) throw StateError('url is required');
+
+    return HandshakeDataModel(
+      headers: _headers!,
+      time: _time!,
+      address: _address!,
+      xdomain: _xdomain,
+      secure: _secure,
+      issued: _issued!,
+      url: _url!,
+      query: Map.from(_query),
+      auth: _auth,
+    );
+  }
+}
diff --git a/lib/src/models/middleware_models.dart b/lib/src/models/middleware_models.dart
new file mode 100644
index 0000000..732af09
--- /dev/null
+++ b/lib/src/models/middleware_models.dart
@@ -0,0 +1,230 @@
+/// middleware_models.dart
+///
+/// Type-safe models for Socket.IO middleware
+///
+/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+library middleware_models;
+
+import '../value_objects/namespace_name_vo.dart';
+import 'socket_error_models.dart';
+
+/// Context passed to middleware functions.
+///
+/// Contains socket information and methods for middleware to inspect
+/// and potentially modify the connection process.
+class MiddlewareContext {
+  /// The socket attempting to connect.
+  final dynamic socket;
+
+  /// The namespace being connected to.
+  final NamespaceName namespace;
+
+  /// Authentication data provided by the client.
+  final Map? auth;
+
+  /// Custom data that can be set by middleware.
+  final Map data;
+
+  /// Whether the connection has been rejected.
+  bool _rejected = false;
+
+  /// The error that caused rejection, if any.
+  SocketErrorModel? _error;
+
+  /// Creates a middleware context.
+  MiddlewareContext({
+    required this.socket,
+    required this.namespace,
+    this.auth,
+  }) : data = {};
+
+  /// Checks if the connection has been rejected.
+  bool get isRejected => _rejected;
+
+  /// Gets the rejection error, if any.
+  SocketErrorModel? get error => _error;
+
+  /// Rejects the connection with an error.
+  void reject(final SocketErrorModel error) {
+    _rejected = true;
+    _error = error;
+  }
+
+  /// Rejects the connection with a simple message.
+  void rejectWithMessage(final String message) {
+    reject(SocketErrorModel.unauthorized(message));
+  }
+
+  /// Sets a custom data value.
+  void setData(final String key, final Object? value) {
+    data[key] = value;
+  }
+
+  /// Gets a custom data value.
+  Object? getData(final String key) => data[key];
+
+  /// Checks if a data key exists.
+  bool hasData(final String key) => data.containsKey(key);
+}
+
+/// Result of middleware execution.
+class MiddlewareResult {
+  /// Whether the middleware chain should continue.
+  final bool shouldContinue;
+
+  /// The error that caused the middleware to fail, if any.
+  final SocketErrorModel? error;
+
+  const MiddlewareResult._({
+    required this.shouldContinue,
+    this.error,
+  });
+
+  /// Creates a successful result that allows continuation.
+  const MiddlewareResult.success() : this._(shouldContinue: true);
+
+  /// Creates a failed result with an error.
+  const MiddlewareResult.failure(final SocketErrorModel error) : this._(shouldContinue: false, error: error);
+
+  /// Creates a failed result with a message.
+  factory MiddlewareResult.failureWithMessage(final String message) =>
+      MiddlewareResult.failure(SocketErrorModel.unauthorized(message));
+
+  /// Whether the middleware succeeded.
+  bool get isSuccess => shouldContinue;
+
+  /// Whether the middleware failed.
+  bool get isFailure => !shouldContinue;
+}
+
+/// A middleware function that can inspect/modify connection attempts.
+///
+/// Returns a future that resolves to a MiddlewareResult indicating
+/// whether the connection should proceed or be rejected.
+typedef MiddlewareFunction = Future Function(
+  MiddlewareContext context,
+);
+
+/// Synchronous version of middleware function.
+typedef SyncMiddlewareFunction = MiddlewareResult Function(
+  MiddlewareContext context,
+);
+
+/// Manages a chain of middleware functions.
+class MiddlewareChain {
+  final List _middlewares;
+
+  /// Creates an empty middleware chain.
+  MiddlewareChain() : _middlewares = [];
+
+  /// Creates a middleware chain with initial middlewares.
+  MiddlewareChain.withMiddlewares(final List middlewares)
+      : _middlewares = List.from(middlewares);
+
+  /// Adds a middleware to the chain.
+  void add(final MiddlewareFunction middleware) {
+    _middlewares.add(middleware);
+  }
+
+  /// Adds a synchronous middleware to the chain.
+  void addSync(final SyncMiddlewareFunction middleware) {
+    _middlewares.add((final MiddlewareContext context) async => middleware(context));
+  }
+
+  /// Removes a middleware from the chain.
+  bool remove(final MiddlewareFunction middleware) => _middlewares.remove(middleware);
+
+  /// Clears all middlewares.
+  void clear() => _middlewares.clear();
+
+  /// Gets the number of middlewares.
+  int get length => _middlewares.length;
+
+  /// Checks if empty.
+  bool get isEmpty => _middlewares.isEmpty;
+
+  /// Checks if not empty.
+  bool get isNotEmpty => _middlewares.isNotEmpty;
+
+  /// Executes the middleware chain.
+  ///
+  /// Runs each middleware in order until one fails or all succeed.
+  /// Returns the first failure or success if all pass.
+  Future execute(final MiddlewareContext context) async {
+    for (final MiddlewareFunction middleware in _middlewares) {
+      final MiddlewareResult result = await middleware(context);
+      if (result.isFailure) {
+        return result;
+      }
+
+      // Check if context was rejected directly
+      if (context.isRejected) {
+        return MiddlewareResult.failure(
+          context.error ?? SocketErrorModel.unauthorized('Connection rejected'),
+        );
+      }
+    }
+    return const MiddlewareResult.success();
+  }
+
+  @override
+  String toString() => 'MiddlewareChain(${_middlewares.length} middlewares)';
+}
+
+/// Common middleware functions.
+class Middlewares {
+  /// Creates a middleware that logs all connection attempts.
+  static MiddlewareFunction logging({
+    required final void Function(String) log,
+  }) =>
+      (final MiddlewareContext context) async {
+        log('Connection attempt to ${context.namespace}');
+        return const MiddlewareResult.success();
+      };
+
+  /// Creates a middleware that requires authentication.
+  static MiddlewareFunction requireAuth({
+    required final Future Function(Map) validate,
+  }) =>
+      (final MiddlewareContext context) async {
+        if (context.auth == null || context.auth!.isEmpty) {
+          return MiddlewareResult.failureWithMessage('Authentication required');
+        }
+
+        final bool isValid = await validate(context.auth!);
+        if (!isValid) {
+          return MiddlewareResult.failureWithMessage('Invalid authentication');
+        }
+
+        return const MiddlewareResult.success();
+      };
+
+  /// Creates a middleware that checks for specific auth tokens.
+  static MiddlewareFunction requireToken({
+    required final String tokenKey,
+    required final bool Function(String) validate,
+  }) =>
+      (final MiddlewareContext context) async {
+        final Object? token = context.auth?[tokenKey];
+        if (token == null || token is! String) {
+          return MiddlewareResult.failureWithMessage('Token required');
+        }
+
+        if (!validate(token)) {
+          return MiddlewareResult.failureWithMessage('Invalid token');
+        }
+
+        return const MiddlewareResult.success();
+      };
+
+  /// Creates a middleware that rate limits connections.
+  static MiddlewareFunction rateLimit({
+    required final bool Function(dynamic socket) checkLimit,
+  }) =>
+      (final MiddlewareContext context) async {
+        if (!checkLimit(context.socket)) {
+          return MiddlewareResult.failureWithMessage('Rate limit exceeded');
+        }
+        return const MiddlewareResult.success();
+      };
+}
diff --git a/lib/src/models/models.dart b/lib/src/models/models.dart
new file mode 100644
index 0000000..6b6d963
--- /dev/null
+++ b/lib/src/models/models.dart
@@ -0,0 +1,32 @@
+/// models.dart
+///
+/// Barrel file that exports all model classes from the models directory
+///
+/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+library models;
+
+export 'adapter_broadcast_models.dart';
+export 'adapter_room_models.dart';
+export 'callbacks_models.dart';
+export 'client_configuration_models.dart';
+export 'client_connection_models.dart';
+export 'connect_buffer_models.dart';
+export 'engine_handshake_models.dart';
+export 'error_models.dart';
+export 'event_data_models.dart';
+export 'handshake_data_models.dart';
+export 'middleware_models.dart';
+export 'namespace_config_models.dart';
+export 'packet_data_models.dart';
+export 'packet_models.dart';
+export 'room_management_models.dart';
+export 'server_configuration_models.dart';
+export 'server_options_models.dart';
+export 'socket_configuration_models.dart';
+export 'socket_data_models.dart';
+export 'socket_error_models.dart';
+export 'socket_flags_models.dart';
+export 'transport_data_models.dart';
+export 'transport_error_models.dart';
+export 'transport_models.dart';
+export 'validation_error_models.dart';
diff --git a/lib/src/models/namespace_config_models.dart b/lib/src/models/namespace_config_models.dart
new file mode 100644
index 0000000..7cb6128
--- /dev/null
+++ b/lib/src/models/namespace_config_models.dart
@@ -0,0 +1,192 @@
+// namespace_config_models.dart
+//
+// Purpose: Type-safe models for namespace configuration
+//
+// Description: Provides models for configuring Socket.IO namespaces
+// including adapter settings, middleware, and namespace options.
+
+import '../adapter.dart';
+import '../value_objects/namespace_name_vo.dart';
+import 'callbacks_models.dart';
+
+/// Configuration model for a Socket.IO namespace.
+///
+/// Provides type-safe configuration options for namespace creation
+/// and management.
+class NamespaceConfig {
+  /// The name of the namespace (must start with '/').
+  final NamespaceName name;
+
+  /// Custom adapter factory for the namespace.
+  /// If null, uses the server's default adapter.
+  final Adapter Function()? adapterFactory;
+
+  /// List of middleware functions to apply to connections.
+  final List middleware;
+
+  /// Whether to automatically create this namespace on first connection.
+  final bool autoCreate;
+
+  /// Maximum number of sockets allowed in this namespace.
+  /// Null means unlimited.
+  final int? maxSockets;
+
+  /// Creates a namespace configuration.
+  ///
+  /// [name] - The namespace name (must start with '/')
+  /// [adapterFactory] - Optional custom adapter factory
+  /// [middleware] - List of middleware functions (default: empty)
+  /// [autoCreate] - Whether to auto-create namespace (default: true)
+  /// [maxSockets] - Maximum sockets allowed (default: unlimited)
+  const NamespaceConfig({
+    required this.name,
+    this.adapterFactory,
+    this.middleware = const [],
+    this.autoCreate = true,
+    this.maxSockets,
+  });
+
+  /// Creates a namespace configuration from a plain name string.
+  ///
+  /// Validates the namespace name format.
+  factory NamespaceConfig.fromString(
+    final String name, {
+    final Adapter Function()? adapterFactory,
+    final List middleware = const [],
+    final bool autoCreate = true,
+    final int? maxSockets,
+  }) =>
+      NamespaceConfig(
+        name: NamespaceName(name),
+        adapterFactory: adapterFactory,
+        middleware: middleware,
+        autoCreate: autoCreate,
+        maxSockets: maxSockets,
+      );
+
+  /// Creates a copy with updated values.
+  NamespaceConfig copyWith({
+    final NamespaceName? name,
+    final Adapter Function()? adapterFactory,
+    final List? middleware,
+    final bool? autoCreate,
+    final int? maxSockets,
+  }) =>
+      NamespaceConfig(
+        name: name ?? this.name,
+        adapterFactory: adapterFactory ?? this.adapterFactory,
+        middleware: middleware ?? this.middleware,
+        autoCreate: autoCreate ?? this.autoCreate,
+        maxSockets: maxSockets ?? this.maxSockets,
+      );
+
+  /// Adds middleware to the configuration.
+  NamespaceConfig withMiddleware(final MiddlewareCallback middleware) => copyWith(
+        middleware: [...this.middleware, middleware],
+      );
+
+  /// Adds multiple middleware functions.
+  NamespaceConfig withMiddlewares(final List middlewares) => copyWith(
+        middleware: [...middleware, ...middlewares],
+      );
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is NamespaceConfig &&
+          runtimeType == other.runtimeType &&
+          name == other.name &&
+          autoCreate == other.autoCreate &&
+          maxSockets == other.maxSockets;
+
+  @override
+  int get hashCode => Object.hash(name, autoCreate, maxSockets);
+
+  @override
+  String toString() => 'NamespaceConfig('
+      'name: $name, '
+      'adapterFactory: ${adapterFactory != null ? 'custom' : 'default'}, '
+      'middleware: ${middleware.length} functions, '
+      'autoCreate: $autoCreate, '
+      'maxSockets: ${maxSockets ?? 'unlimited'}'
+      ')';
+}
+
+/// Builder for creating namespace configurations.
+///
+/// Provides a fluent API for building namespace configurations.
+class NamespaceConfigBuilder {
+  NamespaceName? _name;
+  Adapter Function()? _adapterFactory;
+  final List _middleware = [];
+  bool _autoCreate = true;
+  int? _maxSockets;
+
+  /// Sets the namespace name.
+  NamespaceConfigBuilder name(final String name) {
+    _name = NamespaceName(name);
+    return this;
+  }
+
+  /// Sets the namespace name as value object.
+  NamespaceConfigBuilder nameVo(final NamespaceName name) {
+    _name = name;
+    return this;
+  }
+
+  /// Sets a custom adapter factory.
+  NamespaceConfigBuilder adapterFactory(final Adapter Function() factory) {
+    _adapterFactory = factory;
+    return this;
+  }
+
+  /// Adds a middleware function.
+  NamespaceConfigBuilder middleware(final MiddlewareCallback middleware) {
+    _middleware.add(middleware);
+    return this;
+  }
+
+  /// Adds multiple middleware functions.
+  NamespaceConfigBuilder middlewares(final List middlewares) {
+    _middleware.addAll(middlewares);
+    return this;
+  }
+
+  /// Sets whether to auto-create the namespace.
+  NamespaceConfigBuilder autoCreate(final bool autoCreate) {
+    _autoCreate = autoCreate;
+    return this;
+  }
+
+  /// Sets the maximum number of sockets.
+  NamespaceConfigBuilder maxSockets(final int maxSockets) {
+    if (maxSockets <= 0) {
+      throw ArgumentError('maxSockets must be positive');
+    }
+    _maxSockets = maxSockets;
+    return this;
+  }
+
+  /// Sets unlimited sockets.
+  NamespaceConfigBuilder unlimitedSockets() {
+    _maxSockets = null;
+    return this;
+  }
+
+  /// Builds the namespace configuration.
+  ///
+  /// Throws [StateError] if required fields are missing.
+  NamespaceConfig build() {
+    if (_name == null) {
+      throw StateError('Namespace name is required');
+    }
+
+    return NamespaceConfig(
+      name: _name!,
+      adapterFactory: _adapterFactory,
+      middleware: List.unmodifiable(_middleware),
+      autoCreate: _autoCreate,
+      maxSockets: _maxSockets,
+    );
+  }
+}
diff --git a/lib/src/models/packet_data_models.dart b/lib/src/models/packet_data_models.dart
new file mode 100644
index 0000000..461b54e
--- /dev/null
+++ b/lib/src/models/packet_data_models.dart
@@ -0,0 +1,452 @@
+/// Specific data models for Socket.IO packet payloads.
+///
+/// These models replace generic `Map` and `List`
+/// usage in packet data fields with type-safe alternatives.
+///
+/// Copyright (C) 2024. All Rights Reserved.
+library packet_data_models;
+
+import '../value_objects/disconnect_reason_vo.dart';
+import '../value_objects/event_arguments_vo.dart';
+
+/// Data model for CONNECT packet payload.
+///
+/// Contains authentication and connection metadata.
+/// In Socket.IO v3+, the server sends the socket ID in the CONNECT response.
+///
+/// Example:
+/// ```dart
+/// // Client request
+/// final ConnectPacketData data = ConnectPacketData(
+///   auth: {'token': 'abc123'},
+///   query: {'userId': '123'},
+/// );
+///
+/// // Server response
+/// final ConnectPacketData response = ConnectPacketData(
+///   sid: 'abc123xyz',
+/// );
+/// ```
+class ConnectPacketData {
+  /// Socket ID (sent by server in CONNECT response for Socket.IO v3+)
+  final String? sid;
+
+  /// Process ID (optional, for server identification)
+  final String? pid;
+
+  /// Authentication data.
+  final Map? auth;
+
+  /// Query parameters.
+  final Map? query;
+
+  /// Additional connection metadata.
+  final Map? metadata;
+
+  /// Creates a new connect packet data model.
+  const ConnectPacketData({
+    this.sid,
+    this.pid,
+    this.auth,
+    this.query,
+    this.metadata,
+  });
+
+  /// Creates from a JSON map.
+  factory ConnectPacketData.fromJson(final Map json) => ConnectPacketData(
+        sid: json['sid'] as String?,
+        pid: json['pid'] as String?,
+        auth: json['auth'] as Map?,
+        query: json['query'] as Map?,
+        metadata: json['metadata'] as Map?,
+      );
+
+  /// Converts to JSON map.
+  Map toJson() {
+    final Map result = {};
+    if (sid != null) {
+      result['sid'] = sid;
+    }
+    if (pid != null) {
+      result['pid'] = pid;
+    }
+    if (auth != null) {
+      result['auth'] = auth;
+    }
+    if (query != null) {
+      result['query'] = query;
+    }
+    if (metadata != null) {
+      result['metadata'] = metadata;
+    }
+    return result;
+  }
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is ConnectPacketData &&
+          runtimeType == other.runtimeType &&
+          sid == other.sid &&
+          pid == other.pid &&
+          _mapEquals(auth, other.auth) &&
+          _mapEquals(query, other.query) &&
+          _mapEquals(metadata, other.metadata);
+
+  @override
+  int get hashCode => Object.hash(sid, pid, auth, query, metadata);
+
+  @override
+  String toString() => 'ConnectPacketData(sid: $sid, pid: $pid, auth: $auth, query: $query)';
+
+  /// Helper to compare maps.
+  static bool _mapEquals(
+    final Map? a,
+    final Map? b,
+  ) {
+    if (a == null) {
+      return b == null;
+    }
+    if (b == null || a.length != b.length) {
+      return false;
+    }
+    for (final MapEntry entry in a.entries) {
+      if (!b.containsKey(entry.key) || b[entry.key] != entry.value) {
+        return false;
+      }
+    }
+    return true;
+  }
+}
+
+/// Data model for DISCONNECT packet payload.
+///
+/// Contains the reason for disconnection and optional metadata.
+///
+/// Example:
+/// ```dart
+/// final DisconnectPacketData data = DisconnectPacketData(
+///   reason: 'client disconnect',
+///   description: 'User logged out',
+/// );
+/// ```
+/// Data model for DISCONNECT packet payload.
+///
+/// Contains the reason for disconnection using type-safe DisconnectReason value object.
+///
+/// Example:
+/// ```dart
+/// // Using typed disconnect reason
+/// final DisconnectPacketData data = DisconnectPacketData(
+///   reason: DisconnectReason.clientDisconnect,
+/// );
+///
+/// // Using string (backward compatible)
+/// final DisconnectPacketData data2 = DisconnectPacketData.fromString('client disconnect');
+/// ```
+class DisconnectPacketData {
+  /// The typed reason for disconnection (preferred).
+  final DisconnectReason? typedReason;
+
+  /// The string reason for disconnection (private, for backward compatibility).
+  /// Note: This is private and only used internally for the deprecated constructor.
+  final String? _rawReason;
+
+  /// Optional human-readable description.
+  final String? description;
+
+  /// Additional metadata.
+  final Map? metadata;
+
+  /// Creates a new disconnect packet data model with typed reason (preferred).
+  const DisconnectPacketData({
+    required final DisconnectReason reason,
+    this.description,
+    this.metadata,
+  })  : typedReason = reason,
+        _rawReason = null;
+
+  /// Creates from a string reason (deprecated, for backward compatibility).
+  @Deprecated('Use DisconnectPacketData() with DisconnectReason instead')
+  const DisconnectPacketData.fromString({
+    required final String reason,
+    this.description,
+    this.metadata,
+  })  : _rawReason = reason,
+        typedReason = null;
+
+  /// Creates from a string reason (factory version).
+  factory DisconnectPacketData.fromReasonString(final String reason) =>
+      DisconnectPacketData(reason: DisconnectReason.fromString(reason));
+
+  /// Creates from a JSON value.
+  factory DisconnectPacketData.fromJson(final Object? json) {
+    if (json is String) {
+      return DisconnectPacketData(reason: DisconnectReason.fromString(json));
+    }
+    if (json is Map) {
+      final String reasonStr = json['reason'] as String? ?? 'unknown';
+      return DisconnectPacketData(
+        reason: DisconnectReason.fromString(reasonStr),
+        description: json['description'] as String?,
+        metadata: json['metadata'] as Map?,
+      );
+    }
+    return DisconnectPacketData(
+      reason: DisconnectReason.fromString(json?.toString() ?? 'unknown'),
+    );
+  }
+
+  /// Gets the reason string value.
+  String get reason {
+    if (typedReason != null) {
+      return typedReason!.value;
+    }
+    return _rawReason ?? 'unknown';
+  }
+
+  /// Gets the typed reason (creates from raw if needed).
+  DisconnectReason get reasonValue {
+    if (typedReason != null) {
+      return typedReason!;
+    }
+    return DisconnectReason.fromString(_rawReason ?? 'unknown');
+  }
+
+  /// Converts to JSON.
+  Object toJson() {
+    if (description == null && metadata == null) {
+      return reason;
+    }
+    final Map result = {'reason': reason};
+    if (description != null) {
+      result['description'] = description;
+    }
+    if (metadata != null) {
+      result['metadata'] = metadata;
+    }
+    return result;
+  }
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is DisconnectPacketData &&
+          runtimeType == other.runtimeType &&
+          reason == other.reason &&
+          description == other.description &&
+          ConnectPacketData._mapEquals(metadata, other.metadata);
+
+  @override
+  int get hashCode => Object.hash(reason, description, metadata);
+
+  @override
+  String toString() => 'DisconnectPacketData($reason)';
+}
+
+/// Data model for EVENT packet payload.
+///
+/// Contains the event name and arguments.
+///
+/// Example:
+/// ```dart
+/// final EventPacketData data = EventPacketData(
+///   eventName: 'message',
+///   arguments: EventArguments(['Hello', 42]),
+/// );
+/// ```
+class EventPacketData {
+  /// The name of the event.
+  final String eventName;
+
+  /// The event arguments.
+  final EventArguments arguments;
+
+  /// Creates a new event packet data model.
+  const EventPacketData({
+    required this.eventName,
+    required this.arguments,
+  });
+
+  /// Creates from a list where first element is event name.
+  factory EventPacketData.fromList(final List list) {
+    if (list.isEmpty) {
+      throw ArgumentError('Event data list cannot be empty');
+    }
+    final String eventName = list.first.toString();
+    final List args = list.length > 1 ? list.sublist(1).cast() : [];
+    return EventPacketData(
+      eventName: eventName,
+      arguments: EventArguments(args),
+    );
+  }
+
+  /// Creates with no arguments.
+  factory EventPacketData.withoutArgs(final String eventName) => EventPacketData(
+        eventName: eventName,
+        arguments: EventArguments.empty(),
+      );
+
+  /// Creates with a single argument.
+  factory EventPacketData.single(final String eventName, final Object? value) => EventPacketData(
+        eventName: eventName,
+        arguments: EventArguments.single(value),
+      );
+
+  /// Converts to a list format (event name followed by arguments).
+  List toList() => [eventName, ...arguments.arguments];
+
+  /// Converts to JSON.
+  List toJson() => toList();
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is EventPacketData &&
+          runtimeType == other.runtimeType &&
+          eventName == other.eventName &&
+          arguments == other.arguments;
+
+  @override
+  int get hashCode => Object.hash(eventName, arguments);
+
+  @override
+  String toString() => 'EventPacketData($eventName, ${arguments.length} args)';
+}
+
+/// Data model for ACK (acknowledgment) packet payload.
+///
+/// Contains the response arguments to an event with callback.
+///
+/// Example:
+/// ```dart
+/// final AckPacketData data = AckPacketData(
+///   arguments: EventArguments(['success', {'status': 200}]),
+/// );
+/// ```
+class AckPacketData {
+  /// The acknowledgment arguments.
+  final EventArguments arguments;
+
+  /// Creates a new ACK packet data model.
+  const AckPacketData({
+    required this.arguments,
+  });
+
+  /// Creates from a list of arguments.
+  factory AckPacketData.fromList(final List list) => AckPacketData(
+        arguments: EventArguments(list.cast()),
+      );
+
+  /// Creates with no arguments (simple acknowledgment).
+  factory AckPacketData.empty() => AckPacketData(arguments: EventArguments.empty());
+
+  /// Creates with a single argument.
+  factory AckPacketData.single(final Object? value) => AckPacketData(arguments: EventArguments.single(value));
+
+  /// Creates success acknowledgment with optional data.
+  factory AckPacketData.success([final Object? data]) {
+    if (data == null) {
+      return AckPacketData(arguments: EventArguments.empty());
+    }
+    return AckPacketData(arguments: EventArguments.single(data));
+  }
+
+  /// Creates error acknowledgment.
+  factory AckPacketData.error(final Object error) => AckPacketData(arguments: EventArguments.single(error));
+
+  /// Converts to a list format.
+  List toList() => arguments.arguments;
+
+  /// Converts to JSON.
+  List toJson() => toList();
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is AckPacketData && runtimeType == other.runtimeType && arguments == other.arguments;
+
+  @override
+  int get hashCode => arguments.hashCode;
+
+  @override
+  String toString() => 'AckPacketData(${arguments.length} args)';
+}
+
+/// Data model for CONNECT_ERROR packet payload.
+///
+/// Contains error information when connection to a namespace fails.
+///
+/// Example:
+/// ```dart
+/// final ConnectErrorPacketData data = ConnectErrorPacketData(
+///   message: 'Authentication failed',
+///   code: 'AUTH_ERROR',
+///   details: {'reason': 'Invalid token'},
+/// );
+/// ```
+class ConnectErrorPacketData {
+  /// The error message.
+  final String message;
+
+  /// Optional error code.
+  final String? code;
+
+  /// Additional error details.
+  final Map? details;
+
+  /// Creates a new connect error packet data model.
+  const ConnectErrorPacketData({
+    required this.message,
+    this.code,
+    this.details,
+  });
+
+  /// Creates from a string message.
+  factory ConnectErrorPacketData.fromMessage(final String message) => ConnectErrorPacketData(message: message);
+
+  /// Creates from a JSON value.
+  factory ConnectErrorPacketData.fromJson(final Object? json) {
+    if (json is String) {
+      return ConnectErrorPacketData(message: json);
+    }
+    if (json is Map) {
+      return ConnectErrorPacketData(
+        message: json['message'] as String? ?? 'Connection error',
+        code: json['code'] as String?,
+        details: json['details'] as Map?,
+      );
+    }
+    return ConnectErrorPacketData(message: json?.toString() ?? 'Unknown error');
+  }
+
+  /// Converts to JSON.
+  Object toJson() {
+    if (code == null && details == null) {
+      return message;
+    }
+    final Map result = {'message': message};
+    if (code != null) {
+      result['code'] = code;
+    }
+    if (details != null) {
+      result['details'] = details;
+    }
+    return result;
+  }
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is ConnectErrorPacketData &&
+          runtimeType == other.runtimeType &&
+          message == other.message &&
+          code == other.code &&
+          ConnectPacketData._mapEquals(details, other.details);
+
+  @override
+  int get hashCode => Object.hash(message, code, details);
+
+  @override
+  String toString() => 'ConnectErrorPacketData($message)';
+}
diff --git a/lib/src/models/packet_models.dart b/lib/src/models/packet_models.dart
new file mode 100644
index 0000000..a68fbfb
--- /dev/null
+++ b/lib/src/models/packet_models.dart
@@ -0,0 +1,486 @@
+/// packet_models.dart
+///
+/// Typed models for Socket.IO packets to replace dynamic map creation
+///
+/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+library packet_models;
+
+import 'package:socket_io_common/socket_io_common.dart';
+
+import 'packet_data_models.dart';
+
+/// Base sealed class for all Socket.IO packets
+///
+/// Using sealed class ensures exhaustive pattern matching and makes the set
+/// of packet types closed - all subtypes are defined in this file.
+sealed class SocketIOPacket {
+  int get type;
+  String? get namespace;
+  Object? get data;
+  String? get id;
+
+  Map toMap();
+}
+
+/// Connect packet - sent when a client connects to a namespace
+class ConnectPacket implements SocketIOPacket {
+  @override
+  final int type = CONNECT;
+
+  @override
+  final String? namespace;
+
+  /// Typed connection data (preferred)
+  final ConnectPacketData? typedData;
+
+  /// Raw map data (deprecated, for backward compatibility)
+  @Deprecated('Use typedData instead')
+  final Map? rawData;
+
+  @override
+  final String? id;
+
+  /// Creates a connect packet with typed data (preferred)
+  ConnectPacket.typed({
+    this.namespace,
+    this.typedData,
+    this.id,
+    // ignore: deprecated_member_use_from_same_package
+  }) : rawData = null;
+
+  /// Creates a connect packet with raw map data (deprecated)
+  @Deprecated('Use ConnectPacket.typed() instead')
+  ConnectPacket({
+    this.namespace,
+    final Map? data,
+    this.id,
+    // ignore: deprecated_member_use_from_same_package
+  })  : rawData = data,
+        typedData = data != null ? ConnectPacketData.fromJson(data) : null;
+
+  @override
+  // ignore: deprecated_member_use_from_same_package
+  Object? get data => typedData ?? rawData;
+
+  @override
+  Map toMap() {
+    final Map map = {'type': type};
+    if (namespace != null) map['nsp'] = namespace;
+    if (typedData != null) {
+      map['data'] = typedData!.toJson();
+      // ignore: deprecated_member_use_from_same_package
+    } else if (rawData != null) {
+      // ignore: deprecated_member_use_from_same_package
+      map['data'] = rawData;
+    }
+    if (id != null) map['id'] = id;
+    return map;
+  }
+}
+
+/// Disconnect packet - sent when a client disconnects
+class DisconnectPacket implements SocketIOPacket {
+  @override
+  final int type = DISCONNECT;
+
+  @override
+  final String? namespace;
+
+  /// Typed disconnect data (preferred)
+  final DisconnectPacketData? typedData;
+
+  /// Raw data (deprecated, for backward compatibility)
+  @Deprecated('Use typedData instead')
+  final Object? rawData;
+
+  @override
+  final String? id;
+
+  /// Creates a disconnect packet with typed data (preferred)
+  DisconnectPacket.typed({
+    this.namespace,
+    this.typedData,
+    this.id,
+    // ignore: deprecated_member_use_from_same_package
+  }) : rawData = null;
+
+  /// Creates a disconnect packet with raw data (deprecated)
+  @Deprecated('Use DisconnectPacket.typed() instead')
+  DisconnectPacket({
+    this.namespace,
+    final Object? data,
+    this.id,
+    // ignore: deprecated_member_use_from_same_package
+  })  : rawData = data,
+        typedData = data != null ? DisconnectPacketData.fromJson(data) : null;
+
+  @override
+  // ignore: deprecated_member_use_from_same_package
+  Object? get data => typedData ?? rawData;
+
+  @override
+  Map toMap() {
+    final Map map = {'type': type};
+    if (namespace != null) map['nsp'] = namespace;
+    if (typedData != null) {
+      map['data'] = typedData!.toJson();
+      // ignore: deprecated_member_use_from_same_package
+    } else if (rawData != null) {
+      // ignore: deprecated_member_use_from_same_package
+      map['data'] = rawData;
+    }
+    if (id != null) map['id'] = id;
+    return map;
+  }
+}
+
+/// Event packet - sent when emitting an event
+class EventPacket implements SocketIOPacket {
+  @override
+  final int type;
+
+  @override
+  final String? namespace;
+
+  /// Typed event data (preferred)
+  final EventPacketData? typedData;
+
+  /// Raw list data (deprecated, for backward compatibility)
+  @Deprecated('Use typedData instead')
+  final List? rawData;
+
+  @override
+  final String? id;
+
+  /// Creates an event packet with typed data (preferred)
+  EventPacket.typed({
+    required this.typedData,
+    this.namespace,
+    this.id,
+    final bool binary = false,
+    // ignore: deprecated_member_use_from_same_package
+  })  : rawData = null,
+        type = binary ? BINARY_EVENT : EVENT;
+
+  /// Creates an event packet with raw list data (deprecated)
+  @Deprecated('Use EventPacket.typed() instead')
+  EventPacket({
+    required final List data,
+    this.namespace,
+    this.id,
+    final bool binary = false,
+    // ignore: deprecated_member_use_from_same_package
+  })  : rawData = data,
+        typedData = EventPacketData.fromList(data),
+        type = binary ? BINARY_EVENT : EVENT;
+
+  @override
+  // ignore: deprecated_member_use_from_same_package
+  Object? get data => typedData?.toList() ?? rawData;
+
+  @override
+  Map toMap() {
+    final Map map = {
+      'type': type,
+      // ignore: deprecated_member_use_from_same_package
+      'data': typedData?.toList() ?? rawData,
+    };
+    if (namespace != null) map['nsp'] = namespace;
+    if (id != null) map['id'] = id;
+    return map;
+  }
+}
+
+/// ACK packet - sent as a response to an event with callback
+class AckPacket implements SocketIOPacket {
+  @override
+  final int type;
+
+  @override
+  final String? namespace;
+
+  /// Typed ACK data (preferred)
+  final AckPacketData? typedData;
+
+  /// Raw list data (deprecated, for backward compatibility)
+  @Deprecated('Use typedData instead')
+  final List? rawData;
+
+  @override
+  final String id;
+
+  /// Creates an ACK packet with typed data (preferred)
+  AckPacket.typed({
+    required this.id,
+    required this.typedData,
+    this.namespace,
+    final bool binary = false,
+    // ignore: deprecated_member_use_from_same_package
+  })  : rawData = null,
+        type = binary ? BINARY_ACK : ACK;
+
+  /// Creates an ACK packet with raw list data (deprecated)
+  @Deprecated('Use AckPacket.typed() instead')
+  AckPacket({
+    required this.id,
+    required final List data,
+    this.namespace,
+    final bool binary = false,
+    // ignore: deprecated_member_use_from_same_package
+  })  : rawData = data,
+        typedData = AckPacketData.fromList(data),
+        type = binary ? BINARY_ACK : ACK;
+
+  @override
+  // ignore: deprecated_member_use_from_same_package
+  Object? get data => typedData?.toList() ?? rawData;
+
+  @override
+  Map toMap() {
+    final Map map = {
+      'type': type,
+      'id': id,
+      // ignore: deprecated_member_use_from_same_package
+      'data': typedData?.toList() ?? rawData,
+    };
+    if (namespace != null) map['nsp'] = namespace;
+    return map;
+  }
+}
+
+/// Connect Error packet - sent when connection to namespace fails
+class ConnectErrorPacket implements SocketIOPacket {
+  @override
+  final int type = CONNECT_ERROR;
+
+  @override
+  final String? namespace;
+
+  /// Typed error data (preferred)
+  final ConnectErrorPacketData? typedData;
+
+  /// Raw data (deprecated, for backward compatibility)
+  @Deprecated('Use typedData instead')
+  final Object? rawData;
+
+  @override
+  final String? id;
+
+  /// Creates a connect error packet with typed data (preferred)
+  ConnectErrorPacket.typed({
+    required this.typedData,
+    this.namespace,
+    this.id,
+    // ignore: deprecated_member_use_from_same_package
+  }) : rawData = null;
+
+  /// Creates a connect error packet with raw data (deprecated)
+  @Deprecated('Use ConnectErrorPacket.typed() instead')
+  ConnectErrorPacket({
+    required final Object? data,
+    this.namespace,
+    this.id,
+    // ignore: deprecated_member_use_from_same_package
+  })  : rawData = data,
+        typedData = ConnectErrorPacketData.fromJson(data);
+
+  @override
+  // ignore: deprecated_member_use_from_same_package
+  Object? get data => typedData?.toJson() ?? rawData;
+
+  @override
+  Map toMap() {
+    final Map map = {
+      'type': type,
+      // ignore: deprecated_member_use_from_same_package
+      'data': typedData?.toJson() ?? rawData,
+    };
+    if (namespace != null) map['nsp'] = namespace;
+    if (id != null) map['id'] = id;
+    return map;
+  }
+}
+
+/// Factory class to create packets from maps (for parsing incoming data)
+class PacketFactory {
+  static SocketIOPacket fromMap(final Map data) {
+    final int type = data['type'] as int;
+    final String? namespace = data['nsp'] as String?;
+    final String? id = data['id'] as String?;
+    final dynamic packetData = data['data'];
+
+    switch (type) {
+      case CONNECT:
+        return ConnectPacket.typed(
+          namespace: namespace,
+          typedData: packetData != null ? ConnectPacketData.fromJson(packetData as Map) : null,
+          id: id,
+        );
+
+      case DISCONNECT:
+        return DisconnectPacket.typed(
+          namespace: namespace,
+          typedData: packetData != null ? DisconnectPacketData.fromJson(packetData) : null,
+          id: id,
+        );
+
+      case EVENT:
+        return EventPacket.typed(
+          typedData: EventPacketData.fromList(packetData as List),
+          namespace: namespace,
+          id: id,
+          binary: false,
+        );
+
+      case BINARY_EVENT:
+        return EventPacket.typed(
+          typedData: EventPacketData.fromList(packetData as List),
+          namespace: namespace,
+          id: id,
+          binary: true,
+        );
+
+      case ACK:
+        return AckPacket.typed(
+          id: id!,
+          typedData: AckPacketData.fromList(packetData as List),
+          namespace: namespace,
+          binary: false,
+        );
+
+      case BINARY_ACK:
+        return AckPacket.typed(
+          id: id!,
+          typedData: AckPacketData.fromList(packetData as List),
+          namespace: namespace,
+          binary: true,
+        );
+
+      case CONNECT_ERROR:
+        return ConnectErrorPacket.typed(
+          typedData: ConnectErrorPacketData.fromJson(packetData),
+          namespace: namespace,
+          id: id,
+        );
+
+      default:
+        throw ArgumentError('Unknown packet type: $type');
+    }
+  }
+}
+
+/// Engine.IO packet models for transport layer
+///
+/// Using sealed class ensures exhaustive pattern matching and makes the set
+/// of packet types closed - all subtypes are defined in this file.
+sealed class EngineIOPacket {
+  String get type;
+  Object? get data; // Engine.IO data can be String or binary
+  Map? get options; // only boolean flags are supported (e.g., compress)
+
+  Map toMap();
+}
+
+/// Ping packet for Engine.IO
+class PingPacket implements EngineIOPacket {
+  @override
+  final String type = 'ping';
+
+  @override
+  final Object? data;
+
+  @override
+  final Map? options;
+
+  PingPacket({this.data, this.options});
+
+  @override
+  Map toMap() {
+    final Map map = {'type': type};
+    if (data != null) map['data'] = data;
+    if (options != null) map['options'] = options;
+    return map;
+  }
+}
+
+/// Pong packet for Engine.IO
+class PongPacket implements EngineIOPacket {
+  @override
+  final String type = 'pong';
+
+  @override
+  final Object? data;
+
+  @override
+  final Map? options;
+
+  PongPacket({this.data, this.options});
+
+  @override
+  Map toMap() {
+    final Map map = {'type': type};
+    if (data != null) map['data'] = data;
+    if (options != null) map['options'] = options;
+    return map;
+  }
+}
+
+/// Noop packet for Engine.IO
+class NoopPacket implements EngineIOPacket {
+  @override
+  final String type = 'noop';
+
+  @override
+  final Object? data;
+
+  @override
+  final Map? options;
+
+  NoopPacket({this.data, this.options});
+
+  @override
+  Map toMap() {
+    final Map map = {'type': type};
+    if (data != null) map['data'] = data;
+    if (options != null) map['options'] = options;
+    return map;
+  }
+}
+
+/// Close packet for Engine.IO
+class ClosePacket implements EngineIOPacket {
+  @override
+  final String type = 'close';
+
+  @override
+  final Object? data;
+
+  @override
+  final Map? options;
+
+  ClosePacket({this.data, this.options});
+
+  @override
+  Map toMap() {
+    final Map map = {'type': type};
+    if (data != null) map['data'] = data;
+    if (options != null) map['options'] = options;
+    return map;
+  }
+}
+
+/// PacketOptions class for packet transmission options
+class PacketOptions {
+  final bool volatile;
+  final bool compress;
+
+  PacketOptions({
+    this.volatile = false,
+    this.compress = false,
+  });
+
+  Map toMap() => {
+        'volatile': volatile,
+        'compress': compress,
+      };
+}
diff --git a/lib/src/models/room_management_models.dart b/lib/src/models/room_management_models.dart
new file mode 100644
index 0000000..8e4add5
--- /dev/null
+++ b/lib/src/models/room_management_models.dart
@@ -0,0 +1,258 @@
+/// room_management_models.dart
+///
+/// Type-safe models for room management and membership tracking
+///
+/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+library room_management_models;
+
+import '../value_objects/room_name_vo.dart';
+
+/// Represents room membership for a socket.
+///
+/// Provides type-safe tracking of which rooms a socket belongs to.
+class RoomMembership {
+  final Map _rooms;
+
+  /// Creates an empty room membership.
+  RoomMembership() : _rooms = {};
+
+  /// Creates from an existing map of room names.
+  RoomMembership.fromNames(final Iterable roomNames) : _rooms = {} {
+    for (final String name in roomNames) {
+      final RoomName room = RoomName(name);
+      _rooms[room.value] = room;
+    }
+  }
+
+  /// Creates from RoomName value objects.
+  RoomMembership.fromRooms(final Iterable rooms) : _rooms = {} {
+    for (final RoomName room in rooms) {
+      _rooms[room.value] = room;
+    }
+  }
+
+  /// Adds a room to membership.
+  void add(final RoomName room) {
+    _rooms[room.value] = room;
+  }
+
+  /// Adds a room by name.
+  void addByName(final String roomName) {
+    final RoomName room = RoomName(roomName);
+    _rooms[room.value] = room;
+  }
+
+  /// Removes a room from membership.
+  bool remove(final RoomName room) => _rooms.remove(room.value) != null;
+
+  /// Removes a room by name.
+  bool removeByName(final String roomName) => _rooms.remove(roomName) != null;
+
+  /// Checks if a room is in membership.
+  bool contains(final RoomName room) => _rooms.containsKey(room.value);
+
+  /// Checks if a room name is in membership.
+  bool containsName(final String roomName) => _rooms.containsKey(roomName);
+
+  /// Gets a room by name, or null if not found.
+  RoomName? get(final String roomName) => _rooms[roomName];
+
+  /// Returns all rooms.
+  Iterable get rooms => _rooms.values;
+
+  /// Returns all room names.
+  Iterable get roomNames => _rooms.keys;
+
+  /// Returns the number of rooms.
+  int get length => _rooms.length;
+
+  /// Checks if empty.
+  bool get isEmpty => _rooms.isEmpty;
+
+  /// Checks if not empty.
+  bool get isNotEmpty => _rooms.isNotEmpty;
+
+  /// Clears all rooms.
+  void clear() => _rooms.clear();
+
+  /// Converts to a list of room names.
+  List toList() => _rooms.keys.toList();
+
+  /// Converts to a set of room names.
+  Set toSet() => _rooms.keys.toSet();
+
+  /// Creates a copy.
+  RoomMembership copy() => RoomMembership.fromRooms(_rooms.values);
+
+  @override
+  String toString() => 'RoomMembership(${_rooms.keys.join(', ')})';
+}
+
+/// Represents a collection of sockets in a room.
+///
+/// Tracks which sockets are members of a specific room.
+class RoomSocketCollection {
+  final RoomName roomName;
+  final Set _socketIds;
+
+  /// Creates a new room socket collection.
+  RoomSocketCollection(this.roomName) : _socketIds = {};
+
+  /// Creates from an existing set of socket IDs.
+  RoomSocketCollection.fromIds(this.roomName, final Iterable socketIds)
+      : _socketIds = Set.from(socketIds);
+
+  /// Adds a socket to the room.
+  bool add(final String socketId) => _socketIds.add(socketId);
+
+  /// Removes a socket from the room.
+  bool remove(final String socketId) => _socketIds.remove(socketId);
+
+  /// Checks if a socket is in the room.
+  bool contains(final String socketId) => _socketIds.contains(socketId);
+
+  /// Returns all socket IDs in the room.
+  Set get socketIds => Set.from(_socketIds);
+
+  /// Returns the number of sockets in the room.
+  int get length => _socketIds.length;
+
+  /// Checks if empty.
+  bool get isEmpty => _socketIds.isEmpty;
+
+  /// Checks if not empty.
+  bool get isNotEmpty => _socketIds.isNotEmpty;
+
+  /// Clears all sockets from the room.
+  void clear() => _socketIds.clear();
+
+  /// Creates a copy.
+  RoomSocketCollection copy() => RoomSocketCollection.fromIds(roomName, _socketIds);
+
+  @override
+  String toString() => 'RoomSocketCollection($roomName: ${_socketIds.length} sockets)';
+}
+
+/// Manages room memberships and socket-to-room mappings.
+///
+/// Provides efficient bidirectional lookup between sockets and rooms.
+class RoomManager {
+  // Socket ID -> RoomMembership
+  final Map _socketRooms;
+
+  // Room name -> Set of socket IDs
+  final Map> _roomSockets;
+
+  /// Creates a new room manager.
+  RoomManager()
+      : _socketRooms = {},
+        _roomSockets = >{};
+
+  /// Adds a socket to a room.
+  void join(final String socketId, final RoomName room) {
+    // Add to socket's room list
+    _socketRooms
+        .putIfAbsent(
+          socketId,
+          RoomMembership.new,
+        )
+        .add(room);
+
+    // Add to room's socket list
+    _roomSockets
+        .putIfAbsent(
+          room.value,
+          () => {},
+        )
+        .add(socketId);
+  }
+
+  /// Adds a socket to a room by name.
+  void joinByName(final String socketId, final String roomName) {
+    join(socketId, RoomName(roomName));
+  }
+
+  /// Removes a socket from a room.
+  void leave(final String socketId, final RoomName room) {
+    // Remove from socket's room list
+    final RoomMembership? membership = _socketRooms[socketId];
+    if (membership != null) {
+      membership.remove(room);
+      if (membership.isEmpty) {
+        _socketRooms.remove(socketId);
+      }
+    }
+
+    // Remove from room's socket list
+    final Set? sockets = _roomSockets[room.value];
+    if (sockets != null) {
+      sockets.remove(socketId);
+      if (sockets.isEmpty) {
+        _roomSockets.remove(room.value);
+      }
+    }
+  }
+
+  /// Removes a socket from a room by name.
+  void leaveByName(final String socketId, final String roomName) {
+    leave(socketId, RoomName(roomName));
+  }
+
+  /// Removes a socket from all rooms.
+  void leaveAll(final String socketId) {
+    final RoomMembership? membership = _socketRooms.remove(socketId);
+    if (membership != null) {
+      for (final RoomName room in membership.rooms) {
+        final Set? sockets = _roomSockets[room.value];
+        if (sockets != null) {
+          sockets.remove(socketId);
+          if (sockets.isEmpty) {
+            _roomSockets.remove(room.value);
+          }
+        }
+      }
+    }
+  }
+
+  /// Gets all rooms for a socket.
+  RoomMembership? getRoomsForSocket(final String socketId) => _socketRooms[socketId]?.copy();
+
+  /// Gets all sockets in a room.
+  Set getSocketsInRoom(final RoomName room) {
+    final Set? sockets = _roomSockets[room.value];
+    return sockets != null ? Set.from(sockets) : {};
+  }
+
+  /// Gets all sockets in a room by name.
+  Set getSocketsInRoomByName(final String roomName) => getSocketsInRoom(RoomName(roomName));
+
+  /// Checks if a socket is in a room.
+  bool isInRoom(final String socketId, final RoomName room) {
+    final RoomMembership? membership = _socketRooms[socketId];
+    return membership?.contains(room) ?? false;
+  }
+
+  /// Checks if a socket is in a room by name.
+  bool isInRoomByName(final String socketId, final String roomName) => isInRoom(socketId, RoomName(roomName));
+
+  /// Gets all room names.
+  Iterable get allRoomNames => _roomSockets.keys;
+
+  /// Gets all rooms.
+  Iterable get allRooms => _roomSockets.keys.map(RoomName.new);
+
+  /// Gets the number of rooms.
+  int get roomCount => _roomSockets.length;
+
+  /// Gets the number of sockets being tracked.
+  int get socketCount => _socketRooms.length;
+
+  /// Clears all room data.
+  void clear() {
+    _socketRooms.clear();
+    _roomSockets.clear();
+  }
+
+  @override
+  String toString() => 'RoomManager($roomCount rooms, $socketCount sockets)';
+}
diff --git a/lib/src/models/server_configuration_models.dart b/lib/src/models/server_configuration_models.dart
new file mode 100644
index 0000000..f7828c2
--- /dev/null
+++ b/lib/src/models/server_configuration_models.dart
@@ -0,0 +1,460 @@
+/// server_configuration_models.dart
+///
+/// Typed configuration models for Socket.IO server to replace Map usage.
+/// These models provide type safety, validation, and better API design.
+library server_configuration_models;
+
+import 'package:meta/meta.dart';
+
+/// Configuration model for Socket.IO server options.
+/// Replaces the Map options parameter in Server constructor.
+@immutable
+class ServerConfigurationModel {
+  /// The path where the Socket.IO client files are served.
+  final String path;
+
+  /// Whether to serve the client files.
+  final bool serveClient;
+
+  /// The adapter to use for handling connections.
+  final String adapter;
+
+  /// The allowed origins for cross-origin requests.
+  final String origins;
+
+  /// Additional engine configuration options.
+  final EngineConfigurationModel? engineConfig;
+
+  /// Cookie configuration for the server.
+  final CookieConfigurationModel? cookieConfig;
+
+  /// Compression configuration for the server.
+  final CompressionConfigurationModel? compressionConfig;
+
+  /// Creates a new server configuration model.
+  ///
+  /// [path] defaults to '/socket.io'
+  /// [serveClient] defaults to true
+  /// [adapter] defaults to 'default'
+  /// [origins] defaults to '*:*'
+  const ServerConfigurationModel({
+    this.path = '/socket.io',
+    this.serveClient = true,
+    this.adapter = 'default',
+    this.origins = '*:*',
+    this.engineConfig,
+    this.cookieConfig,
+    this.compressionConfig,
+  });
+
+  /// Creates a server configuration from a legacy Map.
+  /// Used for backward compatibility during migration.
+  factory ServerConfigurationModel.fromMap(final Map map) => ServerConfigurationModel(
+        path: map['path'] as String? ?? '/socket.io',
+        serveClient: map['serveClient'] as bool? ?? true,
+        adapter: map['adapter'] as String? ?? 'default',
+        origins: map['origins'] as String? ?? '*:*',
+        engineConfig:
+            map.containsKey('engine') ? EngineConfigurationModel.fromMap(map['engine'] as Map) : null,
+        cookieConfig:
+            map.containsKey('cookie') ? CookieConfigurationModel.fromMap(map['cookie'] as Map) : null,
+        compressionConfig: map.containsKey('compression')
+            ? CompressionConfigurationModel.fromMap(map['compression'] as Map)
+            : null,
+      );
+
+  /// Converts this configuration to a Map for backward compatibility.
+  Map toMap() {
+    final Map map = {
+      'path': path,
+      'serveClient': serveClient,
+      'adapter': adapter,
+      'origins': origins,
+    };
+
+    if (engineConfig != null) {
+      map['engine'] = engineConfig!.toMap();
+    }
+    if (cookieConfig != null) {
+      map['cookie'] = cookieConfig!.toMap();
+    }
+    if (compressionConfig != null) {
+      map['compression'] = compressionConfig!.toMap();
+    }
+
+    return map;
+  }
+
+  /// Creates a copy of this configuration with optional parameter overrides.
+  ServerConfigurationModel copyWith({
+    final String? path,
+    final bool? serveClient,
+    final String? adapter,
+    final String? origins,
+    final EngineConfigurationModel? engineConfig,
+    final CookieConfigurationModel? cookieConfig,
+    final CompressionConfigurationModel? compressionConfig,
+  }) =>
+      ServerConfigurationModel(
+        path: path ?? this.path,
+        serveClient: serveClient ?? this.serveClient,
+        adapter: adapter ?? this.adapter,
+        origins: origins ?? this.origins,
+        engineConfig: engineConfig ?? this.engineConfig,
+        cookieConfig: cookieConfig ?? this.cookieConfig,
+        compressionConfig: compressionConfig ?? this.compressionConfig,
+      );
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is ServerConfigurationModel &&
+          runtimeType == other.runtimeType &&
+          path == other.path &&
+          serveClient == other.serveClient &&
+          adapter == other.adapter &&
+          origins == other.origins &&
+          engineConfig == other.engineConfig &&
+          cookieConfig == other.cookieConfig &&
+          compressionConfig == other.compressionConfig;
+
+  @override
+  int get hashCode =>
+      path.hashCode ^
+      serveClient.hashCode ^
+      adapter.hashCode ^
+      origins.hashCode ^
+      engineConfig.hashCode ^
+      cookieConfig.hashCode ^
+      compressionConfig.hashCode;
+
+  @override
+  String toString() => 'ServerConfigurationModel('
+      'path: $path, '
+      'serveClient: $serveClient, '
+      'adapter: $adapter, '
+      'origins: $origins, '
+      'engineConfig: $engineConfig, '
+      'cookieConfig: $cookieConfig, '
+      'compressionConfig: $compressionConfig)';
+}
+
+/// Configuration model for Engine.IO options.
+@immutable
+class EngineConfigurationModel {
+  /// Ping timeout in milliseconds.
+  final int pingTimeout;
+
+  /// Ping interval in milliseconds.
+  final int pingInterval;
+
+  /// Maximum HTTP buffer size.
+  final int maxHttpBufferSize;
+
+  /// Whether to allow upgrades from polling to websocket.
+  final bool allowUpgrades;
+
+  /// The allowed transports.
+  final List transports;
+
+  /// Creates a new engine configuration model.
+  const EngineConfigurationModel({
+    this.pingTimeout = 60000,
+    this.pingInterval = 25000,
+    this.maxHttpBufferSize = 100000,
+    this.allowUpgrades = true,
+    this.transports = const ['websocket', 'polling'],
+  });
+
+  /// Creates an engine configuration from a Map.
+  factory EngineConfigurationModel.fromMap(final Map map) => EngineConfigurationModel(
+        pingTimeout: map['pingTimeout'] as int? ?? 60000,
+        pingInterval: map['pingInterval'] as int? ?? 25000,
+        maxHttpBufferSize: map['maxHttpBufferSize'] as int? ?? 100000,
+        allowUpgrades: map['allowUpgrades'] as bool? ?? true,
+        transports: (map['transports'] as List?)?.map((final dynamic e) => e.toString()).toList() ??
+            const ['websocket', 'polling'],
+      );
+
+  /// Converts this configuration to a Map.
+  Map toMap() => {
+        'pingTimeout': pingTimeout,
+        'pingInterval': pingInterval,
+        'maxHttpBufferSize': maxHttpBufferSize,
+        'allowUpgrades': allowUpgrades,
+        'transports': transports,
+      };
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is EngineConfigurationModel &&
+          runtimeType == other.runtimeType &&
+          pingTimeout == other.pingTimeout &&
+          pingInterval == other.pingInterval &&
+          maxHttpBufferSize == other.maxHttpBufferSize &&
+          allowUpgrades == other.allowUpgrades &&
+          transports.toString() == other.transports.toString();
+
+  @override
+  int get hashCode =>
+      pingTimeout.hashCode ^
+      pingInterval.hashCode ^
+      maxHttpBufferSize.hashCode ^
+      allowUpgrades.hashCode ^
+      transports.hashCode;
+
+  @override
+  String toString() => 'EngineConfigurationModel('
+      'pingTimeout: $pingTimeout, '
+      'pingInterval: $pingInterval, '
+      'maxHttpBufferSize: $maxHttpBufferSize, '
+      'allowUpgrades: $allowUpgrades, '
+      'transports: $transports)';
+}
+
+/// Configuration model for cookie options.
+@immutable
+class CookieConfigurationModel {
+  /// Cookie name.
+  final String name;
+
+  /// Cookie value.
+  final String? value;
+
+  /// Cookie domain.
+  final String? domain;
+
+  /// Cookie path.
+  final String? path;
+
+  /// Whether the cookie is HTTP-only.
+  final bool httpOnly;
+
+  /// Whether the cookie requires HTTPS.
+  final bool secure;
+
+  /// Cookie same-site policy.
+  final String? sameSite;
+
+  /// Creates a new cookie configuration model.
+  const CookieConfigurationModel({
+    this.name = 'io',
+    this.value,
+    this.domain,
+    this.path,
+    this.httpOnly = false,
+    this.secure = false,
+    this.sameSite,
+  });
+
+  /// Creates a cookie configuration from a Map.
+  factory CookieConfigurationModel.fromMap(final Map map) => CookieConfigurationModel(
+        name: map['name'] as String? ?? 'io',
+        value: map['value'] as String?,
+        domain: map['domain'] as String?,
+        path: map['path'] as String?,
+        httpOnly: map['httpOnly'] as bool? ?? false,
+        secure: map['secure'] as bool? ?? false,
+        sameSite: map['sameSite'] as String?,
+      );
+
+  /// Converts this configuration to a Map.
+  Map toMap() {
+    final Map map = {
+      'name': name,
+      'httpOnly': httpOnly,
+      'secure': secure,
+    };
+
+    if (value != null) map['value'] = value;
+    if (domain != null) map['domain'] = domain;
+    if (path != null) map['path'] = path;
+    if (sameSite != null) map['sameSite'] = sameSite;
+
+    return map;
+  }
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is CookieConfigurationModel &&
+          runtimeType == other.runtimeType &&
+          name == other.name &&
+          value == other.value &&
+          domain == other.domain &&
+          path == other.path &&
+          httpOnly == other.httpOnly &&
+          secure == other.secure &&
+          sameSite == other.sameSite;
+
+  @override
+  int get hashCode =>
+      name.hashCode ^
+      value.hashCode ^
+      domain.hashCode ^
+      path.hashCode ^
+      httpOnly.hashCode ^
+      secure.hashCode ^
+      sameSite.hashCode;
+
+  @override
+  String toString() => 'CookieConfigurationModel('
+      'name: $name, '
+      'value: $value, '
+      'domain: $domain, '
+      'path: $path, '
+      'httpOnly: $httpOnly, '
+      'secure: $secure, '
+      'sameSite: $sameSite)';
+}
+
+/// Configuration model for compression options.
+@immutable
+class CompressionConfigurationModel {
+  /// Whether to enable per-message deflate compression for WebSocket.
+  final bool perMessageDeflate;
+
+  /// Whether to enable HTTP compression.
+  final bool httpCompression;
+
+  /// Compression threshold in bytes.
+  final int threshold;
+
+  /// Creates a new compression configuration model.
+  const CompressionConfigurationModel({
+    this.perMessageDeflate = true,
+    this.httpCompression = true,
+    this.threshold = 1024,
+  });
+
+  /// Creates a compression configuration from a Map.
+  factory CompressionConfigurationModel.fromMap(final Map map) => CompressionConfigurationModel(
+        perMessageDeflate: map['perMessageDeflate'] as bool? ?? true,
+        httpCompression: map['httpCompression'] as bool? ?? true,
+        threshold: map['threshold'] as int? ?? 1024,
+      );
+
+  /// Converts this configuration to a Map.
+  Map toMap() => {
+        'perMessageDeflate': perMessageDeflate,
+        'httpCompression': httpCompression,
+        'threshold': threshold,
+      };
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is CompressionConfigurationModel &&
+          runtimeType == other.runtimeType &&
+          perMessageDeflate == other.perMessageDeflate &&
+          httpCompression == other.httpCompression &&
+          threshold == other.threshold;
+
+  @override
+  int get hashCode => perMessageDeflate.hashCode ^ httpCompression.hashCode ^ threshold.hashCode;
+
+  @override
+  String toString() => 'CompressionConfigurationModel('
+      'perMessageDeflate: $perMessageDeflate, '
+      'httpCompression: $httpCompression, '
+      'threshold: $threshold)';
+}
+
+/// Legacy settings model to replace the global oldSettings map.
+@immutable
+class LegacySettingsModel {
+  /// Map of old setting names to new setting names.
+  static const Map settingsMapping = {
+    'transports': 'transports',
+    'heartbeat timeout': 'pingTimeout',
+    'heartbeat interval': 'pingInterval',
+    'destroy buffer size': 'maxHttpBufferSize',
+  };
+
+  /// Gets the modern setting name for a legacy setting.
+  static String? getModernSetting(final String legacySetting) => settingsMapping[legacySetting];
+
+  /// Gets all legacy setting names.
+  static List get legacySettings => settingsMapping.keys.toList();
+
+  /// Gets all modern setting names.
+  static List get modernSettings => settingsMapping.values.toList();
+}
+
+/// Origins configuration model for origin validation.
+@immutable
+class OriginsConfigurationModel {
+  /// The allowed origins pattern.
+  final String pattern;
+
+  /// Whether to allow all origins.
+  final bool allowAll;
+
+  /// List of specific allowed origins.
+  final List allowedOrigins;
+
+  /// Creates a new origins configuration model.
+  const OriginsConfigurationModel({
+    this.pattern = '*:*',
+    this.allowAll = true,
+    this.allowedOrigins = const [],
+  });
+
+  /// Creates an origins configuration from a string pattern.
+  factory OriginsConfigurationModel.fromPattern(final String pattern) => OriginsConfigurationModel(
+        pattern: pattern,
+        allowAll: pattern == '*:*',
+        allowedOrigins:
+            pattern == '*:*' ? const [] : pattern.split(' ').where((final String s) => s.isNotEmpty).toList(),
+      );
+
+  /// Checks if an origin is allowed.
+  bool isOriginAllowed(String origin) {
+    if (allowAll) return true;
+    final String normalizedOrigin = origin.isEmpty ? '*' : origin;
+
+    // Handle file:// URLs
+    // Parse origin
+    try {
+      final Uri uri = Uri.parse(normalizedOrigin);
+      final int port = uri.port;
+      final String host = uri.host;
+
+      return allowedOrigins.any((final String allowed) {
+        if (allowed == '*:*') return true;
+
+        final List parts = allowed.split(':');
+        if (parts.length != 2) return false;
+
+        final String allowedHost = parts[0];
+        final String allowedPort = parts[1];
+
+        final bool hostMatches = allowedHost == '*' || allowedHost == host;
+        final bool portMatches = allowedPort == '*' || allowedPort == port.toString();
+
+        return hostMatches && portMatches;
+      });
+    } catch (_) {
+      return false;
+    }
+  }
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is OriginsConfigurationModel &&
+          runtimeType == other.runtimeType &&
+          pattern == other.pattern &&
+          allowAll == other.allowAll &&
+          allowedOrigins.toString() == other.allowedOrigins.toString();
+
+  @override
+  int get hashCode => pattern.hashCode ^ allowAll.hashCode ^ allowedOrigins.hashCode;
+
+  @override
+  String toString() => 'OriginsConfigurationModel('
+      'pattern: $pattern, '
+      'allowAll: $allowAll, '
+      'allowedOrigins: $allowedOrigins)';
+}
diff --git a/lib/src/models/server_options_models.dart b/lib/src/models/server_options_models.dart
new file mode 100644
index 0000000..516cbe2
--- /dev/null
+++ b/lib/src/models/server_options_models.dart
@@ -0,0 +1,352 @@
+// server_options_models.dart
+//
+// Typed models for Engine.IO server configuration options
+//
+// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+
+import '../value_objects/timeout_duration_vo.dart';
+import '../value_objects/transport_name_vo.dart';
+import '../value_objects/url_path_vo.dart';
+
+/// Callback type for request verification
+typedef AllowRequestCallback = bool Function(dynamic request, dynamic Function(dynamic, bool) callback);
+
+/// Cookie configuration - either a string name or disabled (false)
+sealed class CookieConfig {
+  const CookieConfig();
+
+  /// Cookie is enabled with the given name
+  factory CookieConfig.enabled(final String name) = EnabledCookie;
+
+  /// Cookie is disabled
+  factory CookieConfig.disabled() = DisabledCookie;
+
+  /// Check if cookie is enabled
+  bool get isEnabled;
+
+  /// Get the cookie name (null if disabled)
+  String? get name;
+
+  /// Convert to dynamic for backward compatibility
+  dynamic toCompatibility() => switch (this) {
+        EnabledCookie() => (this as EnabledCookie).cookieName,
+        DisabledCookie() => false,
+      };
+
+  /// Create from dynamic value
+  factory CookieConfig.fromDynamic(final dynamic value) {
+    if (value == false || value == null) {
+      return CookieConfig.disabled();
+    }
+    return CookieConfig.enabled(value.toString());
+  }
+}
+
+final class EnabledCookie extends CookieConfig {
+  final String cookieName;
+
+  const EnabledCookie(this.cookieName);
+
+  @override
+  bool get isEnabled => true;
+
+  @override
+  String? get name => cookieName;
+}
+
+final class DisabledCookie extends CookieConfig {
+  const DisabledCookie();
+
+  @override
+  bool get isEnabled => false;
+
+  @override
+  String? get name => null;
+}
+
+/// Cookie path configuration - either a string path or disabled (false)
+sealed class CookiePathConfig {
+  const CookiePathConfig();
+
+  /// Cookie path is enabled with the given path
+  factory CookiePathConfig.enabled(final String path) = EnabledCookiePath;
+
+  /// Cookie path is disabled
+  factory CookiePathConfig.disabled() = DisabledCookiePath;
+
+  /// Check if cookie path is enabled
+  bool get isEnabled;
+
+  /// Get the cookie path (null if disabled)
+  String? get path;
+
+  /// Convert to dynamic for backward compatibility
+  dynamic toCompatibility() => switch (this) {
+        EnabledCookiePath() => (this as EnabledCookiePath).cookiePath,
+        DisabledCookiePath() => false,
+      };
+
+  /// Create from dynamic value
+  factory CookiePathConfig.fromDynamic(final dynamic value) {
+    if (value == false || value == null) {
+      return CookiePathConfig.disabled();
+    }
+    return CookiePathConfig.enabled(value.toString());
+  }
+}
+
+final class EnabledCookiePath extends CookiePathConfig {
+  final String cookiePath;
+
+  const EnabledCookiePath(this.cookiePath);
+
+  @override
+  bool get isEnabled => true;
+
+  @override
+  String? get path => cookiePath;
+}
+
+final class DisabledCookiePath extends CookiePathConfig {
+  const DisabledCookiePath();
+
+  @override
+  bool get isEnabled => false;
+
+  @override
+  String? get path => null;
+}
+
+/// Per-message deflate compression configuration
+class PerMessageDeflateConfig {
+  /// Threshold in bytes below which messages are not compressed
+  final int threshold;
+
+  /// Additional configuration options
+  final Map additionalOptions;
+
+  const PerMessageDeflateConfig({
+    this.threshold = 1024,
+    this.additionalOptions = const {},
+  });
+
+  /// Create from Map for backward compatibility
+  factory PerMessageDeflateConfig.fromMap(final Map? map) {
+    if (map == null || map.isEmpty) {
+      return const PerMessageDeflateConfig();
+    }
+
+    final int threshold = map['threshold'] as int? ?? 1024;
+    final Map additional = Map.from(map)..remove('threshold');
+
+    return PerMessageDeflateConfig(
+      threshold: threshold,
+      additionalOptions: additional,
+    );
+  }
+
+  /// Convert to Map for backward compatibility
+  Map toMap() => {
+        'threshold': threshold,
+        ...additionalOptions,
+      };
+}
+
+/// HTTP compression configuration
+class HttpCompressionConfig {
+  /// Threshold in bytes below which responses are not compressed
+  final int threshold;
+
+  /// Additional configuration options
+  final Map additionalOptions;
+
+  const HttpCompressionConfig({
+    this.threshold = 1024,
+    this.additionalOptions = const {},
+  });
+
+  /// Create from Map for backward compatibility
+  factory HttpCompressionConfig.fromMap(final Map? map) {
+    if (map == null || map.isEmpty) {
+      return const HttpCompressionConfig();
+    }
+
+    final int threshold = map['threshold'] as int? ?? 1024;
+    final Map additional = Map.from(map)..remove('threshold');
+
+    return HttpCompressionConfig(
+      threshold: threshold,
+      additionalOptions: additional,
+    );
+  }
+
+  /// Convert to Map for backward compatibility
+  Map toMap() => {
+        'threshold': threshold,
+        ...additionalOptions,
+      };
+}
+
+/// Server configuration options with proper typing
+class ServerOptionsModel {
+  /// The ping timeout duration
+  final TimeoutDuration pingTimeout;
+
+  /// The ping interval duration
+  final TimeoutDuration pingInterval;
+
+  /// The upgrade timeout duration
+  final TimeoutDuration upgradeTimeout;
+
+  /// Maximum buffer size for HTTP requests in bytes
+  final double maxHttpBufferSize;
+
+  /// List of allowed transports
+  final List transports;
+
+  /// Whether to allow transport upgrades
+  final bool allowUpgrades;
+
+  /// Custom request verification function
+  final AllowRequestCallback? allowRequest;
+
+  /// Cookie configuration
+  final CookieConfig cookie;
+
+  /// Cookie path configuration
+  final CookiePathConfig cookiePath;
+
+  /// Whether cookie should be HTTP only
+  final bool cookieHttpOnly;
+
+  /// Per-message deflate configuration
+  final PerMessageDeflateConfig perMessageDeflate;
+
+  /// HTTP compression configuration
+  final HttpCompressionConfig httpCompression;
+
+  /// Initial packet to send (can be any serializable data)
+  final Object? initialPacket;
+
+  /// Path for the engine.io endpoint
+  final UrlPath path;
+
+  ServerOptionsModel({
+    final TimeoutDuration? pingTimeout,
+    final TimeoutDuration? pingInterval,
+    final TimeoutDuration? upgradeTimeout,
+    this.maxHttpBufferSize = 10E7,
+    this.transports = const [TransportName.polling, TransportName.websocket],
+    this.allowUpgrades = true,
+    this.allowRequest,
+    final CookieConfig? cookie,
+    final CookiePathConfig? cookiePath,
+    this.cookieHttpOnly = true,
+    final PerMessageDeflateConfig? perMessageDeflate,
+    final HttpCompressionConfig? httpCompression,
+    this.initialPacket,
+    final UrlPath? path,
+  })  : pingTimeout = pingTimeout ?? TimeoutDuration.milliseconds(60000),
+        pingInterval = pingInterval ?? TimeoutDuration.milliseconds(25000),
+        upgradeTimeout = upgradeTimeout ?? TimeoutDuration.milliseconds(10000),
+        cookie = cookie ?? const EnabledCookie('io'),
+        cookiePath = cookiePath ?? const EnabledCookiePath('/'),
+        perMessageDeflate = perMessageDeflate ?? const PerMessageDeflateConfig(),
+        httpCompression = httpCompression ?? const HttpCompressionConfig(),
+        path = path ?? UrlPath('/engine.io');
+
+  /// Create ServerOptionsModel from a Map (for backward compatibility)
+  factory ServerOptionsModel.fromMap(final Map? opts) {
+    final Map options = opts ?? {};
+
+    // Handle perMessageDeflate
+    final PerMessageDeflateConfig perMessageDeflate;
+    if (!options.containsKey('perMessageDeflate') || options['perMessageDeflate'] == true) {
+      final dynamic deflateValue = options['perMessageDeflate'];
+      if (deflateValue is Map) {
+        perMessageDeflate = PerMessageDeflateConfig.fromMap(Map.from(deflateValue));
+      } else {
+        perMessageDeflate = const PerMessageDeflateConfig();
+      }
+    } else {
+      perMessageDeflate = const PerMessageDeflateConfig(threshold: 1024);
+    }
+
+    // Handle httpCompression
+    final HttpCompressionConfig httpCompression = options['httpCompression'] is Map
+        ? HttpCompressionConfig.fromMap(Map.from(options['httpCompression'] as Map))
+        : const HttpCompressionConfig();
+
+    // Handle transports
+    final List transports = options['transports'] is List
+        ? (options['transports'] as List)
+            .map((final dynamic t) => TransportName.fromString(t.toString()))
+            .toList()
+        : const [TransportName.polling, TransportName.websocket];
+
+    return ServerOptionsModel(
+      pingTimeout: TimeoutDuration.milliseconds(options['pingTimeout'] as int? ?? 60000),
+      pingInterval: TimeoutDuration.milliseconds(options['pingInterval'] as int? ?? 25000),
+      upgradeTimeout: TimeoutDuration.milliseconds(options['upgradeTimeout'] as int? ?? 10000),
+      maxHttpBufferSize: options['maxHttpBufferSize'] as double? ?? 10E7,
+      transports: transports,
+      allowUpgrades: options['allowUpgrades'] != false,
+      allowRequest: options['allowRequest'] as AllowRequestCallback?,
+      cookie: CookieConfig.fromDynamic(options['cookie'] ?? 'io'),
+      cookiePath: CookiePathConfig.fromDynamic(options['cookiePath'] ?? '/'),
+      cookieHttpOnly: options['cookieHttpOnly'] != false,
+      perMessageDeflate: perMessageDeflate,
+      httpCompression: httpCompression,
+      initialPacket: options['initialPacket'],
+      path: UrlPath(options['path'] as String? ?? '/engine.io'),
+    );
+  }
+
+  /// Convert back to Map for backward compatibility
+  Map toMap() => {
+        'pingTimeout': pingTimeout.inMilliseconds,
+        'pingInterval': pingInterval.inMilliseconds,
+        'upgradeTimeout': upgradeTimeout.inMilliseconds,
+        'maxHttpBufferSize': maxHttpBufferSize,
+        'transports': transports.map((final TransportName t) => t.value).toList(),
+        'allowUpgrades': allowUpgrades,
+        'allowRequest': allowRequest,
+        'cookie': cookie.toCompatibility(),
+        'cookiePath': cookiePath.toCompatibility(),
+        'cookieHttpOnly': cookieHttpOnly,
+        'perMessageDeflate': perMessageDeflate.toMap(),
+        'httpCompression': httpCompression.toMap(),
+        'initialPacket': initialPacket,
+        'path': path.value,
+      };
+}
+
+/// Attachment options for server configuration
+class AttachmentOptionsModel {
+  /// Path for the engine.io endpoint
+  final UrlPath path;
+
+  /// Additional custom options
+  final Map custom;
+
+  AttachmentOptionsModel({
+    final UrlPath? path,
+    this.custom = const {},
+  }) : path = path ?? UrlPath('/engine.io');
+
+  factory AttachmentOptionsModel.fromMap(final Map? opts) {
+    final Map options = opts ?? {};
+    final String pathValue = options['path'] as String? ?? '/engine.io';
+    final Map custom = Map.from(options)..remove('path');
+
+    return AttachmentOptionsModel(
+      path: UrlPath(pathValue),
+      custom: custom,
+    );
+  }
+
+  Map toMap() => {
+        'path': path.value,
+        ...custom,
+      };
+}
diff --git a/lib/src/models/socket_configuration_models.dart b/lib/src/models/socket_configuration_models.dart
new file mode 100644
index 0000000..25c05da
--- /dev/null
+++ b/lib/src/models/socket_configuration_models.dart
@@ -0,0 +1,753 @@
+/// socket_configuration_models.dart
+///
+/// Typed configuration models for Socket.IO socket to replace Map handshake,
+/// Map flags, and other socket-related dynamic types with proper model classes.
+library socket_configuration_models;
+
+import 'package:meta/meta.dart';
+import 'client_configuration_models.dart';
+
+/// Configuration model for socket handshake data.
+/// Replaces Map handshake throughout the codebase.
+@immutable
+class HandshakeModel {
+  /// The socket ID assigned during handshake.
+  final String id;
+
+  /// Headers sent during the handshake.
+  final Map headers;
+
+  /// Query parameters from the handshake.
+  final QueryParametersModel query;
+
+  /// Authentication data if provided.
+  final Map auth;
+
+  /// Client address information.
+  final String address;
+
+  /// Whether the connection is secure (HTTPS/WSS).
+  final bool secure;
+
+  /// The time when the handshake was performed.
+  final DateTime handshakeTime;
+
+  /// User agent information.
+  final String? userAgent;
+
+  /// Referer information.
+  final String? referer;
+
+  /// Origin of the request.
+  final String? origin;
+
+  /// Additional custom data from the handshake.
+  final Map customData;
+
+  /// Creates a new handshake model.
+  const HandshakeModel({
+    required this.id,
+    this.headers = const {},
+    this.query = const QueryParametersModel.empty(),
+    this.auth = const {},
+    required this.address,
+    this.secure = false,
+    required this.handshakeTime,
+    this.userAgent,
+    this.referer,
+    this.origin,
+    this.customData = const {},
+  });
+
+  /// Creates a handshake model from legacy Map.
+  factory HandshakeModel.fromMap(final Map map) => HandshakeModel(
+        id: map['id'] as String? ?? '',
+        headers: _parseHeaders(map['headers']),
+        query: QueryParametersModel.fromMap(map['query'] as Map?),
+        auth: map['auth'] as Map? ?? {},
+        address: map['address'] as String? ?? '',
+        secure: map['secure'] as bool? ?? false,
+        handshakeTime: _parseDateTime(map['time']) ?? DateTime.now(),
+        userAgent: map['userAgent'] as String?,
+        referer: map['referer'] as String?,
+        origin: map['origin'] as String?,
+        customData: _extractCustomData(map),
+      );
+
+  /// Creates a handshake model from an HTTP request context.
+  factory HandshakeModel.fromRequest({
+    required final String id,
+    required final String address,
+    final Map headers = const {},
+    final QueryParametersModel query = const QueryParametersModel.empty(),
+    final Map auth = const {},
+    final bool secure = false,
+  }) =>
+      HandshakeModel(
+        id: id,
+        headers: headers,
+        query: query,
+        auth: auth,
+        address: address,
+        secure: secure,
+        handshakeTime: DateTime.now(),
+        userAgent: headers['user-agent'],
+        referer: headers['referer'],
+        origin: headers['origin'],
+      );
+
+  /// Gets a header value by name (case-insensitive).
+  String? getHeader(final String name) {
+    final String lowerName = name.toLowerCase();
+    for (final MapEntry entry in headers.entries) {
+      if (entry.key.toLowerCase() == lowerName) {
+        return entry.value;
+      }
+    }
+    return null;
+  }
+
+  /// Gets authentication data by key.
+  T? getAuth(final String key) => auth[key] as T?;
+
+  /// Checks if authentication data exists.
+  bool hasAuth(final String key) => auth.containsKey(key);
+
+  /// Gets custom data by key.
+  T? getCustomData(final String key) => customData[key] as T?;
+
+  /// Checks if custom data exists.
+  bool hasCustomData(final String key) => customData.containsKey(key);
+
+  /// Converts to a legacy Map for backward compatibility.
+  Map toMap() {
+    final Map map = {
+      'id': id,
+      'headers': headers,
+      'query': query.toMap(),
+      'auth': auth,
+      'address': address,
+      'secure': secure,
+      'time': handshakeTime.millisecondsSinceEpoch,
+    };
+
+    if (userAgent != null) map['userAgent'] = userAgent;
+    if (referer != null) map['referer'] = referer;
+    if (origin != null) map['origin'] = origin;
+
+    // Add custom data to the map
+    map.addAll(customData);
+
+    return map;
+  }
+
+  /// Creates a copy with optional parameter overrides.
+  HandshakeModel copyWith({
+    final String? id,
+    final Map? headers,
+    final QueryParametersModel? query,
+    final Map? auth,
+    final String? address,
+    final bool? secure,
+    final DateTime? handshakeTime,
+    final String? userAgent,
+    final String? referer,
+    final String? origin,
+    final Map? customData,
+  }) =>
+      HandshakeModel(
+        id: id ?? this.id,
+        headers: headers ?? this.headers,
+        query: query ?? this.query,
+        auth: auth ?? this.auth,
+        address: address ?? this.address,
+        secure: secure ?? this.secure,
+        handshakeTime: handshakeTime ?? this.handshakeTime,
+        userAgent: userAgent ?? this.userAgent,
+        referer: referer ?? this.referer,
+        origin: origin ?? this.origin,
+        customData: customData ?? this.customData,
+      );
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is HandshakeModel &&
+          runtimeType == other.runtimeType &&
+          id == other.id &&
+          _mapsEqual(headers, other.headers) &&
+          query == other.query &&
+          _mapsEqual(auth, other.auth) &&
+          address == other.address &&
+          secure == other.secure &&
+          handshakeTime == other.handshakeTime &&
+          userAgent == other.userAgent &&
+          referer == other.referer &&
+          origin == other.origin &&
+          _mapsEqual(customData, other.customData);
+
+  @override
+  int get hashCode =>
+      id.hashCode ^
+      headers.hashCode ^
+      query.hashCode ^
+      auth.hashCode ^
+      address.hashCode ^
+      secure.hashCode ^
+      handshakeTime.hashCode ^
+      userAgent.hashCode ^
+      referer.hashCode ^
+      origin.hashCode ^
+      customData.hashCode;
+
+  @override
+  String toString() => 'HandshakeModel('
+      'id: $id, '
+      'address: $address, '
+      'secure: $secure, '
+      'handshakeTime: $handshakeTime, '
+      'headers: ${headers.length}, '
+      'query: $query, '
+      'auth: ${auth.length}, '
+      'customData: ${customData.length})';
+
+  // Helper methods
+  static Map _parseHeaders(final dynamic headers) {
+    if (headers is Map) return headers;
+    if (headers is Map) {
+      return headers.map((final dynamic k, final dynamic v) => MapEntry(k.toString(), v.toString()));
+    }
+    return {};
+  }
+
+  static DateTime? _parseDateTime(final dynamic time) {
+    if (time is DateTime) return time;
+    if (time is int) return DateTime.fromMillisecondsSinceEpoch(time);
+    if (time is String) return DateTime.tryParse(time);
+    return null;
+  }
+
+  static Map _extractCustomData(final Map map) {
+    final Map customData = {};
+    final Set knownKeys = {
+      'id',
+      'headers',
+      'query',
+      'auth',
+      'address',
+      'secure',
+      'time',
+      'userAgent',
+      'referer',
+      'origin'
+    };
+
+    for (final MapEntry entry in map.entries) {
+      if (!knownKeys.contains(entry.key)) {
+        customData[entry.key] = entry.value;
+      }
+    }
+
+    return customData;
+  }
+
+  /// Helper method to compare maps for equality.
+  bool _mapsEqual(final Map a, final Map b) {
+    if (a.length != b.length) return false;
+    for (final MapEntry entry in a.entries) {
+      if (b[entry.key] != entry.value) return false;
+    }
+    return true;
+  }
+}
+
+/// Configuration model for socket flags.
+/// Replaces Map flags with proper typed structure.
+@immutable
+class SocketFlagsModel {
+  /// Whether the message is volatile (not persisted if client is offline).
+  final bool volatile;
+
+  /// Whether to compress the message.
+  final bool compress;
+
+  /// Whether to broadcast to all clients.
+  final bool broadcast;
+
+  /// Whether to include the sender.
+  final bool includeSender;
+
+  /// Whether this is a binary message.
+  final bool binary;
+
+  /// Whether to acknowledge receipt.
+  final bool ack;
+
+  /// Custom flags for extensibility.
+  final Map customFlags;
+
+  /// Creates a new socket flags model.
+  const SocketFlagsModel({
+    this.volatile = false,
+    this.compress = false,
+    this.broadcast = false,
+    this.includeSender = true,
+    this.binary = false,
+    this.ack = false,
+    this.customFlags = const {},
+  });
+
+  /// Creates socket flags from legacy Map.
+  factory SocketFlagsModel.fromMap(final Map? map) {
+    if (map == null || map.isEmpty) {
+      return const SocketFlagsModel();
+    }
+
+    return SocketFlagsModel(
+      volatile: map['volatile'] ?? false,
+      compress: map['compress'] ?? false,
+      broadcast: map['broadcast'] ?? false,
+      includeSender: map['includeSender'] ?? true,
+      binary: map['binary'] ?? false,
+      ack: map['ack'] ?? false,
+      customFlags: _extractCustomFlags(map),
+    );
+  }
+
+  /// Creates volatile socket flags.
+  const SocketFlagsModel.volatile() : this(volatile: true);
+
+  /// Creates compress socket flags.
+  const SocketFlagsModel.compress() : this(compress: true);
+
+  /// Creates broadcast socket flags.
+  const SocketFlagsModel.broadcast() : this(broadcast: true);
+
+  /// Creates binary socket flags.
+  const SocketFlagsModel.binary() : this(binary: true);
+
+  /// Creates acknowledgment socket flags.
+  const SocketFlagsModel.ack() : this(ack: true);
+
+  /// Gets a flag value by name.
+  bool getFlag(final String name) {
+    switch (name) {
+      case 'volatile':
+        return volatile;
+      case 'compress':
+        return compress;
+      case 'broadcast':
+        return broadcast;
+      case 'includeSender':
+        return includeSender;
+      case 'binary':
+        return binary;
+      case 'ack':
+        return ack;
+      default:
+        return customFlags[name] ?? false;
+    }
+  }
+
+  /// Checks if any flags are set.
+  bool get hasAnyFlag => volatile || compress || broadcast || !includeSender || binary || ack || customFlags.isNotEmpty;
+
+  /// Gets all active flags as a list.
+  List get activeFlags {
+    final List flags = [];
+    if (volatile) flags.add('volatile');
+    if (compress) flags.add('compress');
+    if (broadcast) flags.add('broadcast');
+    if (!includeSender) flags.add('excludeSender');
+    if (binary) flags.add('binary');
+    if (ack) flags.add('ack');
+
+    for (final MapEntry entry in customFlags.entries) {
+      if (entry.value) flags.add(entry.key);
+    }
+
+    return flags;
+  }
+
+  /// Converts to legacy Map for backward compatibility.
+  Map toMap() {
+    final Map map = {
+      'volatile': volatile,
+      'compress': compress,
+      'broadcast': broadcast,
+      'includeSender': includeSender,
+      'binary': binary,
+      'ack': ack,
+    }..addAll(customFlags);
+
+    return map;
+  }
+
+  /// Creates a copy with optional parameter overrides.
+  SocketFlagsModel copyWith({
+    final bool? volatile,
+    final bool? compress,
+    final bool? broadcast,
+    final bool? includeSender,
+    final bool? binary,
+    final bool? ack,
+    final Map? customFlags,
+  }) =>
+      SocketFlagsModel(
+        volatile: volatile ?? this.volatile,
+        compress: compress ?? this.compress,
+        broadcast: broadcast ?? this.broadcast,
+        includeSender: includeSender ?? this.includeSender,
+        binary: binary ?? this.binary,
+        ack: ack ?? this.ack,
+        customFlags: customFlags ?? this.customFlags,
+      );
+
+  /// Combines this flags model with another.
+  SocketFlagsModel combine(final SocketFlagsModel other) {
+    final Map combinedCustomFlags = Map.from(customFlags)..addAll(other.customFlags);
+
+    return SocketFlagsModel(
+      volatile: volatile || other.volatile,
+      compress: compress || other.compress,
+      broadcast: broadcast || other.broadcast,
+      includeSender: includeSender && other.includeSender,
+      binary: binary || other.binary,
+      ack: ack || other.ack,
+      customFlags: combinedCustomFlags,
+    );
+  }
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is SocketFlagsModel &&
+          runtimeType == other.runtimeType &&
+          volatile == other.volatile &&
+          compress == other.compress &&
+          broadcast == other.broadcast &&
+          includeSender == other.includeSender &&
+          binary == other.binary &&
+          ack == other.ack &&
+          _mapsEqual(customFlags, other.customFlags);
+
+  @override
+  int get hashCode =>
+      volatile.hashCode ^
+      compress.hashCode ^
+      broadcast.hashCode ^
+      includeSender.hashCode ^
+      binary.hashCode ^
+      ack.hashCode ^
+      customFlags.hashCode;
+
+  @override
+  String toString() => 'SocketFlagsModel('
+      'volatile: $volatile, '
+      'compress: $compress, '
+      'broadcast: $broadcast, '
+      'includeSender: $includeSender, '
+      'binary: $binary, '
+      'ack: $ack, '
+      'customFlags: $customFlags)';
+
+  // Helper methods
+  static Map _extractCustomFlags(final Map map) {
+    final Map customFlags = {};
+    final Set knownFlags = {'volatile', 'compress', 'broadcast', 'includeSender', 'binary', 'ack'};
+
+    for (final MapEntry entry in map.entries) {
+      if (!knownFlags.contains(entry.key)) {
+        customFlags[entry.key] = entry.value;
+      }
+    }
+
+    return customFlags;
+  }
+
+  /// Helper method to compare maps for equality.
+  bool _mapsEqual(final Map a, final Map b) {
+    if (a.length != b.length) return false;
+    for (final MapEntry entry in a.entries) {
+      if (b[entry.key] != entry.value) return false;
+    }
+    return true;
+  }
+}
+
+/// Configuration model for room management.
+/// Replaces roomMap and roomList management with proper typed structure.
+@immutable
+class RoomManagementModel {
+  /// Map of room names to socket IDs.
+  final Map> rooms;
+
+  /// Map of socket IDs to room names they belong to.
+  final Map> socketRooms;
+
+  /// Maximum number of rooms a socket can join.
+  final int maxRoomsPerSocket;
+
+  /// Maximum number of sockets per room.
+  final int maxSocketsPerRoom;
+
+  /// Creates a new room management model.
+  const RoomManagementModel({
+    this.rooms = const >{},
+    this.socketRooms = const >{},
+    this.maxRoomsPerSocket = 100,
+    this.maxSocketsPerRoom = 10000,
+  });
+
+  /// Creates an empty room management model.
+  const RoomManagementModel.empty() : this();
+
+  /// Adds a socket to a room.
+  RoomManagementModel addSocketToRoom(final String socketId, final String roomName) {
+    // Check limits
+    final Set currentRooms = socketRooms[socketId] ?? {};
+    if (currentRooms.length >= maxRoomsPerSocket) {
+      return this; // Limit reached
+    }
+
+    final Set currentSockets = rooms[roomName] ?? {};
+    if (currentSockets.length >= maxSocketsPerRoom) {
+      return this; // Limit reached
+    }
+
+    // Create new maps
+    final Map> newRooms = Map>.from(rooms);
+    final Map> newSocketRooms = Map>.from(socketRooms);
+
+    // Add socket to room
+    final Set roomSockets = Set.from(currentSockets)..add(socketId);
+    newRooms[roomName] = roomSockets;
+
+    // Add room to socket
+    final Set socketRoomSet = Set.from(currentRooms)..add(roomName);
+    newSocketRooms[socketId] = socketRoomSet;
+
+    return copyWith(rooms: newRooms, socketRooms: newSocketRooms);
+  }
+
+  /// Removes a socket from a room.
+  RoomManagementModel removeSocketFromRoom(final String socketId, final String roomName) {
+    final Map> newRooms = Map>.from(rooms);
+    final Map> newSocketRooms = Map>.from(socketRooms);
+
+    // Remove socket from room
+    if (newRooms.containsKey(roomName)) {
+      final Set roomSockets = Set.from(newRooms[roomName]!)..remove(socketId);
+      if (roomSockets.isEmpty) {
+        newRooms.remove(roomName);
+      } else {
+        newRooms[roomName] = roomSockets;
+      }
+    }
+
+    // Remove room from socket
+    if (newSocketRooms.containsKey(socketId)) {
+      final Set socketRoomSet = Set.from(newSocketRooms[socketId]!)..remove(roomName);
+      if (socketRoomSet.isEmpty) {
+        newSocketRooms.remove(socketId);
+      } else {
+        newSocketRooms[socketId] = socketRoomSet;
+      }
+    }
+
+    return copyWith(rooms: newRooms, socketRooms: newSocketRooms);
+  }
+
+  /// Removes a socket from all rooms.
+  RoomManagementModel removeSocket(final String socketId) {
+    final Set socketRoomSet = socketRooms[socketId] ?? {};
+    if (socketRoomSet.isEmpty) return this;
+
+    RoomManagementModel result = this;
+    for (final String roomName in socketRoomSet) {
+      result = result.removeSocketFromRoom(socketId, roomName);
+    }
+    return result;
+  }
+
+  /// Gets all sockets in a room.
+  Set getSocketsInRoom(final String roomName) => Set.from(rooms[roomName] ?? {});
+
+  /// Gets all rooms a socket belongs to.
+  Set getSocketRooms(final String socketId) => Set.from(socketRooms[socketId] ?? {});
+
+  /// Checks if a socket is in a room.
+  bool isSocketInRoom(final String socketId, final String roomName) => rooms[roomName]?.contains(socketId) ?? false;
+
+  /// Gets all room names.
+  Set get allRooms => rooms.keys.toSet();
+
+  /// Gets all socket IDs.
+  Set get allSockets => socketRooms.keys.toSet();
+
+  /// Gets the total number of rooms.
+  int get roomCount => rooms.length;
+
+  /// Gets the total number of sockets.
+  int get socketCount => socketRooms.length;
+
+  /// Checks if the model is empty.
+  bool get isEmpty => rooms.isEmpty && socketRooms.isEmpty;
+
+  /// Converts to legacy format for backward compatibility.
+  Map toLegacyFormat() => {
+        'rooms': rooms.map(
+            (final String room, final Set sockets) => MapEntry>(room, sockets.toList())),
+        'socketRooms': socketRooms.map(
+            (final String socket, final Set rooms) => MapEntry>(socket, rooms.toList())),
+      };
+
+  /// Creates a copy with optional parameter overrides.
+  RoomManagementModel copyWith({
+    final Map>? rooms,
+    final Map>? socketRooms,
+    final int? maxRoomsPerSocket,
+    final int? maxSocketsPerRoom,
+  }) =>
+      RoomManagementModel(
+        rooms: rooms ?? this.rooms,
+        socketRooms: socketRooms ?? this.socketRooms,
+        maxRoomsPerSocket: maxRoomsPerSocket ?? this.maxRoomsPerSocket,
+        maxSocketsPerRoom: maxSocketsPerRoom ?? this.maxSocketsPerRoom,
+      );
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is RoomManagementModel &&
+          runtimeType == other.runtimeType &&
+          _mapSetsEqual(rooms, other.rooms) &&
+          _mapSetsEqual(socketRooms, other.socketRooms) &&
+          maxRoomsPerSocket == other.maxRoomsPerSocket &&
+          maxSocketsPerRoom == other.maxSocketsPerRoom;
+
+  @override
+  int get hashCode => rooms.hashCode ^ socketRooms.hashCode ^ maxRoomsPerSocket.hashCode ^ maxSocketsPerRoom.hashCode;
+
+  @override
+  String toString() => 'RoomManagementModel('
+      'roomCount: $roomCount, '
+      'socketCount: $socketCount, '
+      'maxRoomsPerSocket: $maxRoomsPerSocket, '
+      'maxSocketsPerRoom: $maxSocketsPerRoom)';
+
+  /// Helper method to compare map of sets for equality.
+  bool _mapSetsEqual(final Map> a, final Map> b) {
+    if (a.length != b.length) return false;
+    for (final MapEntry> entry in a.entries) {
+      final Set? bSet = b[entry.key];
+      if (bSet == null || entry.value.length != bSet.length) return false;
+      for (final String item in entry.value) {
+        if (!bSet.contains(item)) return false;
+      }
+    }
+    return true;
+  }
+}
+
+/// Query builder model to replace nested buildQuery() functions.
+/// Provides a clean, testable way to build query strings.
+@immutable
+class QueryBuilderModel {
+  /// Base query parameters.
+  final QueryParametersModel baseQuery;
+
+  /// Additional dynamic parameters.
+  final Map dynamicParams;
+
+  /// Whether to URL encode parameters.
+  final bool urlEncode;
+
+  /// Creates a new query builder model.
+  const QueryBuilderModel({
+    this.baseQuery = const QueryParametersModel.empty(),
+    this.dynamicParams = const {},
+    this.urlEncode = true,
+  });
+
+  /// Creates a query builder from legacy parameters.
+  factory QueryBuilderModel.fromLegacyParams(final Map? params) => QueryBuilderModel(
+        baseQuery: QueryParametersModel.fromMap(params),
+      );
+
+  /// Adds a parameter to the builder.
+  QueryBuilderModel addParameter(final String key, final String value) {
+    final Map newDynamicParams = Map.from(dynamicParams);
+    newDynamicParams[key] = value;
+    return copyWith(dynamicParams: newDynamicParams);
+  }
+
+  /// Adds multiple parameters to the builder.
+  QueryBuilderModel addParameters(final Map params) {
+    final Map newDynamicParams = Map.from(dynamicParams)..addAll(params);
+    return copyWith(dynamicParams: newDynamicParams);
+  }
+
+  /// Removes a parameter from the builder.
+  QueryBuilderModel removeParameter(final String key) {
+    final Map newDynamicParams = Map.from(dynamicParams)..remove(key);
+    return copyWith(dynamicParams: newDynamicParams);
+  }
+
+  /// Builds the final query parameters.
+  QueryParametersModel build() {
+    final Map allParams = Map.from(baseQuery.parameters)..addAll(dynamicParams);
+    return QueryParametersModel(allParams);
+  }
+
+  /// Builds a query string.
+  String buildQueryString() => build().toQueryString();
+
+  /// Gets all parameters as a merged map.
+  Map getAllParameters() {
+    final Map allParams = Map.from(baseQuery.parameters)..addAll(dynamicParams);
+    return allParams;
+  }
+
+  /// Checks if the builder has any parameters.
+  bool get hasParameters => baseQuery.isNotEmpty || dynamicParams.isNotEmpty;
+
+  /// Creates a copy with optional parameter overrides.
+  QueryBuilderModel copyWith({
+    final QueryParametersModel? baseQuery,
+    final Map? dynamicParams,
+    final bool? urlEncode,
+  }) =>
+      QueryBuilderModel(
+        baseQuery: baseQuery ?? this.baseQuery,
+        dynamicParams: dynamicParams ?? this.dynamicParams,
+        urlEncode: urlEncode ?? this.urlEncode,
+      );
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is QueryBuilderModel &&
+          runtimeType == other.runtimeType &&
+          baseQuery == other.baseQuery &&
+          _mapsEqual(dynamicParams, other.dynamicParams) &&
+          urlEncode == other.urlEncode;
+
+  @override
+  int get hashCode => baseQuery.hashCode ^ dynamicParams.hashCode ^ urlEncode.hashCode;
+
+  @override
+  String toString() => 'QueryBuilderModel('
+      'baseQuery: $baseQuery, '
+      'dynamicParams: $dynamicParams, '
+      'urlEncode: $urlEncode)';
+
+  /// Helper method to compare maps for equality.
+  bool _mapsEqual(final Map a, final Map b) {
+    if (a.length != b.length) return false;
+    for (final MapEntry entry in a.entries) {
+      if (b[entry.key] != entry.value) return false;
+    }
+    return true;
+  }
+}
diff --git a/lib/src/models/socket_data_models.dart b/lib/src/models/socket_data_models.dart
new file mode 100644
index 0000000..61ce13f
--- /dev/null
+++ b/lib/src/models/socket_data_models.dart
@@ -0,0 +1,100 @@
+/// socket_data_models.dart
+///
+/// Type-safe models for socket data storage
+///
+/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+library socket_data_models;
+
+/// Type-safe data store for socket-specific data.
+///
+/// Replaces `Map data` with proper type safety.
+class SocketDataModel {
+  final Map _data;
+
+  /// Creates a new empty SocketDataModel.
+  SocketDataModel() : _data = {};
+
+  /// Creates from an existing map.
+  SocketDataModel.fromMap(final Map data) : _data = Map.from(data);
+
+  /// Gets a value by key.
+  Object? operator [](final String key) => _data[key];
+
+  /// Sets a value by key.
+  void operator []=(final String key, final Object? value) {
+    _data[key] = value;
+  }
+
+  /// Gets a string value, or null if not found or wrong type.
+  String? getString(final String key) {
+    final Object? value = _data[key];
+    return value is String ? value : null;
+  }
+
+  /// Gets an int value, or null if not found or wrong type.
+  int? getInt(final String key) {
+    final Object? value = _data[key];
+    return value is int ? value : null;
+  }
+
+  /// Gets a bool value, or null if not found or wrong type.
+  bool? getBool(final String key) {
+    final Object? value = _data[key];
+    return value is bool ? value : null;
+  }
+
+  /// Gets a double value, or null if not found or wrong type.
+  double? getDouble(final String key) {
+    final Object? value = _data[key];
+    return value is double ? value : null;
+  }
+
+  /// Gets a map value, or null if not found or wrong type.
+  Map? getMap(final String key) {
+    final Object? value = _data[key];
+    if (value is Map) return value;
+    if (value is Map) return Map.from(value);
+    return null;
+  }
+
+  /// Gets a list value, or null if not found or wrong type.
+  List? getList(final String key) {
+    final Object? value = _data[key];
+    if (value is List) return value;
+    if (value is List) return List.from(value);
+    return null;
+  }
+
+  /// Checks if a key exists.
+  bool containsKey(final String key) => _data.containsKey(key);
+
+  /// Removes a key.
+  Object? remove(final String key) => _data.remove(key);
+
+  /// Clears all data.
+  void clear() => _data.clear();
+
+  /// Returns all keys.
+  Iterable get keys => _data.keys;
+
+  /// Returns all values.
+  Iterable get values => _data.values;
+
+  /// Returns the number of entries.
+  int get length => _data.length;
+
+  /// Checks if empty.
+  bool get isEmpty => _data.isEmpty;
+
+  /// Checks if not empty.
+  bool get isNotEmpty => _data.isNotEmpty;
+
+  /// Converts to a plain map.
+  Map toMap() => Map.from(_data);
+
+  /// Creates a copy.
+  SocketDataModel copy() => SocketDataModel.fromMap(_data);
+
+  @override
+  String toString() => 'SocketDataModel($_data)';
+}
diff --git a/lib/src/models/socket_error_models.dart b/lib/src/models/socket_error_models.dart
new file mode 100644
index 0000000..04aeb92
--- /dev/null
+++ b/lib/src/models/socket_error_models.dart
@@ -0,0 +1,214 @@
+/// socket_error_models.dart
+///
+/// Type-safe models for Socket.IO errors
+///
+/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+library socket_error_models;
+
+/// Enumeration of error types in Socket.IO.
+enum SocketErrorType {
+  /// Connection error.
+  connection,
+
+  /// Transport error.
+  transport,
+
+  /// Timeout error.
+  timeout,
+
+  /// Authentication error.
+  authentication,
+
+  /// Invalid namespace error.
+  invalidNamespace,
+
+  /// Parse error.
+  parse,
+
+  /// Encoding error.
+  encoding,
+
+  /// Decoding error.
+  decoding,
+
+  /// Unknown/generic error.
+  unknown,
+}
+
+/// Model representing a Socket.IO error.
+class SocketErrorModel implements Exception {
+  /// Type of error.
+  final SocketErrorType type;
+
+  /// Error message.
+  final String message;
+
+  /// Original error object (if any).
+  final Object? originalError;
+
+  /// Stack trace (if available).
+  final StackTrace? stackTrace;
+
+  /// Additional error data.
+  final Map? data;
+
+  const SocketErrorModel({
+    required this.type,
+    required this.message,
+    this.originalError,
+    this.stackTrace,
+    this.data,
+  });
+
+  /// Creates a connection error.
+  factory SocketErrorModel.connection(
+    final String message, {
+    final Object? originalError,
+    final StackTrace? stackTrace,
+  }) =>
+      SocketErrorModel(
+        type: SocketErrorType.connection,
+        message: message,
+        originalError: originalError,
+        stackTrace: stackTrace,
+      );
+
+  /// Creates a transport error.
+  factory SocketErrorModel.transport(
+    final String message, {
+    final Object? originalError,
+    final StackTrace? stackTrace,
+    final Map? data,
+  }) =>
+      SocketErrorModel(
+        type: SocketErrorType.transport,
+        message: message,
+        originalError: originalError,
+        stackTrace: stackTrace,
+        data: data,
+      );
+
+  /// Creates a timeout error.
+  factory SocketErrorModel.timeout(
+    final String message, {
+    final Duration? duration,
+  }) =>
+      SocketErrorModel(
+        type: SocketErrorType.timeout,
+        message: message,
+        data: duration != null ? {'duration': duration.inMilliseconds} : null,
+      );
+
+  /// Creates an authentication error.
+  factory SocketErrorModel.authentication(final String message) => SocketErrorModel(
+        type: SocketErrorType.authentication,
+        message: message,
+      );
+
+  /// Creates an unauthorized error (alias for authentication).
+  factory SocketErrorModel.unauthorized(final String message) => SocketErrorModel(
+        type: SocketErrorType.authentication,
+        message: message,
+      );
+
+  /// Creates an invalid namespace error.
+  factory SocketErrorModel.invalidNamespace(final String namespace) => SocketErrorModel(
+        type: SocketErrorType.invalidNamespace,
+        message: 'Invalid namespace: $namespace',
+        data: {'namespace': namespace},
+      );
+
+  /// Creates a parse error.
+  factory SocketErrorModel.parse(
+    final String message, {
+    final Object? originalError,
+    final StackTrace? stackTrace,
+  }) =>
+      SocketErrorModel(
+        type: SocketErrorType.parse,
+        message: message,
+        originalError: originalError,
+        stackTrace: stackTrace,
+      );
+
+  /// Creates an encoding error.
+  factory SocketErrorModel.encoding(
+    final String message, {
+    final Object? originalError,
+  }) =>
+      SocketErrorModel(
+        type: SocketErrorType.encoding,
+        message: message,
+        originalError: originalError,
+      );
+
+  /// Creates a decoding error.
+  factory SocketErrorModel.decoding(
+    final String message, {
+    final Object? originalError,
+  }) =>
+      SocketErrorModel(
+        type: SocketErrorType.decoding,
+        message: message,
+        originalError: originalError,
+      );
+
+  /// Creates an unknown/generic error.
+  factory SocketErrorModel.unknown(
+    final String message, {
+    final Object? originalError,
+    final StackTrace? stackTrace,
+  }) =>
+      SocketErrorModel(
+        type: SocketErrorType.unknown,
+        message: message,
+        originalError: originalError,
+        stackTrace: stackTrace,
+      );
+
+  /// Creates from a generic Object error.
+  factory SocketErrorModel.fromObject(final Object error, [final StackTrace? stackTrace]) {
+    if (error is SocketErrorModel) return error;
+    if (error is Exception) {
+      return SocketErrorModel(
+        type: SocketErrorType.unknown,
+        message: error.toString(),
+        originalError: error,
+        stackTrace: stackTrace,
+      );
+    }
+    return SocketErrorModel(
+      type: SocketErrorType.unknown,
+      message: error.toString(),
+      originalError: error,
+      stackTrace: stackTrace,
+    );
+  }
+
+  /// Converts to a Map for serialization.
+  Map toMap() => {
+        'type': type.name,
+        'message': message,
+        if (data != null) 'data': data,
+      };
+
+  /// Converts to a JSON-compatible Map for sending over the wire.
+  Map toJson() => toMap();
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is SocketErrorModel && runtimeType == other.runtimeType && type == other.type && message == other.message;
+
+  @override
+  int get hashCode => Object.hash(type, message);
+
+  @override
+  String toString() => 'SocketErrorModel(type: ${type.name}, message: $message)';
+}
+
+/// Extension methods for error handling.
+extension ErrorConversion on Object {
+  /// Converts any object to a SocketErrorModel.
+  SocketErrorModel toSocketError([final StackTrace? stackTrace]) => SocketErrorModel.fromObject(this, stackTrace);
+}
diff --git a/lib/src/models/socket_flags_models.dart b/lib/src/models/socket_flags_models.dart
new file mode 100644
index 0000000..4b4267f
--- /dev/null
+++ b/lib/src/models/socket_flags_models.dart
@@ -0,0 +1,169 @@
+/// socket_flags_models.dart
+///
+/// Type-safe models for Socket.IO socket flags to replace Map
+///
+/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+library socket_flags_models;
+
+/// Socket flags control various aspects of how messages are sent
+///
+/// This model replaces the generic Map with a type-safe,
+/// immutable structure that makes flag management explicit and prevents typos.
+class SocketFlags {
+  /// Whether to use JSON serialization
+  final bool json;
+
+  /// Whether the message is volatile (may be dropped if client is not ready)
+  final bool volatile;
+
+  /// Whether to broadcast to all clients
+  final bool broadcast;
+
+  /// Whether to compress the message
+  final bool compress;
+
+  /// Whether the packet is pre-encoded
+  final bool preEncoded;
+
+  /// Creates socket flags with specified values
+  const SocketFlags({
+    this.json = false,
+    this.volatile = false,
+    this.broadcast = false,
+    this.compress = false,
+    this.preEncoded = false,
+  });
+
+  /// Creates default flags (all false)
+  const SocketFlags.none()
+      : json = false,
+        volatile = false,
+        broadcast = false,
+        compress = false,
+        preEncoded = false;
+
+  /// Creates flags for JSON serialization
+  const SocketFlags.json()
+      : json = true,
+        volatile = false,
+        broadcast = false,
+        compress = false,
+        preEncoded = false;
+
+  /// Creates flags for volatile messages
+  const SocketFlags.volatile()
+      : json = false,
+        volatile = true,
+        broadcast = false,
+        compress = false,
+        preEncoded = false;
+
+  /// Creates flags for broadcast messages
+  const SocketFlags.broadcast()
+      : json = false,
+        volatile = false,
+        broadcast = true,
+        compress = false,
+        preEncoded = false;
+
+  /// Creates flags for compressed messages
+  const SocketFlags.compress()
+      : json = false,
+        volatile = false,
+        broadcast = false,
+        compress = true,
+        preEncoded = false;
+
+  /// Creates flags for pre-encoded packets
+  const SocketFlags.preEncoded()
+      : json = false,
+        volatile = false,
+        broadcast = false,
+        compress = false,
+        preEncoded = true;
+
+  /// Creates from a legacy map (for backward compatibility)
+  factory SocketFlags.fromMap(final Map map) => SocketFlags(
+        json: map['json'] ?? false,
+        volatile: map['volatile'] ?? false,
+        broadcast: map['broadcast'] ?? false,
+        compress: map['compress'] ?? false,
+        preEncoded: map['preEncoded'] ?? false,
+      );
+
+  /// Converts to legacy map format (for backward compatibility)
+  Map toMap() => {
+        if (json) 'json': true,
+        if (volatile) 'volatile': true,
+        if (broadcast) 'broadcast': true,
+        if (compress) 'compress': true,
+        if (preEncoded) 'preEncoded': true,
+      };
+
+  /// Creates a copy with modified flags
+  SocketFlags copyWith({
+    final bool? json,
+    final bool? volatile,
+    final bool? broadcast,
+    final bool? compress,
+    final bool? preEncoded,
+  }) =>
+      SocketFlags(
+        json: json ?? this.json,
+        volatile: volatile ?? this.volatile,
+        broadcast: broadcast ?? this.broadcast,
+        compress: compress ?? this.compress,
+        preEncoded: preEncoded ?? this.preEncoded,
+      );
+
+  /// Checks if any flag is set
+  bool get hasAnyFlag => json || volatile || broadcast || compress || preEncoded;
+
+  /// Checks if no flags are set
+  bool get hasNoFlags => !hasAnyFlag;
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is SocketFlags &&
+          json == other.json &&
+          volatile == other.volatile &&
+          broadcast == other.broadcast &&
+          compress == other.compress &&
+          preEncoded == other.preEncoded;
+
+  @override
+  int get hashCode => Object.hash(json, volatile, broadcast, compress, preEncoded);
+
+  @override
+  String toString() {
+    final List flags = [];
+    if (json) flags.add('json');
+    if (volatile) flags.add('volatile');
+    if (broadcast) flags.add('broadcast');
+    if (compress) flags.add('compress');
+    if (preEncoded) flags.add('preEncoded');
+    return 'SocketFlags(${flags.isEmpty ? 'none' : flags.join(', ')})';
+  }
+}
+
+/// Extension to convert Map to SocketFlags
+extension MapToSocketFlags on Map {
+  /// Converts this map to SocketFlags
+  SocketFlags toSocketFlags() => SocketFlags.fromMap(this);
+}
+
+/// Extension to convert Map? to SocketFlags?
+extension NullableMapToSocketFlags on Map? {
+  /// Converts this nullable map to SocketFlags, returning SocketFlags.none() if null
+  SocketFlags toSocketFlagsOrNone() {
+    final Map? self = this;
+    return self == null ? const SocketFlags.none() : SocketFlags.fromMap(self);
+  }
+
+  /// Converts this nullable map to nullable SocketFlags
+  SocketFlags? toSocketFlagsOrNull() {
+    final Map? self = this;
+    return self == null ? null : SocketFlags.fromMap(self);
+  }
+}
diff --git a/lib/src/models/transport_data_models.dart b/lib/src/models/transport_data_models.dart
new file mode 100644
index 0000000..6038a93
--- /dev/null
+++ b/lib/src/models/transport_data_models.dart
@@ -0,0 +1,178 @@
+/// transport_data_models.dart
+///
+/// Type-safe models for transport layer data to replace Object/dynamic usage
+///
+/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+library transport_data_models;
+
+/// Base sealed class for all transport data types
+///
+/// Using sealed class ensures exhaustive pattern matching and makes the set
+/// of transport data types closed - all subtypes are defined in this file.
+/// This replaces the generic Object type used for transport data,
+/// providing type safety through inheritance.
+sealed class TransportData {
+  /// Converts the transport data to its raw form for transmission
+  Object toRaw();
+}
+
+/// String-based transport data (most common for text protocols)
+class StringTransportData extends TransportData {
+  final String value;
+
+  StringTransportData(this.value);
+
+  @override
+  String toRaw() => value;
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) || other is StringTransportData && value == other.value;
+
+  @override
+  int get hashCode => value.hashCode;
+
+  @override
+  String toString() => 'StringTransportData($value)';
+}
+
+/// Binary transport data (for binary protocols or mixed content)
+class BinaryTransportData extends TransportData {
+  final List bytes;
+
+  BinaryTransportData(this.bytes);
+
+  /// Creates from a list that may contain mixed types
+  factory BinaryTransportData.fromList(final List data) {
+    final List bytes = [];
+    for (final dynamic item in data) {
+      if (item is int) {
+        bytes.add(item);
+      } else if (item is List) {
+        bytes.addAll(item);
+      }
+    }
+    return BinaryTransportData(bytes);
+  }
+
+  @override
+  List toRaw() => List.unmodifiable(bytes);
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is BinaryTransportData && bytes.length == other.bytes.length && _listEquals(bytes, other.bytes);
+
+  static bool _listEquals(final List a, final List b) {
+    for (int i = 0; i < a.length; i++) {
+      if (a[i] != b[i]) return false;
+    }
+    return true;
+  }
+
+  @override
+  int get hashCode => Object.hashAll(bytes);
+
+  @override
+  String toString() => 'BinaryTransportData(${bytes.length} bytes)';
+}
+
+/// JSON-encoded transport data (for structured data)
+class JsonTransportData extends TransportData {
+  final Map data;
+
+  JsonTransportData(this.data);
+
+  @override
+  Map toRaw() => Map.unmodifiable(data);
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) || other is JsonTransportData && _mapEquals(data, other.data);
+
+  static bool _mapEquals(final Map a, final Map b) {
+    if (a.length != b.length) return false;
+    for (final String key in a.keys) {
+      if (!b.containsKey(key) || a[key] != b[key]) return false;
+    }
+    return true;
+  }
+
+  @override
+  int get hashCode => Object.hashAll(data.entries);
+
+  @override
+  String toString() => 'JsonTransportData($data)';
+}
+
+/// List-based transport data (for arrays or multiple payloads)
+class ListTransportData extends TransportData {
+  final List items;
+
+  ListTransportData(this.items);
+
+  @override
+  List toRaw() => List.unmodifiable(items);
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is ListTransportData && items.length == other.items.length && _listEquals(items, other.items);
+
+  static bool _listEquals(final List a, final List b) {
+    for (int i = 0; i < a.length; i++) {
+      if (a[i] != b[i]) return false;
+    }
+    return true;
+  }
+
+  @override
+  int get hashCode => Object.hashAll(items);
+
+  @override
+  String toString() => 'ListTransportData($items)';
+}
+
+/// Mixed transport data (when data type is ambiguous or mixed)
+class MixedTransportData extends TransportData {
+  final Object value;
+
+  MixedTransportData(this.value);
+
+  @override
+  Object toRaw() => value;
+
+  @override
+  bool operator ==(final Object other) => identical(this, other) || other is MixedTransportData && value == other.value;
+
+  @override
+  int get hashCode => value.hashCode;
+
+  @override
+  String toString() => 'MixedTransportData($value)';
+}
+
+/// Extension to convert Object to TransportData
+extension ObjectToTransportData on Object {
+  /// Converts a generic Object to the appropriate TransportData type
+  TransportData toTransportData() {
+    final Object self = this;
+    if (self is String) {
+      return StringTransportData(self);
+    } else if (self is List) {
+      return BinaryTransportData(self);
+    } else if (self is Map) {
+      return JsonTransportData(self);
+    } else if (self is List) {
+      return ListTransportData(self);
+    } else {
+      return MixedTransportData(self);
+    }
+  }
+}
+
+/// Extension to convert TransportData back to Object for legacy code
+extension TransportDataToObject on TransportData {
+  /// Converts TransportData to Object for compatibility with untyped APIs
+  Object toObject() => toRaw();
+}
diff --git a/lib/src/models/transport_error_models.dart b/lib/src/models/transport_error_models.dart
new file mode 100644
index 0000000..a9e0336
--- /dev/null
+++ b/lib/src/models/transport_error_models.dart
@@ -0,0 +1,20 @@
+// transport_error_models.dart
+//
+// Strongly-typed transport error model used by Transports.onError.
+
+class TransportError {
+  final String? msg;
+  final String? desc;
+  final String type;
+
+  const TransportError({this.msg, this.desc, this.type = 'TransportError'});
+
+  Map toMap() => {
+        'msg': msg,
+        'desc': desc,
+        'type': type,
+      }..removeWhere((final String key, final Object? value) => value == null);
+
+  @override
+  String toString() => 'TransportError(type: $type, msg: ${msg ?? ''}, desc: ${desc ?? ''})';
+}
diff --git a/lib/src/models/transport_models.dart b/lib/src/models/transport_models.dart
new file mode 100644
index 0000000..0d16886
--- /dev/null
+++ b/lib/src/models/transport_models.dart
@@ -0,0 +1,856 @@
+/// transport_models.dart
+///
+/// Typed configuration models for Socket.IO transport layer to replace dynamic types
+/// and provide proper configuration management for WebSocket, polling, and other transports.
+library transport_models;
+
+import 'package:meta/meta.dart';
+
+/// Configuration model for transport-specific options.
+/// Centralizes transport configuration in a typed structure.
+@immutable
+class TransportConfigurationModel {
+  /// The transport name (e.g., 'websocket', 'polling').
+  final String name;
+
+  /// Whether the transport is enabled.
+  final bool enabled;
+
+  /// Transport priority (lower numbers = higher priority).
+  final int priority;
+
+  /// Connection timeout in milliseconds.
+  final int connectionTimeout;
+
+  /// Maximum number of retry attempts.
+  final int maxRetries;
+
+  /// Retry delay in milliseconds.
+  final int retryDelay;
+
+  /// Whether to use binary encoding.
+  final bool binaryEncoding;
+
+  /// Transport-specific options.
+  final Map options;
+
+  /// Creates a new transport configuration model.
+  const TransportConfigurationModel({
+    required this.name,
+    this.enabled = true,
+    this.priority = 0,
+    this.connectionTimeout = 20000,
+    this.maxRetries = 3,
+    this.retryDelay = 1000,
+    this.binaryEncoding = false,
+    this.options = const {},
+  });
+
+  /// Creates a transport configuration from a legacy map.
+  factory TransportConfigurationModel.fromMap(final Map map, final String name) =>
+      TransportConfigurationModel(
+        name: name,
+        enabled: map['enabled'] as bool? ?? true,
+        priority: map['priority'] as int? ?? 0,
+        connectionTimeout: map['connectionTimeout'] as int? ?? 20000,
+        maxRetries: map['maxRetries'] as int? ?? 3,
+        retryDelay: map['retryDelay'] as int? ?? 1000,
+        binaryEncoding: map['binaryEncoding'] as bool? ?? false,
+        options: Map.from((map['options'] as Map?) ?? {}),
+      );
+
+  /// Creates a WebSocket transport configuration.
+  factory TransportConfigurationModel.websocket({
+    final bool enabled = true,
+    final int priority = 1,
+    final int connectionTimeout = 20000,
+    final bool binaryEncoding = true,
+    final Map options = const {},
+  }) =>
+      TransportConfigurationModel(
+        name: 'websocket',
+        enabled: enabled,
+        priority: priority,
+        connectionTimeout: connectionTimeout,
+        binaryEncoding: binaryEncoding,
+        options: options,
+      );
+
+  /// Creates a polling transport configuration.
+  factory TransportConfigurationModel.polling({
+    final bool enabled = true,
+    final int priority = 2,
+    final int connectionTimeout = 30000,
+    final bool binaryEncoding = false,
+    final Map options = const {},
+  }) =>
+      TransportConfigurationModel(
+        name: 'polling',
+        enabled: enabled,
+        priority: priority,
+        connectionTimeout: connectionTimeout,
+        binaryEncoding: binaryEncoding,
+        options: options,
+      );
+
+  /// Gets an option value by key.
+  T? getOption(final String key) => options[key] as T?;
+
+  /// Checks if an option exists.
+  bool hasOption(final String key) => options.containsKey(key);
+
+  /// Adds or updates an option.
+  TransportConfigurationModel setOption(final String key, final Object? value) {
+    final Map newOptions = Map.from(options);
+    newOptions[key] = value;
+    return copyWith(options: newOptions);
+  }
+
+  /// Removes an option.
+  TransportConfigurationModel removeOption(final String key) {
+    final Map newOptions = Map.from(options)..remove(key);
+    return copyWith(options: newOptions);
+  }
+
+  /// Converts to a map for backward compatibility.
+  Map toMap() => {
+        'name': name,
+        'enabled': enabled,
+        'priority': priority,
+        'connectionTimeout': connectionTimeout,
+        'maxRetries': maxRetries,
+        'retryDelay': retryDelay,
+        'binaryEncoding': binaryEncoding,
+        'options': options,
+      };
+
+  /// Creates a copy with optional parameter overrides.
+  TransportConfigurationModel copyWith({
+    final String? name,
+    final bool? enabled,
+    final int? priority,
+    final int? connectionTimeout,
+    final int? maxRetries,
+    final int? retryDelay,
+    final bool? binaryEncoding,
+    final Map? options,
+  }) =>
+      TransportConfigurationModel(
+        name: name ?? this.name,
+        enabled: enabled ?? this.enabled,
+        priority: priority ?? this.priority,
+        connectionTimeout: connectionTimeout ?? this.connectionTimeout,
+        maxRetries: maxRetries ?? this.maxRetries,
+        retryDelay: retryDelay ?? this.retryDelay,
+        binaryEncoding: binaryEncoding ?? this.binaryEncoding,
+        options: options ?? this.options,
+      );
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is TransportConfigurationModel &&
+          runtimeType == other.runtimeType &&
+          name == other.name &&
+          enabled == other.enabled &&
+          priority == other.priority &&
+          connectionTimeout == other.connectionTimeout &&
+          maxRetries == other.maxRetries &&
+          retryDelay == other.retryDelay &&
+          binaryEncoding == other.binaryEncoding &&
+          _mapsEqual(options, other.options);
+
+  @override
+  int get hashCode =>
+      name.hashCode ^
+      enabled.hashCode ^
+      priority.hashCode ^
+      connectionTimeout.hashCode ^
+      maxRetries.hashCode ^
+      retryDelay.hashCode ^
+      binaryEncoding.hashCode ^
+      options.hashCode;
+
+  @override
+  String toString() => 'TransportConfigurationModel('
+      'name: $name, '
+      'enabled: $enabled, '
+      'priority: $priority, '
+      'connectionTimeout: $connectionTimeout, '
+      'maxRetries: $maxRetries, '
+      'retryDelay: $retryDelay, '
+      'binaryEncoding: $binaryEncoding, '
+      'optionsCount: ${options.length})';
+
+  /// Helper method to compare maps for equality.
+  bool _mapsEqual(final Map a, final Map b) {
+    if (a.length != b.length) return false;
+    for (final MapEntry entry in a.entries) {
+      if (b[entry.key] != entry.value) return false;
+    }
+    return true;
+  }
+}
+
+/// Configuration model specific to WebSocket transport.
+@immutable
+class WebSocketConfigurationModel extends TransportConfigurationModel {
+  /// Whether to enable per-message deflate compression.
+  final bool perMessageDeflate;
+
+  /// Maximum window bits for compression.
+  final int maxWindowBits;
+
+  /// Compression threshold in bytes.
+  final int compressionThreshold;
+
+  /// WebSocket subprotocol.
+  final String? subprotocol;
+
+  /// Custom headers for WebSocket handshake.
+  final Map headers;
+
+  /// Creates a new WebSocket configuration model.
+  const WebSocketConfigurationModel({
+    super.enabled = true,
+    super.priority = 1,
+    super.connectionTimeout = 20000,
+    super.maxRetries = 3,
+    super.retryDelay = 1000,
+    super.options = const {},
+    this.perMessageDeflate = true,
+    this.maxWindowBits = 15,
+    this.compressionThreshold = 1024,
+    this.subprotocol,
+    this.headers = const {},
+  }) : super(
+          name: 'websocket',
+          binaryEncoding: true,
+        );
+
+  /// Creates a WebSocket configuration from a legacy map.
+  factory WebSocketConfigurationModel.fromMap(final Map map) => WebSocketConfigurationModel(
+        enabled: map['enabled'] as bool? ?? true,
+        priority: map['priority'] as int? ?? 1,
+        connectionTimeout: map['connectionTimeout'] as int? ?? 20000,
+        maxRetries: map['maxRetries'] as int? ?? 3,
+        retryDelay: map['retryDelay'] as int? ?? 1000,
+        options: Map.from((map['options'] as Map?) ?? {}),
+        perMessageDeflate: map['perMessageDeflate'] as bool? ?? true,
+        maxWindowBits: map['maxWindowBits'] as int? ?? 15,
+        compressionThreshold: map['compressionThreshold'] as int? ?? 1024,
+        subprotocol: map['subprotocol'] as String?,
+        headers: Map.from((map['headers'] as Map?) ?? {}),
+      );
+
+  /// Gets a header value by name.
+  String? getHeader(final String name) => headers[name];
+
+  /// Adds or updates a header.
+  WebSocketConfigurationModel setHeader(final String name, final String value) {
+    final Map newHeaders = Map.from(headers);
+    newHeaders[name] = value;
+    return copyWith(headers: newHeaders);
+  }
+
+  /// Removes a header.
+  WebSocketConfigurationModel removeHeader(final String name) {
+    final Map newHeaders = Map.from(headers)..remove(name);
+    return copyWith(headers: newHeaders);
+  }
+
+  @override
+  Map toMap() {
+    final Map map = super.toMap()
+      ..addAll({
+        'perMessageDeflate': perMessageDeflate,
+        'maxWindowBits': maxWindowBits,
+        'compressionThreshold': compressionThreshold,
+        'headers': headers,
+      });
+    if (subprotocol != null) map['subprotocol'] = subprotocol;
+    return map;
+  }
+
+  /// Creates a copy with optional parameter overrides.
+  @override
+  WebSocketConfigurationModel copyWith({
+    final String? name,
+    final bool? enabled,
+    final int? priority,
+    final int? connectionTimeout,
+    final int? maxRetries,
+    final int? retryDelay,
+    final bool? binaryEncoding,
+    final Map? options,
+    final bool? perMessageDeflate,
+    final int? maxWindowBits,
+    final int? compressionThreshold,
+    final String? subprotocol,
+    final Map? headers,
+  }) =>
+      WebSocketConfigurationModel(
+        enabled: enabled ?? this.enabled,
+        priority: priority ?? this.priority,
+        connectionTimeout: connectionTimeout ?? this.connectionTimeout,
+        maxRetries: maxRetries ?? this.maxRetries,
+        retryDelay: retryDelay ?? this.retryDelay,
+        options: options ?? this.options,
+        perMessageDeflate: perMessageDeflate ?? this.perMessageDeflate,
+        maxWindowBits: maxWindowBits ?? this.maxWindowBits,
+        compressionThreshold: compressionThreshold ?? this.compressionThreshold,
+        subprotocol: subprotocol ?? this.subprotocol,
+        headers: headers ?? this.headers,
+      );
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is WebSocketConfigurationModel &&
+          super == other &&
+          perMessageDeflate == other.perMessageDeflate &&
+          maxWindowBits == other.maxWindowBits &&
+          compressionThreshold == other.compressionThreshold &&
+          subprotocol == other.subprotocol &&
+          _mapsEqual(headers, other.headers);
+
+  @override
+  int get hashCode =>
+      super.hashCode ^
+      perMessageDeflate.hashCode ^
+      maxWindowBits.hashCode ^
+      compressionThreshold.hashCode ^
+      subprotocol.hashCode ^
+      headers.hashCode;
+
+  @override
+  String toString() => 'WebSocketConfigurationModel('
+      'enabled: $enabled, '
+      'priority: $priority, '
+      'perMessageDeflate: $perMessageDeflate, '
+      'maxWindowBits: $maxWindowBits, '
+      'compressionThreshold: $compressionThreshold, '
+      'subprotocol: $subprotocol, '
+      'headersCount: ${headers.length})';
+
+  /// Helper method to compare maps for equality.
+  @override
+  bool _mapsEqual(final Map a, final Map b) {
+    if (a.length != b.length) return false;
+    for (final MapEntry entry in a.entries) {
+      if (b[entry.key] != entry.value) return false;
+    }
+    return true;
+  }
+}
+
+/// Configuration model specific to polling transport.
+@immutable
+class PollingConfigurationModel extends TransportConfigurationModel {
+  /// Polling interval in milliseconds.
+  final int pollingInterval;
+
+  /// Maximum number of HTTP requests in flight.
+  final int maxHttpRequests;
+
+  /// Whether to use JSONP for cross-domain requests.
+  final bool useJsonp;
+
+  /// JSONP callback parameter name.
+  final String jsonpCallback;
+
+  /// HTTP method for polling requests.
+  final String httpMethod;
+
+  /// Additional HTTP headers.
+  final Map httpHeaders;
+
+  /// Creates a new polling configuration model.
+  const PollingConfigurationModel({
+    super.enabled = true,
+    super.priority = 2,
+    super.connectionTimeout = 30000,
+    super.maxRetries = 3,
+    super.retryDelay = 1000,
+    super.options = const {},
+    this.pollingInterval = 100,
+    this.maxHttpRequests = 10,
+    this.useJsonp = false,
+    this.jsonpCallback = 'callback',
+    this.httpMethod = 'GET',
+    this.httpHeaders = const {},
+  }) : super(
+          name: 'polling',
+          binaryEncoding: false,
+        );
+
+  /// Creates a polling configuration from a legacy map.
+  factory PollingConfigurationModel.fromMap(final Map map) => PollingConfigurationModel(
+        enabled: map['enabled'] as bool? ?? true,
+        priority: map['priority'] as int? ?? 2,
+        connectionTimeout: map['connectionTimeout'] as int? ?? 30000,
+        maxRetries: map['maxRetries'] as int? ?? 3,
+        retryDelay: map['retryDelay'] as int? ?? 1000,
+        options: Map.from((map['options'] as Map?) ?? {}),
+        pollingInterval: map['pollingInterval'] as int? ?? 100,
+        maxHttpRequests: map['maxHttpRequests'] as int? ?? 10,
+        useJsonp: map['useJsonp'] as bool? ?? false,
+        jsonpCallback: map['jsonpCallback'] as String? ?? 'callback',
+        httpMethod: map['httpMethod'] as String? ?? 'GET',
+        httpHeaders: Map.from((map['httpHeaders'] as Map?) ?? {}),
+      );
+
+  /// Gets an HTTP header value by name.
+  String? getHttpHeader(final String name) => httpHeaders[name];
+
+  /// Adds or updates an HTTP header.
+  PollingConfigurationModel setHttpHeader(final String name, final String value) {
+    final Map newHeaders = Map.from(httpHeaders);
+    newHeaders[name] = value;
+    return copyWith(httpHeaders: newHeaders);
+  }
+
+  /// Removes an HTTP header.
+  PollingConfigurationModel removeHttpHeader(final String name) {
+    final Map newHeaders = Map.from(httpHeaders)..remove(name);
+    return copyWith(httpHeaders: newHeaders);
+  }
+
+  @override
+  Map toMap() {
+    final Map map = super.toMap()
+      ..addAll({
+        'pollingInterval': pollingInterval,
+        'maxHttpRequests': maxHttpRequests,
+        'useJsonp': useJsonp,
+        'jsonpCallback': jsonpCallback,
+        'httpMethod': httpMethod,
+        'httpHeaders': httpHeaders,
+      });
+    return map;
+  }
+
+  /// Creates a copy with optional parameter overrides.
+  @override
+  PollingConfigurationModel copyWith({
+    final String? name,
+    final bool? enabled,
+    final int? priority,
+    final int? connectionTimeout,
+    final int? maxRetries,
+    final int? retryDelay,
+    final bool? binaryEncoding,
+    final Map? options,
+    final int? pollingInterval,
+    final int? maxHttpRequests,
+    final bool? useJsonp,
+    final String? jsonpCallback,
+    final String? httpMethod,
+    final Map? httpHeaders,
+  }) =>
+      PollingConfigurationModel(
+        enabled: enabled ?? this.enabled,
+        priority: priority ?? this.priority,
+        connectionTimeout: connectionTimeout ?? this.connectionTimeout,
+        maxRetries: maxRetries ?? this.maxRetries,
+        retryDelay: retryDelay ?? this.retryDelay,
+        options: options ?? this.options,
+        pollingInterval: pollingInterval ?? this.pollingInterval,
+        maxHttpRequests: maxHttpRequests ?? this.maxHttpRequests,
+        useJsonp: useJsonp ?? this.useJsonp,
+        jsonpCallback: jsonpCallback ?? this.jsonpCallback,
+        httpMethod: httpMethod ?? this.httpMethod,
+        httpHeaders: httpHeaders ?? this.httpHeaders,
+      );
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is PollingConfigurationModel &&
+          super == other &&
+          pollingInterval == other.pollingInterval &&
+          maxHttpRequests == other.maxHttpRequests &&
+          useJsonp == other.useJsonp &&
+          jsonpCallback == other.jsonpCallback &&
+          httpMethod == other.httpMethod &&
+          _mapsEqual(httpHeaders, other.httpHeaders);
+
+  @override
+  int get hashCode =>
+      super.hashCode ^
+      pollingInterval.hashCode ^
+      maxHttpRequests.hashCode ^
+      useJsonp.hashCode ^
+      jsonpCallback.hashCode ^
+      httpMethod.hashCode ^
+      httpHeaders.hashCode;
+
+  @override
+  String toString() => 'PollingConfigurationModel('
+      'enabled: $enabled, '
+      'priority: $priority, '
+      'pollingInterval: $pollingInterval, '
+      'maxHttpRequests: $maxHttpRequests, '
+      'useJsonp: $useJsonp, '
+      'httpMethod: $httpMethod, '
+      'headersCount: ${httpHeaders.length})';
+
+  /// Helper method to compare maps for equality.
+  @override
+  bool _mapsEqual(final Map a, final Map b) {
+    if (a.length != b.length) return false;
+    for (final MapEntry entry in a.entries) {
+      if (b[entry.key] != entry.value) return false;
+    }
+    return true;
+  }
+}
+
+/// Model for transport state management.
+/// Tracks the current state and history of transport connections.
+@immutable
+class TransportStateModel {
+  /// The current transport state.
+  final TransportState state;
+
+  /// Timestamp when the current state was entered.
+  final DateTime stateTimestamp;
+
+  /// The transport configuration being used.
+  final TransportConfigurationModel configuration;
+
+  /// Number of connection attempts.
+  final int attempts;
+
+  /// Last error if any.
+  final String? lastError;
+
+  /// Connection statistics.
+  final TransportStatsModel stats;
+
+  /// Additional state metadata.
+  final Map metadata;
+
+  /// Creates a new transport state model.
+  TransportStateModel({
+    required this.state,
+    required this.stateTimestamp,
+    required this.configuration,
+    this.attempts = 0,
+    this.lastError,
+    final TransportStatsModel? stats,
+    this.metadata = const {},
+  }) : stats = stats ?? TransportStatsModel();
+
+  /// Creates an initial transport state.
+  factory TransportStateModel.initial(final TransportConfigurationModel configuration) => TransportStateModel(
+        state: TransportState.disconnected,
+        stateTimestamp: DateTime.now(),
+        configuration: configuration,
+      );
+
+  /// Creates a connecting state.
+  TransportStateModel connecting() => copyWith(
+        state: TransportState.connecting,
+        stateTimestamp: DateTime.now(),
+        attempts: attempts + 1,
+      );
+
+  /// Creates a connected state.
+  TransportStateModel connected() => copyWith(
+        state: TransportState.connected,
+        stateTimestamp: DateTime.now(),
+        lastError: null,
+        stats: stats.incrementConnections(),
+      );
+
+  /// Creates a disconnected state.
+  TransportStateModel disconnected([final String? error]) => copyWith(
+        state: TransportState.disconnected,
+        stateTimestamp: DateTime.now(),
+        lastError: error,
+        stats: stats.incrementDisconnections(),
+      );
+
+  /// Creates an error state.
+  TransportStateModel error(final String errorMessage) => copyWith(
+        state: TransportState.error,
+        stateTimestamp: DateTime.now(),
+        lastError: errorMessage,
+        stats: stats.incrementErrors(),
+      );
+
+  /// Gets the duration in the current state.
+  Duration get stateAge => DateTime.now().difference(stateTimestamp);
+
+  /// Checks if the transport is connected.
+  bool get isConnected => state == TransportState.connected;
+
+  /// Checks if the transport is connecting.
+  bool get isConnecting => state == TransportState.connecting;
+
+  /// Checks if the transport is disconnected.
+  bool get isDisconnected => state == TransportState.disconnected;
+
+  /// Checks if the transport has an error.
+  bool get hasError => state == TransportState.error;
+
+  /// Checks if max retry attempts have been reached.
+  bool get maxAttemptsReached => attempts >= configuration.maxRetries;
+
+  /// Adds metadata to the state.
+  TransportStateModel addMetadata(final String key, final Object? value) {
+    final Map newMetadata = Map.from(metadata);
+    newMetadata[key] = value;
+    return copyWith(metadata: newMetadata);
+  }
+
+  /// Gets metadata value by key.
+  T? getMetadata(final String key) => metadata[key] as T?;
+
+  /// Converts to a map for serialization.
+  Map toMap() => {
+        'state': state.name,
+        'stateTimestamp': stateTimestamp.toIso8601String(),
+        'configuration': configuration.toMap(),
+        'attempts': attempts,
+        'lastError': lastError,
+        'stats': stats.toMap(),
+        'metadata': metadata,
+      };
+
+  /// Creates a copy with optional parameter overrides.
+  TransportStateModel copyWith({
+    final TransportState? state,
+    final DateTime? stateTimestamp,
+    final TransportConfigurationModel? configuration,
+    final int? attempts,
+    final String? lastError,
+    final TransportStatsModel? stats,
+    final Map? metadata,
+  }) =>
+      TransportStateModel(
+        state: state ?? this.state,
+        stateTimestamp: stateTimestamp ?? this.stateTimestamp,
+        configuration: configuration ?? this.configuration,
+        attempts: attempts ?? this.attempts,
+        lastError: lastError ?? this.lastError,
+        stats: stats ?? this.stats,
+        metadata: metadata ?? this.metadata,
+      );
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is TransportStateModel &&
+          runtimeType == other.runtimeType &&
+          state == other.state &&
+          stateTimestamp == other.stateTimestamp &&
+          configuration == other.configuration &&
+          attempts == other.attempts &&
+          lastError == other.lastError &&
+          stats == other.stats &&
+          _mapsEqual(metadata, other.metadata);
+
+  @override
+  int get hashCode =>
+      state.hashCode ^
+      stateTimestamp.hashCode ^
+      configuration.hashCode ^
+      attempts.hashCode ^
+      lastError.hashCode ^
+      stats.hashCode ^
+      metadata.hashCode;
+
+  @override
+  String toString() => 'TransportStateModel('
+      'state: $state, '
+      'transport: ${configuration.name}, '
+      'attempts: $attempts, '
+      'stateAge: ${stateAge.inMilliseconds}ms, '
+      'hasError: $hasError)';
+
+  /// Helper method to compare maps for equality.
+  bool _mapsEqual(final Map a, final Map b) {
+    if (a.length != b.length) return false;
+    for (final MapEntry entry in a.entries) {
+      if (b[entry.key] != entry.value) return false;
+    }
+    return true;
+  }
+}
+
+/// Enumeration of transport states.
+enum TransportState {
+  /// Transport is disconnected.
+  disconnected,
+
+  /// Transport is attempting to connect.
+  connecting,
+
+  /// Transport is connected and ready.
+  connected,
+
+  /// Transport encountered an error.
+  error,
+}
+
+/// Model for transport connection statistics.
+@immutable
+class TransportStatsModel {
+  /// Total number of connection attempts.
+  final int connectionAttempts;
+
+  /// Number of successful connections.
+  final int successfulConnections;
+
+  /// Number of disconnections.
+  final int disconnections;
+
+  /// Number of errors encountered.
+  final int errors;
+
+  /// Total bytes sent.
+  final int bytesSent;
+
+  /// Total bytes received.
+  final int bytesReceived;
+
+  /// Number of packets sent.
+  final int packetsSent;
+
+  /// Number of packets received.
+  final int packetsReceived;
+
+  /// Statistics start time.
+  final DateTime startTime;
+
+  /// Creates a new transport statistics model.
+  TransportStatsModel({
+    this.connectionAttempts = 0,
+    this.successfulConnections = 0,
+    this.disconnections = 0,
+    this.errors = 0,
+    this.bytesSent = 0,
+    this.bytesReceived = 0,
+    this.packetsSent = 0,
+    this.packetsReceived = 0,
+    final DateTime? startTime,
+  }) : startTime = startTime ?? DateTime.fromMillisecondsSinceEpoch(0);
+
+  /// Creates initial statistics.
+  factory TransportStatsModel.initial() => TransportStatsModel(startTime: DateTime.now());
+
+  /// Increments connection attempts.
+  TransportStatsModel incrementAttempts() => copyWith(connectionAttempts: connectionAttempts + 1);
+
+  /// Increments successful connections.
+  TransportStatsModel incrementConnections() => copyWith(
+        connectionAttempts: connectionAttempts + 1,
+        successfulConnections: successfulConnections + 1,
+      );
+
+  /// Increments disconnections.
+  TransportStatsModel incrementDisconnections() => copyWith(disconnections: disconnections + 1);
+
+  /// Increments errors.
+  TransportStatsModel incrementErrors() => copyWith(errors: errors + 1);
+
+  /// Records data sent.
+  TransportStatsModel recordDataSent(final int bytes, final int packets) => copyWith(
+        bytesSent: bytesSent + bytes,
+        packetsSent: packetsSent + packets,
+      );
+
+  /// Records data received.
+  TransportStatsModel recordDataReceived(final int bytes, final int packets) => copyWith(
+        bytesReceived: bytesReceived + bytes,
+        packetsReceived: packetsReceived + packets,
+      );
+
+  /// Gets the connection success rate.
+  double get successRate {
+    if (connectionAttempts == 0) return 0.0;
+    return successfulConnections / connectionAttempts;
+  }
+
+  /// Gets the uptime duration.
+  Duration get uptime => DateTime.now().difference(startTime);
+
+  /// Converts to a map for serialization.
+  Map toMap() => {
+        'connectionAttempts': connectionAttempts,
+        'successfulConnections': successfulConnections,
+        'disconnections': disconnections,
+        'errors': errors,
+        'bytesSent': bytesSent,
+        'bytesReceived': bytesReceived,
+        'packetsSent': packetsSent,
+        'packetsReceived': packetsReceived,
+        'startTime': startTime.toIso8601String(),
+        'successRate': successRate,
+        'uptime': uptime.inMilliseconds,
+      };
+
+  /// Creates a copy with optional parameter overrides.
+  TransportStatsModel copyWith({
+    final int? connectionAttempts,
+    final int? successfulConnections,
+    final int? disconnections,
+    final int? errors,
+    final int? bytesSent,
+    final int? bytesReceived,
+    final int? packetsSent,
+    final int? packetsReceived,
+    final DateTime? startTime,
+  }) =>
+      TransportStatsModel(
+        connectionAttempts: connectionAttempts ?? this.connectionAttempts,
+        successfulConnections: successfulConnections ?? this.successfulConnections,
+        disconnections: disconnections ?? this.disconnections,
+        errors: errors ?? this.errors,
+        bytesSent: bytesSent ?? this.bytesSent,
+        bytesReceived: bytesReceived ?? this.bytesReceived,
+        packetsSent: packetsSent ?? this.packetsSent,
+        packetsReceived: packetsReceived ?? this.packetsReceived,
+        startTime: startTime ?? this.startTime,
+      );
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is TransportStatsModel &&
+          runtimeType == other.runtimeType &&
+          connectionAttempts == other.connectionAttempts &&
+          successfulConnections == other.successfulConnections &&
+          disconnections == other.disconnections &&
+          errors == other.errors &&
+          bytesSent == other.bytesSent &&
+          bytesReceived == other.bytesReceived &&
+          packetsSent == other.packetsSent &&
+          packetsReceived == other.packetsReceived &&
+          startTime == other.startTime;
+
+  @override
+  int get hashCode =>
+      connectionAttempts.hashCode ^
+      successfulConnections.hashCode ^
+      disconnections.hashCode ^
+      errors.hashCode ^
+      bytesSent.hashCode ^
+      bytesReceived.hashCode ^
+      packetsSent.hashCode ^
+      packetsReceived.hashCode ^
+      startTime.hashCode;
+
+  @override
+  String toString() => 'TransportStatsModel('
+      'attempts: $connectionAttempts, '
+      'connections: $successfulConnections, '
+      'disconnections: $disconnections, '
+      'errors: $errors, '
+      'sent: ${bytesSent}b/${packetsSent}p, '
+      'received: ${bytesReceived}b/${packetsReceived}p, '
+      'successRate: ${(successRate * 100).toStringAsFixed(1)}%, '
+      'uptime: ${uptime.inSeconds}s)';
+}
diff --git a/lib/src/models/validation_error_models.dart b/lib/src/models/validation_error_models.dart
new file mode 100644
index 0000000..81190e0
--- /dev/null
+++ b/lib/src/models/validation_error_models.dart
@@ -0,0 +1,420 @@
+/// validation_error_models.dart
+///
+/// Models for validation errors throughout the Socket.IO codebase
+///
+/// Provides a type-safe way to represent validation failures
+/// instead of throwing exceptions or returning null.
+///
+/// Copyright (C) 2024 Potix Corporation. All Rights Reserved.
+library validation_error_models;
+
+/// Base sealed class for all validation errors
+///
+/// Using sealed class ensures exhaustive pattern matching and makes
+/// validation error handling explicit and type-safe.
+sealed class ValidationError {
+  /// The field or property that failed validation
+  final String field;
+
+  /// Human-readable error message
+  final String message;
+
+  /// Optional error code for programmatic handling
+  final String? code;
+
+  const ValidationError({
+    required this.field,
+    required this.message,
+    this.code,
+  });
+
+  @override
+  String toString() => 'ValidationError($field): $message${code != null ? ' [code: $code]' : ''}';
+}
+
+/// Validation error for required fields that are missing or null
+final class RequiredFieldError extends ValidationError {
+  const RequiredFieldError({
+    required super.field,
+    super.message = 'Required field is missing',
+    super.code = 'required_field',
+  });
+}
+
+/// Validation error for values that don't meet format requirements
+final class FormatError extends ValidationError {
+  /// The expected format description
+  final String expectedFormat;
+
+  /// The actual value that failed validation
+  final Object? actualValue;
+
+  const FormatError({
+    required super.field,
+    required this.expectedFormat,
+    this.actualValue,
+    super.message = 'Invalid format',
+    super.code = 'invalid_format',
+  });
+
+  @override
+  String toString() =>
+      'FormatError($field): Expected $expectedFormat, got $actualValue${code != null ? ' [code: $code]' : ''}';
+}
+
+/// Validation error for values outside acceptable ranges
+final class RangeValidationError extends ValidationError {
+  /// The minimum acceptable value (if any)
+  final num? min;
+
+  /// The maximum acceptable value (if any)
+  final num? max;
+
+  /// The actual value that failed validation
+  final num actualValue;
+
+  const RangeValidationError({
+    required super.field,
+    this.min,
+    this.max,
+    required this.actualValue,
+    super.message = 'Value out of range',
+    super.code = 'out_of_range',
+  });
+
+  @override
+  String toString() {
+    final String rangeDesc = min != null && max != null
+        ? 'between $min and $max'
+        : min != null
+            ? 'at least $min'
+            : 'at most $max';
+    return 'RangeValidationError($field): Expected $rangeDesc, got $actualValue${code != null ? ' [code: $code]' : ''}';
+  }
+}
+
+/// Validation error for string length constraints
+final class LengthError extends ValidationError {
+  /// The minimum acceptable length (if any)
+  final int? minLength;
+
+  /// The maximum acceptable length (if any)
+  final int? maxLength;
+
+  /// The actual length that failed validation
+  final int actualLength;
+
+  const LengthError({
+    required super.field,
+    this.minLength,
+    this.maxLength,
+    required this.actualLength,
+    super.message = 'Invalid length',
+    super.code = 'invalid_length',
+  });
+
+  @override
+  String toString() {
+    final String lengthDesc = minLength != null && maxLength != null
+        ? 'between $minLength and $maxLength characters'
+        : minLength != null
+            ? 'at least $minLength characters'
+            : 'at most $maxLength characters';
+    return 'LengthError($field): Expected $lengthDesc, got $actualLength${code != null ? ' [code: $code]' : ''}';
+  }
+}
+
+/// Validation error for values not in allowed set
+final class InvalidValueError extends ValidationError {
+  /// The set of allowed values
+  final List allowedValues;
+
+  /// The actual value that failed validation
+  final Object? actualValue;
+
+  const InvalidValueError({
+    required super.field,
+    required this.allowedValues,
+    this.actualValue,
+    super.message = 'Invalid value',
+    super.code = 'invalid_value',
+  });
+
+  @override
+  String toString() =>
+      'InvalidValueError($field): Expected one of $allowedValues, got $actualValue${code != null ? ' [code: $code]' : ''}';
+}
+
+/// Validation error for custom business rules
+final class CustomValidationError extends ValidationError {
+  /// Additional context or details about the error
+  final Map? details;
+
+  const CustomValidationError({
+    required super.field,
+    required super.message,
+    super.code,
+    this.details,
+  });
+
+  @override
+  String toString() {
+    final String detailsStr = details != null ? ', details: $details' : '';
+    return 'CustomValidationError($field): $message$detailsStr${code != null ? ' [code: $code]' : ''}';
+  }
+}
+
+/// Result type for validation operations
+///
+/// Represents either a successful validation (with value T)
+/// or a validation failure (with list of errors).
+sealed class ValidationResult {
+  const ValidationResult();
+
+  /// Returns true if validation succeeded
+  bool get isValid => this is ValidationSuccess;
+
+  /// Returns true if validation failed
+  bool get isInvalid => this is ValidationFailure;
+
+  /// Gets the value if validation succeeded, throws if failed
+  T get value {
+    final ValidationResult self = this;
+    if (self is ValidationSuccess) {
+      return self.value;
+    }
+    throw StateError('Cannot get value from failed validation');
+  }
+
+  /// Gets the errors if validation failed, throws if succeeded
+  List get errors {
+    final ValidationResult self = this;
+    if (self is ValidationFailure) {
+      return self.errors;
+    }
+    throw StateError('Cannot get errors from successful validation');
+  }
+
+  /// Gets the value if valid, otherwise returns the result of calling [orElse]
+  T getOrElse(final T Function() orElse) {
+    final ValidationResult self = this;
+    if (self is ValidationSuccess) {
+      return self.value;
+    }
+    return orElse();
+  }
+
+  /// Gets the value if valid, otherwise returns [defaultValue]
+  T getOrDefault(final T defaultValue) {
+    final ValidationResult self = this;
+    if (self is ValidationSuccess) {
+      return self.value;
+    }
+    return defaultValue;
+  }
+
+  /// Transforms the value if validation succeeded
+  ValidationResult map(final R Function(T value) transform) {
+    final ValidationResult self = this;
+    if (self is ValidationSuccess) {
+      return ValidationSuccess(transform(self.value));
+    }
+    return ValidationFailure((self as ValidationFailure).errors);
+  }
+
+  /// Chains validation operations
+  ValidationResult flatMap(final ValidationResult Function(T value) transform) {
+    final ValidationResult self = this;
+    if (self is ValidationSuccess) {
+      return transform(self.value);
+    }
+    return ValidationFailure((self as ValidationFailure).errors);
+  }
+}
+
+/// Successful validation result
+final class ValidationSuccess extends ValidationResult {
+  @override
+  final T value;
+
+  const ValidationSuccess(this.value);
+
+  @override
+  String toString() => 'ValidationSuccess($value)';
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is ValidationSuccess && runtimeType == other.runtimeType && value == other.value;
+
+  @override
+  int get hashCode => value.hashCode;
+}
+
+/// Failed validation result
+final class ValidationFailure extends ValidationResult {
+  @override
+  final List errors;
+
+  const ValidationFailure(this.errors);
+
+  /// Creates a failure with a single error
+  factory ValidationFailure.single(final ValidationError error) => ValidationFailure([error]);
+
+  @override
+  String toString() =>
+      'ValidationFailure(${errors.length} errors: ${errors.map((final ValidationError e) => e.toString()).join(', ')})';
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is ValidationFailure &&
+          runtimeType == other.runtimeType &&
+          errors.length == other.errors.length &&
+          errors.every(other.errors.contains);
+
+  @override
+  int get hashCode => Object.hashAll(errors);
+}
+
+/// Utility functions for validation
+class ValidationUtils {
+  ValidationUtils._();
+
+  /// Validates that a value is not null
+  static ValidationResult required(
+    final T? value,
+    final String field, {
+    final String? message,
+  }) {
+    if (value == null) {
+      return ValidationFailure.single(
+        RequiredFieldError(
+          field: field,
+          message: message ?? 'Required field is missing',
+        ),
+      );
+    }
+    return ValidationSuccess(value);
+  }
+
+  /// Validates that a string is not empty
+  static ValidationResult nonEmpty(
+    final String value,
+    final String field, {
+    final String? message,
+  }) {
+    if (value.isEmpty) {
+      return ValidationFailure.single(
+        RequiredFieldError(
+          field: field,
+          message: message ?? 'Value cannot be empty',
+        ),
+      );
+    }
+    return ValidationSuccess(value);
+  }
+
+  /// Validates that a number is within range
+  static ValidationResult inRange(
+    final T value,
+    final String field, {
+    final T? min,
+    final T? max,
+    final String? message,
+  }) {
+    if ((min != null && value < min) || (max != null && value > max)) {
+      return ValidationFailure.single(
+        RangeValidationError(
+          field: field,
+          min: min,
+          max: max,
+          actualValue: value,
+          message: message ?? 'Value out of range',
+        ),
+      );
+    }
+    return ValidationSuccess(value);
+  }
+
+  /// Validates string length
+  static ValidationResult lengthInRange(
+    final String value,
+    final String field, {
+    final int? minLength,
+    final int? maxLength,
+    final String? message,
+  }) {
+    final int length = value.length;
+    if ((minLength != null && length < minLength) || (maxLength != null && length > maxLength)) {
+      return ValidationFailure.single(
+        LengthError(
+          field: field,
+          minLength: minLength,
+          maxLength: maxLength,
+          actualLength: length,
+          message: message ?? 'Invalid length',
+        ),
+      );
+    }
+    return ValidationSuccess(value);
+  }
+
+  /// Validates that value matches pattern
+  static ValidationResult matchesPattern(
+    final String value,
+    final String field,
+    final RegExp pattern, {
+    final String? message,
+    final String? expectedFormat,
+  }) {
+    if (!pattern.hasMatch(value)) {
+      return ValidationFailure.single(
+        FormatError(
+          field: field,
+          expectedFormat: expectedFormat ?? pattern.pattern,
+          actualValue: value,
+          message: message ?? 'Invalid format',
+        ),
+      );
+    }
+    return ValidationSuccess(value);
+  }
+
+  /// Validates that value is in allowed set
+  static ValidationResult oneOf(
+    final T value,
+    final String field,
+    final List allowedValues, {
+    final String? message,
+  }) {
+    if (!allowedValues.contains(value)) {
+      return ValidationFailure.single(
+        InvalidValueError(
+          field: field,
+          allowedValues: allowedValues,
+          actualValue: value,
+          message: message ?? 'Invalid value',
+        ),
+      );
+    }
+    return ValidationSuccess(value);
+  }
+
+  /// Combines multiple validation results
+  static ValidationResult combine(
+    final T value,
+    final List> results,
+  ) {
+    final List allErrors = [];
+    for (final ValidationResult result in results) {
+      if (result is ValidationFailure) {
+        allErrors.addAll(result.errors);
+      }
+    }
+    if (allErrors.isEmpty) {
+      return ValidationSuccess(value);
+    }
+    return ValidationFailure(allErrors);
+  }
+}
diff --git a/lib/src/namespace.dart b/lib/src/namespace.dart
index 192325a..7d1b333 100644
--- a/lib/src/namespace.dart
+++ b/lib/src/namespace.dart
@@ -1,41 +1,91 @@
-/// namespace.dart
-///
-/// Purpose:
-///
-/// Description:
-///
-/// History:
-///    17/02/2017, Created by jumperchen
-///
-/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+// ignore_for_file: implementation_imports
+// namespace.dart
+//
+// Purpose:
+//
+// Description:
+//
+// History:
+//    17/02/2017, Created by jumperchen
+//
+// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
 import 'dart:async';
+
 import 'package:logging/logging.dart';
-import 'package:socket_io/src/adapter/adapter.dart';
-import 'package:socket_io/src/client.dart';
 import 'package:socket_io_common/src/parser/parser.dart';
-import 'package:socket_io/src/server.dart';
-import 'package:socket_io/src/socket.dart';
-import 'package:socket_io/src/util/event_emitter.dart';
-
-/// Blacklisted events.
-
-List events = [
-  'connect', // for symmetry with client
-  'connection', 'newListener'
-];
-
-/// Flags.
-List flags = ['json', 'volatile'];
+import 'adapter.dart';
+import 'client.dart';
+import 'models/callbacks_models.dart';
+import 'server.dart';
+import 'socket.dart';
+import 'util/event_emitter.dart';
+import 'value_objects/event_name_vo.dart';
+import 'value_objects/query_parameters_vo.dart';
 
+/// A Socket.IO namespace for organizing socket connections.
+///
+/// Namespaces allow you to split the logic of your application over a single
+/// shared connection. This is useful for implementing multiplexed applications
+/// or for authorization.
+///
+/// ## Basic Usage
+///
+/// ```dart
+/// // Access default namespace
+/// io.on('connection', (socket) {
+///   print('Connected to default namespace');
+/// });
+///
+/// // Create custom namespace
+/// final chat = io.of('/chat');
+/// chat.on('connection', (socket) {
+///   print('Connected to /chat');
+///   socket.emit('welcome', ['Welcome to chat!']);
+/// });
+/// ```
+///
+/// ## With Middleware
+///
+/// ```dart
+/// final admin = io.of('/admin');
+///
+/// // Add authentication middleware
+/// admin.use((socket, next) {
+///   final token = socket.handshake?['auth']?['token'];
+///   if (isValidToken(token)) {
+///     next(null);  // Allow connection
+///   } else {
+///     next('Authentication failed');  // Reject
+///   }
+/// });
+///
+/// admin.on('connection', (socket) {
+///   print('Authenticated admin connected');
+/// });
+/// ```
+///
+/// ## Broadcasting
+///
+/// ```dart
+/// // Broadcast to all clients in namespace
+/// chat.emit('announcement', ['Server message']);
+///
+/// // Broadcast to specific room
+/// chat.to('room1').emit('message', ['Room message']);
+/// ```
+///
+/// See also:
+/// * [Server.of] to create or access namespaces
+/// * [Socket] for individual client connections within a namespace
 class Namespace extends EventEmitter {
   String name;
   Server server;
-  List sockets = [];
-  Map connected = {};
-  List fns = [];
+  List sockets = [];
+  Map connected = {};
+  List fns = [];
   int ids = 0;
-  List rooms = [];
-  Map flags = {};
+  List rooms = [];
+  Map flags = {};
   late Adapter adapter;
   final Logger _logger = Logger('socket_io:Namespace');
 
@@ -61,7 +111,7 @@ class Namespace extends EventEmitter {
   ///
   /// @return {Namespace} self
   /// @api public
-  Namespace use(fn) {
+  Namespace use(final MiddlewareCallback fn) {
     fns.add(fn);
     return this;
   }
@@ -71,44 +121,51 @@ class Namespace extends EventEmitter {
   /// @param {Socket} socket that will get added
   /// @param {Function} last fn call in the middleware
   /// @api private
-  void run(socket, Function fn) {
-    var fns = this.fns.sublist(0);
-    if (fns.isEmpty) return fn(null);
+  void run(final Socket socket, final MiddlewareNext fn) {
+    final List fns = this.fns.sublist(0);
+    if (fns.isEmpty) {
+      fn(null);
+      return;
+    }
 
     run0(0, fns, socket, fn);
   }
 
-  //TODO: Figure out return type for this method
-  static Object run0(
-      int index, List fns, Socket socket, Function fn) {
-    return fns[index](socket, (err) {
+  static void run0(
+    final int index,
+    final List fns,
+    final Socket socket,
+    final MiddlewareNext fn,
+  ) {
+    fns[index](socket, (final Object? err) {
       // upon error, short-circuit
-      if (err) return fn(err);
+      if (err != null) {
+        fn(err);
+        return;
+      }
 
       // if no middleware left, summon callback
-      if (fns.length <= index + 1) return fn(null);
+      if (fns.length <= index + 1) {
+        fn(null);
+        return;
+      }
 
       // go on to next
-      return run0(index + 1, fns, socket, fn);
+      run0(index + 1, fns, socket, fn);
+      return;
     });
   }
 
   /// Targets a room when emitting.
   ///
   /// @param {String} name
-  /// @return {Namespace} self
-  /// @api public
-//    in(String name) {
-//        to(name);
-//    }
-
   /// Targets a room when emitting.
   ///
   /// @param {String} name
   /// @return {Namespace} self
   /// @api public
-  Namespace to(String name) {
-    rooms = rooms.isNotEmpty == true ? rooms : [];
+  Namespace to(final String name) {
+    rooms = rooms.isNotEmpty == true ? rooms : [];
     if (!rooms.contains(name)) rooms.add(name);
     return this;
   }
@@ -117,15 +174,15 @@ class Namespace extends EventEmitter {
   ///
   /// @return {Socket}
   /// @api private
-  Socket add(Client client, query, Function? fn) {
+  Socket add(final Client client, final Map? query, final Function? fn) {
     _logger.fine('adding socket to nsp $name');
-    var socket = Socket(this, client, query);
-    var self = this;
-    run(socket, (err) {
+    final Socket socket = Socket(this, client, query);
+    final Namespace self = this;
+    run(socket, (final Object? err) {
       // don't use Timer.run() here
       scheduleMicrotask(() {
         if ('open' == client.conn.readyState) {
-          if (err != null) return socket.error(err.data || err.message);
+          if (err != null) return socket.error(err);
 
           // track socket
           self.sockets.add(socket);
@@ -138,8 +195,45 @@ class Namespace extends EventEmitter {
           if (fn != null) fn(socket);
 
           // fire user-set events
-          self.emit('connect', socket);
-          self.emit('connection', socket);
+          self
+            ..emit('connect', socket)
+            ..emit('connection', socket);
+        } else {
+          _logger.fine('next called after client was closed - ignoring socket');
+        }
+      });
+    });
+    return socket;
+  }
+
+  /// Adds a new client using typed QueryParameters.
+  ///
+  /// @return {Socket}
+  /// @api public
+  Socket addTyped(final Client client, final QueryParameters? query, final ConnectionCallback? fn) {
+    _logger.fine('adding socket to nsp $name');
+    final Socket socket = Socket.typed(this, client, query);
+    final Namespace self = this;
+    run(socket, (final Object? err) {
+      // don't use Timer.run() here
+      scheduleMicrotask(() {
+        if ('open' == client.conn.readyState) {
+          if (err != null) return socket.error(err);
+
+          // track socket
+          self.sockets.add(socket);
+
+          // it's paramount that the internal `onconnect` logic
+          // fires before user-set events to prevent state order
+          // violations (such as a disconnection before the connection
+          // logic is complete)
+          socket.onconnect();
+          if (fn != null) fn(socket);
+
+          // fire user-set events
+          self
+            ..emit('connect', socket)
+            ..emit('connection', socket);
         } else {
           _logger.fine('next called after client was closed - ignoring socket');
         }
@@ -151,7 +245,7 @@ class Namespace extends EventEmitter {
   /// Removes a client. Called by each `Socket`.
   ///
   /// @api private
-  void remove(socket) {
+  void remove(final Socket socket) {
     if (sockets.contains(socket)) {
       sockets.remove(socket);
     } else {
@@ -163,22 +257,19 @@ class Namespace extends EventEmitter {
   ///
   /// @api public
   @override
-  void emit(String event, [dynamic argument]) {
-    if (events.contains(event)) {
+  void emit(final String event, [final dynamic argument]) {
+    if (EventName.isReserved(event)) {
       super.emit(event, argument);
     } else {
-      // @todo check how to handle it with Dart
-      // if (hasBin(args)) { parserType = ParserType.binaryEvent; } // binary
-
       // ignore: omit_local_variable_types
-      List data = argument == null ? [event] : [event, argument];
+      final List data = argument == null ? [event] : [event, argument];
 
-      final packet = {'type': EVENT, 'data': data};
+      final Map packet = {'type': EVENT, 'data': data};
 
-      adapter.broadcast(packet, {'rooms': rooms, 'flags': flags});
+      adapter.broadcast(packet, {'rooms': rooms, 'flags': flags});
 
-      rooms = [];
-      flags = {};
+      rooms = [];
+      flags = {};
     }
   }
 
@@ -186,11 +277,9 @@ class Namespace extends EventEmitter {
   ///
   /// @return {Namespace} self
   /// @api public
-  Namespace send([args]) {
-    return write(args);
-  }
+  Namespace send([final Object? args]) => write(args);
 
-  Namespace write([args]) {
+  Namespace write([final Object? args]) {
     emit('message', args);
     return this;
   }
@@ -199,13 +288,9 @@ class Namespace extends EventEmitter {
   ///
   /// @return {Namespace} self
   /// @api public
-  ///
-  /// TODO: Fix this description or code. Add type parameters to [fn([_])]
-  ///
-  // ignore: use_function_type_syntax_for_parameters
-  Namespace clients(fn([_])) {
-    adapter.clients(rooms, fn);
-    rooms = [];
+  Namespace clients(final List? rooms, [final Function? fn]) {
+    adapter.clients(rooms ?? this.rooms, fn as void Function(List p1)?);
+    this.rooms = [];
     return this;
   }
 
@@ -214,19 +299,17 @@ class Namespace extends EventEmitter {
   /// @param {Boolean} if `true`, compresses the sending data
   /// @return {Namespace} self
   /// @api public
-  Namespace compress(compress) {
-    flags = flags.isEmpty ? flags : {};
+  Namespace compress(final bool compress) {
+    flags = flags.isEmpty ? flags : {};
     flags['compress'] = compress;
     return this;
   }
+
+  /// Special method for connection events that pass Socket objects
+  /// This provides proper typing for connection handlers
+  void onConnection(final EventHandler handler) {
+    on('connection', (final dynamic data) => handler(data as Socket));
+  }
 }
 
 /// Apply flags from `Socket`.
-// @todo
-//exports.flags.forEach(function(flag){
-//    Namespace.prototype.__defineGetter__(flag, function(){
-//    this.flags = this.flags || {};
-//    this.flags[flag] = true;
-//    return this;
-//    });
-//});
diff --git a/lib/src/server.dart b/lib/src/server.dart
index ba71551..634773f 100644
--- a/lib/src/server.dart
+++ b/lib/src/server.dart
@@ -1,27 +1,33 @@
-/// server.dart
-///
-/// Purpose:
-///
-/// Description:
-///
-/// History:
-///    22/02/2017, Created by jumperchen
-///
-/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+/*
+server.dart
+
+Purpose:
+
+Description:
+
+History:
+   22/02/2017, Created by jumperchen
+
+Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+*/
 import 'dart:async';
 import 'dart:io';
+
 import 'package:logging/logging.dart';
-import 'package:socket_io/src/client.dart';
-import 'package:socket_io/src/engine/engine.dart';
-import 'package:socket_io/src/namespace.dart';
-import 'package:socket_io_common/src/parser/parser.dart';
+import 'package:socket_io_common/socket_io_common.dart';
 import 'package:stream/stream.dart';
 
+import 'client.dart';
+import 'engine/engine.dart';
+import 'models/callbacks_models.dart';
 import 'namespace.dart';
+import 'socket.dart';
+import 'util/async_utils.dart';
+import 'util/event_emitter.dart';
 
 /// Socket.IO client source.
 /// Old settings for backwards compatibility
-Map oldSettings = {
+Map oldSettings = {
   'transports': 'transports',
   'heartbeat timeout': 'pingTimeout',
   'heartbeat interval': 'pingInterval',
@@ -30,11 +36,65 @@ Map oldSettings = {
 
 final Logger _logger = Logger('socket_io:Server');
 
+/// A Socket.IO server for real-time bidirectional communication.
+///
+/// The [Server] class is the main entry point for creating a Socket.IO server.
+/// It manages namespaces, handles client connections, and configures transport options.
+///
+/// ## Basic Usage
+///
+/// ```dart
+/// // Create a server
+/// final io = Server();
+///
+/// // Listen for connections
+/// io.on('connection', (socket) {
+///   print('Client connected: ${socket.id}');
+///
+///   socket.on('message', (data) {
+///     print('Received: $data');
+///     socket.emit('response', ['Message received']);
+///   });
+/// });
+///
+/// // Start listening on a port
+/// io.listen(3000);
+/// ```
+///
+/// ## With Options
+///
+/// ```dart
+/// final io = Server(options: {
+///   'path': '/socket.io',
+///   'pingTimeout': 60000,
+///   'pingInterval': 25000,
+///   'transports': ['websocket', 'polling'],
+/// });
+/// ```
+///
+/// ## Multiple Namespaces
+///
+/// ```dart
+/// // Default namespace
+/// io.on('connection', (socket) {
+///   print('Connected to default namespace');
+/// });
+///
+/// // Custom namespace
+/// final chat = io.of('/chat');
+/// chat.on('connection', (socket) {
+///   print('Connected to /chat namespace');
+/// });
+/// ```
+///
+/// See also:
+/// * [Namespace] for namespace-specific configuration
+/// * [Socket] for individual client connections
 class Server {
   // Namespaces
-  Map nsps = {};
+  Map nsps = {};
   late Namespace sockets;
-  dynamic _origins;
+  String _origins = '*:*';
   bool? _serveClient;
   String? _path;
   String _adapter = 'default';
@@ -43,16 +103,29 @@ class Server {
   Encoder encoder = Encoder();
   Future? _ready;
 
-  /// Server is ready
+  /// Indicates whether the server is ready to accept connections.
   ///
-  /// @return a Future that resolves to true whenever the server is ready
-  /// @api public
-  Future get ready => _ready ?? Future.value(false);
-
-  /// Server's port
+  /// Returns a [Future] that completes with `true` when the server
+  /// is fully initialized and ready to accept client connections.
   ///
-  /// @return the port number where the server is listening
-  /// @api public
+  /// Example:
+  /// ```dart
+  /// final io = Server();
+  /// await io.ready;  // Wait for server to be ready
+  /// print('Server is ready on port ${io.port}');
+  /// ```
+  Future get ready => _ready ?? Future.value(false);
+
+  /// The port number on which the server is listening.
+  ///
+  /// Returns `null` if the server is not yet listening on any port.
+  ///
+  /// Example:
+  /// ```dart
+  /// final io = Server();
+  /// io.listen(3000);
+  /// print('Listening on port ${io.port}');  // Output: Listening on port 3000
+  /// ```
   int? get port {
     if (httpServer == null || httpServer!.channels.isEmpty) {
       return null;
@@ -60,63 +133,107 @@ class Server {
     return httpServer!.channels[0].port;
   }
 
-  /// Server constructor.
+  /// Creates a new Socket.IO server instance.
   ///
-  /// @param {http.Server|Number|Object} http server, port or options
-  /// @param {Object} options
-  /// @api public
-  Server({server, Map? options}) {
-    options ??= {};
-    path(options.containsKey('path') ? options['path'] : '/socket.io');
+  /// The [server] parameter is optional and can be a [StreamServer] to attach to.
+  /// The [options] parameter configures server behavior.
+  ///
+  /// ## Available Options
+  ///
+  /// * `path` (String): The path to capture ('/' by default, '/socket.io')
+  /// * `serveClient` (bool): Whether to serve the client files (true by default)
+  /// * `adapter` (String): The adapter to use ('default' for in-memory)
+  /// * `origins` (String): Allowed origins ('*:*' by default)
+  /// * `pingTimeout` (int): How many ms without a pong packet to consider the connection closed (60000)
+  /// * `pingInterval` (int): How many ms before sending a new ping packet (25000)
+  /// * `transports` (List): Transports to allow (['websocket', 'polling'])
+  ///
+  /// ## Examples
+  ///
+  /// ### Basic server
+  /// ```dart
+  /// final io = Server();
+  /// io.listen(3000);
+  /// ```
+  ///
+  /// ### With existing HTTP server
+  /// ```dart
+  /// final httpServer = await StreamServer.bind('localhost', 3000);
+  /// final io = Server(server: httpServer);
+  /// ```
+  ///
+  /// ### With custom options
+  /// ```dart
+  /// final io = Server(options: {
+  ///   'path': '/my-socket',
+  ///   'pingTimeout': 120000,
+  ///   'pingInterval': 30000,
+  ///   'transports': ['websocket'],  // WebSocket only
+  /// });
+  /// ```
+  Server({final StreamServer? server, Map? options}) {
+    options ??= {};
+    path(options.containsKey('path') ? options['path'] as String? : '/socket.io');
     serveClient(false != options['serveClient']);
-    adapter = options.containsKey('adapter') ? options['adapter'] : 'default';
-    origins(options.containsKey('origins') ? options['origins'] : '*:*');
+    adapter = options.containsKey('adapter') ? options['adapter'] as String : 'default';
+    origins(options.containsKey('origins') ? options['origins'] as String? : '*:*');
     sockets = of('/');
     if (server != null) {
-      _ready = Future(() async {
+      _ready = Future(() async {
         await attach(server, options);
         return true;
       });
     } else {
-      _ready = Future.value(true);
+      _ready = Future.value(true);
     }
   }
 
   /// Server request verification function, that checks for allowed origins
   ///
   /// @param {http.IncomingMessage} request
-  /// @param {Function} callback to be called with the result: `fn(err, success)`
-  void checkRequest(HttpRequest req, [Function? fn]) {
-    var origin = req.headers.value('origin') ?? req.headers.value('referer');
+  /// @return {Future} true if the request is allowed, false otherwise
+  Future checkRequest(final HttpRequest req) async {
+    String? origin = req.headers.value('origin') ?? req.headers.value('referer');
 
     // file:// URLs produce a null Origin which can't be authorized via echo-back
     if (origin == null || origin.isEmpty) {
       origin = '*';
     }
 
-    if (origin.isNotEmpty && _origins is Function) {
-      return _origins(origin, fn);
-    }
-
-    if (_origins.contains('*:*')) {
-      return fn!(null, true);
+    if (origin.isNotEmpty && _origins.contains('*:*')) {
+      return true;
     }
 
     if (origin.isNotEmpty) {
       try {
-        var parts = Uri.parse(origin);
-        var port = parts.port;
-        var ok = _origins.indexOf(parts.host + ':' + port.toString()) >= 0 ||
-            _origins.indexOf(parts.host + ':*') >= 0 ||
-            _origins.indexOf('*:' + port.toString()) >= 0;
+        final Uri parts = Uri.parse(origin);
+        final int port = parts.port;
+        final bool ok = _origins.contains('${parts.host}:$port') ||
+            _origins.contains('${parts.host}:*') ||
+            _origins.contains('*:$port');
 
-        return fn!(null, ok);
+        return ok;
       } catch (ex) {
-        print(ex);
+        _logger.severe(ex);
       }
     }
 
-    fn!(null, false);
+    return false;
+  }
+
+  /// Wrapper to convert async checkRequest to callback-based for engine.io
+  bool _checkRequestWrapper(final dynamic req, final dynamic Function(dynamic, bool) callback) {
+    if (req is HttpRequest) {
+      // Fire and forget - the callback will be called asynchronously
+      unawaited(checkRequest(req).then((final bool result) {
+        callback(null, result);
+      }).catchError((final dynamic error) {
+        callback(error, false);
+      }));
+      return true; // Indicate that callback will be called asynchronously
+    }
+    callback(null, false);
+    return false;
   }
 
   /// Sets/gets whether client code is being served.
@@ -124,9 +241,9 @@ class Server {
   /// @param {Boolean} whether to serve client code
   /// @return {Server|Boolean} self when setting or value when getting
   /// @api public
-  dynamic serveClient([bool? v]) {
+  Object serveClient([final bool? v]) {
     if (v == null) {
-      return _serveClient;
+      return _serveClient ?? false;
     }
 
     _serveClient = v;
@@ -136,27 +253,27 @@ class Server {
   /// Backwards compatiblity.
   ///
   /// @api public
-  Server set(String key, [val]) {
+  Server set(final String key, [final Object? val]) {
     if ('authorization' == key && val != null) {
-      use((socket, next) {
-        val(socket.request, (err, authorized) {
-          if (err) {
+      use((final Object socket, final MiddlewareNext next) {
+        (val as Function)((socket as Socket).request, (final Object? err, final bool authorized) {
+          if (err != null) {
             return next(Exception(err));
           }
-          ;
-          if (!authorized) {
+          if (authorized != true) {
             return next(Exception('Not authorized'));
           }
 
-          next();
+          next(null);
         });
       });
     } else if ('origins' == key && val != null) {
-      origins(val);
+      origins(val as String?);
     } else if ('resource' == key) {
-      path(val);
-    } else if (oldSettings[key] && engine![oldSettings[key]]) {
-      engine![oldSettings[key]] = val;
+      path(val as String?);
+    } else if (oldSettings.containsKey(key) && engine != null) {
+      // Note: Engine type needs to be checked for containsKey method
+      _logger.fine('Setting engine property ${oldSettings[key]} to $val');
     } else {
       _logger.severe('Option $key is not valid. Please refer to the README.');
     }
@@ -169,9 +286,9 @@ class Server {
   /// @param {String} pathname
   /// @return {Server|String} self when setting or value when getting
   /// @api public
-  dynamic path([String? v]) {
-    if (v == null || v.isEmpty) return _path;
-    _path = v.replaceFirst(RegExp(r'/\/$/'), '');
+  Object path([final String? v]) {
+    if (v == null || v.isEmpty) return _path ?? '';
+    _path = v.replaceFirst(RegExp(r'/$'), '');
     return this;
   }
 
@@ -182,10 +299,10 @@ class Server {
   /// @api public
   String get adapter => _adapter;
 
-  set adapter(String v) {
+  set adapter(final String v) {
     _adapter = v;
     if (nsps.isNotEmpty) {
-      nsps.forEach((String i, Namespace nsp) {
+      nsps.forEach((final String i, final Namespace nsp) {
         nsp.initAdapter();
       });
     }
@@ -194,12 +311,12 @@ class Server {
   /// Sets the allowed origins for requests.
   ///
   /// @param {String} origins
-  /// @return {Server|Adapter} self when setting or value when getting
+  /// @return {Server|String} self when setting or value when getting
   /// @api public
-  dynamic origins([String? v]) {
+  Object origins([final String? v]) {
     if (v == null || v.isEmpty) return _origins;
 
-    _origins = v;
+    _origins = v.isNotEmpty ? v : '*:*';
     return this;
   }
 
@@ -209,96 +326,76 @@ class Server {
   /// @param {Object} options passed to engine.io
   /// @return {Server} self
   /// @api public
-  Future listen(srv, [Map? opts]) async {
-    await attach(srv, opts);
+  Future listen(final Object srv, [final Map? opts]) async {
+    if (srv is StreamServer) {
+      await attach(srv, opts);
+    } else if (srv is num) {
+      await attachToPort(srv.toInt(), opts);
+    } else if (srv is String && int.tryParse(srv) != null) {
+      await attachToPort(int.parse(srv), opts);
+    } else {
+      throw ArgumentError('Invalid server type. Expected StreamServer, int, or string representation of port number.');
+    }
   }
 
-  /// Attaches socket.io to a server or port.
+  /// Attaches socket.io to a StreamServer instance.
   ///
-  /// @param {http.Server|Number} server or port
+  /// @param {StreamServer} server instance
   /// @param {Object} options passed to engine.io
   /// @return {Server} self
   /// @api public
-  Future attach(dynamic srv, [Map? opts]) async {
-    if (srv is Function) {
-      var msg = 'You are trying to attach socket.io to an express '
-          'request handler function. Please pass a http.Server instance.';
-      throw Exception(msg);
-    }
+  Future attach(final StreamServer srv, [Map? opts]) async {
+    opts ??= {};
 
-    // handle a port as a string
-    if (srv is String && int.parse(srv.toString()).toString() == srv) {
-      srv = int.parse(srv.toString());
+    // set engine.io path to `/socket.io`
+    if (!opts.containsKey('path')) {
+      opts['path'] = path();
     }
+    // set origins verification
+    opts['allowRequest'] = _checkRequestWrapper;
+
+    _logger.fine('creating engine.io instance with opts $opts');
+    // initialize engine
+    engine = Engine.attach(srv, opts);
+
+    // Export http server
+    httpServer = srv;
+
+    // bind to engine events
+    bind(engine!);
+
+    return this;
+  }
 
-    opts ??= {};
+  /// Attaches socket.io to a port number by creating a new StreamServer.
+  ///
+  /// @param {int} port number
+  /// @param {Object} options passed to engine.io
+  /// @return {Server} self
+  /// @api public
+  Future attachToPort(final int port, [Map? opts]) async {
+    opts ??= {};
 
     // set engine.io path to `/socket.io`
     if (!opts.containsKey('path')) {
       opts['path'] = path();
     }
     // set origins verification
-    opts['allowRequest'] = checkRequest;
-
-    if (srv is num) {
-      _logger.fine('creating http server and binding to $srv');
-      var port = srv.toInt();
-      var server = StreamServer();
-      await server.start(port: port);
-//      HttpServer.bind(InternetAddress.ANY_IP_V4, port).then((
-//          HttpServer server) {
-//        this.httpServer = server;
-////                server.listen((HttpRequest request) {
-////                    HttpResponse response = request.response;
-////                    response.statusCode = HttpStatus.NOT_FOUND;
-////                    response.close();
-////                });
-
-      var completer = Completer();
-      var connectPacket = {'type': CONNECT, 'nsp': '/'};
-      encoder.encode(connectPacket, (encodedPacket) {
-        // the CONNECT packet will be merged with Engine.IO handshake,
-        // to reduce the number of round trips
-        opts!['initialPacket'] = encodedPacket;
-
-        _logger.fine('creating engine.io instance with opts $opts');
-        // initialize engine
-        engine = Engine.attach(server, opts);
-
-        // attach static file serving
-//        if (self._serveClient) self.attachServe(srv);
-
-        // Export http server
-        httpServer = server;
-
-        // bind to engine events
-        bind(engine!);
-
-        completer.complete();
-      });
-      await completer.future;
-//      });
-    } else {
-      var connectPacket = {'type': CONNECT, 'nsp': '/'};
-      encoder.encode(connectPacket, (encodedPacket) {
-        // the CONNECT packet will be merged with Engine.IO handshake,
-        // to reduce the number of round trips
-        opts!['initialPacket'] = encodedPacket;
+    opts['allowRequest'] = _checkRequestWrapper;
 
-        _logger.fine('creating engine.io instance with opts $opts');
-        // initialize engine
-        engine = Engine.attach(srv, opts);
+    _logger.fine('creating http server and binding to $port');
+    final StreamServer server = StreamServer();
+    await server.start(port: port);
 
-        // attach static file serving
-//        if (self._serveClient) self.attachServe(srv);
+    _logger.fine('creating engine.io instance with opts $opts');
+    // initialize engine
+    engine = Engine.attach(server, opts);
 
-        // Export http server
-        httpServer = srv;
+    // Export http server
+    httpServer = server;
 
-        // bind to engine events
-        bind(engine!);
-      });
-    }
+    // bind to engine events
+    bind(engine!);
 
     return this;
   }
@@ -309,53 +406,13 @@ class Server {
   /// @api private
   /// @todo Include better way to serve files
 //    attachServe(srv){
-//        _logger.fine()('attaching client serving req handler');
-//        var url = this._path + '/socket.io.js';
-//        var evs = srv.listeners('request').slice(0);
-//        var self = this;
-//        srv.removeAllListeners('request');
-//        srv.on('request', function(req, res) {
-//        if (0 === req.url.indexOf(url)) {
-//        self.serve(req, res);
-//        } else {
-//        for (var i = 0; i < evs.length; i++) {
-//        evs[i].call(srv, req, res);
-//        }
-//        }
-//        })
-//    }
-
-  /// Handles a request serving `/socket.io.js`
-  ///
-  /// @param {http.Request} req
-  /// @param {http.Response} res
-  /// @api private
-  /// @todo Include better way to serve files
-
-//    serve(req, res){
-//        var etag = req.headers['if-none-match'];
-//        if (etag) {
-//            if (clientVersion == etag) {
-//                debug('serve client 304');
-//                res.writeHead(304);
-//                res.end();
-//                return;
-//            }
-//        }
-//
-//        debug('serve client source');
-//        res.setHeader('Content-Type', 'application/javascript');
-//        res.setHeader('ETag', clientVersion);
-//        res.writeHead(200);
-//        res.end(clientSource);
-//    }
 
   /// Binds socket.io to an engine.io instance.
   ///
   /// @param {engine.Server} engine.io (or compatible) server
   /// @return {Server} self
   /// @api public
-  Server bind(Engine engine) {
+  Server bind(final Engine engine) {
     this.engine = engine;
     this.engine!.on('connection', onconnection);
     return this;
@@ -366,10 +423,9 @@ class Server {
   /// @param {engine.Socket} socket
   /// @return {Server} self
   /// @api public
-  Server onconnection(conn) {
-    _logger.fine('incoming connection with id ${conn.id}');
-    var client = Client(this, conn);
-    client.connect('/');
+  Server onconnection(final dynamic conn) {
+    _logger.fine('incoming connection with id ${conn.toString()}');
+    Client(this, conn).connect('/');
     return this;
   }
 
@@ -378,18 +434,39 @@ class Server {
   /// @param {String} nsp name
   /// @param {Function} optional, nsp `connection` ev handler
   /// @api public
-  Namespace of(name, [fn]) {
-    if (name.toString()[0] != '/') {
-      name = '/' + name;
+  // Namespace of(Object name, [Function? fn]) {
+  //   if (name is! String) {
+  //     fn = name as Function?;
+  //     name = '/';
+  //   }
+  //   String nameStr = name.toString();
+  //   if (nameStr[0] != '/') {
+  //     nameStr = '/$nameStr';
+  //   }
+  //
+  //   if (!nsps.containsKey(nameStr)) {
+  //     _logger.fine('initializing namespace $nameStr');
+  //     final Namespace nsp = Namespace(this, nameStr);
+  //     nsps[nameStr] = nsp;
+  //   }
+  //   if (fn != null) {
+  //     nsps[nameStr]!.on('connect', (final dynamic socket) => fn!(socket));
+  //   }
+  //   return nsps[nameStr]!;
+  // }
+  Namespace of(Object name) {
+    final Object rawName = name is String ? name : '/';
+    String nameStr = rawName.toString();
+    if (nameStr[0] != '/') {
+      nameStr = '/$nameStr';
     }
 
-    if (!nsps.containsKey(name)) {
-      _logger.fine('initializing namespace $name');
-      var nsp = Namespace(this, name);
-      nsps[name] = nsp;
+    if (!nsps.containsKey(nameStr)) {
+      _logger.fine('initializing namespace $nameStr');
+      final Namespace nsp = Namespace(this, nameStr);
+      nsps[nameStr] = nsp;
     }
-    if (fn != null) nsps[name]!.on('connect', fn);
-    return nsps[name]!;
+    return nsps[nameStr]!;
   }
 
   /// Closes server connection
@@ -397,7 +474,7 @@ class Server {
   /// @return a Future that resolves when the httpServer is closed
   /// @api public
   Future close() async {
-    nsps['/']!.sockets.toList(growable: false).forEach((socket) {
+    nsps['/']!.sockets.toList(growable: false).forEach((final Socket socket) {
       socket.onclose();
     });
 
@@ -411,16 +488,28 @@ class Server {
   }
 
   // redirect to sockets method
-  Namespace to(_) => sockets.to(_);
-  Namespace use(_) => sockets.use(_);
-  void send(_) => sockets.send(_);
-  Namespace write(_) => sockets.write(_);
-  Namespace clients(_) => sockets.clients(_);
-  Namespace compress(_) => sockets.compress(_);
+  Namespace to(final String data) => sockets.to(data);
+  Namespace use(final MiddlewareCallback middleware) => sockets.use(middleware);
+  void send(final Object data) => sockets.send(data);
+  Namespace write(final Object data) => sockets.write(data);
+  Namespace clients(final List? rooms) => sockets.clients(rooms);
+  Namespace compress(final bool compress) => sockets.compress(compress);
 
   // emitter
-  void emit(event, data) => sockets.emit(event, data);
-  void on(event, handler) => sockets.on(event, handler);
-  void once(event, handler) => sockets.once(event, handler);
-  void off(event, handler) => sockets.off(event, handler);
+  void emit(final String event, final Object? data) => sockets.emit(event, data);
+
+  // Special overload for connection events that pass Socket objects
+  void onConnection(final void Function(Socket) handler) =>
+      sockets.on('connection', (final dynamic data) => handler(data as Socket));
+
+  // General event handler for other events
+  void on(final String event, final EventHandler handler) {
+    if (event == 'connection') {
+      throw ArgumentError('Use onConnection() method for connection events instead of on()');
+    }
+    sockets.on(event, handler);
+  }
+
+  void once(final String event, final EventHandler handler) => sockets.once(event, handler);
+  void off(final String event, final EventHandler handler) => sockets.off(event, handler);
 }
diff --git a/lib/src/socket.dart b/lib/src/socket.dart
index 016c08a..c246dc3 100644
--- a/lib/src/socket.dart
+++ b/lib/src/socket.dart
@@ -1,127 +1,336 @@
-/// socket.dart
+/*
+socket.dart
+
+Purpose:
+
+Description:
+
+History:
+   22/02/2017, Created by jumperchen
+
+Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+*/
+import 'dart:io';
+
+import 'package:socket_io_common/socket_io_common.dart';
+
+import 'adapter.dart';
+import 'client.dart';
+import 'engine/socket.dart' as engine;
+import 'models/callbacks_models.dart';
+import 'models/handshake_data_models.dart';
+import 'models/packet_data_models.dart';
+import 'models/packet_models.dart';
+import 'models/room_management_models.dart';
+import 'models/socket_data_models.dart';
+import 'namespace.dart';
+import 'server.dart';
+import 'util/event_emitter.dart';
+import 'value_objects/connection_id_vo.dart';
+import 'value_objects/event_name_vo.dart';
+import 'value_objects/query_parameters_vo.dart';
+
+/// Represents an individual client connection to the Socket.IO server.
 ///
-/// Purpose:
+/// A [Socket] is the fundamental class for interacting with a client. It allows
+/// you to send and receive events, manage rooms, and handle disconnections.
 ///
-/// Description:
+/// ## Basic Usage
 ///
-/// History:
-///    22/02/2017, Created by jumperchen
+/// ```dart
+/// io.on('connection', (socket) {
+///   print('Client connected: ${socket.id}');
 ///
-/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
-import 'dart:io';
-import 'package:socket_io/src/adapter/adapter.dart';
-import 'package:socket_io/src/client.dart';
-import 'package:socket_io_common/src/parser/parser.dart';
-import 'package:socket_io/src/namespace.dart';
-import 'package:socket_io/src/server.dart';
-import 'package:socket_io/src/util/event_emitter.dart';
-
-/// Module exports.
-//
-//module.exports = exports = Socket;
-
-/// Blacklisted events.
+///   // Listen for events
+///   socket.on('message', (data) {
+///     print('Received: $data');
+///   });
 ///
-/// @api public
-
-List events = [
-  'error',
-  'connect',
-  'disconnect',
-  'newListener',
-  'removeListener'
-];
-
-/// Flags.
+///   // Emit events
+///   socket.emit('welcome', ['Hello, client!']);
 ///
-/// @api private
-List flags = ['json', 'volatile', 'broadcast'];
-
-const List EVENTS = [
-  'error',
-  'connect',
-  'disconnect',
-  'disconnecting',
-  'newListener',
-  'removeListener'
-];
-
+///   // Handle disconnection
+///   socket.on('disconnect', (reason) {
+///     print('Client disconnected: $reason');
+///   });
+/// });
+/// ```
+///
+/// ## Rooms
+///
+/// ```dart
+/// // Join a room
+/// socket.join('room1');
+///
+/// // Emit to all clients in a room
+/// io.to('room1').emit('message', ['Room broadcast']);
+///
+/// // Emit to all except sender
+/// socket.broadcast.to('room1').emit('message', ['Others in room']);
+///
+/// // Leave a room
+/// socket.leave('room1');
+/// ```
+///
+/// ## Acknowledgments
+///
+/// ```dart
+/// // Server requests acknowledgment
+/// socket.emit('question', ['What is 2+2?'], ack: (response) {
+///   print('Client answered: $response');
+/// });
+///
+/// // Server sends acknowledgment
+/// socket.on('question', (data) {
+///   // Process question
+///   return ['The answer is 42'];  // Sent as acknowledgment
+/// });
+/// ```
+///
+/// ## Binary Data
+///
+/// ```dart
+/// // Send binary data
+/// final bytes = Uint8List.fromList([1, 2, 3, 4]);
+/// socket.emit('binary', [bytes]);
+///
+/// // Receive binary data
+/// socket.on('binary', (data) {
+///   if (data is List) {
+///     print('Received ${data.length} bytes');
+///   }
+/// });
+/// ```
+///
+/// ## Per-Socket Data
+///
+/// ```dart
+/// // Using typed API
+/// socket.socketData.set('userId', 123);
+/// final userId = socket.socketData.getInt('userId');
+///
+/// // Using legacy API
+/// socket.data['userId'] = 123;
+/// final userId = socket.data['userId'];
+/// ```
+///
+/// See also:
+/// * [Namespace] for namespace-level operations
+/// * [Server.on] for handling connection events
 class Socket extends EventEmitter {
   // ignore: undefined_class
   Namespace nsp;
   Client client;
   late Server server;
   late Adapter adapter;
-  late String id;
+  late ConnectionId connectionId;
+  late String id; // Keep for backward compatibility
   late HttpRequest request;
-  var conn;
-  Map roomMap = {};
-  List roomList = [];
-  Map acks = {};
+  late engine.Socket conn;
+
+  // New typed room management
+  RoomMembership roomMembership = RoomMembership();
+  // Keep for backward compatibility
+  Map roomMap = {};
+
+  List roomList = [];
+  Map acksTyped = {}; // New typed version
+  Map acks = {}; // Keep for backward compatibility
   bool connected = true;
   bool disconnected = false;
-  Map? handshake;
+  HandshakeDataModel? handshakeData; // New typed version
+  Map? handshake; // Keep for backward compatibility
   Map? flags;
 
-  // a data store for each socket.
-  Map data = {};
+  // a data store for each socket - new typed version
+  SocketDataModel socketData = SocketDataModel();
+  // Keep old version for backward compatibility
+  Map data = {};
+
+  // New typed query parameters
+  QueryParameters? queryParameters;
 
-  Socket(this.nsp, this.client, query) {
+  Socket(this.nsp, this.client, final Map? query) {
     server = nsp.server;
     adapter = nsp.adapter;
     id = client.id;
+    connectionId = ConnectionId(client.id);
     request = client.request;
     conn = client.conn;
+
+    // Convert query to QueryParameters if provided
+    if (query != null && query.isNotEmpty) {
+      queryParameters = QueryParameters(query);
+    }
+
     handshake = buildHandshake(query);
+    handshakeData = buildHandshakeTyped(query);
+    // Sync data between old and new storage
+    data = socketData.toMap();
   }
 
-  /// Builds the `handshake` BC object
+  /// Typed constructor using QueryParameters
+  Socket.typed(this.nsp, this.client, this.queryParameters) {
+    server = nsp.server;
+    adapter = nsp.adapter;
+    id = client.id;
+    connectionId = ConnectionId(client.id);
+    request = client.request;
+    conn = client.conn;
+
+    // Convert QueryParameters to Map for backward compatibility
+    final Map? query = queryParameters?.toDynamicMap();
+
+    handshake = buildHandshake(query);
+    handshakeData = buildHandshakeTyped(query);
+    // Sync data between old and new storage
+    data = socketData.toMap();
+  }
+
+  /// Checks if the current connection is secure (HTTPS/WSS).
   ///
+  /// Determines security based on:
+  /// - URI scheme (https/wss)
+  /// - X-Forwarded-Proto header (for proxied connections)
+  ///
+  /// @return {bool} true if connection is secure
   /// @api private
-  Map buildHandshake(query) {
-    final buildQuery = () {
-      var requestQuery = request.uri.queryParameters;
+  bool _isSecureConnection() {
+    // Check if request URI scheme is secure
+    final String scheme = request.uri.scheme.toLowerCase();
+    if (scheme == 'https' || scheme == 'wss') {
+      return true;
+    }
+
+    // Check X-Forwarded-Proto header for proxied HTTPS connections
+    final String? forwardedProto = request.headers.value('x-forwarded-proto');
+    if (forwardedProto?.toLowerCase() == 'https') {
+      return true;
+    }
+
+    // Check connection info if available (some platforms may not support this)
+    try {
+      final HttpConnectionInfo? connectionInfo = request.connectionInfo;
+      if (connectionInfo != null) {
+        // For HTTP/2 or when the connection is encrypted
+        return connectionInfo.remotePort == 443;
+      }
+    } catch (e) {
+      // connectionInfo may not be available on all platforms
+    }
+
+    return false;
+  }
+
+  /// Detects if data contains binary content.
+  ///
+  /// Binary data is detected by checking if:
+  /// - Data is a List (byte array)
+  /// - Data is a List containing any List elements
+  ///
+  /// @param {dynamic} data - The data to check
+  /// @return {bool} true if data contains binary content
+  /// @api private
+  bool _containsBinaryData(final dynamic data) {
+    if (data == null) return false;
+
+    // Check if data itself is binary (List)
+    if (data is List) return true;
+
+    // Check if data is a List containing binary data
+    if (data is List) {
+      for (final dynamic item in data) {
+        if (item is List) return true;
+        // Recursively check nested lists
+        if (item is List && _containsBinaryData(item)) return true;
+      }
+    }
+
+    // Check if data is a Map containing binary values
+    if (data is Map) {
+      for (final dynamic value in data.values) {
+        if (_containsBinaryData(value)) return true;
+      }
+    }
+
+    return false;
+  }
+
+  /// Builds the `handshake` BC object (legacy version)
+  ///
+  /// @api private
+  Map buildHandshake(final Map? query) {
+    Map buildQuery() {
+      final Map requestQuery = request.uri.queryParameters;
       //if socket-specific query exist, replace query strings in requestQuery
-      return query != null
-          ? (Map.from(query)..addAll(requestQuery))
-          : requestQuery;
-    };
-    return {
+      return query != null ? (Map.from(query)..addAll(requestQuery)) : requestQuery;
+    }
+
+    return {
       'headers': request.headers,
       'time': DateTime.now().toString(),
       'address': conn.remoteAddress,
       'xdomain': request.headers.value('origin') != null,
-      // TODO  'secure': ! !this.request.connectionInfo.encrypted,
+      'secure': _isSecureConnection(),
       'issued': DateTime.now().millisecondsSinceEpoch,
       'url': request.uri.path,
       'query': buildQuery()
     };
   }
 
+  /// Builds the typed `handshake` object (new version)
+  ///
+  /// @api private
+  HandshakeDataModel buildHandshakeTyped(final Map? query) {
+    Map buildQuery() {
+      final Map requestQuery = request.uri.queryParameters;
+      //if socket-specific query exist, replace query strings in requestQuery
+      if (query != null) {
+        final Map combined = Map.from(requestQuery);
+        query.forEach((final String key, final dynamic value) {
+          combined[key] = value.toString();
+        });
+        return combined;
+      }
+      return requestQuery;
+    }
+
+    return HandshakeDataBuilder()
+        .headers(request.headers)
+        .time(DateTime.now())
+        .address(conn.remoteAddress)
+        .xdomain(request.headers.value('origin') != null)
+        .secure(_isSecureConnection())
+        .issued(DateTime.now().millisecondsSinceEpoch)
+        .url(request.uri.path)
+        .queryMap(buildQuery())
+        .build();
+  }
+
   Socket get json {
-    flags = flags ?? {};
+    flags = flags ?? {};
     flags!['json'] = true;
     return this;
   }
 
   Socket get volatile {
-    flags = flags ?? {};
+    flags = flags ?? {};
     flags!['volatile'] = true;
     return this;
   }
 
   Socket get broadcast {
-    flags = flags ?? {};
+    flags = flags ?? {};
     flags!['broadcast'] = true;
     return this;
   }
 
   @override
-  void emit(String event, [data]) {
+  void emit(final String event, [final dynamic data]) {
     emitWithAck(event, data);
   }
 
-  void emitWithBinary(String event, [data]) {
+  void emitWithBinary(final String event, [final dynamic data]) {
     emitWithAck(event, data, binary: true);
   }
 
@@ -129,46 +338,50 @@ class Socket extends EventEmitter {
   ///
   /// @return {Socket} self
   /// @api public
-  void emitWithAck(String event, dynamic data,
-      {Function? ack, bool binary = false}) {
-    if (EVENTS.contains(event)) {
+  void emitWithAck(final String event, final dynamic data, {final Function? ack, final bool binary = false}) {
+    if (EventName.isReserved(event)) {
       super.emit(event, data);
     } else {
-      var packet = {};
-      var sendData = data == null ? [event] : [event, data];
-
-      var flags = this.flags ?? {};
+      final List sendData = data == null ? [event] : [event, data];
+      final Map flags = this.flags ?? {};
 
+      String? packetId;
       if (ack != null) {
         if (roomList.isNotEmpty || flags['broadcast'] == true) {
-          throw UnsupportedError(
-              'Callbacks are not supported when broadcasting');
+          throw UnsupportedError('Callbacks are not supported when broadcasting');
         }
 
-        acks['${nsp.ids}'] = ack;
-        packet['id'] = '${nsp.ids++}';
+        packetId = '${nsp.ids++}';
+        acks[packetId] = ack;
       }
 
-      packet['type'] = binary ? BINARY_EVENT : EVENT;
-      packet['data'] = sendData;
+      // Create typed packet instead of dynamic map
+      final EventPacket eventPacket = EventPacket.typed(
+        typedData: EventPacketData.fromList(sendData),
+        namespace: nsp.name,
+        id: packetId,
+        binary: binary,
+      );
 
       if (roomList.isNotEmpty || flags['broadcast'] == true) {
-        adapter.broadcast(packet, {
-          'except': [id],
+        adapter.broadcast(eventPacket.toMap(), {
+          'except': [id],
           'rooms': roomList,
           'flags': flags
         });
       } else {
-        // dispatch packet
-        this.packet(packet,
-            {'volatile': flags['volatile'], compress: flags['compress']});
+        // dispatch packet with proper options typing
+        sendPacket(
+            eventPacket,
+            PacketOptions(
+              volatile: flags['volatile'] ?? false,
+              compress: flags['compress'] ?? false,
+            ));
       }
 
-//      // reset flags
-      roomList = [];
+      // reset flags
+      roomList = [];
       this.flags = null;
-//    }
-//    return this;
     }
   }
 
@@ -177,7 +390,7 @@ class Socket extends EventEmitter {
   /// @param {String} name
   /// @return {Socket} self
   /// @api public
-  Socket to(String name) {
+  Socket to(final String name) {
     if (!roomList.contains(name)) roomList.add(name);
     return this;
   }
@@ -186,28 +399,40 @@ class Socket extends EventEmitter {
   ///
   /// @return {Socket} self
   /// @api public
-  void send(_) {
-    write(_);
+  void send(final dynamic data) {
+    write(data as List);
   }
 
-  Socket write(List data) {
+  Socket write(final List data) {
     emit('message', data);
     return this;
   }
 
-  /// Writes a packet.
+  /// Writes a packet using typed models.
+  ///
+  /// @param {SocketIOPacket} packet typed packet object
+  /// @param {PacketOptions} options packet options
+  /// @api private
+  void sendPacket(final SocketIOPacket packet, final PacketOptions options) {
+    final Map packetMap = packet.toMap();
+    // Set namespace if not already set
+    if (packetMap['nsp'] == null) {
+      packetMap['nsp'] = nsp.name;
+    }
+    client.packet(packetMap, options.toMap());
+  }
+
+  /// Writes a packet (legacy method for backward compatibility).
   ///
   /// @param {Object} packet object
   /// @param {Object} options
   /// @api private
-  void packet(packet, [opts]) {
+  void packet(final Map packet, [final Map? opts]) {
     // ignore preEncoded = true.
-    if (packet is Map) {
-      packet['nsp'] = nsp.name;
-    }
-    opts = opts ?? {};
-    opts['compress'] = false != opts['compress'];
-    client.packet(packet, opts);
+    packet['nsp'] = nsp.name;
+    final Map options = opts ?? {};
+    options['compress'] = options['compress'] != false;
+    client.packet(packet, options);
   }
 
   /// Joins a room.
@@ -216,15 +441,19 @@ class Socket extends EventEmitter {
   /// @param {Function} optional, callback
   /// @return {Socket} self
   /// @api private
-  Socket join(room, [fn]) {
-//    debug('joining room %s', room);
-    if (roomMap.containsKey(room)) {
+  Socket join(final String room, [final Function? fn]) {
+    if (roomMembership.containsName(room)) {
       if (fn != null) fn(null);
       return this;
     }
-    adapter.add(id, room, ([err]) {
-      if (err != null) return fn?.call(err);
-//      _logger.info('joined room %s', room);
+    adapter.add(id, room, ([final Object? err]) {
+      if (err != null) {
+        fn?.call(err);
+        return;
+      }
+      // Update new typed model
+      roomMembership.addByName(room);
+      // Keep old map in sync for backward compatibility
       roomMap[room] = room;
       if (fn != null) fn(null);
     });
@@ -237,11 +466,15 @@ class Socket extends EventEmitter {
   /// @param {Function} optional, callback
   /// @return {Socket} self
   /// @api private
-  Socket leave(room, fn) {
-//    debug('leave room %s', room);
-    adapter.del(id, room, ([err]) {
-      if (err != null) return fn?.call(err);
-//      _logger.info('left room %s', room);
+  Socket leave(final String room, final Function? fn) {
+    adapter.del(id, room, ([final Object? err]) {
+      if (err != null) {
+        fn?.call(err);
+        return;
+      }
+      // Update new typed model
+      roomMembership.removeByName(room);
+      // Keep old map in sync for backward compatibility
       roomMap.remove(room);
       fn?.call(null);
     });
@@ -254,7 +487,10 @@ class Socket extends EventEmitter {
 
   void leaveAll() {
     adapter.delAll(id);
-    roomMap = {};
+    // Clear new typed model
+    roomMembership.clear();
+    // Keep old map in sync for backward compatibility
+    roomMap = {};
   }
 
   /// Called by `Namespace` upon succesful
@@ -263,19 +499,21 @@ class Socket extends EventEmitter {
   /// @api private
 
   void onconnect() {
-//    debug('socket connected - writing packet');
     nsp.connected[id] = this;
     join(id);
-    packet({'type': CONNECT});
+    // Socket.IO v3: Include socket ID in CONNECT packet using typed model
+    final ConnectPacket connectPacket = ConnectPacket.typed(
+      namespace: nsp.name,
+      typedData: ConnectPacketData(sid: id),
+    );
+    sendPacket(connectPacket, PacketOptions());
   }
 
   /// Called with each packet. Called by `Client`.
   ///
   /// @param {Object} packet
   /// @api private
-
-  void onpacket(packet) {
-//    debug('got packet %j', packet);
+  void onpacket(final Map packet) {
     switch (packet['type']) {
       case EVENT:
         onevent(packet);
@@ -297,7 +535,7 @@ class Socket extends EventEmitter {
         ondisconnect();
         break;
 
-      case ERROR:
+      case CONNECT_ERROR:
         emit('error', packet['data']);
     }
   }
@@ -306,18 +544,16 @@ class Socket extends EventEmitter {
   ///
   /// @param {Object} packet object
   /// @api private
-  void onevent(packet) {
-    List args = packet['data'] ?? [];
-//    debug('emitting event %j', args);
+  void onevent(final Map packet) {
+    final List args = (packet['data'] as List?) ?? [];
 
     if (null != packet['id']) {
-//      debug('attaching ack callback to event');
       args.add(ack(packet['id']));
     }
 
     // dart doesn't support "String... rest" syntax.
     if (args.length > 2) {
-      Function.apply(super.emit, [args.first, args.sublist(1)]);
+      Function.apply(super.emit, [args.first, args.sublist(1)]);
     } else {
       Function.apply(super.emit, args);
     }
@@ -327,20 +563,21 @@ class Socket extends EventEmitter {
   ///
   /// @param {Number} packet id
   /// @api private
-  Function ack(id) {
-    var sent = false;
-    return (_) {
+  AckFunction ack(final String id) {
+    bool sent = false;
+    return (final dynamic data) {
       // prevent double callbacks
       if (sent) return;
 //      var args = Array.prototype.slice.call(arguments);
-//      debug('sending ack %j', args);
-
-      var type = /*hasBin(args) ? parser.BINARY_ACK : parser.*/ ACK;
-      packet({
-        'id': id,
-        'type': type,
-        'data': [_]
-      });
+
+      // Use typed AckPacket instead of dynamic map
+      final AckPacket ackPacket = AckPacket.typed(
+        id: id.toString(),
+        typedData: AckPacketData.fromList([data]),
+        namespace: nsp.name,
+        binary: _containsBinaryData(data),
+      );
+      sendPacket(ackPacket, PacketOptions());
       sent = true;
     };
   }
@@ -348,28 +585,22 @@ class Socket extends EventEmitter {
   /// Called upon ack packet.
   ///
   /// @api private
-  void onack(packet) {
-    Function ack = acks.remove(packet['id']);
-    if (ack is Function) {
-//      debug('calling ack %s with %j', packet.id, packet.data);
-      Function.apply(ack, packet['data']);
-    } else {
-//      debug('bad ack %s', packet.id);
-    }
+  void onack(final Map packet) {
+    final Function ack = acks.remove(packet['id']) as Function;
+    Function.apply(ack, packet['data'] as List?);
   }
 
   /// Called upon client disconnect packet.
   ///
   /// @api private
   void ondisconnect() {
-//    debug('got disconnect packet');
     onclose('client namespace disconnect');
   }
 
   /// Handles a client error.
   ///
   /// @api private
-  void onerror(err) {
+  void onerror(final Object err) {
     if (hasListeners('error')) {
       emit('error', err);
     } else {
@@ -383,9 +614,8 @@ class Socket extends EventEmitter {
   /// @param {String} reason
   /// @param {Error} optional error object
   /// @api private
-  dynamic onclose([reason]) {
-    if (!connected) return this;
-//    debug('closing socket - reason %s', reason);
+  void onclose([final String? reason]) {
+    if (!connected) return;
     emit('disconnecting', reason);
     leaveAll();
     nsp.remove(this);
@@ -400,8 +630,12 @@ class Socket extends EventEmitter {
   ///
   /// @param {Object} error object
   /// @api private
-  void error(err) {
-    packet({'type': ERROR, 'data': err});
+  void error(final Object err) {
+    final ConnectErrorPacket errorPacket = ConnectErrorPacket.typed(
+      typedData: ConnectErrorPacketData.fromJson(err),
+      namespace: nsp.name,
+    );
+    sendPacket(errorPacket, PacketOptions());
   }
 
   /// Disconnects this client.
@@ -409,13 +643,15 @@ class Socket extends EventEmitter {
   /// @param {Boolean} if `true`, closes the underlying connection
   /// @return {Socket} self
   /// @api public
-
-  Socket disconnect([close]) {
+  Socket disconnect([final bool? close]) {
     if (!connected) return this;
     if (close == true) {
       client.disconnect();
     } else {
-      packet({'type': DISCONNECT});
+      final DisconnectPacket disconnectPacket = DisconnectPacket.typed(
+        namespace: nsp.name,
+      );
+      sendPacket(disconnectPacket, PacketOptions());
       onclose('server namespace disconnect');
     }
     return this;
@@ -426,9 +662,9 @@ class Socket extends EventEmitter {
   /// @param {Boolean} if `true`, compresses the sending data
   /// @return {Socket} self
   /// @api public
-  Socket compress(compress) {
-    flags = flags ?? {};
-    flags!['compress'] = compress;
+  Socket compress(final dynamic compress) {
+    flags = flags ?? {};
+    flags!['compress'] = compress as bool;
     return this;
   }
 }
diff --git a/lib/src/types/common_types.dart b/lib/src/types/common_types.dart
new file mode 100644
index 0000000..993b40e
--- /dev/null
+++ b/lib/src/types/common_types.dart
@@ -0,0 +1,17 @@
+/// common_types.dart
+///
+/// Common type aliases used across the Socket.IO library.
+///
+/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+library common_types;
+
+/// General JSON-like map used within internal processing.
+///
+/// This is used for decoded packet data and other internal JSON structures.
+typedef JsonMap = Map;
+
+/// HTTP headers map used internally in transports before assigning to HttpHeaders.
+///
+/// Values may be String, int, or Iterable depending on the header type.
+/// This is primarily used in polling transport for constructing response headers.
+typedef Headers = Map;
diff --git a/lib/src/util/async_utils.dart b/lib/src/util/async_utils.dart
new file mode 100644
index 0000000..9c667b0
--- /dev/null
+++ b/lib/src/util/async_utils.dart
@@ -0,0 +1,21 @@
+/// async_utils.dart
+///
+/// Utilities for async operations
+///
+/// Copyright (C) 2024 Potix Corporation. All Rights Reserved.
+library async_utils;
+
+/// Fire-and-forget async operation handler.
+///
+/// Use this when you intentionally want to ignore a Future result.
+/// This is critical for operations that should not block execution
+/// and where errors are handled by the caller's error handling.
+///
+/// Example:
+/// ```dart
+/// unawaited(someAsyncOperation());
+/// ```
+void unawaited(final Future future) {
+  // Intentionally ignore the future - this is fire-and-forget
+  // Any errors will be handled by the caller's error handling
+}
diff --git a/lib/src/util/event_emitter.dart b/lib/src/util/event_emitter.dart
index 7c8b66e..bb13b86 100644
--- a/lib/src/util/event_emitter.dart
+++ b/lib/src/util/event_emitter.dart
@@ -1,60 +1,88 @@
-/// event_emitter.dart
-///
-/// Purpose:
-///
-/// Description:
-///
-/// History:
-///     11/23/2016, Created by Henri Chen
-///
-/// Copyright (C) 2016 Potix Corporation. All Rights Reserved.
+/*
+event_emitter.dart
+
+Purpose:
+
+Description:
+
+History:
+    11/23/2016, Created by Henri Chen
+
+Copyright (C) 2016 Potix Corporation. All Rights Reserved.
+*/
 import 'dart:collection' show HashMap;
 
+/// Union type for Socket.IO event data - represents the actual types used in the library
+typedef SocketIOEventData = dynamic;
+
+/// More specific type constraints for Socket.IO events
+/// This represents the actual data types passed in Socket.IO events:
+/// - String: Simple text messages
+/// - Map: JSON-like objects (connection data, error objects)
+/// - List: Arrays of mixed data (event arguments)
+/// - int/num: Numeric values (IDs, counts, etc.)
+/// - bool: Boolean flags
+/// - Socket: Socket objects for connection events
+/// - null: Empty events
+bool isValidSocketIOData(final dynamic data) =>
+    data == null ||
+    data is String ||
+    data is num ||
+    data is bool ||
+    data is Map ||
+    data is List ||
+    data.runtimeType.toString().contains('Socket'); // Allow Socket objects
+
 /// Handler type for handling the event emitted by an [EventEmitter].
-typedef EventHandler = dynamic Function(T data);
+/// Uses SocketIOEventData to be more explicit about expected types
+typedef EventHandler = void Function(T data);
 
 /// Generic event emitting and handling.
 class EventEmitter {
   /// Mapping of events to a list of event handlers
-  Map> _events =
-      HashMap>();
+  HashMap>> _events =
+      HashMap>>();
 
   /// Mapping of events to a list of one-time event handlers
-  Map> _eventsOnce =
-      HashMap>();
+  Map>> _eventsOnce =
+      HashMap>>();
 
   /// This function triggers all the handlers currently listening
   /// to [event] and passes them [data].
-  void emit(String event, [dynamic data]) {
-    final list0 = _events[event];
+  void emit(final String event, [final SocketIOEventData data]) {
+    // Validate data type in debug mode
+    assert(isValidSocketIOData(data), 'Invalid data type for Socket.IO event: {data.runtimeType}');
+
+    final List>? list0 = _events[event];
     // todo: try to optimize this. Maybe remember the off() handlers and remove later?
     // handler might be off() inside handler; make a copy first
-    final list = list0 != null ? List.from(list0) : null;
-    list?.forEach((handler) {
+    final List>? list =
+        list0 != null ? List>.from(list0) : null;
+    list?.forEach((final EventHandler handler) {
       handler(data);
     });
 
-    _eventsOnce.remove(event)?.forEach((EventHandler handler) {
+    _eventsOnce.remove(event)?.forEach((final EventHandler handler) {
       handler(data);
     });
   }
 
   /// This function binds the [handler] as a listener to the [event]
-  void on(String event, EventHandler handler) {
-    _events.putIfAbsent(event, () => []);
+  void on(final String event, final EventHandler handler) {
+    _events.putIfAbsent(event, () => >[]);
     _events[event]!.add(handler);
   }
 
   /// This function binds the [handler] as a listener to the first
   /// occurrence of the [event]. When [handler] is called once,
   /// it is removed.
-  void once(String event, EventHandler handler) {
-    _eventsOnce.putIfAbsent(event, () => []);
+  void once(final String event, final EventHandler handler) {
+    _eventsOnce.putIfAbsent(event, () => >[]);
     _eventsOnce[event]!.add(handler);
   }
 
   /// This function attempts to unbind the [handler] from the [event]
-  void off(String event, [EventHandler? handler]) {
+  void off(final String event, [final EventHandler? handler]) {
     if (handler != null) {
       _events[event]?.remove(handler);
       _eventsOnce[event]?.remove(handler);
@@ -72,13 +100,84 @@ class EventEmitter {
 
   /// This function unbinds all the handlers for all the events.
   void clearListeners() {
-    _events = HashMap>();
-    _eventsOnce = HashMap>();
+    _events = HashMap>>();
+    _eventsOnce = HashMap>>();
   }
 
   /// Returns whether the event has registered.
-  bool hasListeners(String event) {
-    return _events[event]?.isNotEmpty == true ||
-        _eventsOnce[event]?.isNotEmpty == true;
+  bool hasListeners(final String event) => _events[event]?.isNotEmpty == true || _eventsOnce[event]?.isNotEmpty == true;
+}
+
+/// Type-safe event emitter for specific event data types
+///
+/// This provides a strongly-typed alternative to EventEmitter where you can
+/// specify the type of data that will be emitted/handled for events.
+///
+/// Example:
+/// ```dart
+/// final TypedEventEmitter stringEmitter = TypedEventEmitter();
+/// stringEmitter.on('message', (String data) => print('Received: $data'));
+/// stringEmitter.emit('message', 'Hello World');
+/// ```
+class TypedEventEmitter {
+  /// Mapping of events to a list of event handlers
+  HashMap>> _events = HashMap>>();
+
+  /// Mapping of events to a list of one-time event handlers
+  Map>> _eventsOnce = HashMap>>();
+
+  /// This function triggers all the handlers currently listening
+  /// to [event] and passes them [data].
+  void emit(final String event, final T data) {
+    final List>? list0 = _events[event];
+    // handler might be off() inside handler; make a copy first
+    final List>? list = list0 != null ? List>.from(list0) : null;
+    list?.forEach((final EventHandler handler) {
+      handler(data);
+    });
+
+    _eventsOnce.remove(event)?.forEach((final EventHandler handler) {
+      handler(data);
+    });
   }
+
+  /// This function binds the [handler] as a listener to the [event]
+  void on(final String event, final EventHandler handler) {
+    _events.putIfAbsent(event, () => >[]);
+    _events[event]!.add(handler);
+  }
+
+  /// This function binds the [handler] as a listener to the first
+  /// occurrence of the [event]. When [handler] is called once,
+  /// it is removed.
+  void once(final String event, final EventHandler handler) {
+    _eventsOnce.putIfAbsent(event, () => >[]);
+    _eventsOnce[event]!.add(handler);
+  }
+
+  /// This function attempts to unbind the [handler] from the [event]
+  void off(final String event, [final EventHandler? handler]) {
+    if (handler != null) {
+      _events[event]?.remove(handler);
+      _eventsOnce[event]?.remove(handler);
+      if (_events[event]?.isEmpty == true) {
+        _events.remove(event);
+      }
+      if (_eventsOnce[event]?.isEmpty == true) {
+        _eventsOnce.remove(event);
+      }
+    } else {
+      _events.remove(event);
+      _eventsOnce.remove(event);
+    }
+  }
+
+  /// This function unbinds all the handlers for all the events.
+  void clearListeners() {
+    _events = HashMap>>();
+    _eventsOnce = HashMap>>();
+  }
+
+  /// Returns whether the event has registered.
+  bool hasListeners(final String event) => _events[event]?.isNotEmpty == true || _eventsOnce[event]?.isNotEmpty == true;
 }
diff --git a/lib/src/value_objects/connection_id_vo.dart b/lib/src/value_objects/connection_id_vo.dart
new file mode 100644
index 0000000..cdb101d
--- /dev/null
+++ b/lib/src/value_objects/connection_id_vo.dart
@@ -0,0 +1,76 @@
+/// connection_id_vo.dart
+///
+/// Value object for socket connection ID with validation
+///
+/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+library connection_id_vo;
+
+/// Value object representing a validated socket connection ID.
+///
+/// Connection IDs are automatically generated unique identifiers for each
+/// client connection. This value object ensures they are non-empty and
+/// provides type safety.
+///
+/// ## Usage
+///
+/// ```dart
+/// // Created automatically for each socket
+/// final socket = ...;
+/// print('Connected: ${socket.connectionId}');
+///
+/// // Manual creation (typically not needed)
+/// final id = ConnectionId('abc123');
+///
+/// // Comparison
+/// if (socket1.connectionId == socket2.connectionId) {
+///   print('Same connection');
+/// }
+/// ```
+///
+/// See also:
+/// * [Socket.id] for the string representation
+/// * [Socket.connectionId] for the typed value object
+class ConnectionId {
+  final String value;
+
+  const ConnectionId._(this.value);
+
+  /// Creates a ConnectionId from a string with validation.
+  ///
+  /// The [id] parameter must be a non-empty string.
+  ///
+  /// Throws [ArgumentError] if the ID is empty.
+  ///
+  /// Example:
+  /// ```dart
+  /// final id = ConnectionId('socket-123');
+  /// print(id.value);  // 'socket-123'
+  /// ```
+  factory ConnectionId(final String id) {
+    if (id.isEmpty) {
+      throw ArgumentError('Connection ID cannot be empty');
+    }
+    return ConnectionId._(id);
+  }
+
+  /// Creates a ConnectionId without validation.
+  ///
+  /// Use this constructor only when you're certain the value is valid,
+  /// such as when deserializing from a trusted source.
+  ///
+  /// Example:
+  /// ```dart
+  /// const id = ConnectionId.unchecked('trusted-id');
+  /// ```
+  const ConnectionId.unchecked(this.value);
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) || other is ConnectionId && runtimeType == other.runtimeType && value == other.value;
+
+  @override
+  int get hashCode => value.hashCode;
+
+  @override
+  String toString() => value;
+}
diff --git a/lib/src/value_objects/disconnect_reason_vo.dart b/lib/src/value_objects/disconnect_reason_vo.dart
new file mode 100644
index 0000000..d05f626
--- /dev/null
+++ b/lib/src/value_objects/disconnect_reason_vo.dart
@@ -0,0 +1,141 @@
+/// disconnect_reason_vo.dart
+///
+/// Value object for disconnect reason with validation
+///
+/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+library disconnect_reason_vo;
+
+/// Enumeration of standard disconnect reasons.
+enum DisconnectReasonType {
+  /// Client initiated disconnect.
+  clientDisconnect,
+
+  /// Server initiated disconnect.
+  serverDisconnect,
+
+  /// Connection timeout.
+  pingTimeout,
+
+  /// Transport closed.
+  transportClose,
+
+  /// Transport error.
+  transportError,
+
+  /// Parse error.
+  parseError,
+
+  /// Forced server closure.
+  forcedClose,
+
+  /// Invalid namespace.
+  invalidNamespace,
+
+  /// IO server disconnect.
+  ioServerDisconnect,
+
+  /// IO client disconnect.
+  ioClientDisconnect,
+}
+
+/// Extension methods for DisconnectReasonType.
+extension DisconnectReasonTypeExtension on DisconnectReasonType {
+  /// Returns the string representation.
+  String get value {
+    switch (this) {
+      case DisconnectReasonType.clientDisconnect:
+        return 'client disconnect';
+      case DisconnectReasonType.serverDisconnect:
+        return 'server disconnect';
+      case DisconnectReasonType.pingTimeout:
+        return 'ping timeout';
+      case DisconnectReasonType.transportClose:
+        return 'transport close';
+      case DisconnectReasonType.transportError:
+        return 'transport error';
+      case DisconnectReasonType.parseError:
+        return 'parse error';
+      case DisconnectReasonType.forcedClose:
+        return 'forced close';
+      case DisconnectReasonType.invalidNamespace:
+        return 'invalid namespace';
+      case DisconnectReasonType.ioServerDisconnect:
+        return 'io server disconnect';
+      case DisconnectReasonType.ioClientDisconnect:
+        return 'io client disconnect';
+    }
+  }
+}
+
+/// Helper class for DisconnectReasonType.
+class DisconnectReasonTypeHelper {
+  /// Creates from string.
+  static DisconnectReasonType fromString(final String reason) {
+    switch (reason.toLowerCase().trim()) {
+      case 'client disconnect':
+        return DisconnectReasonType.clientDisconnect;
+      case 'server disconnect':
+        return DisconnectReasonType.serverDisconnect;
+      case 'ping timeout':
+        return DisconnectReasonType.pingTimeout;
+      case 'transport close':
+        return DisconnectReasonType.transportClose;
+      case 'transport error':
+        return DisconnectReasonType.transportError;
+      case 'parse error':
+        return DisconnectReasonType.parseError;
+      case 'forced close':
+        return DisconnectReasonType.forcedClose;
+      case 'invalid namespace':
+        return DisconnectReasonType.invalidNamespace;
+      case 'io server disconnect':
+        return DisconnectReasonType.ioServerDisconnect;
+      case 'io client disconnect':
+        return DisconnectReasonType.ioClientDisconnect;
+      default:
+        throw ArgumentError('Unknown disconnect reason: $reason');
+    }
+  }
+}
+
+/// Value object representing a disconnect reason.
+class DisconnectReason {
+  final DisconnectReasonType type;
+  final String? details;
+
+  const DisconnectReason._(this.type, this.details);
+
+  /// Creates a DisconnectReason from a type.
+  const DisconnectReason(this.type, [this.details]);
+
+  /// Creates from string.
+  factory DisconnectReason.fromString(final String reason) {
+    try {
+      return DisconnectReason(DisconnectReasonTypeHelper.fromString(reason));
+    } catch (_) {
+      // If not a standard reason, create a custom one
+      return DisconnectReason(DisconnectReasonType.serverDisconnect, reason);
+    }
+  }
+
+  /// Common disconnect reasons as constants.
+  static const DisconnectReason clientDisconnect = DisconnectReason._(DisconnectReasonType.clientDisconnect, null);
+  static const DisconnectReason serverDisconnect = DisconnectReason._(DisconnectReasonType.serverDisconnect, null);
+  static const DisconnectReason pingTimeout = DisconnectReason._(DisconnectReasonType.pingTimeout, null);
+  static const DisconnectReason transportClose = DisconnectReason._(DisconnectReasonType.transportClose, null);
+  static const DisconnectReason transportError = DisconnectReason._(DisconnectReasonType.transportError, null);
+
+  /// Returns the string value.
+  String get value => details ?? type.value;
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is DisconnectReason && runtimeType == other.runtimeType && type == other.type && details == other.details;
+
+  @override
+  int get hashCode => Object.hash(type, details);
+
+  @override
+  String toString() => value;
+}
diff --git a/lib/src/value_objects/error_code_vo.dart b/lib/src/value_objects/error_code_vo.dart
new file mode 100644
index 0000000..16d2d9e
--- /dev/null
+++ b/lib/src/value_objects/error_code_vo.dart
@@ -0,0 +1,174 @@
+/// Value object for error codes in Socket.IO.
+///
+/// Wraps error codes with validation to ensure they are in valid format.
+/// Supports both numeric codes (e.g., 1001, 2001) and string codes (e.g., 'AUTH_ERROR').
+///
+/// Copyright (C) 2024. All Rights Reserved.
+library error_code_vo;
+
+import 'package:meta/meta.dart';
+
+/// Value object representing an error code.
+///
+/// Error codes can be:
+/// - Numeric: HTTP-style codes (e.g., 400, 404, 1001, 2001)
+/// - String: Named error codes (e.g., 'AUTH_ERROR', 'CONNECTION_FAILED')
+///
+/// Example:
+/// ```dart
+/// // Numeric error code
+/// final ErrorCode numericCode = ErrorCode.numeric(1001);
+/// print(numericCode.value); // '1001'
+/// print(numericCode.isNumeric); // true
+/// print(numericCode.numericValue); // 1001
+///
+/// // String error code
+/// final ErrorCode stringCode = ErrorCode.string('AUTH_ERROR');
+/// print(stringCode.value); // 'AUTH_ERROR'
+/// print(stringCode.isNumeric); // false
+///
+/// // Auto-detect from string
+/// final ErrorCode autoCode = ErrorCode('404');
+/// print(autoCode.isNumeric); // true
+/// print(autoCode.numericValue); // 404
+/// ```
+@immutable
+class ErrorCode {
+  /// The raw error code value.
+  final String value;
+
+  /// The numeric value, if the code is numeric.
+  final int? _numericValue;
+
+  /// Creates an error code value object.
+  ///
+  /// Validates that the code is not empty and is in a valid format.
+  ///
+  /// Throws [ArgumentError] if:
+  /// - [value] is empty
+  /// - [value] contains only whitespace
+  factory ErrorCode(final String value) {
+    if (value.trim().isEmpty) {
+      throw ArgumentError('Error code cannot be empty');
+    }
+
+    final String trimmed = value.trim();
+
+    // Check if it's a valid numeric code
+    final int? numericValue = int.tryParse(trimmed);
+    if (numericValue != null) {
+      if (numericValue < 0) {
+        throw ArgumentError('Numeric error code must be non-negative, got: $numericValue');
+      }
+      return ErrorCode._internal(trimmed, numericValue);
+    }
+
+    // String error code - validate format
+    // Should be uppercase letters, numbers, underscores, and hyphens
+    final RegExp validPattern = RegExp(r'^[A-Z0-9_\-]+$');
+    if (!validPattern.hasMatch(trimmed.toUpperCase())) {
+      throw ArgumentError(
+        'String error code must contain only letters, numbers, underscores, and hyphens: $trimmed',
+      );
+    }
+
+    return ErrorCode._internal(trimmed.toUpperCase(), null);
+  }
+
+  /// Creates a numeric error code.
+  ///
+  /// Throws [ArgumentError] if [code] is negative.
+  factory ErrorCode.numeric(final int code) {
+    if (code < 0) {
+      throw ArgumentError('Numeric error code must be non-negative, got: $code');
+    }
+    return ErrorCode._internal(code.toString(), code);
+  }
+
+  /// Creates a string error code.
+  ///
+  /// The code will be normalized to uppercase.
+  ///
+  /// Throws [ArgumentError] if:
+  /// - [code] is empty
+  /// - [code] contains invalid characters
+  factory ErrorCode.string(final String code) {
+    if (code.trim().isEmpty) {
+      throw ArgumentError('Error code cannot be empty');
+    }
+
+    final String normalized = code.trim().toUpperCase();
+
+    // Validate format
+    final RegExp validPattern = RegExp(r'^[A-Z0-9_\-]+$');
+    if (!validPattern.hasMatch(normalized)) {
+      throw ArgumentError(
+        'String error code must contain only letters, numbers, underscores, and hyphens: $code',
+      );
+    }
+
+    return ErrorCode._internal(normalized, null);
+  }
+
+  /// Private constructor.
+  const ErrorCode._internal(this.value, this._numericValue);
+
+  /// Unchecked constructor that bypasses validation.
+  ///
+  /// Use with caution - only when you're certain the value is valid.
+  const ErrorCode.unchecked(this.value) : _numericValue = null;
+
+  /// Whether this is a numeric error code.
+  bool get isNumeric => _numericValue != null;
+
+  /// Whether this is a string error code.
+  bool get isString => !isNumeric;
+
+  /// Gets the numeric value if this is a numeric code.
+  ///
+  /// Returns null if this is a string code.
+  int? get numericValue => _numericValue;
+
+  /// Common numeric error codes.
+  ///
+  /// Connection errors (1xxx)
+  static final ErrorCode connectionTimeout = ErrorCode.numeric(1001);
+  static final ErrorCode connectionRefused = ErrorCode.numeric(1002);
+  static final ErrorCode connectionLost = ErrorCode.numeric(1003);
+  static final ErrorCode connectionFailed = ErrorCode.numeric(1004);
+
+  /// Transport errors (2xxx)
+  static final ErrorCode transportError = ErrorCode.numeric(2001);
+  static final ErrorCode transportClosed = ErrorCode.numeric(2002);
+  static final ErrorCode transportTimeout = ErrorCode.numeric(2003);
+  static final ErrorCode transportFailed = ErrorCode.numeric(2004);
+
+  /// Protocol errors (3xxx)
+  static final ErrorCode protocolError = ErrorCode.numeric(3001);
+  static final ErrorCode invalidPacket = ErrorCode.numeric(3002);
+
+  /// Authentication errors (4xxx)
+  static final ErrorCode authenticationFailed = ErrorCode.numeric(4001);
+  static final ErrorCode authorizationFailed = ErrorCode.numeric(4002);
+  static final ErrorCode invalidCredentials = ErrorCode.numeric(4003);
+
+  /// Unknown error
+  static final ErrorCode unknown = ErrorCode.numeric(9999);
+
+  /// Common string error codes.
+  static final ErrorCode authError = ErrorCode.string('AUTH_ERROR');
+  static final ErrorCode timeoutError = ErrorCode.string('TIMEOUT_ERROR');
+  static final ErrorCode networkError = ErrorCode.string('NETWORK_ERROR');
+  static final ErrorCode parseError = ErrorCode.string('PARSE_ERROR');
+  static final ErrorCode invalidData = ErrorCode.string('INVALID_DATA');
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) || other is ErrorCode && runtimeType == other.runtimeType && value == other.value;
+
+  @override
+  int get hashCode => value.hashCode;
+
+  @override
+  String toString() => 'ErrorCode($value)';
+}
diff --git a/lib/src/value_objects/event_arguments_vo.dart b/lib/src/value_objects/event_arguments_vo.dart
new file mode 100644
index 0000000..e4abb9c
--- /dev/null
+++ b/lib/src/value_objects/event_arguments_vo.dart
@@ -0,0 +1,245 @@
+/// Value object representing event arguments in Socket.IO communication.
+///
+/// Provides a type-safe wrapper around event arguments, supporting various data types
+/// and providing safe access methods.
+library event_arguments_vo;
+
+import '../models/event_data_models.dart';
+
+/// Represents a list of event arguments with type-safe access methods.
+///
+/// Event arguments can contain any JSON-serializable data including strings,
+/// numbers, booleans, maps, lists, and null values.
+///
+/// Example:
+/// ```dart
+/// final EventArguments args = EventArguments(['hello', 42, {'key': 'value'}]);
+/// final String? first = args.getStringAt(0);
+/// final int? second = args.getIntAt(1);
+/// final Map? third = args.getMapAt(2);
+/// ```
+class EventArguments {
+  /// The underlying list of arguments.
+  final List arguments;
+
+  /// Creates event arguments from a list of objects.
+  ///
+  /// Throws [ArgumentError] if [arguments] is null.
+  factory EventArguments(final List arguments) =>
+      EventArguments.unchecked(List.unmodifiable(arguments));
+
+  /// Creates empty event arguments.
+  factory EventArguments.empty() => const EventArguments.unchecked([]);
+
+  /// Creates event arguments from a single value.
+  factory EventArguments.single(final Object? value) => EventArguments.unchecked([value]);
+
+  /// Creates event arguments from multiple values.
+  factory EventArguments.multiple(final List values) => EventArguments(values);
+
+  /// Creates event arguments from EventData objects.
+  factory EventArguments.fromEventData(final List eventData) {
+    final List values = [];
+    for (final EventData data in eventData) {
+      if (data is StringEventData) {
+        values.add(data.value);
+      } else if (data is NumericEventData) {
+        values.add(data.value);
+      } else if (data is BooleanEventData) {
+        values.add(data.value);
+      } else if (data is MapEventData) {
+        values.add(data.value);
+      } else if (data is ListEventData) {
+        values.add(data.value);
+      } else if (data is NullEventData) {
+        values.add(null);
+      } else if (data is ObjectEventData) {
+        values.add(data.value);
+      }
+    }
+    return EventArguments.unchecked(values);
+  }
+
+  /// Unchecked constructor for internal use.
+  ///
+  /// Does not validate or copy the input list.
+  /// Use when you are certain the input is valid and immutable.
+  const EventArguments.unchecked(this.arguments);
+
+  /// The number of arguments.
+  int get length => arguments.length;
+
+  /// Whether there are no arguments.
+  bool get isEmpty => arguments.isEmpty;
+
+  /// Whether there are arguments.
+  bool get isNotEmpty => arguments.isNotEmpty;
+
+  /// Gets the argument at the specified index.
+  ///
+  /// Returns null if index is out of bounds.
+  Object? operator [](final int index) {
+    if (index < 0 || index >= arguments.length) {
+      return null;
+    }
+    return arguments[index];
+  }
+
+  /// Gets the argument at the specified index as a String.
+  ///
+  /// Returns null if the index is out of bounds or the value is not a String.
+  String? getStringAt(final int index) {
+    final Object? value = this[index];
+    return value is String ? value : null;
+  }
+
+  /// Gets the argument at the specified index as an int.
+  ///
+  /// Returns null if the index is out of bounds or the value is not an int.
+  /// Attempts to convert num to int if needed.
+  int? getIntAt(final int index) {
+    final Object? value = this[index];
+    if (value is int) {
+      return value;
+    }
+    if (value is num) {
+      return value.toInt();
+    }
+    return null;
+  }
+
+  /// Gets the argument at the specified index as a double.
+  ///
+  /// Returns null if the index is out of bounds or the value is not a number.
+  double? getDoubleAt(final int index) {
+    final Object? value = this[index];
+    if (value is double) {
+      return value;
+    }
+    if (value is num) {
+      return value.toDouble();
+    }
+    return null;
+  }
+
+  /// Gets the argument at the specified index as a bool.
+  ///
+  /// Returns null if the index is out of bounds or the value is not a bool.
+  bool? getBoolAt(final int index) {
+    final Object? value = this[index];
+    return value is bool ? value : null;
+  }
+
+  /// Gets the argument at the specified index as a Map.
+  ///
+  /// Returns null if the index is out of bounds or the value is not a Map.
+  Map? getMapAt(final int index) {
+    final Object? value = this[index];
+    if (value is Map) {
+      return value;
+    }
+    if (value is Map) {
+      return Map.from(value);
+    }
+    return null;
+  }
+
+  /// Gets the argument at the specified index as a List.
+  ///
+  /// Returns null if the index is out of bounds or the value is not a List.
+  List? getListAt(final int index) {
+    final Object? value = this[index];
+    if (value is List) {
+      return value;
+    }
+    if (value is List) {
+      return List.from(value);
+    }
+    return null;
+  }
+
+  /// Converts the arguments to EventData objects.
+  List toEventData() {
+    final List result = [];
+    for (final Object? arg in arguments) {
+      if (arg is String) {
+        result.add(StringEventData(arg));
+      } else if (arg is num) {
+        result.add(NumericEventData(arg));
+      } else if (arg is bool) {
+        result.add(BooleanEventData(arg));
+      } else if (arg is Map) {
+        result.add(MapEventData(arg));
+      } else if (arg is Map) {
+        result.add(MapEventData(Map.from(arg)));
+      } else if (arg is List) {
+        result.add(ListEventData(arg));
+      } else if (arg == null) {
+        result.add(const NullEventData());
+      } else {
+        result.add(ObjectEventData(arg));
+      }
+    }
+    return result;
+  }
+
+  /// Converts to a raw list.
+  List toList() => List.from(arguments);
+
+  /// Converts to JSON-serializable list.
+  List toJson() => toList();
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) ||
+      other is EventArguments && runtimeType == other.runtimeType && _listEquals(arguments, other.arguments);
+
+  @override
+  int get hashCode => Object.hashAll(arguments);
+
+  @override
+  String toString() => 'EventArguments(${arguments.length} args)';
+
+  /// Helper to compare lists deeply.
+  static bool _listEquals(final List? a, final List? b) {
+    if (a == null) {
+      return b == null;
+    }
+    if (b == null || a.length != b.length) {
+      return false;
+    }
+    for (int index = 0; index < a.length; index++) {
+      if (a[index] != b[index]) {
+        return false;
+      }
+    }
+    return true;
+  }
+}
+
+/// Extension methods for EventArguments.
+extension EventArgumentsExtension on EventArguments {
+  /// Maps each argument through a function.
+  List map(final T Function(Object?) fn) => arguments.map(fn).toList();
+
+  /// Filters arguments by a predicate.
+  List where(final bool Function(Object?) test) => arguments.where(test).toList();
+
+  /// Returns the first argument, or null if empty.
+  Object? get firstOrNull => isEmpty ? null : arguments.first;
+
+  /// Returns the last argument, or null if empty.
+  Object? get lastOrNull => isEmpty ? null : arguments.last;
+
+  /// Checks if all arguments match a predicate.
+  bool every(final bool Function(Object?) test) => arguments.every(test);
+
+  /// Checks if any argument matches a predicate.
+  bool any(final bool Function(Object?) test) => arguments.any(test);
+
+  /// Takes the first n arguments.
+  EventArguments take(final int count) => EventArguments.unchecked(arguments.take(count).toList());
+
+  /// Skips the first n arguments.
+  EventArguments skip(final int count) => EventArguments.unchecked(arguments.skip(count).toList());
+}
diff --git a/lib/src/value_objects/event_name_vo.dart b/lib/src/value_objects/event_name_vo.dart
new file mode 100644
index 0000000..4e9286f
--- /dev/null
+++ b/lib/src/value_objects/event_name_vo.dart
@@ -0,0 +1,65 @@
+/// event_name_vo.dart
+///
+/// Value object for event name with validation
+///
+/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+library event_name_vo;
+
+/// Value object representing a validated event name.
+///
+/// Event names must be non-empty strings and not reserved event names.
+class EventName {
+  final String value;
+
+  const EventName._(this.value);
+
+  /// Reserved event names that cannot be used for custom events.
+  ///
+  /// These events are reserved for:
+  /// - Connection lifecycle management (connect, connection, disconnect, disconnecting)
+  /// - Error handling (error, connect_error)
+  /// - Event listener management (newListener, removeListener)
+  static const Set reservedNames = {
+    'connect',
+    'connect_error',
+    'connection', // server-side connection event
+    'disconnect',
+    'disconnecting',
+    'newListener',
+    'removeListener',
+    'error',
+  };
+
+  /// Creates an EventName from a string with validation.
+  ///
+  /// Throws [ArgumentError] if the event name is empty or reserved.
+  factory EventName(final String name) {
+    if (name.isEmpty) {
+      throw ArgumentError('Event name cannot be empty');
+    }
+    if (reservedNames.contains(name)) {
+      throw ArgumentError('Event name "$name" is reserved');
+    }
+    return EventName._(name);
+  }
+
+  /// Creates an EventName without validation (use with caution).
+  /// Useful for internal/reserved event names.
+  const EventName.unchecked(this.value);
+
+  /// Check if this event name is reserved/blacklisted
+  bool get isBlacklisted => reservedNames.contains(value);
+
+  /// Static helper to check if a string event name is reserved
+  static bool isReserved(final String eventName) => reservedNames.contains(eventName);
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) || other is EventName && runtimeType == other.runtimeType && value == other.value;
+
+  @override
+  int get hashCode => value.hashCode;
+
+  @override
+  String toString() => value;
+}
diff --git a/lib/src/value_objects/namespace_name_vo.dart b/lib/src/value_objects/namespace_name_vo.dart
new file mode 100644
index 0000000..f9981cb
--- /dev/null
+++ b/lib/src/value_objects/namespace_name_vo.dart
@@ -0,0 +1,48 @@
+/// namespace_name_vo.dart
+///
+/// Value object for namespace name with validation
+///
+/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+library namespace_name_vo;
+
+/// Value object representing a validated namespace name.
+///
+/// Namespace names must start with '/' and contain valid characters.
+class NamespaceName {
+  final String value;
+
+  const NamespaceName._(this.value);
+
+  /// Creates a NamespaceName from a string with validation.
+  ///
+  /// Throws [ArgumentError] if the namespace name is invalid.
+  factory NamespaceName(final String name) {
+    if (name.isEmpty) {
+      throw ArgumentError('Namespace name cannot be empty');
+    }
+    if (!name.startsWith('/')) {
+      throw ArgumentError('Namespace name must start with "/"');
+    }
+    // Check for invalid characters
+    if (name.contains(RegExp(r'[^\w\-/.~]'))) {
+      throw ArgumentError('Namespace name contains invalid characters');
+    }
+    return NamespaceName._(name);
+  }
+
+  /// Creates a NamespaceName without validation (use with caution).
+  const NamespaceName.unchecked(this.value);
+
+  /// Returns the default namespace "/".
+  static const NamespaceName defaultNamespace = NamespaceName.unchecked('/');
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) || other is NamespaceName && runtimeType == other.runtimeType && value == other.value;
+
+  @override
+  int get hashCode => value.hashCode;
+
+  @override
+  String toString() => value;
+}
diff --git a/lib/src/value_objects/packet_id_vo.dart b/lib/src/value_objects/packet_id_vo.dart
new file mode 100644
index 0000000..7f39afe
--- /dev/null
+++ b/lib/src/value_objects/packet_id_vo.dart
@@ -0,0 +1,49 @@
+/// packet_id_vo.dart
+///
+/// Value object for packet ID with validation
+///
+/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+library packet_id_vo;
+
+/// Value object representing a validated packet ID.
+///
+/// Packet IDs are typically numeric identifiers for acknowledgments.
+class PacketId {
+  final String value;
+
+  const PacketId._(this.value);
+
+  /// Creates a PacketId from a string with validation.
+  ///
+  /// Throws [ArgumentError] if the ID is empty or invalid.
+  factory PacketId(final String id) {
+    if (id.isEmpty) {
+      throw ArgumentError('Packet ID cannot be empty');
+    }
+    return PacketId._(id);
+  }
+
+  /// Creates a PacketId from an integer.
+  factory PacketId.fromInt(final int id) {
+    if (id < 0) {
+      throw ArgumentError('Packet ID cannot be negative');
+    }
+    return PacketId._(id.toString());
+  }
+
+  /// Creates a PacketId without validation (use with caution).
+  const PacketId.unchecked(this.value);
+
+  /// Converts to integer if possible.
+  int? toInt() => int.tryParse(value);
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) || other is PacketId && runtimeType == other.runtimeType && value == other.value;
+
+  @override
+  int get hashCode => value.hashCode;
+
+  @override
+  String toString() => value;
+}
diff --git a/lib/src/value_objects/port_number_vo.dart b/lib/src/value_objects/port_number_vo.dart
new file mode 100644
index 0000000..783f16f
--- /dev/null
+++ b/lib/src/value_objects/port_number_vo.dart
@@ -0,0 +1,50 @@
+/// port_number_vo.dart
+///
+/// Value object for port number with validation
+///
+/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+library port_number_vo;
+
+/// Value object representing a validated port number.
+///
+/// Port numbers must be in the valid range (1-65535).
+class PortNumber {
+  final int value;
+
+  const PortNumber._(this.value);
+
+  /// Creates a PortNumber from an integer with validation.
+  ///
+  /// Throws [ArgumentError] if the port is outside valid range.
+  factory PortNumber(final int port) {
+    if (port < 1 || port > 65535) {
+      throw ArgumentError('Port number must be between 1 and 65535, got: $port');
+    }
+    return PortNumber._(port);
+  }
+
+  /// Creates a PortNumber without validation (use with caution).
+  const PortNumber.unchecked(this.value);
+
+  /// Common HTTP port (80).
+  static const PortNumber http = PortNumber.unchecked(80);
+
+  /// Common HTTPS port (443).
+  static const PortNumber https = PortNumber.unchecked(443);
+
+  /// Common development port (3000).
+  static const PortNumber dev = PortNumber.unchecked(3000);
+
+  /// Common alternative HTTP port (8080).
+  static const PortNumber altHttp = PortNumber.unchecked(8080);
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) || other is PortNumber && runtimeType == other.runtimeType && value == other.value;
+
+  @override
+  int get hashCode => value.hashCode;
+
+  @override
+  String toString() => value.toString();
+}
diff --git a/lib/src/value_objects/query_parameters_vo.dart b/lib/src/value_objects/query_parameters_vo.dart
new file mode 100644
index 0000000..38568d5
--- /dev/null
+++ b/lib/src/value_objects/query_parameters_vo.dart
@@ -0,0 +1,152 @@
+/// Value object for query parameters in Socket.IO connections.
+///
+/// Wraps query parameters with validation and type safety.
+/// Query parameters are used during socket connection and namespace operations.
+///
+/// Example:
+/// ```dart
+/// final query = QueryParameters({'token': 'abc123', 'room': 'chat'});
+/// print(query.get('token')); // 'abc123'
+/// print(query.getOrDefault('missing', 'default')); // 'default'
+/// ```
+class QueryParameters {
+  final Map _parameters;
+
+  /// Creates a QueryParameters from a map.
+  ///
+  /// Validates that all values can be represented as strings.
+  /// Throws [ArgumentError] if any value is null or cannot be converted to string.
+  QueryParameters(final Map parameters) : _parameters = _validateAndConvert(parameters);
+
+  /// Creates an empty QueryParameters.
+  QueryParameters.empty() : _parameters = {};
+
+  /// Creates QueryParameters from a query string.
+  ///
+  /// Example: `'token=abc&room=chat'` becomes `{'token': 'abc', 'room': 'chat'}`
+  factory QueryParameters.fromQueryString(final String queryString) {
+    if (queryString.isEmpty) {
+      return QueryParameters.empty();
+    }
+
+    final Map params = {};
+    final String cleanQuery = queryString.startsWith('?') ? queryString.substring(1) : queryString;
+
+    for (final String pair in cleanQuery.split('&')) {
+      if (pair.isEmpty) continue;
+
+      final List parts = pair.split('=');
+      if (parts.isEmpty) continue;
+
+      final String key = Uri.decodeComponent(parts[0]);
+      final String value = parts.length > 1 ? Uri.decodeComponent(parts[1]) : '';
+      params[key] = value;
+    }
+
+    return QueryParameters(params);
+  }
+
+  static Map _validateAndConvert(final Map parameters) {
+    final Map result = {};
+
+    for (final MapEntry entry in parameters.entries) {
+      final String key = entry.key;
+      final dynamic value = entry.value;
+
+      if (value == null) {
+        throw ArgumentError('Query parameter value for key "$key" cannot be null');
+      }
+
+      result[key] = value.toString();
+    }
+
+    return result;
+  }
+
+  /// Gets a parameter value by key.
+  ///
+  /// Returns null if the key doesn't exist.
+  String? get(final String key) => _parameters[key];
+
+  /// Gets a parameter value by key, or returns a default value.
+  String getOrDefault(final String key, final String defaultValue) => _parameters[key] ?? defaultValue;
+
+  /// Checks if a parameter exists.
+  bool has(final String key) => _parameters.containsKey(key);
+
+  /// Returns all parameter keys.
+  Iterable get keys => _parameters.keys;
+
+  /// Returns all parameter values.
+  Iterable get values => _parameters.values;
+
+  /// Returns all parameters as entries.
+  Iterable> get entries => _parameters.entries;
+
+  /// Returns the number of parameters.
+  int get length => _parameters.length;
+
+  /// Checks if there are no parameters.
+  bool get isEmpty => _parameters.isEmpty;
+
+  /// Checks if there are any parameters.
+  bool get isNotEmpty => _parameters.isNotEmpty;
+
+  /// Converts to a Map.
+  Map toMap() => Map.from(_parameters);
+
+  /// Converts to a Map for backward compatibility.
+  Map toDynamicMap() => Map.from(_parameters);
+
+  /// Converts to a query string.
+  ///
+  /// Example: `{'token': 'abc', 'room': 'chat'}` becomes `'token=abc&room=chat'`
+  String toQueryString() {
+    if (isEmpty) return '';
+
+    return entries
+        .map((final MapEntry e) => '${Uri.encodeComponent(e.key)}=${Uri.encodeComponent(e.value)}')
+        .join('&');
+  }
+
+  /// Creates a new QueryParameters with an additional parameter.
+  QueryParameters withParameter(final String key, final String value) =>
+      QueryParameters({..._parameters, key: value});
+
+  /// Creates a new QueryParameters without a specific parameter.
+  QueryParameters withoutParameter(final String key) => QueryParameters(
+        Map.from(_parameters)..remove(key),
+      );
+
+  /// Merges with another QueryParameters.
+  ///
+  /// Parameters from [other] take precedence in case of conflicts.
+  QueryParameters merge(final QueryParameters other) =>
+      QueryParameters({..._parameters, ...other._parameters});
+
+  @override
+  bool operator ==(final Object other) {
+    if (identical(this, other)) return true;
+    if (other is! QueryParameters) return false;
+
+    if (_parameters.length != other._parameters.length) return false;
+
+    for (final MapEntry entry in _parameters.entries) {
+      if (other._parameters[entry.key] != entry.value) return false;
+    }
+
+    return true;
+  }
+
+  @override
+  int get hashCode {
+    int hash = 0;
+    for (final MapEntry entry in _parameters.entries) {
+      hash ^= entry.key.hashCode ^ entry.value.hashCode;
+    }
+    return hash;
+  }
+
+  @override
+  String toString() => 'QueryParameters(${toQueryString()})';
+}
diff --git a/lib/src/value_objects/room_name_vo.dart b/lib/src/value_objects/room_name_vo.dart
new file mode 100644
index 0000000..6cad125
--- /dev/null
+++ b/lib/src/value_objects/room_name_vo.dart
@@ -0,0 +1,77 @@
+/// room_name_vo.dart
+///
+/// Value object for room name with validation
+///
+/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+library room_name_vo;
+
+/// Value object representing a validated room name.
+///
+/// Rooms are arbitrary channels that sockets can join and leave. They provide
+/// a way to broadcast events to a subset of connected clients. This value object
+/// ensures room names are non-empty and provides type safety.
+///
+/// ## Usage
+///
+/// ```dart
+/// // Join a room
+/// final roomName = RoomName('chatRoom');
+/// socket.join(roomName.value);
+///
+/// // Broadcast to room
+/// io.to('chatRoom').emit('message', ['Hello room!']);
+///
+/// // Leave room
+/// socket.leave('chatRoom');
+///
+/// // Check room membership
+/// if (socket.roomMembership.contains(RoomName('chatRoom'))) {
+///   print('Socket is in chatRoom');
+/// }
+/// ```
+///
+/// ## Common Room Patterns
+///
+/// ```dart
+/// // User-specific rooms
+/// socket.join(RoomName('user-${userId}'));
+///
+/// // Broadcast channels
+/// socket.join(RoomName('notifications'));
+///
+/// // Temporary rooms
+/// socket.join(RoomName('game-${gameId}'));
+/// ```
+///
+/// See also:
+/// * [Socket.join] for joining rooms
+/// * [Socket.leave] for leaving rooms
+/// * [Namespace.to] for room-based broadcasting
+class RoomName {
+  final String value;
+
+  const RoomName._(this.value);
+
+  /// Creates a RoomName from a string with validation.
+  ///
+  /// Throws [ArgumentError] if the room name is empty or invalid.
+  factory RoomName(final String name) {
+    if (name.isEmpty) {
+      throw ArgumentError('Room name cannot be empty');
+    }
+    return RoomName._(name);
+  }
+
+  /// Creates a RoomName without validation (use with caution).
+  const RoomName.unchecked(this.value);
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) || other is RoomName && runtimeType == other.runtimeType && value == other.value;
+
+  @override
+  int get hashCode => value.hashCode;
+
+  @override
+  String toString() => value;
+}
diff --git a/lib/src/value_objects/socket_state_vo.dart b/lib/src/value_objects/socket_state_vo.dart
new file mode 100644
index 0000000..f26fafe
--- /dev/null
+++ b/lib/src/value_objects/socket_state_vo.dart
@@ -0,0 +1,91 @@
+/// socket_state_vo.dart
+///
+/// Value object for socket connection state
+///
+/// Provides type-safe representation of socket connection lifecycle states
+/// to replace string literals like 'connected', 'disconnected', etc.
+///
+/// Copyright (C) 2024 Potix Corporation. All Rights Reserved.
+library socket_state_vo;
+
+/// Socket connection state
+///
+/// Represents the lifecycle states of a Socket.IO connection.
+/// This enum provides type safety and exhaustive pattern matching
+/// for socket state checks.
+enum SocketState {
+  /// Socket is in the process of establishing a connection
+  connecting('connecting'),
+
+  /// Socket is fully connected and ready to send/receive events
+  connected('connected'),
+
+  /// Socket is in the process of disconnecting
+  disconnecting('disconnecting'),
+
+  /// Socket is disconnected and not active
+  disconnected('disconnected');
+
+  /// String value used in protocol and for backward compatibility
+  final String value;
+
+  const SocketState(this.value);
+
+  /// Creates a SocketState from a string value
+  ///
+  /// Returns null if the string doesn't match any known state.
+  ///
+  /// Example:
+  /// ```dart
+  /// final SocketState? state = SocketState.fromString('connected');
+  /// assert(state == SocketState.connected);
+  /// ```
+  static SocketState? fromString(final String value) {
+    switch (value) {
+      case 'connecting':
+        return SocketState.connecting;
+      case 'connected':
+        return SocketState.connected;
+      case 'disconnecting':
+        return SocketState.disconnecting;
+      case 'disconnected':
+        return SocketState.disconnected;
+      default:
+        return null;
+    }
+  }
+
+  /// Creates a SocketState from a string value with fallback
+  ///
+  /// Returns the provided [defaultState] if the string doesn't match
+  /// any known state.
+  ///
+  /// Example:
+  /// ```dart
+  /// final SocketState state = SocketState.fromStringOrDefault(
+  ///   'invalid',
+  ///   SocketState.disconnected,
+  /// );
+  /// assert(state == SocketState.disconnected);
+  /// ```
+  static SocketState fromStringOrDefault(
+    final String value,
+    final SocketState defaultState,
+  ) =>
+      fromString(value) ?? defaultState;
+
+  /// Check if socket is in a connected state
+  bool get isConnected => this == SocketState.connected;
+
+  /// Check if socket is in a disconnected state
+  bool get isDisconnected => this == SocketState.disconnected;
+
+  /// Check if socket is in a transitional state (connecting or disconnecting)
+  bool get isTransitioning => this == SocketState.connecting || this == SocketState.disconnecting;
+
+  /// Check if socket can send/receive events
+  bool get canCommunicate => this == SocketState.connected;
+
+  @override
+  String toString() => value;
+}
diff --git a/lib/src/value_objects/timeout_duration_vo.dart b/lib/src/value_objects/timeout_duration_vo.dart
new file mode 100644
index 0000000..1de71c5
--- /dev/null
+++ b/lib/src/value_objects/timeout_duration_vo.dart
@@ -0,0 +1,73 @@
+/// timeout_duration_vo.dart
+///
+/// Value object for timeout duration with validation
+///
+/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+library timeout_duration_vo;
+
+/// Value object representing a validated timeout duration.
+class TimeoutDuration {
+  final Duration value;
+
+  const TimeoutDuration._(this.value);
+
+  /// Creates a TimeoutDuration from a Duration with validation.
+  ///
+  /// Throws [ArgumentError] if the duration is negative or too long.
+  factory TimeoutDuration(final Duration duration) {
+    if (duration.isNegative) {
+      throw ArgumentError('Timeout duration cannot be negative');
+    }
+    if (duration.inSeconds > 3600) {
+      // Max 1 hour
+      throw ArgumentError('Timeout duration cannot exceed 1 hour');
+    }
+    return TimeoutDuration._(duration);
+  }
+
+  /// Creates from milliseconds.
+  factory TimeoutDuration.milliseconds(final int milliseconds) => TimeoutDuration(Duration(milliseconds: milliseconds));
+
+  /// Creates from seconds.
+  factory TimeoutDuration.seconds(final int seconds) => TimeoutDuration(Duration(seconds: seconds));
+
+  /// Creates from minutes.
+  factory TimeoutDuration.minutes(final int minutes) => TimeoutDuration(Duration(minutes: minutes));
+
+  /// Creates without validation (use with caution).
+  const TimeoutDuration.unchecked(this.value);
+
+  /// Default connection timeout (20 seconds).
+  static final TimeoutDuration defaultConnection = TimeoutDuration.seconds(20);
+
+  /// Default ping timeout (5 seconds).
+  static final TimeoutDuration defaultPing = TimeoutDuration.seconds(5);
+
+  /// Returns duration in milliseconds.
+  int get inMilliseconds => value.inMilliseconds;
+
+  /// Returns duration in seconds.
+  int get inSeconds => value.inSeconds;
+
+  /// Adds two TimeoutDurations together.
+  TimeoutDuration operator +(final TimeoutDuration other) => TimeoutDuration.unchecked(value + other.value);
+
+  /// Subtracts one TimeoutDuration from another.
+  TimeoutDuration operator -(final TimeoutDuration other) => TimeoutDuration.unchecked(value - other.value);
+
+  /// Multiplies the duration by a factor.
+  TimeoutDuration operator *(final int factor) => TimeoutDuration.unchecked(value * factor);
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) || other is TimeoutDuration && runtimeType == other.runtimeType && value == other.value;
+
+  @override
+  int get hashCode => value.hashCode;
+
+  /// Converts to JSON (milliseconds as int for Engine.IO protocol).
+  int toJson() => inMilliseconds;
+
+  @override
+  String toString() => '${value.inMilliseconds}ms';
+}
diff --git a/lib/src/value_objects/transport_name_vo.dart b/lib/src/value_objects/transport_name_vo.dart
new file mode 100644
index 0000000..0f0d6f9
--- /dev/null
+++ b/lib/src/value_objects/transport_name_vo.dart
@@ -0,0 +1,81 @@
+/// transport_name_vo.dart
+///
+/// Value object for transport name with validation
+///
+/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+library transport_name_vo;
+
+/// Enumeration of valid transport types.
+enum TransportType {
+  websocket,
+  polling,
+  webtransport,
+}
+
+/// Extension methods for TransportType.
+extension TransportTypeExtension on TransportType {
+  /// Returns the string representation.
+  String get value {
+    switch (this) {
+      case TransportType.websocket:
+        return 'websocket';
+      case TransportType.polling:
+        return 'polling';
+      case TransportType.webtransport:
+        return 'webtransport';
+    }
+  }
+}
+
+/// Helper class for TransportType.
+class TransportTypeHelper {
+  /// Creates from string.
+  static TransportType fromString(final String name) {
+    switch (name.toLowerCase()) {
+      case 'websocket':
+      case 'ws':
+        return TransportType.websocket;
+      case 'polling':
+        return TransportType.polling;
+      case 'webtransport':
+        return TransportType.webtransport;
+      default:
+        throw ArgumentError('Invalid transport type: $name');
+    }
+  }
+}
+
+/// Value object representing a validated transport name.
+class TransportName {
+  final TransportType type;
+
+  const TransportName._(this.type);
+
+  /// Creates a TransportName from a transport type.
+  const TransportName(this.type);
+
+  /// Creates from string.
+  factory TransportName.fromString(final String name) => TransportName(TransportTypeHelper.fromString(name));
+
+  /// Creates a WebSocket transport name.
+  static const TransportName websocket = TransportName._(TransportType.websocket);
+
+  /// Creates a Polling transport name.
+  static const TransportName polling = TransportName._(TransportType.polling);
+
+  /// Creates a WebTransport transport name.
+  static const TransportName webtransport = TransportName._(TransportType.webtransport);
+
+  /// Returns the string value.
+  String get value => type.value;
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) || other is TransportName && runtimeType == other.runtimeType && type == other.type;
+
+  @override
+  int get hashCode => type.hashCode;
+
+  @override
+  String toString() => value;
+}
diff --git a/lib/src/value_objects/url_path_vo.dart b/lib/src/value_objects/url_path_vo.dart
new file mode 100644
index 0000000..344f2b2
--- /dev/null
+++ b/lib/src/value_objects/url_path_vo.dart
@@ -0,0 +1,47 @@
+/// url_path_vo.dart
+///
+/// Value object for URL path with validation
+///
+/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+library url_path_vo;
+
+/// Value object representing a validated URL path.
+///
+/// URL paths must start with '/' and contain valid characters.
+class UrlPath {
+  final String value;
+
+  const UrlPath._(this.value);
+
+  /// Creates a UrlPath from a string with validation.
+  ///
+  /// Throws [ArgumentError] if the path is invalid.
+  factory UrlPath(final String path) {
+    if (path.isEmpty) {
+      throw ArgumentError('URL path cannot be empty');
+    }
+    if (!path.startsWith('/')) {
+      throw ArgumentError('URL path must start with "/"');
+    }
+    return UrlPath._(path);
+  }
+
+  /// Creates a UrlPath without validation (use with caution).
+  const UrlPath.unchecked(this.value);
+
+  /// Default Socket.IO path.
+  static const UrlPath defaultSocketIO = UrlPath.unchecked('/socket.io');
+
+  /// Root path.
+  static const UrlPath root = UrlPath.unchecked('/');
+
+  @override
+  bool operator ==(final Object other) =>
+      identical(this, other) || other is UrlPath && runtimeType == other.runtimeType && value == other.value;
+
+  @override
+  int get hashCode => value.hashCode;
+
+  @override
+  String toString() => value;
+}
diff --git a/lib/src/value_objects/value_objects.dart b/lib/src/value_objects/value_objects.dart
new file mode 100644
index 0000000..16805bc
--- /dev/null
+++ b/lib/src/value_objects/value_objects.dart
@@ -0,0 +1,21 @@
+/// value_objects.dart
+///
+/// Barrel file exporting all value objects
+///
+/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+library value_objects;
+
+export 'connection_id_vo.dart';
+export 'disconnect_reason_vo.dart';
+export 'error_code_vo.dart';
+export 'event_arguments_vo.dart';
+export 'event_name_vo.dart';
+export 'namespace_name_vo.dart';
+export 'packet_id_vo.dart';
+export 'port_number_vo.dart';
+export 'query_parameters_vo.dart';
+export 'room_name_vo.dart';
+export 'socket_state_vo.dart';
+export 'timeout_duration_vo.dart';
+export 'transport_name_vo.dart';
+export 'url_path_vo.dart';
diff --git a/pubspec.yaml b/pubspec.yaml
index b42d222..1e4ba0a 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,20 +1,21 @@
 name: socket_io
-description: >
-  Port of JS/Node library Socket.io. It enables real-time, bidirectional and 
+version: 2.0.1
+description: |
+  Port of JS/Node library Socket.io. It enables real-time, bidirectional and
   event-based communication cross-platform.
-version: 1.0.1
+  Fork of rikulo/socket.io-dart with polling-transport fixes for Engine.IO v4
+  / Socket.IO v3+ clients and full Dart 3 modernisation.
 homepage: https://www.zkoss.org
-repository: https://github.com/rikulo/socket.io-dart
-issue_tracker: https://github.com/rikulo/socket.io-dart/issues
-
+repository: https://github.com/reinbeumer/socket.io-dart
+issue_tracker: https://github.com/reinbeumer/socket.io-dart/issues
 environment:
-  sdk: '>=2.12.0 <3.0.0'
-
+  sdk: ">=3.0.0 <4.0.0"
 dependencies:
-  stream: ^3.0.0
-  socket_io_common: ^1.0.1
-  uuid: ^3.0.4
-  logging: ^1.0.0
+  logging: ^1.3.0
+  meta: ^1.17.0
+  socket_io_common: ^3.1.1
+  stream: ^4.2.2
+  uuid: ^4.5.1
 dev_dependencies:
-  test: ^1.16.8
-  pedantic: ^1.11.0
+  lints: ^6.0.0
+  test: ^1.26.3
diff --git a/test/adapter_broadcast_models_test.dart b/test/adapter_broadcast_models_test.dart
new file mode 100644
index 0000000..4dbfb81
--- /dev/null
+++ b/test/adapter_broadcast_models_test.dart
@@ -0,0 +1,446 @@
+import 'package:test/test.dart';
+import 'package:socket_io/src/models/adapter_broadcast_models.dart';
+import 'package:socket_io/src/models/socket_flags_models.dart';
+import 'package:socket_io/src/value_objects/room_name_vo.dart';
+import 'package:socket_io/src/value_objects/connection_id_vo.dart';
+
+void main() {
+  group('BroadcastOptions', () {
+    group('Construction', () {
+      test('creates with default values', () {
+        final BroadcastOptions options = BroadcastOptions();
+
+        expect(options.rooms, isEmpty);
+        expect(options.except, isEmpty);
+        expect(options.flags, equals(const SocketFlags()));
+      });
+
+      test('creates with specific values', () {
+        final List rooms = [RoomName('room1'), RoomName('room2')];
+        final List except = [ConnectionId('socket1')];
+        final SocketFlags flags = SocketFlags(volatile: true);
+
+        final BroadcastOptions options = BroadcastOptions(
+          rooms: rooms,
+          except: except,
+          flags: flags,
+        );
+
+        expect(options.rooms, equals(rooms));
+        expect(options.except, equals(except));
+        expect(options.flags, equals(flags));
+      });
+    });
+
+    group('fromMap', () {
+      test('creates from legacy map format', () {
+        final Map map = {
+          'rooms': ['room1', 'room2'],
+          'except': ['socket1', 'socket2'],
+          'flags': {'volatile': true, 'compress': false},
+        };
+
+        final BroadcastOptions options = BroadcastOptions.fromMap(map);
+
+        expect(options.rooms.length, equals(2));
+        expect(options.rooms[0].value, equals('room1'));
+        expect(options.rooms[1].value, equals('room2'));
+        expect(options.except.length, equals(2));
+        expect(options.except[0].value, equals('socket1'));
+        expect(options.flags.volatile, isTrue);
+        expect(options.flags.compress, isFalse);
+      });
+
+      test('handles empty map', () {
+        final BroadcastOptions options = BroadcastOptions.fromMap({});
+
+        expect(options.rooms, isEmpty);
+        expect(options.except, isEmpty);
+        expect(options.flags, equals(const SocketFlags()));
+      });
+
+      test('handles null values in map', () {
+        final Map map = {
+          'rooms': null,
+          'except': null,
+          'flags': null,
+        };
+
+        final BroadcastOptions options = BroadcastOptions.fromMap(map);
+
+        expect(options.rooms, isEmpty);
+        expect(options.except, isEmpty);
+        expect(options.flags, equals(const SocketFlags()));
+      });
+    });
+
+    group('toMap', () {
+      test('converts to legacy map format', () {
+        final BroadcastOptions options = BroadcastOptions(
+          rooms: [RoomName('room1')],
+          except: [ConnectionId('socket1')],
+          flags: SocketFlags(volatile: true),
+        );
+
+        final Map map = options.toMap();
+
+        expect(map['rooms'], equals(['room1']));
+        expect(map['except'], equals(['socket1']));
+        expect(map['flags'], isA>());
+      });
+    });
+
+    group('copyWith', () {
+      test('creates copy with modified rooms', () {
+        final BroadcastOptions original = BroadcastOptions(
+          rooms: [RoomName('room1')],
+        );
+        final List newRooms = [RoomName('room2')];
+
+        final BroadcastOptions copy = original.copyWith(rooms: newRooms);
+
+        expect(copy.rooms, equals(newRooms));
+        expect(copy.except, equals(original.except));
+        expect(copy.flags, equals(original.flags));
+      });
+
+      test('creates copy with modified except', () {
+        final BroadcastOptions original = BroadcastOptions(
+          except: [ConnectionId('socket1')],
+        );
+        final List newExcept = [ConnectionId('socket2')];
+
+        final BroadcastOptions copy = original.copyWith(except: newExcept);
+
+        expect(copy.rooms, equals(original.rooms));
+        expect(copy.except, equals(newExcept));
+        expect(copy.flags, equals(original.flags));
+      });
+
+      test('creates copy with modified flags', () {
+        final BroadcastOptions original = BroadcastOptions(
+          flags: SocketFlags(volatile: false),
+        );
+        final SocketFlags newFlags = SocketFlags(volatile: true);
+
+        final BroadcastOptions copy = original.copyWith(flags: newFlags);
+
+        expect(copy.rooms, equals(original.rooms));
+        expect(copy.except, equals(original.except));
+        expect(copy.flags, equals(newFlags));
+      });
+    });
+
+    group('Fluent API', () {
+      test('addRoom adds a single room', () {
+        final BroadcastOptions original = BroadcastOptions();
+
+        final BroadcastOptions modified = original.addRoom(RoomName('room1'));
+
+        expect(modified.rooms.length, equals(1));
+        expect(modified.rooms[0].value, equals('room1'));
+      });
+
+      test('addRooms adds multiple rooms', () {
+        final BroadcastOptions original = BroadcastOptions();
+        final List rooms = [RoomName('room1'), RoomName('room2')];
+
+        final BroadcastOptions modified = original.addRooms(rooms);
+
+        expect(modified.rooms.length, equals(2));
+      });
+
+      test('excludeSocket excludes a single socket', () {
+        final BroadcastOptions original = BroadcastOptions();
+
+        final BroadcastOptions modified = original.excludeSocket(ConnectionId('socket1'));
+
+        expect(modified.except.length, equals(1));
+        expect(modified.except[0].value, equals('socket1'));
+      });
+
+      test('excludeSockets excludes multiple sockets', () {
+        final BroadcastOptions original = BroadcastOptions();
+        final List sockets = [ConnectionId('socket1'), ConnectionId('socket2')];
+
+        final BroadcastOptions modified = original.excludeSockets(sockets);
+
+        expect(modified.except.length, equals(2));
+      });
+
+      test('withFlags sets flags', () {
+        final BroadcastOptions original = BroadcastOptions();
+        final SocketFlags flags = SocketFlags(volatile: true);
+
+        final BroadcastOptions modified = original.withFlags(flags);
+
+        expect(modified.flags.volatile, isTrue);
+      });
+
+      test('supports method chaining', () {
+        final BroadcastOptions options = BroadcastOptions()
+            .addRoom(RoomName('room1'))
+            .excludeSocket(ConnectionId('socket1'))
+            .withFlags(SocketFlags(volatile: true));
+
+        expect(options.rooms.length, equals(1));
+        expect(options.except.length, equals(1));
+        expect(options.flags.volatile, isTrue);
+      });
+    });
+
+    group('Query methods', () {
+      test('hasRoomFilter returns true when rooms specified', () {
+        final BroadcastOptions options = BroadcastOptions(
+          rooms: [RoomName('room1')],
+        );
+
+        expect(options.hasRoomFilter, isTrue);
+      });
+
+      test('hasRoomFilter returns false when no rooms', () {
+        final BroadcastOptions options = BroadcastOptions();
+
+        expect(options.hasRoomFilter, isFalse);
+      });
+
+      test('hasExclusions returns true when sockets excluded', () {
+        final BroadcastOptions options = BroadcastOptions(
+          except: [ConnectionId('socket1')],
+        );
+
+        expect(options.hasExclusions, isTrue);
+      });
+
+      test('hasExclusions returns false when no exclusions', () {
+        final BroadcastOptions options = BroadcastOptions();
+
+        expect(options.hasExclusions, isFalse);
+      });
+
+      test('isExcluded checks if socket is excluded', () {
+        final ConnectionId socket1 = ConnectionId('socket1');
+        final ConnectionId socket2 = ConnectionId('socket2');
+        final BroadcastOptions options = BroadcastOptions(
+          except: [socket1],
+        );
+
+        expect(options.isExcluded(socket1), isTrue);
+        expect(options.isExcluded(socket2), isFalse);
+      });
+
+      test('broadcastToAll returns true when no rooms specified', () {
+        final BroadcastOptions options = BroadcastOptions();
+
+        expect(options.broadcastToAll, isTrue);
+      });
+
+      test('broadcastToAll returns false when rooms specified', () {
+        final BroadcastOptions options = BroadcastOptions(
+          rooms: [RoomName('room1')],
+        );
+
+        expect(options.broadcastToAll, isFalse);
+      });
+    });
+
+    group('Equality', () {
+      test('equal instances are equal', () {
+        final BroadcastOptions options1 = BroadcastOptions(
+          rooms: [RoomName('room1')],
+          except: [ConnectionId('socket1')],
+          flags: SocketFlags(volatile: true),
+        );
+        final BroadcastOptions options2 = BroadcastOptions(
+          rooms: [RoomName('room1')],
+          except: [ConnectionId('socket1')],
+          flags: SocketFlags(volatile: true),
+        );
+
+        expect(options1, equals(options2));
+        expect(options1.hashCode, equals(options2.hashCode));
+      });
+
+      test('different instances are not equal', () {
+        final BroadcastOptions options1 = BroadcastOptions(
+          rooms: [RoomName('room1')],
+        );
+        final BroadcastOptions options2 = BroadcastOptions(
+          rooms: [RoomName('room2')],
+        );
+
+        expect(options1, isNot(equals(options2)));
+      });
+    });
+
+    test('toString returns readable representation', () {
+      final BroadcastOptions options = BroadcastOptions(
+        rooms: [RoomName('room1')],
+        except: [ConnectionId('socket1')],
+      );
+
+      final String str = options.toString();
+      expect(str, contains('BroadcastOptions'));
+      expect(str, contains('room1'));
+      expect(str, contains('socket1'));
+    });
+  });
+
+  group('RoomFilter', () {
+    group('Construction', () {
+      test('creates with default values', () {
+        final RoomFilter filter = RoomFilter();
+
+        expect(filter.includeRooms, isEmpty);
+        expect(filter.excludeRooms, isEmpty);
+        expect(filter.namePattern, isNull);
+      });
+
+      test('creates with include rooms', () {
+        final List rooms = [RoomName('room1')];
+        final RoomFilter filter = RoomFilter(includeRooms: rooms);
+
+        expect(filter.includeRooms, equals(rooms));
+      });
+
+      test('creates with exclude rooms', () {
+        final List rooms = [RoomName('room1')];
+        final RoomFilter filter = RoomFilter(excludeRooms: rooms);
+
+        expect(filter.excludeRooms, equals(rooms));
+      });
+
+      test('creates with name pattern', () {
+        final RoomFilter filter = RoomFilter(namePattern: 'game.*');
+
+        expect(filter.namePattern, equals('game.*'));
+      });
+    });
+
+    group('Factory constructors', () {
+      test('include creates filter with inclusions', () {
+        final List rooms = [RoomName('room1')];
+        final RoomFilter filter = RoomFilter.include(rooms);
+
+        expect(filter.includeRooms, equals(rooms));
+        expect(filter.excludeRooms, isEmpty);
+      });
+
+      test('exclude creates filter with exclusions', () {
+        final List rooms = [RoomName('room1')];
+        final RoomFilter filter = RoomFilter.exclude(rooms);
+
+        expect(filter.excludeRooms, equals(rooms));
+        expect(filter.includeRooms, isEmpty);
+      });
+
+      test('pattern creates filter with pattern', () {
+        final RoomFilter filter = RoomFilter.pattern('game.*');
+
+        expect(filter.namePattern, equals('game.*'));
+      });
+    });
+
+    group('matches', () {
+      test('matches all when filter is empty', () {
+        final RoomFilter filter = RoomFilter();
+        final RoomName room = RoomName('any-room');
+
+        expect(filter.matches(room), isTrue);
+      });
+
+      test('matches only included rooms', () {
+        final RoomName room1 = RoomName('room1');
+        final RoomName room2 = RoomName('room2');
+        final RoomFilter filter = RoomFilter(includeRooms: [room1]);
+
+        expect(filter.matches(room1), isTrue);
+        expect(filter.matches(room2), isFalse);
+      });
+
+      test('excludes excluded rooms', () {
+        final RoomName room1 = RoomName('room1');
+        final RoomName room2 = RoomName('room2');
+        final RoomFilter filter = RoomFilter(excludeRooms: [room1]);
+
+        expect(filter.matches(room1), isFalse);
+        expect(filter.matches(room2), isTrue);
+      });
+
+      test('matches name pattern', () {
+        final RoomFilter filter = RoomFilter(namePattern: r'^game\d+$');
+        final RoomName match = RoomName('game123');
+        final RoomName noMatch = RoomName('room456');
+
+        expect(filter.matches(match), isTrue);
+        expect(filter.matches(noMatch), isFalse);
+      });
+
+      test('combines include and exclude', () {
+        final RoomName room1 = RoomName('room1');
+        final RoomName room2 = RoomName('room2');
+        final RoomName room3 = RoomName('room3');
+        final RoomFilter filter = RoomFilter(
+          includeRooms: [room1, room2],
+          excludeRooms: [room2],
+        );
+
+        expect(filter.matches(room1), isTrue);
+        expect(filter.matches(room2), isFalse); // excluded even though included
+        expect(filter.matches(room3), isFalse);
+      });
+    });
+
+    group('filter', () {
+      test('filters list of rooms', () {
+        final RoomName room1 = RoomName('game1');
+        final RoomName room2 = RoomName('game2');
+        final RoomName room3 = RoomName('chat1');
+        final List rooms = [room1, room2, room3];
+        final RoomFilter filter = RoomFilter(namePattern: r'^game');
+
+        final List filtered = filter.filter(rooms);
+
+        expect(filtered.length, equals(2));
+        expect(filtered, contains(room1));
+        expect(filtered, contains(room2));
+        expect(filtered, isNot(contains(room3)));
+      });
+    });
+
+    group('Equality', () {
+      test('equal instances are equal', () {
+        final RoomFilter filter1 = RoomFilter(
+          includeRooms: [RoomName('room1')],
+          namePattern: 'game.*',
+        );
+        final RoomFilter filter2 = RoomFilter(
+          includeRooms: [RoomName('room1')],
+          namePattern: 'game.*',
+        );
+
+        expect(filter1, equals(filter2));
+        expect(filter1.hashCode, equals(filter2.hashCode));
+      });
+
+      test('different instances are not equal', () {
+        final RoomFilter filter1 = RoomFilter(namePattern: 'game.*');
+        final RoomFilter filter2 = RoomFilter(namePattern: 'chat.*');
+
+        expect(filter1, isNot(equals(filter2)));
+      });
+    });
+
+    test('toString returns readable representation', () {
+      final RoomFilter filter = RoomFilter(
+        includeRooms: [RoomName('room1')],
+        namePattern: 'game.*',
+      );
+
+      final String str = filter.toString();
+      expect(str, contains('RoomFilter'));
+      expect(str, contains('room1'));
+      expect(str, contains('game.*'));
+    });
+  });
+}
diff --git a/test/integration_basic_test.dart b/test/integration_basic_test.dart
new file mode 100644
index 0000000..cf0960a
--- /dev/null
+++ b/test/integration_basic_test.dart
@@ -0,0 +1,252 @@
+/// Basic integration tests for Socket.IO server
+///
+/// These tests verify server initialization, configuration, and basic operations.
+library integration_basic_test;
+
+import 'package:test/test.dart';
+import 'package:socket_io/socket_io.dart' as sio;
+
+void main() {
+  group('Server Integration', () {
+    late sio.Server server;
+
+    setUp(() {
+      server = sio.Server();
+    });
+
+    test('server starts successfully', () async {
+      server.listen(0); // Random port
+      await server.ready;
+
+      expect(server.port, isNotNull);
+      expect(server.port, greaterThan(0));
+    });
+
+    test('server creates default namespace', () {
+      expect(server.sockets, isNotNull);
+      expect(server.sockets.name, equals('/'));
+    });
+
+    test('server creates custom namespaces', () {
+      final chat = server.of('/chat');
+      final admin = server.of('/admin');
+
+      expect(chat, isNotNull);
+      expect(chat.name, equals('/chat'));
+      expect(admin.name, equals('/admin'));
+      expect(chat, isNot(equals(admin)));
+    });
+
+    test('server retrieves same namespace instance', () {
+      final chat1 = server.of('/chat');
+      final chat2 = server.of('/chat');
+
+      expect(identical(chat1, chat2), isTrue);
+    });
+
+    test('namespace adds middleware', () {
+      final namespace = server.of('/test');
+
+      namespace.use((socket, next) {
+        next(null);
+      });
+
+      expect(namespace.fns.length, equals(1));
+    });
+
+    test('server handles multiple namespaces', () {
+      for (var i = 0; i < 10; i++) {
+        server.of('/namespace$i');
+      }
+
+      expect(server.nsps.length, greaterThanOrEqualTo(10));
+    });
+
+    test('server configuration can be set', () {
+      final configuredServer = sio.Server(options: {
+        'path': '/custom-socket',
+        'pingTimeout': 60000,
+        'pingInterval': 25000,
+      });
+
+      expect(configuredServer, isNotNull);
+    });
+  });
+
+  group('Namespace Operations', () {
+    late sio.Server server;
+    late sio.Namespace namespace;
+
+    setUp(() {
+      server = sio.Server();
+      namespace = server.of('/test');
+    });
+
+    test('namespace initializes adapter', () {
+      expect(namespace.adapter, isNotNull);
+    });
+
+    test('namespace tracks sockets', () {
+      expect(namespace.sockets, isEmpty);
+      expect(namespace.connected, isEmpty);
+    });
+
+    test('namespace can add multiple middleware', () {
+      namespace.use((socket, next) => next(null));
+      namespace.use((socket, next) => next(null));
+      namespace.use((socket, next) => next(null));
+
+      expect(namespace.fns.length, equals(3));
+    });
+
+    test('namespace maintains state independently', () {
+      final chat = server.of('/chat');
+      final admin = server.of('/admin');
+
+      chat.use((socket, next) => next(null));
+      admin.use((socket, next) => next(null));
+      admin.use((socket, next) => next(null));
+
+      expect(chat.fns.length, equals(1));
+      expect(admin.fns.length, equals(2));
+    });
+  });
+
+  group('Server Options', () {
+    test('server accepts ping timeout option', () {
+      final server = sio.Server(options: {
+        'pingTimeout': 120000,
+      });
+
+      expect(server, isNotNull);
+    });
+
+    test('server accepts ping interval option', () {
+      final server = sio.Server(options: {
+        'pingInterval': 30000,
+      });
+
+      expect(server, isNotNull);
+    });
+
+    test('server accepts transport options', () {
+      final server = sio.Server(options: {
+        'transports': ['websocket', 'polling'],
+      });
+
+      expect(server, isNotNull);
+    });
+
+    test('server accepts path option', () {
+      final server = sio.Server(options: {
+        'path': '/my-socket.io',
+      });
+
+      expect(server, isNotNull);
+    });
+
+    test('server accepts adapter option', () {
+      final server = sio.Server(options: {
+        'adapter': 'default',
+      });
+
+      expect(server, isNotNull);
+    });
+
+    test('server accepts multiple options', () {
+      final server = sio.Server(options: {
+        'path': '/socket',
+        'pingTimeout': 60000,
+        'pingInterval': 25000,
+        'transports': ['websocket'],
+        'adapter': 'default',
+      });
+
+      expect(server, isNotNull);
+    });
+  });
+
+  group('Adapter Operations', () {
+    late sio.Server server;
+    late sio.Namespace namespace;
+
+    setUp(() {
+      server = sio.Server();
+      namespace = server.of('/test');
+    });
+
+    test('adapter is initialized correctly', () {
+      expect(namespace.adapter, isNotNull);
+    });
+
+    test('adapter can handle room operations', () {
+      // Test that adapter methods are accessible
+      expect(() => namespace.adapter.add('socket1', 'room1'), returnsNormally);
+    });
+
+    test('adapter maintains room state', () {
+      namespace.adapter.add('socket1', 'room1');
+      namespace.adapter.add('socket2', 'room1');
+      namespace.adapter.add('socket3', 'room2');
+
+      // Adapter should maintain internal room state
+      expect(namespace.adapter, isNotNull);
+    });
+  });
+
+  group('Performance', () {
+    late sio.Server server;
+
+    setUp(() {
+      server = sio.Server();
+    });
+
+    test('handles creating many namespaces', () {
+      final stopwatch = Stopwatch()..start();
+
+      for (var i = 0; i < 100; i++) {
+        server.of('/namespace$i');
+      }
+
+      stopwatch.stop();
+
+      expect(server.nsps.length, greaterThanOrEqualTo(100));
+      expect(stopwatch.elapsedMilliseconds, lessThan(1000)); // Should be fast
+    });
+
+    test('handles many event listeners', () {
+      final namespace = server.of('/test');
+      final stopwatch = Stopwatch()..start();
+
+      for (var i = 0; i < 1000; i++) {
+        namespace.on('event$i', (data) {});
+      }
+
+      stopwatch.stop();
+
+      expect(stopwatch.elapsedMilliseconds, lessThan(1000)); // Should be fast
+    });
+  });
+
+  group('Configuration Validation', () {
+    test('server handles various valid configurations', () {
+      expect(() => sio.Server(options: {}), returnsNormally);
+      expect(() => sio.Server(options: {'path': '/test'}), returnsNormally);
+      expect(() => sio.Server(options: {'pingTimeout': 5000}), returnsNormally);
+    });
+
+    test('namespace name starting with slash', () {
+      final server = sio.Server();
+      final ns = server.of('/valid-namespace');
+
+      expect(ns.name, equals('/valid-namespace'));
+    });
+
+    test('creates multiple independent servers', () {
+      final server1 = sio.Server();
+      final server2 = sio.Server();
+
+      expect(server1, isNot(same(server2)));
+    });
+  });
+}
diff --git a/test/models/adapter_room_models_test.dart b/test/models/adapter_room_models_test.dart
new file mode 100644
index 0000000..93bd881
--- /dev/null
+++ b/test/models/adapter_room_models_test.dart
@@ -0,0 +1,179 @@
+/// adapter_room_models_test.dart
+///
+/// Tests for adapter room management models
+import 'package:test/test.dart';
+import 'package:socket_io/src/models/adapter_room_models.dart';
+
+void main() {
+  group('SocketRoomMembership', () {
+    test('creates empty membership', () {
+      final SocketRoomMembership membership = SocketRoomMembership();
+      expect(membership.isEmpty, isTrue);
+      expect(membership.isNotEmpty, isFalse);
+      expect(membership.roomCount, equals(0));
+      expect(membership.rooms, isEmpty);
+    });
+
+    test('creates membership from rooms', () {
+      final SocketRoomMembership membership = SocketRoomMembership.fromRooms(['room1', 'room2']);
+      expect(membership.roomCount, equals(2));
+      expect(membership.isInRoom('room1'), isTrue);
+      expect(membership.isInRoom('room2'), isTrue);
+      expect(membership.isInRoom('room3'), isFalse);
+    });
+
+    test('adds room', () {
+      final SocketRoomMembership membership = SocketRoomMembership();
+      membership.addRoom('room1');
+      expect(membership.roomCount, equals(1));
+      expect(membership.isInRoom('room1'), isTrue);
+    });
+
+    test('adds duplicate room only once', () {
+      final SocketRoomMembership membership = SocketRoomMembership();
+      membership.addRoom('room1');
+      membership.addRoom('room1');
+      expect(membership.roomCount, equals(1));
+    });
+
+    test('removes room', () {
+      final SocketRoomMembership membership = SocketRoomMembership.fromRooms(['room1', 'room2']);
+      final bool removed = membership.removeRoom('room1');
+      expect(removed, isTrue);
+      expect(membership.roomCount, equals(1));
+      expect(membership.isInRoom('room1'), isFalse);
+      expect(membership.isInRoom('room2'), isTrue);
+    });
+
+    test('removeRoom returns false when room not found', () {
+      final SocketRoomMembership membership = SocketRoomMembership();
+      final bool removed = membership.removeRoom('nonexistent');
+      expect(removed, isFalse);
+    });
+
+    test('clears all rooms', () {
+      final SocketRoomMembership membership = SocketRoomMembership.fromRooms(['room1', 'room2']);
+      membership.clear();
+      expect(membership.isEmpty, isTrue);
+      expect(membership.roomCount, equals(0));
+    });
+
+    test('rooms returns unmodifiable set', () {
+      final SocketRoomMembership membership = SocketRoomMembership.fromRooms(['room1']);
+      final Set rooms = membership.rooms;
+      expect(() => rooms.add('room2'), throwsUnsupportedError);
+    });
+
+    test('copies membership', () {
+      final SocketRoomMembership original = SocketRoomMembership.fromRooms(['room1', 'room2']);
+      final SocketRoomMembership copy = original.copy();
+
+      expect(copy.roomCount, equals(original.roomCount));
+      expect(copy.isInRoom('room1'), isTrue);
+      expect(copy.isInRoom('room2'), isTrue);
+
+      // Verify deep copy - modifying copy doesn't affect original
+      copy.addRoom('room3');
+      expect(copy.roomCount, equals(3));
+      expect(original.roomCount, equals(2));
+    });
+
+    test('equality works correctly', () {
+      final SocketRoomMembership membership1 = SocketRoomMembership.fromRooms(['room1', 'room2']);
+      final SocketRoomMembership membership2 =
+          SocketRoomMembership.fromRooms(['room2', 'room1']); // Different order
+      final SocketRoomMembership membership3 = SocketRoomMembership.fromRooms(['room1']);
+
+      expect(membership1, equals(membership2)); // Order doesn't matter
+      expect(membership1, isNot(equals(membership3)));
+    });
+
+    test('hashCode is consistent', () {
+      final SocketRoomMembership membership1 = SocketRoomMembership.fromRooms(['room1', 'room2']);
+      final SocketRoomMembership membership2 = SocketRoomMembership.fromRooms(['room2', 'room1']);
+
+      // Equal objects should have same hashcode (or at least test equality works)
+      expect(membership1 == membership2, isTrue);
+      // Hashcode of same instance should be consistent
+      expect(membership1.hashCode, equals(membership1.hashCode));
+    });
+
+    test('toString includes rooms', () {
+      final SocketRoomMembership membership = SocketRoomMembership.fromRooms(['room1', 'room2']);
+      final String str = membership.toString();
+      expect(str, contains('SocketRoomMembership'));
+      expect(str, contains('rooms'));
+    });
+
+    test('converts to compatibility map', () {
+      final SocketRoomMembership membership = SocketRoomMembership.fromRooms(['room1', 'room2']);
+      final Map compatMap = membership.toCompatibilityMap();
+
+      expect(compatMap.length, equals(2));
+      expect(compatMap['room1'], isTrue);
+      expect(compatMap['room2'], isTrue);
+    });
+
+    test('creates from compatibility map', () {
+      final Map compatMap = {
+        'room1': true,
+        'room2': false, // Value doesn't matter
+        'room3': 'anything', // Value type doesn't matter
+      };
+
+      final SocketRoomMembership membership = SocketRoomMembership.fromCompatibilityMap(compatMap);
+      expect(membership.roomCount, equals(3));
+      expect(membership.isInRoom('room1'), isTrue);
+      expect(membership.isInRoom('room2'), isTrue);
+      expect(membership.isInRoom('room3'), isTrue);
+    });
+
+    test('round trip through compatibility map', () {
+      final SocketRoomMembership original = SocketRoomMembership.fromRooms(['room1', 'room2', 'room3']);
+      final Map compatMap = original.toCompatibilityMap();
+      final SocketRoomMembership roundTrip = SocketRoomMembership.fromCompatibilityMap(compatMap);
+
+      expect(roundTrip, equals(original));
+    });
+  });
+
+  group('AdapterNamespaceData', () {
+    test('creates with name', () {
+      final AdapterNamespaceData data = AdapterNamespaceData(name: '/chat');
+      expect(data.name, equals('/chat'));
+      expect(data.metadata, isEmpty);
+    });
+
+    test('creates with metadata', () {
+      final AdapterNamespaceData data = AdapterNamespaceData(
+        name: '/chat',
+        metadata: {'created': DateTime.now()},
+      );
+      expect(data.name, equals('/chat'));
+      expect(data.metadata, isNotEmpty);
+    });
+
+    test('equality based on name', () {
+      final AdapterNamespaceData data1 = AdapterNamespaceData(name: '/chat');
+      final AdapterNamespaceData data2 = AdapterNamespaceData(name: '/chat', metadata: {'a': 1});
+      final AdapterNamespaceData data3 = AdapterNamespaceData(name: '/other');
+
+      expect(data1, equals(data2)); // Metadata doesn't affect equality
+      expect(data1, isNot(equals(data3)));
+    });
+
+    test('hashCode based on name', () {
+      final AdapterNamespaceData data1 = AdapterNamespaceData(name: '/chat');
+      final AdapterNamespaceData data2 = AdapterNamespaceData(name: '/chat');
+
+      expect(data1.hashCode, equals(data2.hashCode));
+    });
+
+    test('toString includes name', () {
+      final AdapterNamespaceData data = AdapterNamespaceData(name: '/chat');
+      final String str = data.toString();
+      expect(str, contains('AdapterNamespaceData'));
+      expect(str, contains('/chat'));
+    });
+  });
+}
diff --git a/test/models/callbacks_models_test.dart b/test/models/callbacks_models_test.dart
new file mode 100644
index 0000000..5aa24cf
--- /dev/null
+++ b/test/models/callbacks_models_test.dart
@@ -0,0 +1,126 @@
+import 'package:test/test.dart';
+import 'package:socket_io/src/models/callbacks_models.dart';
+import 'package:socket_io/src/value_objects/connection_id_vo.dart';
+
+void main() {
+  group('Callbacks', () {
+    test('AckCallback accepts list of arguments', () {
+      final List receivedArgs = [];
+      final AckCallback callback = (final List args) {
+        receivedArgs.addAll(args);
+      };
+
+      callback(['arg1', 42, true]);
+      expect(receivedArgs, equals(['arg1', 42, true]));
+    });
+
+    test('ErrorCallback accepts error object', () {
+      Object? receivedError;
+      final ErrorCallback callback = (final Object error) {
+        receivedError = error;
+      };
+
+      final Exception testError = Exception('test error');
+      callback(testError);
+      expect(receivedError, equals(testError));
+    });
+
+    test('MiddlewareCallback accepts socket and next function', () {
+      Object? receivedSocket;
+      MiddlewareNext? receivedNext;
+
+      final MiddlewareCallback callback = (final Object socket, final MiddlewareNext next) {
+        receivedSocket = socket;
+        receivedNext = next;
+      };
+
+      final Object testSocket = Object();
+      void testNext(final Object? error) {}
+
+      callback(testSocket, testNext);
+      expect(receivedSocket, equals(testSocket));
+      expect(receivedNext, isNotNull);
+    });
+
+    test('MiddlewareNext accepts optional error', () {
+      Object? receivedError;
+      final MiddlewareNext next = (final Object? error) {
+        receivedError = error;
+      };
+
+      next(null);
+      expect(receivedError, isNull);
+
+      final Exception testError = Exception('error');
+      next(testError);
+      expect(receivedError, equals(testError));
+    });
+
+    test('ClientsCallback accepts list of ConnectionId', () {
+      List? receivedClients;
+      final ClientsCallback callback = (final List clients) {
+        receivedClients = clients;
+      };
+
+      final List testClients = [
+        ConnectionId('id1'),
+        ConnectionId('id2'),
+      ];
+
+      callback(testClients);
+      expect(receivedClients, equals(testClients));
+    });
+
+    test('ClientsStringCallback accepts list of strings', () {
+      List? receivedClients;
+      final ClientsStringCallback callback = (final List clients) {
+        receivedClients = clients;
+      };
+
+      callback(['id1', 'id2']);
+      expect(receivedClients, equals(['id1', 'id2']));
+    });
+
+    test('VoidCallback has no parameters', () {
+      bool called = false;
+      final VoidCallback callback = () {
+        called = true;
+      };
+
+      callback();
+      expect(called, isTrue);
+    });
+
+    test('DataCallback accepts nullable data', () {
+      Object? receivedData;
+      final DataCallback callback = (final Object? data) {
+        receivedData = data;
+      };
+
+      callback('test');
+      expect(receivedData, equals('test'));
+
+      callback(null);
+      expect(receivedData, isNull);
+    });
+
+    test('EmitCallback accepts error and response', () {
+      Object? receivedError;
+      List? receivedResponse;
+
+      final EmitCallback callback = (final Object? error, final List? response) {
+        receivedError = error;
+        receivedResponse = response;
+      };
+
+      callback(null, ['data']);
+      expect(receivedError, isNull);
+      expect(receivedResponse, equals(['data']));
+
+      final Exception testError = Exception('error');
+      callback(testError, null);
+      expect(receivedError, equals(testError));
+      expect(receivedResponse, isNull);
+    });
+  });
+}
diff --git a/test/models/client_connection_models_test.dart b/test/models/client_connection_models_test.dart
new file mode 100644
index 0000000..2eac21f
--- /dev/null
+++ b/test/models/client_connection_models_test.dart
@@ -0,0 +1,298 @@
+/// client_connection_models_test.dart
+///
+/// Tests for client connection state models
+library client_connection_models_test;
+
+import 'package:test/test.dart';
+import 'package:socket_io/src/models/client_connection_models.dart';
+import 'package:socket_io/src/value_objects/namespace_name_vo.dart';
+
+void main() {
+  group('ClientConnectionState', () {
+    test('isActive is true only for connected state', () {
+      expect(ClientConnectionState.connecting.isActive, isFalse);
+      expect(ClientConnectionState.connected.isActive, isTrue);
+      expect(ClientConnectionState.disconnecting.isActive, isFalse);
+      expect(ClientConnectionState.disconnected.isActive, isFalse);
+      expect(ClientConnectionState.error.isActive, isFalse);
+    });
+
+    test('isTransitioning is true for connecting and disconnecting', () {
+      expect(ClientConnectionState.connecting.isTransitioning, isTrue);
+      expect(ClientConnectionState.connected.isTransitioning, isFalse);
+      expect(ClientConnectionState.disconnecting.isTransitioning, isTrue);
+      expect(ClientConnectionState.disconnected.isTransitioning, isFalse);
+      expect(ClientConnectionState.error.isTransitioning, isFalse);
+    });
+
+    test('isInactive is true for disconnected and error states', () {
+      expect(ClientConnectionState.connecting.isInactive, isFalse);
+      expect(ClientConnectionState.connected.isInactive, isFalse);
+      expect(ClientConnectionState.disconnecting.isInactive, isFalse);
+      expect(ClientConnectionState.disconnected.isInactive, isTrue);
+      expect(ClientConnectionState.error.isInactive, isTrue);
+    });
+
+    test('description returns human-readable text', () {
+      expect(ClientConnectionState.connecting.description, contains('Establishing'));
+      expect(ClientConnectionState.connected.description, contains('Connected'));
+      expect(ClientConnectionState.disconnecting.description, contains('Disconnecting'));
+      expect(ClientConnectionState.disconnected.description, contains('Disconnected'));
+      expect(ClientConnectionState.error.description, contains('error'));
+    });
+  });
+
+  group('NamespaceConnectionInfo', () {
+    test('creates instance with required fields', () {
+      final DateTime now = DateTime.now();
+      final NamespaceName namespace = NamespaceName('/test');
+
+      final NamespaceConnectionInfo info = NamespaceConnectionInfo(
+        namespace: namespace,
+        connectedAt: now,
+      );
+
+      expect(info.namespace, equals(namespace));
+      expect(info.connectedAt, equals(now));
+      expect(info.isDefault, isFalse);
+      expect(info.socketCount, equals(1));
+    });
+
+    test('creates default namespace instance', () {
+      final DateTime now = DateTime.now();
+
+      final NamespaceConnectionInfo info = NamespaceConnectionInfo.defaultNamespace(
+        connectedAt: now,
+      );
+
+      expect(info.namespace.value, equals('/'));
+      expect(info.isDefault, isTrue);
+      expect(info.connectedAt, equals(now));
+    });
+
+    test('calculates connection duration', () {
+      final DateTime past = DateTime.now().subtract(const Duration(seconds: 5));
+
+      final NamespaceConnectionInfo info = NamespaceConnectionInfo(
+        namespace: NamespaceName('/test'),
+        connectedAt: past,
+      );
+
+      expect(info.connectedDuration.inSeconds, greaterThanOrEqualTo(4));
+    });
+
+    test('copyWith creates new instance with updated values', () {
+      final NamespaceConnectionInfo original = NamespaceConnectionInfo(
+        namespace: NamespaceName('/test'),
+        connectedAt: DateTime.now(),
+        socketCount: 1,
+      );
+
+      final NamespaceConnectionInfo updated = original.copyWith(
+        socketCount: 2,
+      );
+
+      expect(updated.socketCount, equals(2));
+      expect(updated.namespace, equals(original.namespace));
+      expect(updated.connectedAt, equals(original.connectedAt));
+    });
+
+    test('equality works correctly', () {
+      final DateTime now = DateTime.now();
+      final NamespaceName namespace = NamespaceName('/test');
+
+      final NamespaceConnectionInfo info1 = NamespaceConnectionInfo(
+        namespace: namespace,
+        connectedAt: now,
+      );
+
+      final NamespaceConnectionInfo info2 = NamespaceConnectionInfo(
+        namespace: namespace,
+        connectedAt: now,
+      );
+
+      expect(info1, equals(info2));
+      expect(info1.hashCode, equals(info2.hashCode));
+    });
+
+    test('toString includes all important info', () {
+      final NamespaceConnectionInfo info = NamespaceConnectionInfo(
+        namespace: NamespaceName('/test'),
+        connectedAt: DateTime.now(),
+        socketCount: 3,
+      );
+
+      final String str = info.toString();
+
+      expect(str, contains('/test'));
+      expect(str, contains('socketCount: 3'));
+    });
+  });
+
+  group('ClientConnectionState2', () {
+    test('creates connecting state', () {
+      final ClientConnectionState2 state = ClientConnectionState2.connecting();
+
+      expect(state.state, equals(ClientConnectionState.connecting));
+      expect(state.namespaces, isEmpty);
+      expect(state.initialConnectionTime, isNotNull);
+      expect(state.lastError, isNull);
+    });
+
+    test('creates connected state', () {
+      final ClientConnectionState2 state = ClientConnectionState2.connected();
+
+      expect(state.state, equals(ClientConnectionState.connected));
+      expect(state.namespaces, isEmpty);
+      expect(state.initialConnectionTime, isNotNull);
+    });
+
+    test('creates disconnected state with reason', () {
+      final ClientConnectionState2 state = ClientConnectionState2.disconnected(reason: 'User logout');
+
+      expect(state.state, equals(ClientConnectionState.disconnected));
+      expect(state.lastError, equals('User logout'));
+    });
+
+    test('creates error state', () {
+      final ClientConnectionState2 state = ClientConnectionState2.error('Connection timeout');
+
+      expect(state.state, equals(ClientConnectionState.error));
+      expect(state.lastError, equals('Connection timeout'));
+    });
+
+    test('hasNamespaces is false for empty state', () {
+      final ClientConnectionState2 state = ClientConnectionState2.connected();
+
+      expect(state.hasNamespaces, isFalse);
+      expect(state.namespaceCount, equals(0));
+    });
+
+    test('hasDefaultNamespace detects default namespace', () {
+      final NamespaceConnectionInfo info = NamespaceConnectionInfo.defaultNamespace(
+        connectedAt: DateTime.now(),
+      );
+
+      final ClientConnectionState2 state = ClientConnectionState2.connected(
+        namespaces: {'/': info},
+      );
+
+      expect(state.hasDefaultNamespace, isTrue);
+    });
+
+    test('addNamespace updates state', () {
+      final ClientConnectionState2 initialState = ClientConnectionState2.connecting();
+
+      final NamespaceConnectionInfo info = NamespaceConnectionInfo(
+        namespace: NamespaceName('/test'),
+        connectedAt: DateTime.now(),
+      );
+
+      final ClientConnectionState2 updatedState = initialState.addNamespace('/test', info);
+
+      expect(updatedState.hasNamespaces, isTrue);
+      expect(updatedState.namespaceCount, equals(1));
+      expect(updatedState.namespaces.containsKey('/test'), isTrue);
+      expect(updatedState.state, equals(ClientConnectionState.connected));
+    });
+
+    test('removeNamespace updates state', () {
+      final NamespaceConnectionInfo info = NamespaceConnectionInfo(
+        namespace: NamespaceName('/test'),
+        connectedAt: DateTime.now(),
+      );
+
+      final ClientConnectionState2 initialState = ClientConnectionState2.connected(
+        namespaces: {'/test': info},
+      );
+
+      final ClientConnectionState2 updatedState = initialState.removeNamespace('/test');
+
+      expect(updatedState.hasNamespaces, isFalse);
+      expect(updatedState.namespaceCount, equals(0));
+    });
+
+    test('isActive is true only when connected with namespaces', () {
+      final ClientConnectionState2 connectingState = ClientConnectionState2.connecting();
+      expect(connectingState.isActive, isFalse);
+
+      final ClientConnectionState2 connectedEmpty = ClientConnectionState2.connected();
+      expect(connectedEmpty.isActive, isFalse);
+
+      final NamespaceConnectionInfo info = NamespaceConnectionInfo.defaultNamespace(
+        connectedAt: DateTime.now(),
+      );
+
+      final ClientConnectionState2 connectedWithNs = ClientConnectionState2.connected(
+        namespaces: {'/': info},
+      );
+      expect(connectedWithNs.isActive, isTrue);
+    });
+
+    test('totalConnectionDuration is null when no initial time', () {
+      const ClientConnectionState2 state = ClientConnectionState2(
+        state: ClientConnectionState.disconnected,
+      );
+
+      expect(state.totalConnectionDuration, isNull);
+    });
+
+    test('totalConnectionDuration calculates correctly', () {
+      final ClientConnectionState2 state = ClientConnectionState2.connecting();
+
+      // Small delay to ensure duration is measurable
+      expect(state.totalConnectionDuration, isNotNull);
+      expect(state.totalConnectionDuration!.inMilliseconds, greaterThanOrEqualTo(0));
+    });
+
+    test('copyWith creates new instance with updated values', () {
+      final ClientConnectionState2 original = ClientConnectionState2.connecting();
+
+      final ClientConnectionState2 updated = original.copyWith(
+        state: ClientConnectionState.connected,
+        lastError: 'Test error',
+      );
+
+      expect(updated.state, equals(ClientConnectionState.connected));
+      expect(updated.lastError, equals('Test error'));
+      expect(updated.initialConnectionTime, equals(original.initialConnectionTime));
+    });
+
+    test('toString includes relevant information', () {
+      final ClientConnectionState2 state = ClientConnectionState2.error('Test error');
+
+      final String str = state.toString();
+
+      expect(str, contains('error'));
+      expect(str, contains('Test error'));
+    });
+
+    test('manages multiple namespaces', () {
+      final NamespaceConnectionInfo info1 = NamespaceConnectionInfo.defaultNamespace(
+        connectedAt: DateTime.now(),
+      );
+
+      final NamespaceConnectionInfo info2 = NamespaceConnectionInfo(
+        namespace: NamespaceName('/chat'),
+        connectedAt: DateTime.now(),
+      );
+
+      final NamespaceConnectionInfo info3 = NamespaceConnectionInfo(
+        namespace: NamespaceName('/notifications'),
+        connectedAt: DateTime.now(),
+      );
+
+      ClientConnectionState2 state = ClientConnectionState2.connected();
+      state = state.addNamespace('/', info1);
+      state = state.addNamespace('/chat', info2);
+      state = state.addNamespace('/notifications', info3);
+
+      expect(state.namespaceCount, equals(3));
+      expect(state.hasDefaultNamespace, isTrue);
+      expect(state.isActive, isTrue);
+
+      state = state.removeNamespace('/chat');
+      expect(state.namespaceCount, equals(2));
+    });
+  });
+}
diff --git a/test/models/connect_buffer_models_test.dart b/test/models/connect_buffer_models_test.dart
new file mode 100644
index 0000000..09ae831
--- /dev/null
+++ b/test/models/connect_buffer_models_test.dart
@@ -0,0 +1,276 @@
+/// connect_buffer_models_test.dart
+///
+/// Tests for ConnectBuffer model
+library connect_buffer_models_test;
+
+import 'package:test/test.dart';
+import 'package:socket_io/src/models/connect_buffer_models.dart';
+import 'package:socket_io/src/value_objects/namespace_name_vo.dart';
+
+void main() {
+  group('ConnectBuffer', () {
+    group('Constructors', () {
+      test('default constructor creates empty buffer', () {
+        final ConnectBuffer buffer = ConnectBuffer();
+
+        expect(buffer.isEmpty, isTrue);
+        expect(buffer.isNotEmpty, isFalse);
+        expect(buffer.length, equals(0));
+        expect(buffer.toList(), isEmpty);
+      });
+
+      test('fromList creates buffer with initial namespaces', () {
+        final ConnectBuffer buffer = ConnectBuffer.fromList(['/chat', '/admin', '/news']);
+
+        expect(buffer.isEmpty, isFalse);
+        expect(buffer.isNotEmpty, isTrue);
+        expect(buffer.length, equals(3));
+        expect(buffer.toList(), equals(['/chat', '/admin', '/news']));
+      });
+
+      test('fromList creates independent copy', () {
+        final List original = ['/chat', '/admin'];
+        final ConnectBuffer buffer = ConnectBuffer.fromList(original);
+
+        original.add('/news');
+
+        expect(buffer.length, equals(2));
+        expect(buffer.toList(), equals(['/chat', '/admin']));
+      });
+    });
+
+    group('Adding namespaces', () {
+      test('add adds namespace to buffer', () {
+        final ConnectBuffer buffer = ConnectBuffer();
+
+        buffer.add('/chat');
+
+        expect(buffer.contains('/chat'), isTrue);
+        expect(buffer.length, equals(1));
+      });
+
+      test('add does not add duplicate namespaces', () {
+        final ConnectBuffer buffer = ConnectBuffer();
+
+        buffer.add('/chat');
+        buffer.add('/chat');
+        buffer.add('/chat');
+
+        expect(buffer.length, equals(1));
+        expect(buffer.toList(), equals(['/chat']));
+      });
+
+      test('addTyped adds namespace using NamespaceName', () {
+        final ConnectBuffer buffer = ConnectBuffer();
+        final NamespaceName namespace = NamespaceName('/chat');
+
+        buffer.addTyped(namespace);
+
+        expect(buffer.contains('/chat'), isTrue);
+        expect(buffer.length, equals(1));
+      });
+
+      test('add multiple different namespaces', () {
+        final ConnectBuffer buffer = ConnectBuffer();
+
+        buffer.add('/chat');
+        buffer.add('/admin');
+        buffer.add('/news');
+
+        expect(buffer.length, equals(3));
+        expect(buffer.contains('/chat'), isTrue);
+        expect(buffer.contains('/admin'), isTrue);
+        expect(buffer.contains('/news'), isTrue);
+      });
+    });
+
+    group('Removing namespaces', () {
+      test('remove removes existing namespace', () {
+        final ConnectBuffer buffer = ConnectBuffer.fromList(['/chat', '/admin']);
+
+        final bool removed = buffer.remove('/chat');
+
+        expect(removed, isTrue);
+        expect(buffer.contains('/chat'), isFalse);
+        expect(buffer.length, equals(1));
+      });
+
+      test('remove returns false for non-existent namespace', () {
+        final ConnectBuffer buffer = ConnectBuffer.fromList(['/chat']);
+
+        final bool removed = buffer.remove('/admin');
+
+        expect(removed, isFalse);
+        expect(buffer.length, equals(1));
+      });
+
+      test('removeTyped removes namespace using NamespaceName', () {
+        final ConnectBuffer buffer = ConnectBuffer.fromList(['/chat', '/admin']);
+        final NamespaceName namespace = NamespaceName('/chat');
+
+        final bool removed = buffer.removeTyped(namespace);
+
+        expect(removed, isTrue);
+        expect(buffer.contains('/chat'), isFalse);
+      });
+    });
+
+    group('Clearing buffer', () {
+      test('clear removes all namespaces', () {
+        final ConnectBuffer buffer = ConnectBuffer.fromList(['/chat', '/admin', '/news']);
+
+        buffer.clear();
+
+        expect(buffer.isEmpty, isTrue);
+        expect(buffer.length, equals(0));
+        expect(buffer.toList(), isEmpty);
+      });
+
+      test('clear on empty buffer is safe', () {
+        final ConnectBuffer buffer = ConnectBuffer();
+
+        buffer.clear();
+
+        expect(buffer.isEmpty, isTrue);
+      });
+    });
+
+    group('Querying buffer', () {
+      test('contains returns true for existing namespace', () {
+        final ConnectBuffer buffer = ConnectBuffer.fromList(['/chat', '/admin']);
+
+        expect(buffer.contains('/chat'), isTrue);
+        expect(buffer.contains('/admin'), isTrue);
+      });
+
+      test('contains returns false for non-existent namespace', () {
+        final ConnectBuffer buffer = ConnectBuffer.fromList(['/chat']);
+
+        expect(buffer.contains('/admin'), isFalse);
+        expect(buffer.contains('/news'), isFalse);
+      });
+
+      test('containsTyped checks using NamespaceName', () {
+        final ConnectBuffer buffer = ConnectBuffer.fromList(['/chat']);
+        final NamespaceName exists = NamespaceName('/chat');
+        final NamespaceName notExists = NamespaceName('/admin');
+
+        expect(buffer.containsTyped(exists), isTrue);
+        expect(buffer.containsTyped(notExists), isFalse);
+      });
+
+      test('toList returns immutable copy', () {
+        final ConnectBuffer buffer = ConnectBuffer.fromList(['/chat', '/admin']);
+
+        final List list = buffer.toList();
+
+        expect(() => list.add('/news'), throwsUnsupportedError);
+      });
+    });
+
+    group('Processing buffer', () {
+      test('processAll calls processor for each namespace', () {
+        final ConnectBuffer buffer = ConnectBuffer.fromList(['/chat', '/admin', '/news']);
+        final List processed = [];
+
+        buffer.processAll((final String namespace) {
+          processed.add(namespace);
+        });
+
+        expect(processed, equals(['/chat', '/admin', '/news']));
+      });
+
+      test('processAll clears buffer after processing', () {
+        final ConnectBuffer buffer = ConnectBuffer.fromList(['/chat', '/admin']);
+
+        buffer.processAll((final String namespace) {
+          // Do nothing
+        });
+
+        expect(buffer.isEmpty, isTrue);
+        expect(buffer.length, equals(0));
+      });
+
+      test('processAll handles empty buffer', () {
+        final ConnectBuffer buffer = ConnectBuffer();
+        int callCount = 0;
+
+        buffer.processAll((final String namespace) {
+          callCount++;
+        });
+
+        expect(callCount, equals(0));
+        expect(buffer.isEmpty, isTrue);
+      });
+
+      test('processAll processes snapshot of buffer', () {
+        final ConnectBuffer buffer = ConnectBuffer.fromList(['/chat', '/admin']);
+        final List processed = [];
+
+        buffer.processAll((final String namespace) {
+          processed.add(namespace);
+          // Try to modify buffer during processing
+          if (namespace == '/chat') {
+            buffer.add('/news'); // This should not affect current processing
+          }
+        });
+
+        expect(processed, equals(['/chat', '/admin']));
+        expect(buffer.contains('/news'), isTrue);
+        expect(buffer.length, equals(1)); // Only '/news' remains
+      });
+    });
+
+    group('Equality and hashCode', () {
+      test('equal buffers are equal', () {
+        final ConnectBuffer buffer1 = ConnectBuffer.fromList(['/chat', '/admin']);
+        final ConnectBuffer buffer2 = ConnectBuffer.fromList(['/chat', '/admin']);
+
+        expect(buffer1, equals(buffer2));
+        expect(buffer1.hashCode, equals(buffer2.hashCode));
+      });
+
+      test('different buffers are not equal', () {
+        final ConnectBuffer buffer1 = ConnectBuffer.fromList(['/chat', '/admin']);
+        final ConnectBuffer buffer2 = ConnectBuffer.fromList(['/chat', '/news']);
+
+        expect(buffer1, isNot(equals(buffer2)));
+      });
+
+      test('empty buffers are equal', () {
+        final ConnectBuffer buffer1 = ConnectBuffer();
+        final ConnectBuffer buffer2 = ConnectBuffer();
+
+        expect(buffer1, equals(buffer2));
+        expect(buffer1.hashCode, equals(buffer2.hashCode));
+      });
+
+      test('different order is not equal', () {
+        final ConnectBuffer buffer1 = ConnectBuffer.fromList(['/chat', '/admin']);
+        final ConnectBuffer buffer2 = ConnectBuffer.fromList(['/admin', '/chat']);
+
+        expect(buffer1, isNot(equals(buffer2)));
+      });
+    });
+
+    group('toString', () {
+      test('toString shows pending count and namespaces', () {
+        final ConnectBuffer buffer = ConnectBuffer.fromList(['/chat', '/admin']);
+
+        final String str = buffer.toString();
+
+        expect(str, contains('2 pending'));
+        expect(str, contains('/chat'));
+        expect(str, contains('/admin'));
+      });
+
+      test('toString for empty buffer', () {
+        final ConnectBuffer buffer = ConnectBuffer();
+
+        final String str = buffer.toString();
+
+        expect(str, contains('0 pending'));
+      });
+    });
+  });
+}
diff --git a/test/models/engine_handshake_models_test.dart b/test/models/engine_handshake_models_test.dart
new file mode 100644
index 0000000..b164252
--- /dev/null
+++ b/test/models/engine_handshake_models_test.dart
@@ -0,0 +1,326 @@
+/// engine_handshake_models_test.dart
+///
+/// Tests for Engine.IO handshake models
+library engine_handshake_models_test;
+
+import 'package:test/test.dart';
+import 'package:socket_io/src/models/engine_handshake_models.dart';
+
+void main() {
+  group('EngineHandshakeData', () {
+    test('creates instance with required fields', () {
+      const EngineHandshakeData data = EngineHandshakeData(
+        sid: 'test-session-123',
+        upgrades: ['websocket'],
+        pingInterval: 25000,
+        pingTimeout: 20000,
+      );
+
+      expect(data.sid, equals('test-session-123'));
+      expect(data.upgrades, equals(['websocket']));
+      expect(data.pingInterval, equals(25000));
+      expect(data.pingTimeout, equals(20000));
+      expect(data.maxHttpBufferSize, isNull);
+    });
+
+    test('creates instance with optional maxHttpBufferSize', () {
+      const EngineHandshakeData data = EngineHandshakeData(
+        sid: 'test-session-123',
+        upgrades: ['websocket'],
+        pingInterval: 25000,
+        pingTimeout: 20000,
+        maxHttpBufferSize: 1048576,
+      );
+
+      expect(data.maxHttpBufferSize, equals(1048576));
+    });
+
+    test('creates from JSON with all fields', () {
+      final Map json = {
+        'sid': 'test-session-456',
+        'upgrades': ['websocket'],
+        'pingInterval': 30000,
+        'pingTimeout': 15000,
+        'maxHttpBufferSize': 2097152,
+      };
+
+      final EngineHandshakeData data = EngineHandshakeData.fromJson(json);
+
+      expect(data.sid, equals('test-session-456'));
+      expect(data.upgrades, equals(['websocket']));
+      expect(data.pingInterval, equals(30000));
+      expect(data.pingTimeout, equals(15000));
+      expect(data.maxHttpBufferSize, equals(2097152));
+    });
+
+    test('creates from JSON without optional fields', () {
+      final Map json = {
+        'sid': 'test-session-789',
+        'upgrades': [],
+        'pingInterval': 25000,
+        'pingTimeout': 20000,
+      };
+
+      final EngineHandshakeData data = EngineHandshakeData.fromJson(json);
+
+      expect(data.sid, equals('test-session-789'));
+      expect(data.upgrades, isEmpty);
+      expect(data.pingInterval, equals(25000));
+      expect(data.pingTimeout, equals(20000));
+      expect(data.maxHttpBufferSize, isNull);
+    });
+
+    test('converts to JSON with all fields', () {
+      const EngineHandshakeData data = EngineHandshakeData(
+        sid: 'test-session-abc',
+        upgrades: ['websocket', 'polling'],
+        pingInterval: 25000,
+        pingTimeout: 20000,
+        maxHttpBufferSize: 1048576,
+      );
+
+      final Map json = data.toJson();
+
+      expect(json['sid'], equals('test-session-abc'));
+      expect(json['upgrades'], equals(['websocket', 'polling']));
+      expect(json['pingInterval'], equals(25000));
+      expect(json['pingTimeout'], equals(20000));
+      expect(json['maxHttpBufferSize'], equals(1048576));
+    });
+
+    test('converts to JSON without optional fields', () {
+      const EngineHandshakeData data = EngineHandshakeData(
+        sid: 'test-session-def',
+        upgrades: ['websocket'],
+        pingInterval: 25000,
+        pingTimeout: 20000,
+      );
+
+      final Map json = data.toJson();
+
+      expect(json.containsKey('maxHttpBufferSize'), isFalse);
+    });
+
+    test('equality works correctly', () {
+      const EngineHandshakeData data1 = EngineHandshakeData(
+        sid: 'same-sid',
+        upgrades: ['websocket'],
+        pingInterval: 25000,
+        pingTimeout: 20000,
+      );
+
+      const EngineHandshakeData data2 = EngineHandshakeData(
+        sid: 'same-sid',
+        upgrades: ['websocket'],
+        pingInterval: 25000,
+        pingTimeout: 20000,
+      );
+
+      expect(data1, equals(data2));
+      expect(data1.hashCode, equals(data2.hashCode));
+    });
+
+    test('inequality works correctly', () {
+      const EngineHandshakeData data1 = EngineHandshakeData(
+        sid: 'sid-1',
+        upgrades: ['websocket'],
+        pingInterval: 25000,
+        pingTimeout: 20000,
+      );
+
+      const EngineHandshakeData data2 = EngineHandshakeData(
+        sid: 'sid-2',
+        upgrades: ['websocket'],
+        pingInterval: 25000,
+        pingTimeout: 20000,
+      );
+
+      expect(data1, isNot(equals(data2)));
+    });
+
+    test('toString includes all fields', () {
+      const EngineHandshakeData data = EngineHandshakeData(
+        sid: 'test-sid',
+        upgrades: ['websocket'],
+        pingInterval: 25000,
+        pingTimeout: 20000,
+        maxHttpBufferSize: 1048576,
+      );
+
+      final String str = data.toString();
+
+      expect(str, contains('test-sid'));
+      expect(str, contains('websocket'));
+      expect(str, contains('25000'));
+      expect(str, contains('20000'));
+      expect(str, contains('1048576'));
+    });
+
+    test('handles empty upgrades list', () {
+      const EngineHandshakeData data = EngineHandshakeData(
+        sid: 'test-sid',
+        upgrades: [],
+        pingInterval: 25000,
+        pingTimeout: 20000,
+      );
+
+      expect(data.upgrades, isEmpty);
+
+      final Map json = data.toJson();
+      expect(json['upgrades'], isEmpty);
+    });
+  });
+
+  group('EngineHandshakeRequest', () {
+    test('creates instance with required fields', () {
+      const EngineHandshakeRequest request = EngineHandshakeRequest(
+        transport: 'websocket',
+      );
+
+      expect(request.transport, equals('websocket'));
+      expect(request.supportsBinary, isTrue);
+      expect(request.eid, isNull);
+      expect(request.sid, isNull);
+      expect(request.isReconnection, isFalse);
+    });
+
+    test('creates instance with all fields', () {
+      const EngineHandshakeRequest request = EngineHandshakeRequest(
+        transport: 'polling',
+        supportsBinary: false,
+        eid: '4',
+        sid: 'existing-session-123',
+      );
+
+      expect(request.transport, equals('polling'));
+      expect(request.supportsBinary, isFalse);
+      expect(request.eid, equals('4'));
+      expect(request.sid, equals('existing-session-123'));
+      expect(request.isReconnection, isTrue);
+    });
+
+    test('creates from query parameters with all fields', () {
+      final Map query = {
+        'transport': 'websocket',
+        'b64': '0',
+        'EIO': '4',
+        'sid': 'reconnect-session-456',
+      };
+
+      final EngineHandshakeRequest request = EngineHandshakeRequest.fromQuery(query);
+
+      expect(request.transport, equals('websocket'));
+      expect(request.supportsBinary, isTrue);
+      expect(request.eid, equals('4'));
+      expect(request.sid, equals('reconnect-session-456'));
+      expect(request.isReconnection, isTrue);
+    });
+
+    test('creates from query with b64=1 disables binary support', () {
+      final Map query = {
+        'transport': 'polling',
+        'b64': '1',
+      };
+
+      final EngineHandshakeRequest request = EngineHandshakeRequest.fromQuery(query);
+
+      expect(request.supportsBinary, isFalse);
+    });
+
+    test('creates from query with missing b64 enables binary support', () {
+      final Map query = {
+        'transport': 'websocket',
+      };
+
+      final EngineHandshakeRequest request = EngineHandshakeRequest.fromQuery(query);
+
+      expect(request.supportsBinary, isTrue);
+    });
+
+    test('defaults to polling transport when missing', () {
+      final Map query = {};
+
+      final EngineHandshakeRequest request = EngineHandshakeRequest.fromQuery(query);
+
+      expect(request.transport, equals('polling'));
+    });
+
+    test('isReconnection is true when sid is present', () {
+      const EngineHandshakeRequest request = EngineHandshakeRequest(
+        transport: 'websocket',
+        sid: 'some-session-id',
+      );
+
+      expect(request.isReconnection, isTrue);
+    });
+
+    test('isReconnection is false when sid is absent', () {
+      const EngineHandshakeRequest request = EngineHandshakeRequest(
+        transport: 'websocket',
+      );
+
+      expect(request.isReconnection, isFalse);
+    });
+
+    test('equality works correctly', () {
+      const EngineHandshakeRequest request1 = EngineHandshakeRequest(
+        transport: 'websocket',
+        supportsBinary: true,
+        eid: '4',
+        sid: 'session-abc',
+      );
+
+      const EngineHandshakeRequest request2 = EngineHandshakeRequest(
+        transport: 'websocket',
+        supportsBinary: true,
+        eid: '4',
+        sid: 'session-abc',
+      );
+
+      expect(request1, equals(request2));
+      expect(request1.hashCode, equals(request2.hashCode));
+    });
+
+    test('inequality works correctly', () {
+      const EngineHandshakeRequest request1 = EngineHandshakeRequest(
+        transport: 'websocket',
+      );
+
+      const EngineHandshakeRequest request2 = EngineHandshakeRequest(
+        transport: 'polling',
+      );
+
+      expect(request1, isNot(equals(request2)));
+    });
+
+    test('toString includes all fields', () {
+      const EngineHandshakeRequest request = EngineHandshakeRequest(
+        transport: 'websocket',
+        supportsBinary: true,
+        eid: '4',
+        sid: 'test-session',
+      );
+
+      final String str = request.toString();
+
+      expect(str, contains('websocket'));
+      expect(str, contains('true'));
+      expect(str, contains('4'));
+      expect(str, contains('test-session'));
+      expect(str, contains('isReconnection'));
+    });
+
+    test('handles various protocol versions', () {
+      const List versions = ['3', '4', '5'];
+
+      for (final String version in versions) {
+        final EngineHandshakeRequest request = EngineHandshakeRequest(
+          transport: 'websocket',
+          eid: version,
+        );
+
+        expect(request.eid, equals(version));
+      }
+    });
+  });
+}
diff --git a/test/models/event_data_models_test.dart b/test/models/event_data_models_test.dart
new file mode 100644
index 0000000..89d7f40
--- /dev/null
+++ b/test/models/event_data_models_test.dart
@@ -0,0 +1,166 @@
+import 'package:test/test.dart';
+import 'package:socket_io/src/models/event_data_models.dart';
+
+void main() {
+  group('EventData', () {
+    group('StringEventData', () {
+      test('creates and accesses string value', () {
+        const StringEventData data = StringEventData('hello');
+        expect(data.value, equals('hello'));
+        expect(data.toString(), equals('hello'));
+      });
+
+      test('equality works correctly', () {
+        const StringEventData data1 = StringEventData('same');
+        const StringEventData data2 = StringEventData('same');
+        const StringEventData data3 = StringEventData('different');
+
+        expect(data1, equals(data2));
+        expect(data1, isNot(equals(data3)));
+      });
+    });
+
+    group('NumericEventData', () {
+      test('creates and accesses numeric value', () {
+        const NumericEventData data = NumericEventData(42);
+        expect(data.value, equals(42));
+      });
+
+      test('works with decimals', () {
+        const NumericEventData data = NumericEventData(3.14);
+        expect(data.value, equals(3.14));
+      });
+    });
+
+    group('BooleanEventData', () {
+      test('creates and accesses boolean value', () {
+        const BooleanEventData data = BooleanEventData(true);
+        expect(data.value, isTrue);
+      });
+    });
+
+    group('MapEventData', () {
+      test('creates and accesses map value', () {
+        const MapEventData data = MapEventData({'key': 'value'});
+        expect(data.value, equals({'key': 'value'}));
+      });
+
+      test('equality works correctly', () {
+        const MapEventData data1 = MapEventData({'a': 1});
+        const MapEventData data2 = MapEventData({'a': 1});
+        const MapEventData data3 = MapEventData({'a': 2});
+
+        expect(data1, equals(data2));
+        expect(data1, isNot(equals(data3)));
+      });
+    });
+
+    group('ListEventData', () {
+      test('creates and accesses list value', () {
+        const ListEventData data = ListEventData([1, 'two', 3]);
+        expect(data.value, equals([1, 'two', 3]));
+      });
+
+      test('equality works correctly', () {
+        const ListEventData data1 = ListEventData([1, 2]);
+        const ListEventData data2 = ListEventData([1, 2]);
+        const ListEventData data3 = ListEventData([1, 3]);
+
+        expect(data1, equals(data2));
+        expect(data1, isNot(equals(data3)));
+      });
+    });
+
+    group('NullEventData', () {
+      test('represents null', () {
+        const NullEventData data = NullEventData();
+        expect(data.toString(), equals('null'));
+      });
+
+      test('all instances are equal', () {
+        const NullEventData data1 = NullEventData();
+        const NullEventData data2 = NullEventData();
+        expect(data1, equals(data2));
+      });
+    });
+
+    group('ObjectEventData', () {
+      test('wraps generic objects', () {
+        final Object obj = Object();
+        final ObjectEventData data = ObjectEventData(obj);
+        expect(data.value, same(obj));
+      });
+    });
+
+    group('EventDataConversion', () {
+      test('converts string to StringEventData', () {
+        final EventData data = 'hello'.toEventData();
+        expect(data, isA());
+        expect((data as StringEventData).value, equals('hello'));
+      });
+
+      test('converts num to NumericEventData', () {
+        final EventData data = 42.toEventData();
+        expect(data, isA());
+        expect((data as NumericEventData).value, equals(42));
+      });
+
+      test('converts bool to BooleanEventData', () {
+        final EventData data = true.toEventData();
+        expect(data, isA());
+        expect((data as BooleanEventData).value, isTrue);
+      });
+
+      test('converts Map to MapEventData', () {
+        final EventData data = {'key': 'value'}.toEventData();
+        expect(data, isA());
+        expect((data as MapEventData).value, equals({'key': 'value'}));
+      });
+
+      test('converts List to ListEventData', () {
+        final EventData data = [1, 2, 3].toEventData();
+        expect(data, isA());
+        expect((data as ListEventData).value, equals([1, 2, 3]));
+      });
+
+      test('converts null to NullEventData', () {
+        final EventData data = null.toEventData();
+        expect(data, isA());
+      });
+
+      test('converts generic objects to ObjectEventData', () {
+        final Object obj = Object();
+        final EventData data = obj.toEventData();
+        expect(data, isA());
+        expect((data as ObjectEventData).value, same(obj));
+      });
+    });
+
+    group('Pattern matching behavior', () {
+      test('type checking works with if-else', () {
+        const EventData data = StringEventData('test');
+
+        String result;
+        if (data is StringEventData) {
+          result = 'string: ${data.value}';
+        } else if (data is NumericEventData) {
+          result = 'num: ${data.value}';
+        } else if (data is BooleanEventData) {
+          result = 'bool: ${data.value}';
+        } else if (data is MapEventData) {
+          result = 'map: ${data.value.length}';
+        } else if (data is ListEventData) {
+          result = 'list: ${data.value.length}';
+        } else if (data is NullEventData) {
+          result = 'null';
+        } else if (data is ObjectEventData) {
+          result = 'object: ${data.value.runtimeType}';
+        } else {
+          result = 'unknown';
+        }
+
+        expect(result, equals('string: test'));
+      });
+    });
+  });
+}
diff --git a/test/models/handshake_data_models_test.dart b/test/models/handshake_data_models_test.dart
new file mode 100644
index 0000000..5c7e422
--- /dev/null
+++ b/test/models/handshake_data_models_test.dart
@@ -0,0 +1,317 @@
+import 'dart:io';
+import 'package:test/test.dart';
+import 'package:socket_io/src/models/handshake_data_models.dart';
+
+// Mock HttpHeaders for testing
+class MockHttpHeaders implements HttpHeaders {
+  final Map> _headers = >{};
+
+  @override
+  List? operator [](final String name) => _headers[name.toLowerCase()];
+
+  @override
+  void add(final String name, final Object value, {final bool preserveHeaderCase = false}) {
+    _headers.putIfAbsent(name.toLowerCase(), () => []).add(value.toString());
+  }
+
+  @override
+  void set(final String name, final Object value, {final bool preserveHeaderCase = false}) {
+    _headers[name.toLowerCase()] = [value.toString()];
+  }
+
+  @override
+  void remove(final String name, final Object value) {
+    _headers[name.toLowerCase()]?.remove(value.toString());
+  }
+
+  @override
+  void removeAll(final String name) {
+    _headers.remove(name.toLowerCase());
+  }
+
+  @override
+  void forEach(final void Function(String name, List values) action) {
+    _headers.forEach(action);
+  }
+
+  @override
+  void noFolding(final String name) {}
+
+  @override
+  void clear() => _headers.clear();
+
+  @override
+  String? value(final String name) {
+    final List? values = _headers[name.toLowerCase()];
+    return values?.isNotEmpty ?? false ? values!.first : null;
+  }
+
+  @override
+  bool get chunkedTransferEncoding => false;
+
+  @override
+  set chunkedTransferEncoding(final bool value) {}
+
+  @override
+  int get contentLength => -1;
+
+  @override
+  set contentLength(final int value) {}
+
+  @override
+  ContentType? get contentType => null;
+
+  @override
+  set contentType(final ContentType? value) {}
+
+  @override
+  DateTime? get date => null;
+
+  @override
+  set date(final DateTime? value) {}
+
+  @override
+  DateTime? get expires => null;
+
+  @override
+  set expires(final DateTime? value) {}
+
+  @override
+  String? get host => null;
+
+  @override
+  set host(final String? value) {}
+
+  @override
+  DateTime? get ifModifiedSince => null;
+
+  @override
+  set ifModifiedSince(final DateTime? value) {}
+
+  @override
+  bool get persistentConnection => true;
+
+  @override
+  set persistentConnection(final bool value) {}
+
+  @override
+  int? get port => null;
+
+  @override
+  set port(final int? value) {}
+}
+
+void main() {
+  group('HandshakeDataModel', () {
+    late HttpHeaders mockHeaders;
+    late DateTime testTime;
+    late InternetAddress testAddress;
+
+    setUp(() {
+      mockHeaders = MockHttpHeaders();
+      testTime = DateTime(2024, 1, 1, 12, 0, 0);
+      testAddress = InternetAddress('127.0.0.1');
+    });
+
+    test('creates with all required fields', () {
+      final HandshakeDataModel handshake = HandshakeDataModel(
+        headers: mockHeaders,
+        time: testTime,
+        address: testAddress,
+        xdomain: true,
+        secure: false,
+        issued: 1234567890,
+        url: '/socket.io/',
+        query: {'token': 'abc'},
+      );
+
+      expect(handshake.time, equals(testTime));
+      expect(handshake.address, equals(testAddress));
+      expect(handshake.xdomain, isTrue);
+      expect(handshake.secure, isFalse);
+      expect(handshake.issued, equals(1234567890));
+      expect(handshake.url, equals('/socket.io/'));
+      expect(handshake.query, equals({'token': 'abc'}));
+    });
+
+    test('includes optional auth field', () {
+      final HandshakeDataModel handshake = HandshakeDataModel(
+        headers: mockHeaders,
+        time: testTime,
+        address: testAddress,
+        xdomain: false,
+        secure: true,
+        issued: 1234567890,
+        url: '/socket.io/',
+        query: {},
+        auth: {'userId': 123},
+      );
+
+      expect(handshake.auth, equals({'userId': 123}));
+    });
+
+    test('toMap converts to Map', () {
+      final HandshakeDataModel handshake = HandshakeDataModel(
+        headers: mockHeaders,
+        time: testTime,
+        address: testAddress,
+        xdomain: true,
+        secure: false,
+        issued: 1234567890,
+        url: '/socket.io/',
+        query: {'token': 'abc'},
+      );
+
+      final Map map = handshake.toMap();
+      expect(map['time'], equals(testTime.toString()));
+      expect(map['xdomain'], isTrue);
+      expect(map['secure'], isFalse);
+      expect(map['issued'], equals(1234567890));
+      expect(map['url'], equals('/socket.io/'));
+      expect(map['query'], equals({'token': 'abc'}));
+    });
+
+    test('fromMap creates from Map', () {
+      final Map map = {
+        'headers': mockHeaders,
+        'time': testTime.toString(),
+        'address': testAddress,
+        'xdomain': true,
+        'secure': false,
+        'issued': 1234567890,
+        'url': '/socket.io/',
+        'query': {'token': 'abc'},
+      };
+
+      final HandshakeDataModel handshake = HandshakeDataModel.fromMap(map);
+      expect(handshake.time, equals(testTime));
+      expect(handshake.xdomain, isTrue);
+      expect(handshake.url, equals('/socket.io/'));
+    });
+
+    test('copyWith creates modified copy', () {
+      final HandshakeDataModel original = HandshakeDataModel(
+        headers: mockHeaders,
+        time: testTime,
+        address: testAddress,
+        xdomain: false,
+        secure: false,
+        issued: 1234567890,
+        url: '/socket.io/',
+        query: {},
+      );
+
+      final HandshakeDataModel modified = original.copyWith(
+        xdomain: true,
+        url: '/new-url/',
+      );
+
+      expect(modified.xdomain, isTrue);
+      expect(modified.url, equals('/new-url/'));
+      expect(modified.issued, equals(1234567890)); // unchanged
+    });
+
+    test('equality works correctly', () {
+      final HandshakeDataModel h1 = HandshakeDataModel(
+        headers: mockHeaders,
+        time: testTime,
+        address: testAddress,
+        xdomain: true,
+        secure: false,
+        issued: 1234567890,
+        url: '/socket.io/',
+        query: {'a': 'b'},
+      );
+
+      final HandshakeDataModel h2 = HandshakeDataModel(
+        headers: mockHeaders,
+        time: testTime,
+        address: testAddress,
+        xdomain: true,
+        secure: false,
+        issued: 1234567890,
+        url: '/socket.io/',
+        query: {'a': 'b'},
+      );
+
+      expect(h1, equals(h2));
+    });
+
+    test('toString provides useful representation', () {
+      final HandshakeDataModel handshake = HandshakeDataModel(
+        headers: mockHeaders,
+        time: testTime,
+        address: testAddress,
+        xdomain: true,
+        secure: false,
+        issued: 1234567890,
+        url: '/socket.io/',
+        query: {},
+      );
+
+      final String str = handshake.toString();
+      expect(str, contains('HandshakeDataModel'));
+      expect(str, contains('xdomain: true'));
+      expect(str, contains('/socket.io/'));
+    });
+  });
+
+  group('HandshakeDataBuilder', () {
+    late HttpHeaders mockHeaders;
+    late DateTime testTime;
+    late InternetAddress testAddress;
+
+    setUp(() {
+      mockHeaders = MockHttpHeaders();
+      testTime = DateTime(2024, 1, 1, 12, 0, 0);
+      testAddress = InternetAddress('127.0.0.1');
+    });
+
+    test('builds valid HandshakeDataModel', () {
+      final HandshakeDataModel handshake = HandshakeDataBuilder()
+          .headers(mockHeaders)
+          .time(testTime)
+          .address(testAddress)
+          .xdomain(true)
+          .secure(false)
+          .issued(1234567890)
+          .url('/socket.io/')
+          .addQuery('token', 'abc')
+          .build();
+
+      expect(handshake.xdomain, isTrue);
+      expect(handshake.query, equals({'token': 'abc'}));
+    });
+
+    test('queryMap adds multiple query parameters', () {
+      final HandshakeDataModel handshake = HandshakeDataBuilder()
+          .headers(mockHeaders)
+          .time(testTime)
+          .address(testAddress)
+          .issued(1234567890)
+          .url('/socket.io/')
+          .queryMap({'a': '1', 'b': '2'}).build();
+
+      expect(handshake.query, equals({'a': '1', 'b': '2'}));
+    });
+
+    test('throws StateError when required fields missing', () {
+      expect(
+        () => HandshakeDataBuilder().build(),
+        throwsStateError,
+      );
+    });
+
+    test('auth sets authentication data', () {
+      final HandshakeDataModel handshake = HandshakeDataBuilder()
+          .headers(mockHeaders)
+          .time(testTime)
+          .address(testAddress)
+          .issued(1234567890)
+          .url('/socket.io/')
+          .auth({'userId': 123}).build();
+
+      expect(handshake.auth, equals({'userId': 123}));
+    });
+  });
+}
diff --git a/test/models/middleware_models_test.dart b/test/models/middleware_models_test.dart
new file mode 100644
index 0000000..be48c75
--- /dev/null
+++ b/test/models/middleware_models_test.dart
@@ -0,0 +1,330 @@
+import 'package:test/test.dart';
+import 'package:socket_io/src/models/middleware_models.dart';
+import 'package:socket_io/src/models/socket_error_models.dart';
+import 'package:socket_io/src/value_objects/namespace_name_vo.dart';
+
+void main() {
+  group('MiddlewareContext', () {
+    test('creates context with required fields', () {
+      final MiddlewareContext context = MiddlewareContext(
+        socket: 'test-socket',
+        namespace: NamespaceName('/'),
+      );
+      expect(context.socket, equals('test-socket'));
+      expect(context.namespace.value, equals('/'));
+      expect(context.isRejected, isFalse);
+    });
+
+    test('includes auth data', () {
+      final MiddlewareContext context = MiddlewareContext(
+        socket: 'test-socket',
+        namespace: NamespaceName('/'),
+        auth: {'token': 'abc123'},
+      );
+      expect(context.auth, isNotNull);
+      expect(context.auth!['token'], equals('abc123'));
+    });
+
+    test('rejects with error', () {
+      final MiddlewareContext context = MiddlewareContext(
+        socket: 'test-socket',
+        namespace: NamespaceName('/'),
+      );
+      final SocketErrorModel error = SocketErrorModel.unauthorized('Bad auth');
+      context.reject(error);
+
+      expect(context.isRejected, isTrue);
+      expect(context.error, equals(error));
+    });
+
+    test('rejects with message', () {
+      final MiddlewareContext context = MiddlewareContext(
+        socket: 'test-socket',
+        namespace: NamespaceName('/'),
+      );
+      context.rejectWithMessage('Access denied');
+
+      expect(context.isRejected, isTrue);
+      expect(context.error, isNotNull);
+    });
+
+    test('sets and gets custom data', () {
+      final MiddlewareContext context = MiddlewareContext(
+        socket: 'test-socket',
+        namespace: NamespaceName('/'),
+      );
+      context.setData('userId', 123);
+
+      expect(context.getData('userId'), equals(123));
+      expect(context.hasData('userId'), isTrue);
+      expect(context.hasData('missing'), isFalse);
+    });
+  });
+
+  group('MiddlewareResult', () {
+    test('success result', () {
+      const MiddlewareResult result = MiddlewareResult.success();
+      expect(result.isSuccess, isTrue);
+      expect(result.isFailure, isFalse);
+      expect(result.shouldContinue, isTrue);
+    });
+
+    test('failure result with error', () {
+      final SocketErrorModel error = SocketErrorModel.unauthorized('Failed');
+      final MiddlewareResult result = MiddlewareResult.failure(error);
+
+      expect(result.isSuccess, isFalse);
+      expect(result.isFailure, isTrue);
+      expect(result.shouldContinue, isFalse);
+      expect(result.error, equals(error));
+    });
+
+    test('failure result with message', () {
+      final MiddlewareResult result = MiddlewareResult.failureWithMessage('Error');
+      expect(result.isFailure, isTrue);
+      expect(result.error, isNotNull);
+    });
+  });
+
+  group('MiddlewareChain', () {
+    test('creates empty chain', () {
+      final MiddlewareChain chain = MiddlewareChain();
+      expect(chain.isEmpty, isTrue);
+      expect(chain.length, equals(0));
+    });
+
+    test('creates with initial middlewares', () {
+      final MiddlewareChain chain = MiddlewareChain.withMiddlewares([
+        (final MiddlewareContext ctx) async => const MiddlewareResult.success(),
+      ]);
+      expect(chain.length, equals(1));
+    });
+
+    test('adds middleware', () {
+      final MiddlewareChain chain = MiddlewareChain();
+      chain.add((final MiddlewareContext ctx) async => const MiddlewareResult.success());
+      expect(chain.length, equals(1));
+    });
+
+    test('adds sync middleware', () {
+      final MiddlewareChain chain = MiddlewareChain();
+      chain.addSync((final MiddlewareContext ctx) => const MiddlewareResult.success());
+      expect(chain.length, equals(1));
+    });
+
+    test('removes middleware', () {
+      final MiddlewareFunction middleware = (final MiddlewareContext ctx) async => const MiddlewareResult.success();
+      final MiddlewareChain chain = MiddlewareChain();
+      chain.add(middleware);
+
+      final bool removed = chain.remove(middleware);
+      expect(removed, isTrue);
+      expect(chain.isEmpty, isTrue);
+    });
+
+    test('clears all middlewares', () {
+      final MiddlewareChain chain = MiddlewareChain();
+      chain.add((final MiddlewareContext ctx) async => const MiddlewareResult.success());
+      chain.add((final MiddlewareContext ctx) async => const MiddlewareResult.success());
+
+      chain.clear();
+      expect(chain.isEmpty, isTrue);
+    });
+
+    test('executes successful chain', () async {
+      final MiddlewareChain chain = MiddlewareChain();
+      chain.add((final MiddlewareContext ctx) async => const MiddlewareResult.success());
+      chain.add((final MiddlewareContext ctx) async => const MiddlewareResult.success());
+
+      final MiddlewareContext context = MiddlewareContext(
+        socket: 'test',
+        namespace: NamespaceName('/'),
+      );
+
+      final MiddlewareResult result = await chain.execute(context);
+      expect(result.isSuccess, isTrue);
+    });
+
+    test('executes chain and stops on failure', () async {
+      bool secondCalled = false;
+
+      final MiddlewareChain chain = MiddlewareChain();
+      chain.add((final MiddlewareContext ctx) async => MiddlewareResult.failureWithMessage('Failed'));
+      chain.add((final MiddlewareContext ctx) async {
+        secondCalled = true;
+        return const MiddlewareResult.success();
+      });
+
+      final MiddlewareContext context = MiddlewareContext(
+        socket: 'test',
+        namespace: NamespaceName('/'),
+      );
+
+      final MiddlewareResult result = await chain.execute(context);
+      expect(result.isFailure, isTrue);
+      expect(secondCalled, isFalse); // Second middleware should not run
+    });
+
+    test('detects context rejection', () async {
+      final MiddlewareChain chain = MiddlewareChain();
+      chain.add((final MiddlewareContext ctx) async {
+        ctx.rejectWithMessage('Rejected');
+        return const MiddlewareResult.success();
+      });
+
+      final MiddlewareContext context = MiddlewareContext(
+        socket: 'test',
+        namespace: NamespaceName('/'),
+      );
+
+      final MiddlewareResult result = await chain.execute(context);
+      expect(result.isFailure, isTrue);
+    });
+
+    test('toString provides useful representation', () {
+      final MiddlewareChain chain = MiddlewareChain();
+      chain.add((final MiddlewareContext ctx) async => const MiddlewareResult.success());
+
+      expect(chain.toString(), contains('MiddlewareChain'));
+      expect(chain.toString(), contains('1'));
+    });
+  });
+
+  group('Middlewares common functions', () {
+    test('logging middleware', () async {
+      final List logs = [];
+      final MiddlewareFunction middleware = Middlewares.logging(
+        log: (final String msg) => logs.add(msg),
+      );
+
+      final MiddlewareContext context = MiddlewareContext(
+        socket: 'test',
+        namespace: NamespaceName('/test'),
+      );
+
+      final MiddlewareResult result = await middleware(context);
+      expect(result.isSuccess, isTrue);
+      expect(logs, isNotEmpty);
+      expect(logs.first, contains('/test'));
+    });
+
+    test('requireAuth middleware succeeds with valid auth', () async {
+      final MiddlewareFunction middleware = Middlewares.requireAuth(
+        validate: (final Map auth) async => true,
+      );
+
+      final MiddlewareContext context = MiddlewareContext(
+        socket: 'test',
+        namespace: NamespaceName('/'),
+        auth: {'user': 'test'},
+      );
+
+      final MiddlewareResult result = await middleware(context);
+      expect(result.isSuccess, isTrue);
+    });
+
+    test('requireAuth middleware fails without auth', () async {
+      final MiddlewareFunction middleware = Middlewares.requireAuth(
+        validate: (final Map auth) async => true,
+      );
+
+      final MiddlewareContext context = MiddlewareContext(
+        socket: 'test',
+        namespace: NamespaceName('/'),
+      );
+
+      final MiddlewareResult result = await middleware(context);
+      expect(result.isFailure, isTrue);
+    });
+
+    test('requireAuth middleware fails with invalid auth', () async {
+      final MiddlewareFunction middleware = Middlewares.requireAuth(
+        validate: (final Map auth) async => false,
+      );
+
+      final MiddlewareContext context = MiddlewareContext(
+        socket: 'test',
+        namespace: NamespaceName('/'),
+        auth: {'user': 'test'},
+      );
+
+      final MiddlewareResult result = await middleware(context);
+      expect(result.isFailure, isTrue);
+    });
+
+    test('requireToken middleware succeeds with valid token', () async {
+      final MiddlewareFunction middleware = Middlewares.requireToken(
+        tokenKey: 'token',
+        validate: (final String token) => token == 'valid',
+      );
+
+      final MiddlewareContext context = MiddlewareContext(
+        socket: 'test',
+        namespace: NamespaceName('/'),
+        auth: {'token': 'valid'},
+      );
+
+      final MiddlewareResult result = await middleware(context);
+      expect(result.isSuccess, isTrue);
+    });
+
+    test('requireToken middleware fails with invalid token', () async {
+      final MiddlewareFunction middleware = Middlewares.requireToken(
+        tokenKey: 'token',
+        validate: (final String token) => token == 'valid',
+      );
+
+      final MiddlewareContext context = MiddlewareContext(
+        socket: 'test',
+        namespace: NamespaceName('/'),
+        auth: {'token': 'invalid'},
+      );
+
+      final MiddlewareResult result = await middleware(context);
+      expect(result.isFailure, isTrue);
+    });
+
+    test('requireToken middleware fails without token', () async {
+      final MiddlewareFunction middleware = Middlewares.requireToken(
+        tokenKey: 'token',
+        validate: (final String token) => true,
+      );
+
+      final MiddlewareContext context = MiddlewareContext(
+        socket: 'test',
+        namespace: NamespaceName('/'),
+      );
+
+      final MiddlewareResult result = await middleware(context);
+      expect(result.isFailure, isTrue);
+    });
+
+    test('rateLimit middleware succeeds when under limit', () async {
+      final MiddlewareFunction middleware = Middlewares.rateLimit(
+        checkLimit: (final dynamic socket) => true,
+      );
+
+      final MiddlewareContext context = MiddlewareContext(
+        socket: 'test',
+        namespace: NamespaceName('/'),
+      );
+
+      final MiddlewareResult result = await middleware(context);
+      expect(result.isSuccess, isTrue);
+    });
+
+    test('rateLimit middleware fails when over limit', () async {
+      final MiddlewareFunction middleware = Middlewares.rateLimit(
+        checkLimit: (final dynamic socket) => false,
+      );
+
+      final MiddlewareContext context = MiddlewareContext(
+        socket: 'test',
+        namespace: NamespaceName('/'),
+      );
+
+      final MiddlewareResult result = await middleware(context);
+      expect(result.isFailure, isTrue);
+    });
+  });
+}
diff --git a/test/models/room_management_models_test.dart b/test/models/room_management_models_test.dart
new file mode 100644
index 0000000..2b73971
--- /dev/null
+++ b/test/models/room_management_models_test.dart
@@ -0,0 +1,320 @@
+import 'package:test/test.dart';
+import 'package:socket_io/src/models/room_management_models.dart';
+import 'package:socket_io/src/value_objects/room_name_vo.dart';
+
+void main() {
+  group('RoomMembership', () {
+    test('creates empty membership', () {
+      final RoomMembership membership = RoomMembership();
+      expect(membership.isEmpty, isTrue);
+      expect(membership.length, equals(0));
+    });
+
+    test('creates from room names', () {
+      final RoomMembership membership = RoomMembership.fromNames(['room1', 'room2']);
+      expect(membership.length, equals(2));
+      expect(membership.containsName('room1'), isTrue);
+      expect(membership.containsName('room2'), isTrue);
+    });
+
+    test('creates from RoomName objects', () {
+      final RoomMembership membership = RoomMembership.fromRooms([
+        RoomName('room1'),
+        RoomName('room2'),
+      ]);
+      expect(membership.length, equals(2));
+    });
+
+    test('adds room', () {
+      final RoomMembership membership = RoomMembership();
+      membership.add(RoomName('room1'));
+      expect(membership.containsName('room1'), isTrue);
+      expect(membership.length, equals(1));
+    });
+
+    test('adds room by name', () {
+      final RoomMembership membership = RoomMembership();
+      membership.addByName('room1');
+      expect(membership.containsName('room1'), isTrue);
+    });
+
+    test('removes room', () {
+      final RoomMembership membership = RoomMembership();
+      final RoomName room = RoomName('room1');
+      membership.add(room);
+
+      final bool removed = membership.remove(room);
+      expect(removed, isTrue);
+      expect(membership.isEmpty, isTrue);
+    });
+
+    test('removes room by name', () {
+      final RoomMembership membership = RoomMembership();
+      membership.addByName('room1');
+
+      final bool removed = membership.removeByName('room1');
+      expect(removed, isTrue);
+    });
+
+    test('contains checks membership', () {
+      final RoomMembership membership = RoomMembership();
+      final RoomName room = RoomName('room1');
+
+      expect(membership.contains(room), isFalse);
+      membership.add(room);
+      expect(membership.contains(room), isTrue);
+    });
+
+    test('gets room by name', () {
+      final RoomMembership membership = RoomMembership();
+      membership.addByName('room1');
+
+      final RoomName? room = membership.get('room1');
+      expect(room, isNotNull);
+      expect(room!.value, equals('room1'));
+    });
+
+    test('returns all rooms', () {
+      final RoomMembership membership = RoomMembership.fromNames(['room1', 'room2']);
+      expect(membership.rooms.length, equals(2));
+    });
+
+    test('returns all room names', () {
+      final RoomMembership membership = RoomMembership.fromNames(['room1', 'room2']);
+      expect(membership.roomNames, containsAll(['room1', 'room2']));
+    });
+
+    test('converts to list', () {
+      final RoomMembership membership = RoomMembership.fromNames(['room1', 'room2']);
+      final List list = membership.toList();
+      expect(list, containsAll(['room1', 'room2']));
+    });
+
+    test('converts to set', () {
+      final RoomMembership membership = RoomMembership.fromNames(['room1', 'room2']);
+      final Set set = membership.toSet();
+      expect(set, containsAll(['room1', 'room2']));
+    });
+
+    test('clears all rooms', () {
+      final RoomMembership membership = RoomMembership.fromNames(['room1', 'room2']);
+      membership.clear();
+      expect(membership.isEmpty, isTrue);
+    });
+
+    test('copy creates independent copy', () {
+      final RoomMembership original = RoomMembership.fromNames(['room1']);
+      final RoomMembership copy = original.copy();
+
+      copy.addByName('room2');
+      expect(original.length, equals(1));
+      expect(copy.length, equals(2));
+    });
+
+    test('toString provides useful representation', () {
+      final RoomMembership membership = RoomMembership.fromNames(['room1']);
+      expect(membership.toString(), contains('RoomMembership'));
+      expect(membership.toString(), contains('room1'));
+    });
+  });
+
+  group('RoomSocketCollection', () {
+    test('creates empty collection', () {
+      final RoomSocketCollection collection = RoomSocketCollection(RoomName('room1'));
+      expect(collection.isEmpty, isTrue);
+      expect(collection.roomName.value, equals('room1'));
+    });
+
+    test('creates from socket IDs', () {
+      final RoomSocketCollection collection = RoomSocketCollection.fromIds(
+        RoomName('room1'),
+        ['socket1', 'socket2'],
+      );
+      expect(collection.length, equals(2));
+    });
+
+    test('adds socket', () {
+      final RoomSocketCollection collection = RoomSocketCollection(RoomName('room1'));
+      final bool added = collection.add('socket1');
+      expect(added, isTrue);
+      expect(collection.contains('socket1'), isTrue);
+    });
+
+    test('removes socket', () {
+      final RoomSocketCollection collection = RoomSocketCollection(RoomName('room1'));
+      collection.add('socket1');
+
+      final bool removed = collection.remove('socket1');
+      expect(removed, isTrue);
+      expect(collection.isEmpty, isTrue);
+    });
+
+    test('returns socket IDs', () {
+      final RoomSocketCollection collection = RoomSocketCollection.fromIds(
+        RoomName('room1'),
+        ['socket1', 'socket2'],
+      );
+      expect(collection.socketIds, containsAll(['socket1', 'socket2']));
+    });
+
+    test('clears all sockets', () {
+      final RoomSocketCollection collection = RoomSocketCollection.fromIds(
+        RoomName('room1'),
+        ['socket1', 'socket2'],
+      );
+      collection.clear();
+      expect(collection.isEmpty, isTrue);
+    });
+
+    test('copy creates independent copy', () {
+      final RoomSocketCollection original = RoomSocketCollection(RoomName('room1'));
+      original.add('socket1');
+
+      final RoomSocketCollection copy = original.copy();
+      copy.add('socket2');
+
+      expect(original.length, equals(1));
+      expect(copy.length, equals(2));
+    });
+
+    test('toString provides useful representation', () {
+      final RoomSocketCollection collection = RoomSocketCollection(RoomName('room1'));
+      collection.add('socket1');
+      expect(collection.toString(), contains('RoomSocketCollection'));
+      expect(collection.toString(), contains('room1'));
+    });
+  });
+
+  group('RoomManager', () {
+    test('creates empty manager', () {
+      final RoomManager manager = RoomManager();
+      expect(manager.roomCount, equals(0));
+      expect(manager.socketCount, equals(0));
+    });
+
+    test('joins socket to room', () {
+      final RoomManager manager = RoomManager();
+      manager.join('socket1', RoomName('room1'));
+
+      expect(manager.isInRoom('socket1', RoomName('room1')), isTrue);
+      expect(manager.roomCount, equals(1));
+      expect(manager.socketCount, equals(1));
+    });
+
+    test('joins socket to room by name', () {
+      final RoomManager manager = RoomManager();
+      manager.joinByName('socket1', 'room1');
+
+      expect(manager.isInRoomByName('socket1', 'room1'), isTrue);
+    });
+
+    test('joins multiple sockets to same room', () {
+      final RoomManager manager = RoomManager();
+      manager.joinByName('socket1', 'room1');
+      manager.joinByName('socket2', 'room1');
+
+      final Set sockets = manager.getSocketsInRoomByName('room1');
+      expect(sockets, containsAll(['socket1', 'socket2']));
+    });
+
+    test('joins socket to multiple rooms', () {
+      final RoomManager manager = RoomManager();
+      manager.joinByName('socket1', 'room1');
+      manager.joinByName('socket1', 'room2');
+
+      final RoomMembership? membership = manager.getRoomsForSocket('socket1');
+      expect(membership, isNotNull);
+      expect(membership!.length, equals(2));
+    });
+
+    test('leaves room', () {
+      final RoomManager manager = RoomManager();
+      manager.join('socket1', RoomName('room1'));
+      manager.leave('socket1', RoomName('room1'));
+
+      expect(manager.isInRoom('socket1', RoomName('room1')), isFalse);
+    });
+
+    test('leaves room by name', () {
+      final RoomManager manager = RoomManager();
+      manager.joinByName('socket1', 'room1');
+      manager.leaveByName('socket1', 'room1');
+
+      expect(manager.isInRoomByName('socket1', 'room1'), isFalse);
+    });
+
+    test('leaves all rooms', () {
+      final RoomManager manager = RoomManager();
+      manager.joinByName('socket1', 'room1');
+      manager.joinByName('socket1', 'room2');
+      manager.leaveAll('socket1');
+
+      final RoomMembership? membership = manager.getRoomsForSocket('socket1');
+      expect(membership, isNull);
+      expect(manager.socketCount, equals(0));
+    });
+
+    test('gets sockets in room', () {
+      final RoomManager manager = RoomManager();
+      manager.joinByName('socket1', 'room1');
+      manager.joinByName('socket2', 'room1');
+
+      final Set sockets = manager.getSocketsInRoom(RoomName('room1'));
+      expect(sockets.length, equals(2));
+    });
+
+    test('gets rooms for socket', () {
+      final RoomManager manager = RoomManager();
+      manager.joinByName('socket1', 'room1');
+      manager.joinByName('socket1', 'room2');
+
+      final RoomMembership? membership = manager.getRoomsForSocket('socket1');
+      expect(membership, isNotNull);
+      expect(membership!.containsName('room1'), isTrue);
+      expect(membership.containsName('room2'), isTrue);
+    });
+
+    test('returns all room names', () {
+      final RoomManager manager = RoomManager();
+      manager.joinByName('socket1', 'room1');
+      manager.joinByName('socket2', 'room2');
+
+      expect(manager.allRoomNames, containsAll(['room1', 'room2']));
+    });
+
+    test('returns all rooms', () {
+      final RoomManager manager = RoomManager();
+      manager.joinByName('socket1', 'room1');
+
+      final Iterable rooms = manager.allRooms;
+      expect(rooms.length, equals(1));
+      expect(rooms.first.value, equals('room1'));
+    });
+
+    test('clears all data', () {
+      final RoomManager manager = RoomManager();
+      manager.joinByName('socket1', 'room1');
+      manager.joinByName('socket2', 'room2');
+
+      manager.clear();
+      expect(manager.roomCount, equals(0));
+      expect(manager.socketCount, equals(0));
+    });
+
+    test('handles leaving non-existent room gracefully', () {
+      final RoomManager manager = RoomManager();
+      manager.leaveByName('socket1', 'room1'); // Should not throw
+      expect(manager.roomCount, equals(0));
+    });
+
+    test('toString provides useful representation', () {
+      final RoomManager manager = RoomManager();
+      manager.joinByName('socket1', 'room1');
+
+      final String str = manager.toString();
+      expect(str, contains('RoomManager'));
+      expect(str, contains('1 rooms'));
+      expect(str, contains('1 sockets'));
+    });
+  });
+}
diff --git a/test/models/server_options_models_test.dart b/test/models/server_options_models_test.dart
new file mode 100644
index 0000000..2e8a57c
--- /dev/null
+++ b/test/models/server_options_models_test.dart
@@ -0,0 +1,333 @@
+// test/models/server_options_models_test.dart
+//
+// Tests for ServerOptionsModel and related classes
+//
+// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+
+import 'package:test/test.dart';
+import '../../lib/src/models/server_options_models.dart';
+import '../../lib/src/value_objects/timeout_duration_vo.dart';
+import '../../lib/src/value_objects/transport_name_vo.dart';
+import '../../lib/src/value_objects/url_path_vo.dart';
+
+void main() {
+  group('CookieConfig', () {
+    test('should create enabled cookie with name', () {
+      final CookieConfig config = CookieConfig.enabled('session');
+
+      expect(config.isEnabled, isTrue);
+      expect(config.name, equals('session'));
+      expect(config.toCompatibility(), equals('session'));
+    });
+
+    test('should create disabled cookie', () {
+      final CookieConfig config = CookieConfig.disabled();
+
+      expect(config.isEnabled, isFalse);
+      expect(config.name, isNull);
+      expect(config.toCompatibility(), equals(false));
+    });
+
+    test('should create from dynamic string value', () {
+      final CookieConfig config = CookieConfig.fromDynamic('io');
+
+      expect(config.isEnabled, isTrue);
+      expect(config.name, equals('io'));
+    });
+
+    test('should create disabled from false', () {
+      final CookieConfig config = CookieConfig.fromDynamic(false);
+
+      expect(config.isEnabled, isFalse);
+      expect(config.name, isNull);
+    });
+
+    test('should create disabled from null', () {
+      final CookieConfig config = CookieConfig.fromDynamic(null);
+
+      expect(config.isEnabled, isFalse);
+    });
+  });
+
+  group('CookiePathConfig', () {
+    test('should create enabled cookie path', () {
+      final CookiePathConfig config = CookiePathConfig.enabled('/api');
+
+      expect(config.isEnabled, isTrue);
+      expect(config.path, equals('/api'));
+      expect(config.toCompatibility(), equals('/api'));
+    });
+
+    test('should create disabled cookie path', () {
+      final CookiePathConfig config = CookiePathConfig.disabled();
+
+      expect(config.isEnabled, isFalse);
+      expect(config.path, isNull);
+      expect(config.toCompatibility(), equals(false));
+    });
+
+    test('should create from dynamic string value', () {
+      final CookiePathConfig config = CookiePathConfig.fromDynamic('/');
+
+      expect(config.isEnabled, isTrue);
+      expect(config.path, equals('/'));
+    });
+
+    test('should create disabled from false', () {
+      final CookiePathConfig config = CookiePathConfig.fromDynamic(false);
+
+      expect(config.isEnabled, isFalse);
+    });
+  });
+
+  group('PerMessageDeflateConfig', () {
+    test('should create with default threshold', () {
+      const PerMessageDeflateConfig config = PerMessageDeflateConfig();
+
+      expect(config.threshold, equals(1024));
+      expect(config.additionalOptions, isEmpty);
+    });
+
+    test('should create with custom threshold', () {
+      const PerMessageDeflateConfig config = PerMessageDeflateConfig(
+        threshold: 2048,
+      );
+
+      expect(config.threshold, equals(2048));
+    });
+
+    test('should create from map', () {
+      final PerMessageDeflateConfig config = PerMessageDeflateConfig.fromMap({
+        'threshold': 512,
+        'memLevel': 8,
+      });
+
+      expect(config.threshold, equals(512));
+      expect(config.additionalOptions['memLevel'], equals(8));
+    });
+
+    test('should handle null map', () {
+      final PerMessageDeflateConfig config = PerMessageDeflateConfig.fromMap(null);
+
+      expect(config.threshold, equals(1024));
+      expect(config.additionalOptions, isEmpty);
+    });
+
+    test('should convert to map', () {
+      const PerMessageDeflateConfig config = PerMessageDeflateConfig(
+        threshold: 2048,
+        additionalOptions: {'level': 9},
+      );
+
+      final Map map = config.toMap();
+
+      expect(map['threshold'], equals(2048));
+      expect(map['level'], equals(9));
+    });
+  });
+
+  group('HttpCompressionConfig', () {
+    test('should create with default threshold', () {
+      const HttpCompressionConfig config = HttpCompressionConfig();
+
+      expect(config.threshold, equals(1024));
+      expect(config.additionalOptions, isEmpty);
+    });
+
+    test('should create with custom threshold', () {
+      const HttpCompressionConfig config = HttpCompressionConfig(
+        threshold: 4096,
+      );
+
+      expect(config.threshold, equals(4096));
+    });
+
+    test('should create from map', () {
+      final HttpCompressionConfig config = HttpCompressionConfig.fromMap({
+        'threshold': 256,
+        'level': 6,
+      });
+
+      expect(config.threshold, equals(256));
+      expect(config.additionalOptions['level'], equals(6));
+    });
+
+    test('should convert to map', () {
+      const HttpCompressionConfig config = HttpCompressionConfig(
+        threshold: 512,
+        additionalOptions: {'chunkSize': 8192},
+      );
+
+      final Map map = config.toMap();
+
+      expect(map['threshold'], equals(512));
+      expect(map['chunkSize'], equals(8192));
+    });
+  });
+
+  group('ServerOptionsModel', () {
+    test('should create with default values', () {
+      final ServerOptionsModel options = ServerOptionsModel();
+
+      expect(options.pingTimeout.inMilliseconds, equals(60000));
+      expect(options.pingInterval.inMilliseconds, equals(25000));
+      expect(options.upgradeTimeout.inMilliseconds, equals(10000));
+      expect(options.maxHttpBufferSize, equals(10E7));
+      expect(options.transports.length, equals(2));
+      expect(options.allowUpgrades, isTrue);
+      expect(options.cookieHttpOnly, isTrue);
+      expect(options.path.value, equals('/engine.io'));
+    });
+
+    test('should create with custom values', () {
+      final ServerOptionsModel options = ServerOptionsModel(
+        pingTimeout: TimeoutDuration.milliseconds(30000),
+        pingInterval: TimeoutDuration.milliseconds(10000),
+        upgradeTimeout: TimeoutDuration.milliseconds(5000),
+        maxHttpBufferSize: 5E7,
+        transports: const [TransportName.websocket],
+        allowUpgrades: false,
+        cookieHttpOnly: false,
+        path: UrlPath('/socket'),
+      );
+
+      expect(options.pingTimeout.inMilliseconds, equals(30000));
+      expect(options.pingInterval.inMilliseconds, equals(10000));
+      expect(options.upgradeTimeout.inMilliseconds, equals(5000));
+      expect(options.maxHttpBufferSize, equals(5E7));
+      expect(options.transports.length, equals(1));
+      expect(options.transports.first, equals(TransportName.websocket));
+      expect(options.allowUpgrades, isFalse);
+      expect(options.cookieHttpOnly, isFalse);
+      expect(options.path.value, equals('/socket'));
+    });
+
+    test('should create from map', () {
+      final ServerOptionsModel options = ServerOptionsModel.fromMap({
+        'pingTimeout': 45000,
+        'pingInterval': 20000,
+        'upgradeTimeout': 8000,
+        'transports': ['websocket'],
+        'path': '/api/socket',
+      });
+
+      expect(options.pingTimeout.inMilliseconds, equals(45000));
+      expect(options.pingInterval.inMilliseconds, equals(20000));
+      expect(options.upgradeTimeout.inMilliseconds, equals(8000));
+      expect(options.transports.length, equals(1));
+      expect(options.path.value, equals('/api/socket'));
+    });
+
+    test('should handle null map in fromMap', () {
+      final ServerOptionsModel options = ServerOptionsModel.fromMap(null);
+
+      expect(options.pingTimeout.inMilliseconds, equals(60000));
+      expect(options.path.value, equals('/engine.io'));
+    });
+
+    test('should convert to map', () {
+      final ServerOptionsModel options = ServerOptionsModel(
+        pingTimeout: TimeoutDuration.milliseconds(30000),
+        transports: const [TransportName.polling],
+        path: UrlPath('/custom'),
+      );
+
+      final Map map = options.toMap();
+
+      expect(map['pingTimeout'], equals(30000));
+      expect(map['transports'], equals(['polling']));
+      expect(map['path'], equals('/custom'));
+    });
+
+    test('should handle cookie configuration', () {
+      final ServerOptionsModel options = ServerOptionsModel(
+        cookie: const EnabledCookie('custom_session'),
+      );
+
+      final Map map = options.toMap();
+      expect(map['cookie'], equals('custom_session'));
+    });
+
+    test('should handle disabled cookie', () {
+      final ServerOptionsModel options = ServerOptionsModel(
+        cookie: const DisabledCookie(),
+      );
+
+      final Map map = options.toMap();
+      expect(map['cookie'], equals(false));
+    });
+
+    test('should handle perMessageDeflate configuration', () {
+      final ServerOptionsModel options = ServerOptionsModel(
+        perMessageDeflate: const PerMessageDeflateConfig(threshold: 2048),
+      );
+
+      final Map map = options.toMap();
+      expect(map['perMessageDeflate']['threshold'], equals(2048));
+    });
+
+    test('should handle httpCompression configuration', () {
+      final ServerOptionsModel options = ServerOptionsModel(
+        httpCompression: const HttpCompressionConfig(threshold: 4096),
+      );
+
+      final Map map = options.toMap();
+      expect(map['httpCompression']['threshold'], equals(4096));
+    });
+  });
+
+  group('AttachmentOptionsModel', () {
+    test('should create with default path', () {
+      final AttachmentOptionsModel options = AttachmentOptionsModel();
+
+      expect(options.path.value, equals('/engine.io'));
+      expect(options.custom, isEmpty);
+    });
+
+    test('should create with custom path', () {
+      final AttachmentOptionsModel options = AttachmentOptionsModel(
+        path: UrlPath('/api'),
+      );
+
+      expect(options.path.value, equals('/api'));
+    });
+
+    test('should create with custom options', () {
+      final AttachmentOptionsModel options = AttachmentOptionsModel(
+        path: UrlPath('/socket'),
+        custom: const {'key': 'value'},
+      );
+
+      expect(options.path.value, equals('/socket'));
+      expect(options.custom['key'], equals('value'));
+    });
+
+    test('should create from map', () {
+      final AttachmentOptionsModel options = AttachmentOptionsModel.fromMap({
+        'path': '/custom',
+        'extra': 'data',
+      });
+
+      expect(options.path.value, equals('/custom'));
+      expect(options.custom['extra'], equals('data'));
+    });
+
+    test('should handle null map', () {
+      final AttachmentOptionsModel options = AttachmentOptionsModel.fromMap(null);
+
+      expect(options.path.value, equals('/engine.io'));
+    });
+
+    test('should convert to map', () {
+      final AttachmentOptionsModel options = AttachmentOptionsModel(
+        path: UrlPath('/socket'),
+        custom: const {'key': 'value'},
+      );
+
+      final Map map = options.toMap();
+
+      expect(map['path'], equals('/socket'));
+      expect(map['key'], equals('value'));
+    });
+  });
+}
diff --git a/test/models/socket_data_models_test.dart b/test/models/socket_data_models_test.dart
new file mode 100644
index 0000000..154732c
--- /dev/null
+++ b/test/models/socket_data_models_test.dart
@@ -0,0 +1,171 @@
+import 'package:test/test.dart';
+import 'package:socket_io/src/models/socket_data_models.dart';
+
+void main() {
+  group('SocketDataModel', () {
+    test('creates empty model', () {
+      final SocketDataModel data = SocketDataModel();
+      expect(data.isEmpty, isTrue);
+      expect(data.length, equals(0));
+    });
+
+    test('creates from map', () {
+      final SocketDataModel data = SocketDataModel.fromMap({
+        'name': 'test',
+        'count': 42,
+      });
+      expect(data.length, equals(2));
+      expect(data['name'], equals('test'));
+      expect(data['count'], equals(42));
+    });
+
+    test('stores and retrieves values', () {
+      final SocketDataModel data = SocketDataModel();
+      data['key'] = 'value';
+      expect(data['key'], equals('value'));
+    });
+
+    test('getString returns string or null', () {
+      final SocketDataModel data = SocketDataModel();
+      data['text'] = 'hello';
+      data['number'] = 42;
+
+      expect(data.getString('text'), equals('hello'));
+      expect(data.getString('number'), isNull);
+      expect(data.getString('missing'), isNull);
+    });
+
+    test('getInt returns int or null', () {
+      final SocketDataModel data = SocketDataModel();
+      data['count'] = 42;
+      data['text'] = 'hello';
+
+      expect(data.getInt('count'), equals(42));
+      expect(data.getInt('text'), isNull);
+      expect(data.getInt('missing'), isNull);
+    });
+
+    test('getBool returns bool or null', () {
+      final SocketDataModel data = SocketDataModel();
+      data['flag'] = true;
+      data['text'] = 'hello';
+
+      expect(data.getBool('flag'), isTrue);
+      expect(data.getBool('text'), isNull);
+      expect(data.getBool('missing'), isNull);
+    });
+
+    test('getDouble returns double or null', () {
+      final SocketDataModel data = SocketDataModel();
+      data['value'] = 3.14;
+      data['text'] = 'hello';
+
+      expect(data.getDouble('value'), equals(3.14));
+      expect(data.getDouble('text'), isNull);
+      expect(data.getDouble('missing'), isNull);
+    });
+
+    test('getMap returns map or null', () {
+      final SocketDataModel data = SocketDataModel();
+      data['obj'] = {'nested': 'value'};
+      data['text'] = 'hello';
+
+      expect(data.getMap('obj'), equals({'nested': 'value'}));
+      expect(data.getMap('text'), isNull);
+      expect(data.getMap('missing'), isNull);
+    });
+
+    test('getList returns list or null', () {
+      final SocketDataModel data = SocketDataModel();
+      data['arr'] = [1, 2, 3];
+      data['text'] = 'hello';
+
+      expect(data.getList('arr'), equals([1, 2, 3]));
+      expect(data.getList('text'), isNull);
+      expect(data.getList('missing'), isNull);
+    });
+
+    test('containsKey checks existence', () {
+      final SocketDataModel data = SocketDataModel();
+      data['exists'] = 'value';
+
+      expect(data.containsKey('exists'), isTrue);
+      expect(data.containsKey('missing'), isFalse);
+    });
+
+    test('remove removes key', () {
+      final SocketDataModel data = SocketDataModel();
+      data['key'] = 'value';
+
+      final Object? removed = data.remove('key');
+      expect(removed, equals('value'));
+      expect(data.containsKey('key'), isFalse);
+    });
+
+    test('clear removes all data', () {
+      final SocketDataModel data = SocketDataModel();
+      data['a'] = 1;
+      data['b'] = 2;
+      expect(data.length, equals(2));
+
+      data.clear();
+      expect(data.isEmpty, isTrue);
+      expect(data.length, equals(0));
+    });
+
+    test('keys returns all keys', () {
+      final SocketDataModel data = SocketDataModel();
+      data['a'] = 1;
+      data['b'] = 2;
+
+      expect(data.keys, containsAll(['a', 'b']));
+    });
+
+    test('values returns all values', () {
+      final SocketDataModel data = SocketDataModel();
+      data['a'] = 1;
+      data['b'] = 2;
+
+      expect(data.values, containsAll([1, 2]));
+    });
+
+    test('isNotEmpty works correctly', () {
+      final SocketDataModel data = SocketDataModel();
+      expect(data.isNotEmpty, isFalse);
+
+      data['key'] = 'value';
+      expect(data.isNotEmpty, isTrue);
+    });
+
+    test('toMap creates plain map', () {
+      final SocketDataModel data = SocketDataModel();
+      data['a'] = 1;
+      data['b'] = 'two';
+
+      final Map map = data.toMap();
+      expect(map, equals({'a': 1, 'b': 'two'}));
+      expect(map, isNot(same(data.toMap()))); // Different instances
+    });
+
+    test('copy creates independent copy', () {
+      final SocketDataModel original = SocketDataModel();
+      original['key'] = 'value';
+
+      final SocketDataModel copy = original.copy();
+      expect(copy['key'], equals('value'));
+
+      copy['key'] = 'modified';
+      expect(original['key'], equals('value')); // Original unchanged
+    });
+
+    test('toString provides useful representation', () {
+      final SocketDataModel data = SocketDataModel();
+      data['test'] = 'value';
+
+      final String str = data.toString();
+      expect(str, contains('SocketDataModel'));
+      expect(str, contains('test'));
+      expect(str, contains('value'));
+    });
+  });
+}
diff --git a/test/models/socket_error_models_test.dart b/test/models/socket_error_models_test.dart
new file mode 100644
index 0000000..ce04e76
--- /dev/null
+++ b/test/models/socket_error_models_test.dart
@@ -0,0 +1,250 @@
+import 'package:test/test.dart';
+import 'package:socket_io/src/models/socket_error_models.dart';
+
+void main() {
+  group('SocketErrorType', () {
+    test('enum has all expected values', () {
+      expect(SocketErrorType.values, contains(SocketErrorType.connection));
+      expect(SocketErrorType.values, contains(SocketErrorType.transport));
+      expect(SocketErrorType.values, contains(SocketErrorType.timeout));
+      expect(SocketErrorType.values, contains(SocketErrorType.authentication));
+      expect(SocketErrorType.values, contains(SocketErrorType.invalidNamespace));
+      expect(SocketErrorType.values, contains(SocketErrorType.parse));
+      expect(SocketErrorType.values, contains(SocketErrorType.encoding));
+      expect(SocketErrorType.values, contains(SocketErrorType.decoding));
+      expect(SocketErrorType.values, contains(SocketErrorType.unknown));
+    });
+  });
+
+  group('SocketErrorModel', () {
+    test('creates with basic fields', () {
+      const SocketErrorModel error = SocketErrorModel(
+        type: SocketErrorType.connection,
+        message: 'Connection failed',
+      );
+
+      expect(error.type, equals(SocketErrorType.connection));
+      expect(error.message, equals('Connection failed'));
+      expect(error.originalError, isNull);
+      expect(error.stackTrace, isNull);
+    });
+
+    test('connection factory creates connection error', () {
+      final SocketErrorModel error = SocketErrorModel.connection('Failed to connect');
+
+      expect(error.type, equals(SocketErrorType.connection));
+      expect(error.message, equals('Failed to connect'));
+    });
+
+    test('transport factory creates transport error', () {
+      final SocketErrorModel error = SocketErrorModel.transport(
+        'Transport error',
+        data: {'code': 500},
+      );
+
+      expect(error.type, equals(SocketErrorType.transport));
+      expect(error.message, equals('Transport error'));
+      expect(error.data, equals({'code': 500}));
+    });
+
+    test('timeout factory creates timeout error with duration', () {
+      final SocketErrorModel error = SocketErrorModel.timeout(
+        'Request timed out',
+        duration: const Duration(seconds: 30),
+      );
+
+      expect(error.type, equals(SocketErrorType.timeout));
+      expect(error.message, equals('Request timed out'));
+      expect(error.data?['duration'], equals(30000));
+    });
+
+    test('authentication factory creates auth error', () {
+      final SocketErrorModel error = SocketErrorModel.authentication('Invalid token');
+
+      expect(error.type, equals(SocketErrorType.authentication));
+      expect(error.message, equals('Invalid token'));
+    });
+
+    test('invalidNamespace factory creates namespace error', () {
+      final SocketErrorModel error = SocketErrorModel.invalidNamespace('/invalid');
+
+      expect(error.type, equals(SocketErrorType.invalidNamespace));
+      expect(error.message, contains('/invalid'));
+      expect(error.data?['namespace'], equals('/invalid'));
+    });
+
+    test('parse factory creates parse error', () {
+      final Exception original = Exception('Parse failed');
+      final SocketErrorModel error = SocketErrorModel.parse(
+        'Failed to parse',
+        originalError: original,
+      );
+
+      expect(error.type, equals(SocketErrorType.parse));
+      expect(error.message, equals('Failed to parse'));
+      expect(error.originalError, equals(original));
+    });
+
+    test('encoding factory creates encoding error', () {
+      final SocketErrorModel error = SocketErrorModel.encoding('Encoding failed');
+
+      expect(error.type, equals(SocketErrorType.encoding));
+      expect(error.message, equals('Encoding failed'));
+    });
+
+    test('decoding factory creates decoding error', () {
+      final SocketErrorModel error = SocketErrorModel.decoding('Decoding failed');
+
+      expect(error.type, equals(SocketErrorType.decoding));
+      expect(error.message, equals('Decoding failed'));
+    });
+
+    test('unknown factory creates unknown error', () {
+      final SocketErrorModel error = SocketErrorModel.unknown('Unknown error');
+
+      expect(error.type, equals(SocketErrorType.unknown));
+      expect(error.message, equals('Unknown error'));
+    });
+
+    test('fromObject creates from SocketErrorModel', () {
+      const SocketErrorModel original = SocketErrorModel(
+        type: SocketErrorType.connection,
+        message: 'Test',
+      );
+
+      final SocketErrorModel result = SocketErrorModel.fromObject(original);
+      expect(result, same(original));
+    });
+
+    test('fromObject creates from Exception', () {
+      final Exception original = Exception('Test exception');
+      final SocketErrorModel error = SocketErrorModel.fromObject(original);
+
+      expect(error.type, equals(SocketErrorType.unknown));
+      expect(error.message, contains('Test exception'));
+      expect(error.originalError, equals(original));
+    });
+
+    test('fromObject creates from generic object', () {
+      const String original = 'Error string';
+      final SocketErrorModel error = SocketErrorModel.fromObject(original);
+
+      expect(error.type, equals(SocketErrorType.unknown));
+      expect(error.message, contains('Error string'));
+      expect(error.originalError, equals(original));
+    });
+
+    test('toMap converts to map', () {
+      const SocketErrorModel error = SocketErrorModel(
+        type: SocketErrorType.connection,
+        message: 'Connection failed',
+        data: {'code': 500},
+      );
+
+      final Map map = error.toMap();
+      expect(map['type'], equals('connection'));
+      expect(map['message'], equals('Connection failed'));
+      expect(map['data'], equals({'code': 500}));
+    });
+
+    test('toJson returns same as toMap', () {
+      const SocketErrorModel error = SocketErrorModel(
+        type: SocketErrorType.timeout,
+        message: 'Timeout',
+      );
+
+      expect(error.toJson(), equals(error.toMap()));
+    });
+
+    test('equality works correctly', () {
+      const SocketErrorModel error1 = SocketErrorModel(
+        type: SocketErrorType.connection,
+        message: 'Same error',
+      );
+
+      const SocketErrorModel error2 = SocketErrorModel(
+        type: SocketErrorType.connection,
+        message: 'Same error',
+      );
+
+      const SocketErrorModel error3 = SocketErrorModel(
+        type: SocketErrorType.transport,
+        message: 'Different error',
+      );
+
+      expect(error1, equals(error2));
+      expect(error1, isNot(equals(error3)));
+    });
+
+    test('hashCode works correctly', () {
+      const SocketErrorModel error1 = SocketErrorModel(
+        type: SocketErrorType.connection,
+        message: 'Test',
+      );
+
+      const SocketErrorModel error2 = SocketErrorModel(
+        type: SocketErrorType.connection,
+        message: 'Test',
+      );
+
+      expect(error1.hashCode, equals(error2.hashCode));
+    });
+
+    test('toString provides useful representation', () {
+      const SocketErrorModel error = SocketErrorModel(
+        type: SocketErrorType.connection,
+        message: 'Test error',
+      );
+
+      final String str = error.toString();
+      expect(str, contains('SocketErrorModel'));
+      expect(str, contains('connection'));
+      expect(str, contains('Test error'));
+    });
+
+    test('implements Exception', () {
+      const SocketErrorModel error = SocketErrorModel(
+        type: SocketErrorType.unknown,
+        message: 'Test',
+      );
+
+      expect(error, isA());
+    });
+  });
+
+  group('ErrorConversion extension', () {
+    test('toSocketError converts string', () {
+      const String errorString = 'Error message';
+      final SocketErrorModel error = errorString.toSocketError();
+
+      expect(error.type, equals(SocketErrorType.unknown));
+      expect(error.message, contains('Error message'));
+    });
+
+    test('toSocketError converts exception', () {
+      final Exception exception = Exception('Test exception');
+      final SocketErrorModel error = exception.toSocketError();
+
+      expect(error.type, equals(SocketErrorType.unknown));
+      expect(error.originalError, equals(exception));
+    });
+
+    test('toSocketError preserves SocketErrorModel', () {
+      const SocketErrorModel original = SocketErrorModel(
+        type: SocketErrorType.connection,
+        message: 'Test',
+      );
+
+      final SocketErrorModel result = original.toSocketError();
+      expect(result, same(original));
+    });
+
+    test('toSocketError accepts stacktrace', () {
+      final StackTrace stackTrace = StackTrace.current;
+      const String errorString = 'Error';
+      final SocketErrorModel error = errorString.toSocketError(stackTrace);
+
+      expect(error.stackTrace, equals(stackTrace));
+    });
+  });
+}
diff --git a/test/models/socket_flags_models_test.dart b/test/models/socket_flags_models_test.dart
new file mode 100644
index 0000000..ed5c7c9
--- /dev/null
+++ b/test/models/socket_flags_models_test.dart
@@ -0,0 +1,400 @@
+import 'package:test/test.dart';
+import 'package:socket_io/src/models/socket_flags_models.dart';
+
+void main() {
+  group('SocketFlags', () {
+    group('Constructors', () {
+      test('default constructor creates with specified values', () {
+        final SocketFlags flags = SocketFlags(
+          json: true,
+          volatile: true,
+          broadcast: false,
+          compress: false,
+          preEncoded: false,
+        );
+
+        expect(flags.json, isTrue);
+        expect(flags.volatile, isTrue);
+        expect(flags.broadcast, isFalse);
+        expect(flags.compress, isFalse);
+        expect(flags.preEncoded, isFalse);
+      });
+
+      test('none() constructor creates all false flags', () {
+        const SocketFlags flags = SocketFlags.none();
+
+        expect(flags.json, isFalse);
+        expect(flags.volatile, isFalse);
+        expect(flags.broadcast, isFalse);
+        expect(flags.compress, isFalse);
+        expect(flags.preEncoded, isFalse);
+      });
+
+      test('json() constructor creates json flag', () {
+        const SocketFlags flags = SocketFlags.json();
+
+        expect(flags.json, isTrue);
+        expect(flags.volatile, isFalse);
+        expect(flags.broadcast, isFalse);
+        expect(flags.compress, isFalse);
+        expect(flags.preEncoded, isFalse);
+      });
+
+      test('volatile() constructor creates volatile flag', () {
+        const SocketFlags flags = SocketFlags.volatile();
+
+        expect(flags.json, isFalse);
+        expect(flags.volatile, isTrue);
+        expect(flags.broadcast, isFalse);
+        expect(flags.compress, isFalse);
+        expect(flags.preEncoded, isFalse);
+      });
+
+      test('broadcast() constructor creates broadcast flag', () {
+        const SocketFlags flags = SocketFlags.broadcast();
+
+        expect(flags.json, isFalse);
+        expect(flags.volatile, isFalse);
+        expect(flags.broadcast, isTrue);
+        expect(flags.compress, isFalse);
+        expect(flags.preEncoded, isFalse);
+      });
+
+      test('compress() constructor creates compress flag', () {
+        const SocketFlags flags = SocketFlags.compress();
+
+        expect(flags.json, isFalse);
+        expect(flags.volatile, isFalse);
+        expect(flags.broadcast, isFalse);
+        expect(flags.compress, isTrue);
+        expect(flags.preEncoded, isFalse);
+      });
+
+      test('preEncoded() constructor creates preEncoded flag', () {
+        const SocketFlags flags = SocketFlags.preEncoded();
+
+        expect(flags.json, isFalse);
+        expect(flags.volatile, isFalse);
+        expect(flags.broadcast, isFalse);
+        expect(flags.compress, isFalse);
+        expect(flags.preEncoded, isTrue);
+      });
+    });
+
+    group('fromMap factory', () {
+      test('creates from map with all flags', () {
+        final SocketFlags flags = SocketFlags.fromMap({
+          'json': true,
+          'volatile': true,
+          'broadcast': true,
+          'compress': true,
+          'preEncoded': true,
+        });
+
+        expect(flags.json, isTrue);
+        expect(flags.volatile, isTrue);
+        expect(flags.broadcast, isTrue);
+        expect(flags.compress, isTrue);
+        expect(flags.preEncoded, isTrue);
+      });
+
+      test('creates from map with some flags', () {
+        final SocketFlags flags = SocketFlags.fromMap({
+          'json': true,
+          'compress': true,
+        });
+
+        expect(flags.json, isTrue);
+        expect(flags.volatile, isFalse);
+        expect(flags.broadcast, isFalse);
+        expect(flags.compress, isTrue);
+        expect(flags.preEncoded, isFalse);
+      });
+
+      test('creates from empty map', () {
+        final SocketFlags flags = SocketFlags.fromMap({});
+
+        expect(flags.json, isFalse);
+        expect(flags.volatile, isFalse);
+        expect(flags.broadcast, isFalse);
+        expect(flags.compress, isFalse);
+        expect(flags.preEncoded, isFalse);
+      });
+
+      test('handles missing keys gracefully', () {
+        final SocketFlags flags = SocketFlags.fromMap({
+          'json': true,
+        });
+
+        expect(flags.json, isTrue);
+        expect(flags.volatile, isFalse);
+        expect(flags.broadcast, isFalse);
+        expect(flags.compress, isFalse);
+        expect(flags.preEncoded, isFalse);
+      });
+    });
+
+    group('toMap', () {
+      test('converts to map with all flags', () {
+        const SocketFlags flags = SocketFlags(
+          json: true,
+          volatile: true,
+          broadcast: true,
+          compress: true,
+          preEncoded: true,
+        );
+
+        final Map map = flags.toMap();
+
+        expect(map['json'], isTrue);
+        expect(map['volatile'], isTrue);
+        expect(map['broadcast'], isTrue);
+        expect(map['compress'], isTrue);
+        expect(map['preEncoded'], isTrue);
+      });
+
+      test('converts to map with only set flags', () {
+        const SocketFlags flags = SocketFlags(
+          json: true,
+          compress: true,
+        );
+
+        final Map map = flags.toMap();
+
+        expect(map['json'], isTrue);
+        expect(map.containsKey('volatile'), isFalse);
+        expect(map.containsKey('broadcast'), isFalse);
+        expect(map['compress'], isTrue);
+        expect(map.containsKey('preEncoded'), isFalse);
+      });
+
+      test('converts to empty map when no flags set', () {
+        const SocketFlags flags = SocketFlags.none();
+
+        final Map map = flags.toMap();
+
+        expect(map.isEmpty, isTrue);
+      });
+    });
+
+    group('copyWith', () {
+      test('creates copy with modified flags', () {
+        const SocketFlags original = SocketFlags(
+          json: true,
+          volatile: false,
+        );
+
+        final SocketFlags modified = original.copyWith(
+          volatile: true,
+          compress: true,
+        );
+
+        expect(modified.json, isTrue);
+        expect(modified.volatile, isTrue);
+        expect(modified.broadcast, isFalse);
+        expect(modified.compress, isTrue);
+        expect(modified.preEncoded, isFalse);
+      });
+
+      test('creates copy with no changes when no parameters provided', () {
+        const SocketFlags original = SocketFlags(
+          json: true,
+          volatile: true,
+        );
+
+        final SocketFlags copy = original.copyWith();
+
+        expect(copy.json, equals(original.json));
+        expect(copy.volatile, equals(original.volatile));
+        expect(copy.broadcast, equals(original.broadcast));
+        expect(copy.compress, equals(original.compress));
+        expect(copy.preEncoded, equals(original.preEncoded));
+      });
+
+      test('can turn off flags', () {
+        const SocketFlags original = SocketFlags(
+          json: true,
+          volatile: true,
+          broadcast: true,
+        );
+
+        final SocketFlags modified = original.copyWith(
+          volatile: false,
+          broadcast: false,
+        );
+
+        expect(modified.json, isTrue);
+        expect(modified.volatile, isFalse);
+        expect(modified.broadcast, isFalse);
+      });
+    });
+
+    group('hasAnyFlag', () {
+      test('returns true when any flag is set', () {
+        const SocketFlags flags1 = SocketFlags.json();
+        const SocketFlags flags2 = SocketFlags.volatile();
+        const SocketFlags flags3 = SocketFlags(json: true, compress: true);
+
+        expect(flags1.hasAnyFlag, isTrue);
+        expect(flags2.hasAnyFlag, isTrue);
+        expect(flags3.hasAnyFlag, isTrue);
+      });
+
+      test('returns false when no flags are set', () {
+        const SocketFlags flags = SocketFlags.none();
+
+        expect(flags.hasAnyFlag, isFalse);
+      });
+    });
+
+    group('hasNoFlags', () {
+      test('returns true when no flags are set', () {
+        const SocketFlags flags = SocketFlags.none();
+
+        expect(flags.hasNoFlags, isTrue);
+      });
+
+      test('returns false when any flag is set', () {
+        const SocketFlags flags1 = SocketFlags.json();
+        const SocketFlags flags2 = SocketFlags(json: true, compress: true);
+
+        expect(flags1.hasNoFlags, isFalse);
+        expect(flags2.hasNoFlags, isFalse);
+      });
+    });
+
+    group('equality', () {
+      test('equal flags are equal', () {
+        const SocketFlags flags1 = SocketFlags(json: true, volatile: true);
+        const SocketFlags flags2 = SocketFlags(json: true, volatile: true);
+
+        expect(flags1, equals(flags2));
+      });
+
+      test('different flags are not equal', () {
+        const SocketFlags flags1 = SocketFlags(json: true);
+        const SocketFlags flags2 = SocketFlags(volatile: true);
+
+        expect(flags1, isNot(equals(flags2)));
+      });
+
+      test('none flags are equal', () {
+        const SocketFlags flags1 = SocketFlags.none();
+        const SocketFlags flags2 = SocketFlags();
+
+        expect(flags1, equals(flags2));
+      });
+    });
+
+    group('hashCode', () {
+      test('equal flags have equal hashCodes', () {
+        const SocketFlags flags1 = SocketFlags(json: true, volatile: true);
+        const SocketFlags flags2 = SocketFlags(json: true, volatile: true);
+
+        expect(flags1.hashCode, equals(flags2.hashCode));
+      });
+
+      test('different flags have different hashCodes', () {
+        const SocketFlags flags1 = SocketFlags(json: true);
+        const SocketFlags flags2 = SocketFlags(volatile: true);
+
+        expect(flags1.hashCode, isNot(equals(flags2.hashCode)));
+      });
+    });
+
+    group('toString', () {
+      test('provides useful representation with flags', () {
+        const SocketFlags flags = SocketFlags(json: true, compress: true);
+
+        final String str = flags.toString();
+
+        expect(str, contains('SocketFlags'));
+        expect(str, contains('json'));
+        expect(str, contains('compress'));
+      });
+
+      test('provides useful representation with no flags', () {
+        const SocketFlags flags = SocketFlags.none();
+
+        final String str = flags.toString();
+
+        expect(str, contains('SocketFlags'));
+        expect(str, contains('none'));
+      });
+
+      test('shows all flags when all are set', () {
+        const SocketFlags flags = SocketFlags(
+          json: true,
+          volatile: true,
+          broadcast: true,
+          compress: true,
+          preEncoded: true,
+        );
+
+        final String str = flags.toString();
+
+        expect(str, contains('json'));
+        expect(str, contains('volatile'));
+        expect(str, contains('broadcast'));
+        expect(str, contains('compress'));
+        expect(str, contains('preEncoded'));
+      });
+    });
+
+    group('MapToSocketFlags extension', () {
+      test('converts map to SocketFlags', () {
+        final Map map = {
+          'json': true,
+          'volatile': true,
+        };
+
+        final SocketFlags flags = map.toSocketFlags();
+
+        expect(flags.json, isTrue);
+        expect(flags.volatile, isTrue);
+        expect(flags.broadcast, isFalse);
+      });
+    });
+
+    group('NullableMapToSocketFlags extension', () {
+      test('toSocketFlagsOrNone converts null to none', () {
+        const Map? map = null;
+
+        final SocketFlags flags = map.toSocketFlagsOrNone();
+
+        expect(flags.hasNoFlags, isTrue);
+      });
+
+      test('toSocketFlagsOrNone converts map to SocketFlags', () {
+        final Map? map = {
+          'json': true,
+        };
+
+        final SocketFlags flags = map.toSocketFlagsOrNone();
+
+        expect(flags.json, isTrue);
+        expect(flags.volatile, isFalse);
+      });
+
+      test('toSocketFlagsOrNull converts null to null', () {
+        const Map? map = null;
+
+        final SocketFlags? flags = map.toSocketFlagsOrNull();
+
+        expect(flags, isNull);
+      });
+
+      test('toSocketFlagsOrNull converts map to SocketFlags', () {
+        final Map? map = {
+          'compress': true,
+        };
+
+        final SocketFlags? flags = map.toSocketFlagsOrNull();
+
+        expect(flags, isNotNull);
+        expect(flags!.compress, isTrue);
+        expect(flags.json, isFalse);
+      });
+    });
+  });
+}
diff --git a/test/models/transport_data_models_test.dart b/test/models/transport_data_models_test.dart
new file mode 100644
index 0000000..34fc6d8
--- /dev/null
+++ b/test/models/transport_data_models_test.dart
@@ -0,0 +1,277 @@
+import 'package:test/test.dart';
+import 'package:socket_io/src/models/transport_data_models.dart';
+
+void main() {
+  group('TransportData', () {
+    group('StringTransportData', () {
+      test('creates and accesses string value', () {
+        final StringTransportData data = StringTransportData('hello');
+        expect(data.value, equals('hello'));
+        expect(data.toRaw(), equals('hello'));
+      });
+
+      test('equality works correctly', () {
+        final StringTransportData data1 = StringTransportData('hello');
+        final StringTransportData data2 = StringTransportData('hello');
+        final StringTransportData data3 = StringTransportData('world');
+
+        expect(data1, equals(data2));
+        expect(data1, isNot(equals(data3)));
+      });
+
+      test('hashCode works correctly', () {
+        final StringTransportData data1 = StringTransportData('hello');
+        final StringTransportData data2 = StringTransportData('hello');
+
+        expect(data1.hashCode, equals(data2.hashCode));
+      });
+
+      test('toString provides useful representation', () {
+        final StringTransportData data = StringTransportData('test');
+        expect(data.toString(), contains('StringTransportData'));
+        expect(data.toString(), contains('test'));
+      });
+    });
+
+    group('BinaryTransportData', () {
+      test('creates and accesses binary data', () {
+        final BinaryTransportData data = BinaryTransportData([1, 2, 3]);
+        expect(data.bytes, equals([1, 2, 3]));
+        expect(data.toRaw(), equals([1, 2, 3]));
+      });
+
+      test('fromList factory creates from dynamic list', () {
+        final BinaryTransportData data = BinaryTransportData.fromList([
+          1,
+          2,
+          [3, 4]
+        ]);
+        expect(data.bytes, equals([1, 2, 3, 4]));
+      });
+
+      test('toRaw returns immutable list', () {
+        final BinaryTransportData data = BinaryTransportData([1, 2, 3]);
+        final List raw = data.toRaw();
+        expect(() => raw.add(4), throwsUnsupportedError);
+      });
+
+      test('equality works correctly', () {
+        final BinaryTransportData data1 = BinaryTransportData([1, 2, 3]);
+        final BinaryTransportData data2 = BinaryTransportData([1, 2, 3]);
+        final BinaryTransportData data3 = BinaryTransportData([1, 2, 4]);
+
+        expect(data1, equals(data2));
+        expect(data1, isNot(equals(data3)));
+      });
+
+      test('toString provides useful representation', () {
+        final BinaryTransportData data = BinaryTransportData([1, 2, 3]);
+        expect(data.toString(), contains('BinaryTransportData'));
+        expect(data.toString(), contains('3 bytes'));
+      });
+    });
+
+    group('JsonTransportData', () {
+      test('creates and accesses json data', () {
+        final JsonTransportData data = JsonTransportData({'key': 'value'});
+        expect(data.data, equals({'key': 'value'}));
+        expect(data.toRaw(), equals({'key': 'value'}));
+      });
+
+      test('toRaw returns immutable map', () {
+        final JsonTransportData data = JsonTransportData({'key': 'value'});
+        final Map raw = data.toRaw();
+        expect(() => raw['new'] = 'value', throwsUnsupportedError);
+      });
+
+      test('equality works correctly', () {
+        final JsonTransportData data1 = JsonTransportData({'key': 'value'});
+        final JsonTransportData data2 = JsonTransportData({'key': 'value'});
+        final JsonTransportData data3 = JsonTransportData({'key': 'other'});
+
+        expect(data1, equals(data2));
+        expect(data1, isNot(equals(data3)));
+      });
+
+      test('toString provides useful representation', () {
+        final JsonTransportData data = JsonTransportData({'key': 'value'});
+        expect(data.toString(), contains('JsonTransportData'));
+      });
+    });
+
+    group('ListTransportData', () {
+      test('creates and accesses list data', () {
+        final ListTransportData data = ListTransportData([1, 'two', 3.0]);
+        expect(data.items, equals([1, 'two', 3.0]));
+        expect(data.toRaw(), equals([1, 'two', 3.0]));
+      });
+
+      test('toRaw returns immutable list', () {
+        final ListTransportData data = ListTransportData([1, 2, 3]);
+        final List raw = data.toRaw();
+        expect(() => raw.add(4), throwsUnsupportedError);
+      });
+
+      test('equality works correctly', () {
+        final ListTransportData data1 = ListTransportData([1, 2, 3]);
+        final ListTransportData data2 = ListTransportData([1, 2, 3]);
+        final ListTransportData data3 = ListTransportData([1, 2, 4]);
+
+        expect(data1, equals(data2));
+        expect(data1, isNot(equals(data3)));
+      });
+
+      test('toString provides useful representation', () {
+        final ListTransportData data = ListTransportData([1, 2, 3]);
+        expect(data.toString(), contains('ListTransportData'));
+      });
+    });
+
+    group('MixedTransportData', () {
+      test('creates and accesses mixed data', () {
+        final MixedTransportData data = MixedTransportData(42);
+        expect(data.value, equals(42));
+        expect(data.toRaw(), equals(42));
+      });
+
+      test('accepts any object type', () {
+        final MixedTransportData stringData = MixedTransportData('test');
+        final MixedTransportData numData = MixedTransportData(42);
+        final MixedTransportData boolData = MixedTransportData(true);
+
+        expect(stringData.value, equals('test'));
+        expect(numData.value, equals(42));
+        expect(boolData.value, equals(true));
+      });
+
+      test('equality works correctly', () {
+        final MixedTransportData data1 = MixedTransportData(42);
+        final MixedTransportData data2 = MixedTransportData(42);
+        final MixedTransportData data3 = MixedTransportData(43);
+
+        expect(data1, equals(data2));
+        expect(data1, isNot(equals(data3)));
+      });
+
+      test('toString provides useful representation', () {
+        final MixedTransportData data = MixedTransportData(42);
+        expect(data.toString(), contains('MixedTransportData'));
+        expect(data.toString(), contains('42'));
+      });
+    });
+
+    group('ObjectToTransportData extension', () {
+      test('converts String to StringTransportData', () {
+        final TransportData data = 'hello'.toTransportData();
+        expect(data, isA());
+        expect((data as StringTransportData).value, equals('hello'));
+      });
+
+      test('converts List to BinaryTransportData', () {
+        final TransportData data = [1, 2, 3].toTransportData();
+        expect(data, isA());
+        expect((data as BinaryTransportData).bytes, equals([1, 2, 3]));
+      });
+
+      test('converts Map to JsonTransportData', () {
+        final TransportData data = {'key': 'value'}.toTransportData();
+        expect(data, isA());
+        expect((data as JsonTransportData).data, equals({'key': 'value'}));
+      });
+
+      test('converts List to ListTransportData', () {
+        final TransportData data = [1, 'two', 3.0].toTransportData();
+        expect(data, isA());
+        expect((data as ListTransportData).items, equals([1, 'two', 3.0]));
+      });
+
+      test('converts other types to MixedTransportData', () {
+        final TransportData data = 42.toTransportData();
+        expect(data, isA());
+        expect((data as MixedTransportData).value, equals(42));
+      });
+    });
+
+    group('TransportDataToObject extension', () {
+      test('converts StringTransportData to String', () {
+        final TransportData data = StringTransportData('hello');
+        final Object obj = data.toObject();
+        expect(obj, equals('hello'));
+      });
+
+      test('converts BinaryTransportData to List', () {
+        final TransportData data = BinaryTransportData([1, 2, 3]);
+        final Object obj = data.toObject();
+        expect(obj, equals([1, 2, 3]));
+      });
+
+      test('converts JsonTransportData to Map', () {
+        final TransportData data = JsonTransportData({'key': 'value'});
+        final Object obj = data.toObject();
+        expect(obj, equals({'key': 'value'}));
+      });
+
+      test('converts ListTransportData to List', () {
+        final TransportData data = ListTransportData([1, 2, 3]);
+        final Object obj = data.toObject();
+        expect(obj, equals([1, 2, 3]));
+      });
+
+      test('converts MixedTransportData to original type', () {
+        final TransportData data = MixedTransportData(42);
+        final Object obj = data.toObject();
+        expect(obj, equals(42));
+      });
+    });
+
+    group('Type safety', () {
+      test('abstract class supports type checking', () {
+        // Test that we can check types using is operator
+        final TransportData data = StringTransportData('test');
+        String result;
+        if (data is StringTransportData) {
+          result = 'string';
+        } else if (data is BinaryTransportData) {
+          result = 'binary';
+        } else if (data is JsonTransportData) {
+          result = 'json';
+        } else if (data is ListTransportData) {
+          result = 'list';
+        } else if (data is MixedTransportData) {
+          result = 'mixed';
+        } else {
+          result = 'unknown';
+        }
+        expect(result, equals('string'));
+      });
+
+      test('type checking works with all types', () {
+        final List dataList = [
+          StringTransportData('test'),
+          BinaryTransportData([1, 2, 3]),
+          JsonTransportData({'key': 'value'}),
+          ListTransportData([1, 2, 3]),
+          MixedTransportData(42),
+        ];
+
+        final List types = dataList.map((final TransportData data) {
+          if (data is StringTransportData) {
+            return 'string';
+          } else if (data is BinaryTransportData) {
+            return 'binary';
+          } else if (data is JsonTransportData) {
+            return 'json';
+          } else if (data is ListTransportData) {
+            return 'list';
+          } else if (data is MixedTransportData) {
+            return 'mixed';
+          } else {
+            return 'unknown';
+          }
+        }).toList();
+
+        expect(types, equals(['string', 'binary', 'json', 'list', 'mixed']));
+      });
+    });
+  });
+}
diff --git a/test/models/validation_error_models_test.dart b/test/models/validation_error_models_test.dart
new file mode 100644
index 0000000..bcafaf2
--- /dev/null
+++ b/test/models/validation_error_models_test.dart
@@ -0,0 +1,447 @@
+import 'package:test/test.dart';
+import 'package:socket_io/src/models/validation_error_models.dart' as validation;
+
+void main() {
+  group('ValidationError', () {
+    group('validation.RequiredFieldError', () {
+      test('creates with field name', () {
+        const validation.RequiredFieldError error = validation.RequiredFieldError(field: 'username');
+        expect(error.field, equals('username'));
+        expect(error.message, equals('Required field is missing'));
+        expect(error.code, equals('required_field'));
+      });
+
+      test('creates with custom message', () {
+        const validation.RequiredFieldError error = validation.RequiredFieldError(
+          field: 'email',
+          message: 'Email is required',
+        );
+        expect(error.message, equals('Email is required'));
+      });
+
+      test('toString includes field and message', () {
+        const validation.RequiredFieldError error = validation.RequiredFieldError(field: 'name');
+        expect(
+          error.toString(),
+          equals('ValidationError(name): Required field is missing [code: required_field]'),
+        );
+      });
+    });
+
+    group('validation.FormatError', () {
+      test('creates with format and value', () {
+        const validation.FormatError error = validation.FormatError(
+          field: 'email',
+          expectedFormat: 'email@example.com',
+          actualValue: 'not-an-email',
+        );
+        expect(error.field, equals('email'));
+        expect(error.expectedFormat, equals('email@example.com'));
+        expect(error.actualValue, equals('not-an-email'));
+      });
+
+      test('toString includes format details', () {
+        const validation.FormatError error = validation.FormatError(
+          field: 'url',
+          expectedFormat: 'http://...',
+          actualValue: 'invalid',
+        );
+        expect(
+          error.toString(),
+          contains('Expected http://...'),
+        );
+        expect(error.toString(), contains('got invalid'));
+      });
+    });
+
+    group('validation.RangeValidationError', () {
+      test('creates with min and max', () {
+        const validation.RangeValidationError error = validation.RangeValidationError(
+          field: 'age',
+          min: 0,
+          max: 120,
+          actualValue: -5,
+        );
+        expect(error.min, equals(0));
+        expect(error.max, equals(120));
+        expect(error.actualValue, equals(-5));
+      });
+
+      test('creates with only min', () {
+        const validation.RangeValidationError error = validation.RangeValidationError(
+          field: 'port',
+          min: 1,
+          actualValue: 0,
+        );
+        expect(error.min, equals(1));
+        expect(error.max, isNull);
+      });
+
+      test('toString with min and max', () {
+        const validation.RangeValidationError error = validation.RangeValidationError(
+          field: 'value',
+          min: 1,
+          max: 100,
+          actualValue: 150,
+        );
+        expect(error.toString(), contains('between 1 and 100'));
+        expect(error.toString(), contains('got 150'));
+      });
+
+      test('toString with only min', () {
+        const validation.RangeValidationError error = validation.RangeValidationError(
+          field: 'value',
+          min: 10,
+          actualValue: 5,
+        );
+        expect(error.toString(), contains('at least 10'));
+      });
+
+      test('toString with only max', () {
+        const validation.RangeValidationError error = validation.RangeValidationError(
+          field: 'value',
+          max: 100,
+          actualValue: 150,
+        );
+        expect(error.toString(), contains('at most 100'));
+      });
+    });
+
+    group('validation.LengthError', () {
+      test('creates with min and max length', () {
+        const validation.LengthError error = validation.LengthError(
+          field: 'password',
+          minLength: 8,
+          maxLength: 32,
+          actualLength: 5,
+        );
+        expect(error.minLength, equals(8));
+        expect(error.maxLength, equals(32));
+        expect(error.actualLength, equals(5));
+      });
+
+      test('toString describes length constraint', () {
+        const validation.LengthError error = validation.LengthError(
+          field: 'name',
+          minLength: 1,
+          maxLength: 50,
+          actualLength: 100,
+        );
+        expect(error.toString(), contains('between 1 and 50 characters'));
+        expect(error.toString(), contains('got 100'));
+      });
+    });
+
+    group('validation.InvalidValueError', () {
+      test('creates with allowed values', () {
+        const validation.InvalidValueError error = validation.InvalidValueError(
+          field: 'status',
+          allowedValues: ['active', 'inactive'],
+          actualValue: 'unknown',
+        );
+        expect(error.allowedValues, equals(['active', 'inactive']));
+        expect(error.actualValue, equals('unknown'));
+      });
+
+      test('toString shows allowed values', () {
+        const validation.InvalidValueError error = validation.InvalidValueError(
+          field: 'type',
+          allowedValues: [1, 2, 3],
+          actualValue: 5,
+        );
+        expect(error.toString(), contains('Expected one of'));
+        expect(error.toString(), contains('[1, 2, 3]'));
+        expect(error.toString(), contains('got 5'));
+      });
+    });
+
+    group('validation.CustomValidationError', () {
+      test('creates with message and details', () {
+        const validation.CustomValidationError error = validation.CustomValidationError(
+          field: 'data',
+          message: 'Invalid data structure',
+          details: {'reason': 'missing key'},
+        );
+        expect(error.message, equals('Invalid data structure'));
+        expect(error.details, isNotNull);
+        expect(error.details!['reason'], equals('missing key'));
+      });
+
+      test('toString includes details', () {
+        const validation.CustomValidationError error = validation.CustomValidationError(
+          field: 'config',
+          message: 'Invalid configuration',
+          details: {'key': 'timeout'},
+        );
+        expect(error.toString(), contains('Invalid configuration'));
+        expect(error.toString(), contains('details:'));
+      });
+    });
+  });
+
+  group('validation.ValidationResult', () {
+    group('validation.ValidationSuccess', () {
+      test('creates with value', () {
+        const validation.ValidationSuccess result = validation.ValidationSuccess('test');
+        expect(result.value, equals('test'));
+        expect(result.isValid, isTrue);
+        expect(result.isInvalid, isFalse);
+      });
+
+      test('getOrElse returns value', () {
+        const validation.ValidationSuccess result = validation.ValidationSuccess(42);
+        expect(result.getOrElse(() => 0), equals(42));
+      });
+
+      test('getOrDefault returns value', () {
+        const validation.ValidationSuccess result = validation.ValidationSuccess('hello');
+        expect(result.getOrDefault('default'), equals('hello'));
+      });
+
+      test('map transforms value', () {
+        const validation.ValidationSuccess result = validation.ValidationSuccess(10);
+        final validation.ValidationResult mapped = result.map((int n) => n.toString());
+        expect(mapped.isValid, isTrue);
+        expect(mapped.value, equals('10'));
+      });
+
+      test('flatMap chains operations', () {
+        const validation.ValidationSuccess result = validation.ValidationSuccess(10);
+        final validation.ValidationResult flatMapped = result.flatMap(
+          (int n) => validation.ValidationSuccess(n.toString()),
+        );
+        expect(flatMapped.isValid, isTrue);
+        expect(flatMapped.value, equals('10'));
+      });
+
+      test('equality works correctly', () {
+        const validation.ValidationSuccess result1 = validation.ValidationSuccess(42);
+        const validation.ValidationSuccess result2 = validation.ValidationSuccess(42);
+        const validation.ValidationSuccess result3 = validation.ValidationSuccess(43);
+
+        expect(result1, equals(result2));
+        expect(result1, isNot(equals(result3)));
+      });
+
+      test('hashCode is consistent', () {
+        const validation.ValidationSuccess result1 = validation.ValidationSuccess('test');
+        const validation.ValidationSuccess result2 = validation.ValidationSuccess('test');
+        expect(result1.hashCode, equals(result2.hashCode));
+      });
+    });
+
+    group('validation.ValidationFailure', () {
+      test('creates with errors list', () {
+        const validation.ValidationFailure result =
+            validation.ValidationFailure([
+          validation.RequiredFieldError(field: 'name'),
+        ]);
+        expect(result.errors, hasLength(1));
+        expect(result.isValid, isFalse);
+        expect(result.isInvalid, isTrue);
+      });
+
+      test('creates with single error', () {
+        final validation.ValidationFailure result = validation.ValidationFailure.single(
+          const validation.RangeValidationError(field: 'age', min: 0, actualValue: -1),
+        );
+        expect(result.errors, hasLength(1));
+      });
+
+      test('getOrElse calls orElse function', () {
+        final validation.ValidationFailure result = validation.ValidationFailure.single(
+          const validation.RequiredFieldError(field: 'value'),
+        );
+        expect(result.getOrElse(() => 0), equals(0));
+      });
+
+      test('getOrDefault returns default', () {
+        final validation.ValidationFailure result = validation.ValidationFailure.single(
+          const validation.RequiredFieldError(field: 'name'),
+        );
+        expect(result.getOrDefault('default'), equals('default'));
+      });
+
+      test('value throws StateError', () {
+        final validation.ValidationFailure result = validation.ValidationFailure.single(
+          const validation.RequiredFieldError(field: 'test'),
+        );
+        expect(() => result.value, throwsStateError);
+      });
+
+      test('map preserves errors', () {
+        final validation.ValidationFailure result = validation.ValidationFailure.single(
+          const validation.RequiredFieldError(field: 'num'),
+        );
+        final validation.ValidationResult mapped = result.map((int n) => n.toString());
+        expect(mapped.isInvalid, isTrue);
+        expect(mapped.errors, hasLength(1));
+      });
+
+      test('flatMap preserves errors', () {
+        final validation.ValidationFailure result = validation.ValidationFailure.single(
+          const validation.RequiredFieldError(field: 'num'),
+        );
+        final validation.ValidationResult flatMapped = result.flatMap(
+          (int n) => validation.ValidationSuccess(n.toString()),
+        );
+        expect(flatMapped.isInvalid, isTrue);
+      });
+
+      test('toString includes error count', () {
+        const validation.ValidationFailure result =
+            validation.ValidationFailure([
+          validation.RequiredFieldError(field: 'name'),
+          validation.RequiredFieldError(field: 'email'),
+        ]);
+        expect(result.toString(), contains('2 errors'));
+      });
+    });
+  });
+
+  group('validation.ValidationUtils', () {
+    group('required', () {
+      test('succeeds for non-null value', () {
+        final validation.ValidationResult result = validation.ValidationUtils.required('test', 'field');
+        expect(result.isValid, isTrue);
+        expect(result.value, equals('test'));
+      });
+
+      test('fails for null value', () {
+        final validation.ValidationResult result = validation.ValidationUtils.required(null, 'field');
+        expect(result.isInvalid, isTrue);
+        expect(result.errors.first, isA());
+      });
+    });
+
+    group('nonEmpty', () {
+      test('succeeds for non-empty string', () {
+        final validation.ValidationResult result = validation.ValidationUtils.nonEmpty('hello', 'field');
+        expect(result.isValid, isTrue);
+      });
+
+      test('fails for empty string', () {
+        final validation.ValidationResult result = validation.ValidationUtils.nonEmpty('', 'field');
+        expect(result.isInvalid, isTrue);
+      });
+    });
+
+    group('inRange', () {
+      test('succeeds for value in range', () {
+        final validation.ValidationResult result =
+            validation.ValidationUtils.inRange(50, 'field', min: 0, max: 100);
+        expect(result.isValid, isTrue);
+      });
+
+      test('fails for value below min', () {
+        final validation.ValidationResult result = validation.ValidationUtils.inRange(-1, 'field', min: 0);
+        expect(result.isInvalid, isTrue);
+        expect(result.errors.first, isA());
+      });
+
+      test('fails for value above max', () {
+        final validation.ValidationResult result = validation.ValidationUtils.inRange(101, 'field', max: 100);
+        expect(result.isInvalid, isTrue);
+      });
+    });
+
+    group('lengthInRange', () {
+      test('succeeds for valid length', () {
+        final validation.ValidationResult result =
+            validation.ValidationUtils.lengthInRange('hello', 'field', minLength: 1, maxLength: 10);
+        expect(result.isValid, isTrue);
+      });
+
+      test('fails for too short', () {
+        final validation.ValidationResult result =
+            validation.ValidationUtils.lengthInRange('hi', 'field', minLength: 5);
+        expect(result.isInvalid, isTrue);
+        expect(result.errors.first, isA());
+      });
+
+      test('fails for too long', () {
+        final validation.ValidationResult result =
+            validation.ValidationUtils.lengthInRange('verylongstring', 'field', maxLength: 5);
+        expect(result.isInvalid, isTrue);
+      });
+    });
+
+    group('matchesPattern', () {
+      test('succeeds for matching pattern', () {
+        final validation.ValidationResult result = validation.ValidationUtils.matchesPattern(
+          'test@example.com',
+          'email',
+          RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$'),
+        );
+        expect(result.isValid, isTrue);
+      });
+
+      test('fails for non-matching pattern', () {
+        final validation.ValidationResult result = validation.ValidationUtils.matchesPattern(
+          'not-an-email',
+          'email',
+          RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$'),
+        );
+        expect(result.isInvalid, isTrue);
+        expect(result.errors.first, isA());
+      });
+    });
+
+    group('oneOf', () {
+      test('succeeds for allowed value', () {
+        final validation.ValidationResult result = validation.ValidationUtils.oneOf(
+          'active',
+          'status',
+          ['active', 'inactive'],
+        );
+        expect(result.isValid, isTrue);
+      });
+
+      test('fails for disallowed value', () {
+        final validation.ValidationResult result = validation.ValidationUtils.oneOf(
+          'unknown',
+          'status',
+          ['active', 'inactive'],
+        );
+        expect(result.isInvalid, isTrue);
+        expect(result.errors.first, isA());
+      });
+    });
+
+    group('combine', () {
+      test('succeeds when all validations pass', () {
+        final validation.ValidationResult result = validation.ValidationUtils.combine(
+          'test',
+          >[
+            const validation.ValidationSuccess('test'),
+            const validation.ValidationSuccess('test'),
+          ],
+        );
+        expect(result.isValid, isTrue);
+      });
+
+      test('fails when any validation fails', () {
+        final validation.ValidationResult result = validation.ValidationUtils.combine(
+          'test',
+          >[
+            const validation.ValidationSuccess('test'),
+            validation.ValidationFailure.single(const validation.RequiredFieldError(field: 'test')),
+          ],
+        );
+        expect(result.isInvalid, isTrue);
+      });
+
+      test('collects all errors', () {
+        final validation.ValidationResult result = validation.ValidationUtils.combine(
+          'test',
+          >[
+            validation.ValidationFailure.single(const validation.RequiredFieldError(field: 'field1')),
+            validation.ValidationFailure.single(const validation.RequiredFieldError(field: 'field2')),
+          ],
+        );
+        expect(result.isInvalid, isTrue);
+        expect(result.errors, hasLength(2));
+      });
+    });
+  });
+}
diff --git a/test/namespace_config_models_test.dart b/test/namespace_config_models_test.dart
new file mode 100644
index 0000000..76118d4
--- /dev/null
+++ b/test/namespace_config_models_test.dart
@@ -0,0 +1,243 @@
+// namespace_config_models_test.dart
+//
+// Purpose: Tests for namespace configuration models
+
+import 'package:test/test.dart';
+import 'package:socket_io/src/models/namespace_config_models.dart';
+import 'package:socket_io/src/value_objects/namespace_name_vo.dart';
+
+void main() {
+  group('NamespaceConfig', () {
+    test('creates config with required fields', () {
+      final NamespaceName name = NamespaceName('/test');
+      final NamespaceConfig config = NamespaceConfig(name: name);
+
+      expect(config.name, equals(name));
+      expect(config.adapterFactory, isNull);
+      expect(config.middleware, isEmpty);
+      expect(config.autoCreate, isTrue);
+      expect(config.maxSockets, isNull);
+    });
+
+    test('creates config with all fields', () {
+      final NamespaceName name = NamespaceName('/test');
+      final List middleware =
+          [
+        (final Object socket, final void Function(Object?) next) => next(null),
+      ];
+
+      final NamespaceConfig config = NamespaceConfig(
+        name: name,
+        middleware: middleware,
+        autoCreate: false,
+        maxSockets: 100,
+      );
+
+      expect(config.name, equals(name));
+      expect(config.middleware, hasLength(1));
+      expect(config.autoCreate, isFalse);
+      expect(config.maxSockets, equals(100));
+    });
+
+    test('creates config from string', () {
+      final NamespaceConfig config = NamespaceConfig.fromString('/test');
+
+      expect(config.name.value, equals('/test'));
+      expect(config.autoCreate, isTrue);
+    });
+
+    test('creates config from string with options', () {
+      final NamespaceConfig config = NamespaceConfig.fromString(
+        '/test',
+        autoCreate: false,
+        maxSockets: 50,
+      );
+
+      expect(config.name.value, equals('/test'));
+      expect(config.autoCreate, isFalse);
+      expect(config.maxSockets, equals(50));
+    });
+
+    test('copyWith creates new instance with updated values', () {
+      final NamespaceName name1 = NamespaceName('/test1');
+      final NamespaceName name2 = NamespaceName('/test2');
+      final NamespaceConfig config1 = NamespaceConfig(name: name1);
+      final NamespaceConfig config2 = config1.copyWith(name: name2, maxSockets: 100);
+
+      expect(config2.name, equals(name2));
+      expect(config2.maxSockets, equals(100));
+      expect(config2.autoCreate, equals(config1.autoCreate));
+    });
+
+    test('withMiddleware adds middleware', () {
+      final NamespaceName name = NamespaceName('/test');
+      final NamespaceConfig config1 = NamespaceConfig(name: name);
+      final NamespaceConfig config2 = config1.withMiddleware(
+        (final Object socket, final void Function(Object?) next) => next(null),
+      );
+
+      expect(config1.middleware, isEmpty);
+      expect(config2.middleware, hasLength(1));
+    });
+
+    test('withMiddlewares adds multiple middleware', () {
+      final NamespaceName name = NamespaceName('/test');
+      final NamespaceConfig config1 = NamespaceConfig(name: name);
+      final List middlewares =
+          [
+        (final Object socket, final void Function(Object?) next) => next(null),
+        (final Object socket, final void Function(Object?) next) => next(null),
+      ];
+      final NamespaceConfig config2 = config1.withMiddlewares(middlewares);
+
+      expect(config1.middleware, isEmpty);
+      expect(config2.middleware, hasLength(2));
+    });
+
+    test('equality works correctly', () {
+      final NamespaceName name = NamespaceName('/test');
+      final NamespaceConfig config1 = NamespaceConfig(name: name, maxSockets: 100);
+      final NamespaceConfig config2 = NamespaceConfig(name: name, maxSockets: 100);
+      final NamespaceConfig config3 = NamespaceConfig(name: name, maxSockets: 50);
+
+      expect(config1, equals(config2));
+      expect(config1, isNot(equals(config3)));
+    });
+
+    test('hashCode is consistent', () {
+      final NamespaceName name = NamespaceName('/test');
+      final NamespaceConfig config1 = NamespaceConfig(name: name, maxSockets: 100);
+      final NamespaceConfig config2 = NamespaceConfig(name: name, maxSockets: 100);
+
+      expect(config1.hashCode, equals(config2.hashCode));
+    });
+
+    test('toString includes all key information', () {
+      final NamespaceName name = NamespaceName('/test');
+      final NamespaceConfig config = NamespaceConfig(
+        name: name,
+        autoCreate: false,
+        maxSockets: 100,
+      );
+
+      final String str = config.toString();
+      expect(str, contains('/test'));
+      expect(str, contains('false'));
+      expect(str, contains('100'));
+    });
+  });
+
+  group('NamespaceConfigBuilder', () {
+    test('builds config with name', () {
+      final NamespaceConfig config = NamespaceConfigBuilder().name('/test').build();
+
+      expect(config.name.value, equals('/test'));
+      expect(config.autoCreate, isTrue);
+    });
+
+    test('builds config with all fields', () {
+      final NamespaceConfig config = NamespaceConfigBuilder()
+          .name('/test')
+          .autoCreate(false)
+          .maxSockets(100)
+          .middleware(
+            (final Object socket, final void Function(Object?) next) => next(null),
+          )
+          .build();
+
+      expect(config.name.value, equals('/test'));
+      expect(config.autoCreate, isFalse);
+      expect(config.maxSockets, equals(100));
+      expect(config.middleware, hasLength(1));
+    });
+
+    test('builds config with nameVo', () {
+      final NamespaceName name = NamespaceName('/test');
+      final NamespaceConfig config = NamespaceConfigBuilder().nameVo(name).build();
+
+      expect(config.name, equals(name));
+    });
+
+    test('middleware adds single function', () {
+      final NamespaceConfig config = NamespaceConfigBuilder()
+          .name('/test')
+          .middleware(
+            (final Object socket, final void Function(Object?) next) => next(null),
+          )
+          .build();
+
+      expect(config.middleware, hasLength(1));
+    });
+
+    test('middlewares adds multiple functions', () {
+      final List middlewares =
+          [
+        (final Object socket, final void Function(Object?) next) => next(null),
+        (final Object socket, final void Function(Object?) next) => next(null),
+      ];
+
+      final NamespaceConfig config = NamespaceConfigBuilder().name('/test').middlewares(middlewares).build();
+
+      expect(config.middleware, hasLength(2));
+    });
+
+    test('maxSockets validates positive values', () {
+      expect(
+        () => NamespaceConfigBuilder().maxSockets(0),
+        throwsArgumentError,
+      );
+      expect(
+        () => NamespaceConfigBuilder().maxSockets(-1),
+        throwsArgumentError,
+      );
+    });
+
+    test('unlimitedSockets sets null maxSockets', () {
+      final NamespaceConfig config = NamespaceConfigBuilder().name('/test').maxSockets(100).unlimitedSockets().build();
+
+      expect(config.maxSockets, isNull);
+    });
+
+    test('throws StateError when name is missing', () {
+      expect(
+        () => NamespaceConfigBuilder().build(),
+        throwsStateError,
+      );
+    });
+
+    test('builds immutable middleware list', () {
+      final NamespaceConfig config = NamespaceConfigBuilder()
+          .name('/test')
+          .middleware(
+            (final Object socket, final void Function(Object?) next) => next(null),
+          )
+          .build();
+
+      expect(
+        () => config.middleware.add(
+          (final Object socket, final void Function(Object?) next) => next(null),
+        ),
+        throwsUnsupportedError,
+      );
+    });
+
+    test('builder methods are chainable', () {
+      final NamespaceConfigBuilder builder = NamespaceConfigBuilder();
+      final NamespaceConfigBuilder result = builder.name('/test').autoCreate(false).maxSockets(100);
+
+      expect(result, same(builder));
+    });
+
+    test('builds multiple configs from same builder', () {
+      final NamespaceConfigBuilder builder = NamespaceConfigBuilder().name('/test');
+
+      final NamespaceConfig config1 = builder.build();
+      final NamespaceConfig config2 = builder.maxSockets(100).build();
+
+      expect(config1.name.value, equals('/test'));
+      expect(config2.name.value, equals('/test'));
+      expect(config1.maxSockets, isNull);
+      expect(config2.maxSockets, equals(100));
+    });
+  });
+}
diff --git a/test/packet_data_models_test.dart b/test/packet_data_models_test.dart
new file mode 100644
index 0000000..29508f8
--- /dev/null
+++ b/test/packet_data_models_test.dart
@@ -0,0 +1,464 @@
+/// Tests for packet data models.
+library packet_data_models_test;
+
+import 'package:test/test.dart';
+import 'package:socket_io/src/models/packet_data_models.dart';
+import 'package:socket_io/src/value_objects/disconnect_reason_vo.dart';
+import 'package:socket_io/src/value_objects/event_arguments_vo.dart';
+
+void main() {
+  group('ConnectPacketData', () {
+    test('creates with all fields', () {
+      final ConnectPacketData data = ConnectPacketData(
+        auth: {'token': 'abc123'},
+        query: {'userId': '123'},
+        metadata: {'version': '1.0'},
+      );
+
+      expect(data.auth, {'token': 'abc123'});
+      expect(data.query, {'userId': '123'});
+      expect(data.metadata, {'version': '1.0'});
+    });
+
+    test('creates with null fields', () {
+      const ConnectPacketData data = ConnectPacketData();
+
+      expect(data.auth, isNull);
+      expect(data.query, isNull);
+      expect(data.metadata, isNull);
+    });
+
+    test('converts to and from JSON', () {
+      final ConnectPacketData original = ConnectPacketData(
+        auth: {'token': 'abc123'},
+        query: {'userId': '123'},
+      );
+
+      final Map json = original.toJson();
+      final ConnectPacketData restored = ConnectPacketData.fromJson(json);
+
+      expect(restored, equals(original));
+    });
+
+    test('toJson omits null fields', () {
+      const ConnectPacketData data = ConnectPacketData(
+        auth: {'token': 'abc123'},
+      );
+
+      final Map json = data.toJson();
+
+      expect(json.containsKey('auth'), isTrue);
+      expect(json.containsKey('query'), isFalse);
+      expect(json.containsKey('metadata'), isFalse);
+    });
+
+    test('equality works correctly', () {
+      final ConnectPacketData data1 = ConnectPacketData(
+        auth: {'token': 'abc123'},
+      );
+      final ConnectPacketData data2 = ConnectPacketData(
+        auth: {'token': 'abc123'},
+      );
+      final ConnectPacketData data3 = ConnectPacketData(
+        auth: {'token': 'different'},
+      );
+
+      expect(data1, equals(data2));
+      expect(data1, isNot(equals(data3)));
+    });
+
+    test('hashCode is consistent', () {
+      final ConnectPacketData data = ConnectPacketData(
+        auth: {'token': 'abc123'},
+      );
+
+      // HashCode should be consistent for same object
+      expect(data.hashCode, equals(data.hashCode));
+    });
+
+    test('toString provides meaningful output', () {
+      final ConnectPacketData data = ConnectPacketData(
+        auth: {'token': 'abc123'},
+        query: {'userId': '123'},
+      );
+
+      final String str = data.toString();
+      expect(str, contains('ConnectPacketData'));
+    });
+  });
+
+  group('DisconnectPacketData', () {
+    test('creates from DisconnectReason (typed)', () {
+      final DisconnectPacketData data = DisconnectPacketData(reason: DisconnectReason.clientDisconnect);
+
+      expect(data.reason, 'client disconnect');
+      expect(data.typedReason, equals(DisconnectReason.clientDisconnect));
+      expect(data.description, isNull);
+      expect(data.metadata, isNull);
+    });
+
+    test('creates with all fields', () {
+      final DisconnectPacketData data = DisconnectPacketData(
+        reason: DisconnectReason(DisconnectReasonType.serverDisconnect, 'custom details'),
+        description: 'Maintenance',
+        metadata: {'time': '10:00'},
+      );
+
+      expect(data.reason, 'custom details');
+      expect(data.description, 'Maintenance');
+      expect(data.metadata, {'time': '10:00'});
+    });
+
+    test('fromReasonString factory creates correctly', () {
+      final DisconnectPacketData data = DisconnectPacketData.fromReasonString('ping timeout');
+
+      expect(data.reason, 'ping timeout');
+      expect(data.reasonValue.type, DisconnectReasonType.pingTimeout);
+      expect(data.description, isNull);
+    });
+
+    test('fromJson handles string', () {
+      final DisconnectPacketData data = DisconnectPacketData.fromJson('client disconnect');
+
+      expect(data.reason, 'client disconnect');
+      expect(data.reasonValue.type, DisconnectReasonType.clientDisconnect);
+    });
+
+    test('fromJson handles map', () {
+      final DisconnectPacketData data = DisconnectPacketData.fromJson(
+        {
+          'reason': 'server disconnect',
+          'description': 'Maintenance',
+        },
+      );
+
+      expect(data.reason, 'server disconnect');
+      expect(data.reasonValue.type, DisconnectReasonType.serverDisconnect);
+      expect(data.description, 'Maintenance');
+    });
+
+    test('fromJson handles null', () {
+      final DisconnectPacketData data = DisconnectPacketData.fromJson(null);
+
+      expect(data.reason, 'unknown');
+    });
+
+    test('toJson returns string for simple case', () {
+      final DisconnectPacketData data = DisconnectPacketData(reason: DisconnectReason.clientDisconnect);
+
+      final Object json = data.toJson();
+
+      expect(json, 'client disconnect');
+    });
+
+    test('toJson returns map for complex case', () {
+      final DisconnectPacketData data = DisconnectPacketData(
+        reason: DisconnectReason.serverDisconnect,
+        description: 'Maintenance',
+      );
+
+      final Object json = data.toJson();
+
+      expect(json, isA>());
+      expect((json as Map)['reason'], 'server disconnect');
+      expect(json['description'], 'Maintenance');
+    });
+
+    test('equality works correctly', () {
+      final DisconnectPacketData data1 = DisconnectPacketData(reason: DisconnectReason.clientDisconnect);
+      final DisconnectPacketData data2 = DisconnectPacketData(reason: DisconnectReason.clientDisconnect);
+      final DisconnectPacketData data3 = DisconnectPacketData(reason: DisconnectReason.serverDisconnect);
+
+      expect(data1, equals(data2));
+      expect(data1, isNot(equals(data3)));
+    });
+
+    test('reasonValue getter converts from string', () {
+      // ignore: deprecated_member_use
+      final DisconnectPacketData data = DisconnectPacketData.fromString(reason: 'transport error');
+
+      expect(data.reasonValue.type, DisconnectReasonType.transportError);
+      expect(data.reasonValue.value, 'transport error');
+    });
+
+    test('supports backward compatibility with string constructor', () {
+      // ignore: deprecated_member_use
+      final DisconnectPacketData data = DisconnectPacketData.fromString(
+        reason: 'forced close',
+        description: 'Server maintenance',
+      );
+
+      expect(data.reason, 'forced close');
+      expect(data.description, 'Server maintenance');
+    });
+  });
+
+  group('EventPacketData', () {
+    test('creates with event name and arguments', () {
+      final EventPacketData data = EventPacketData(
+        eventName: 'message',
+        arguments: EventArguments(['Hello', 42]),
+      );
+
+      expect(data.eventName, 'message');
+      expect(data.arguments.length, 2);
+      expect(data.arguments[0], 'Hello');
+      expect(data.arguments[1], 42);
+    });
+
+    test('fromList creates correctly', () {
+      final EventPacketData data = EventPacketData.fromList(['message', 'Hello', 42]);
+
+      expect(data.eventName, 'message');
+      expect(data.arguments.length, 2);
+      expect(data.arguments[0], 'Hello');
+      expect(data.arguments[1], 42);
+    });
+
+    test('fromList throws for empty list', () {
+      expect(
+        () => EventPacketData.fromList([]),
+        throwsArgumentError,
+      );
+    });
+
+    test('withoutArgs factory creates correctly', () {
+      final EventPacketData data = EventPacketData.withoutArgs('ping');
+
+      expect(data.eventName, 'ping');
+      expect(data.arguments.isEmpty, isTrue);
+    });
+
+    test('single factory creates correctly', () {
+      final EventPacketData data = EventPacketData.single('update', 42);
+
+      expect(data.eventName, 'update');
+      expect(data.arguments.length, 1);
+      expect(data.arguments[0], 42);
+    });
+
+    test('toList returns correct format', () {
+      final EventPacketData data = EventPacketData(
+        eventName: 'message',
+        arguments: EventArguments(['Hello', 42]),
+      );
+
+      final List list = data.toList();
+
+      expect(list, ['message', 'Hello', 42]);
+    });
+
+    test('toJson returns list format', () {
+      final EventPacketData data = EventPacketData(
+        eventName: 'message',
+        arguments: EventArguments(['Hello']),
+      );
+
+      final List json = data.toJson();
+
+      expect(json, ['message', 'Hello']);
+    });
+
+    test('equality works correctly', () {
+      final EventPacketData data1 = EventPacketData(
+        eventName: 'message',
+        arguments: EventArguments(['Hello']),
+      );
+      final EventPacketData data2 = EventPacketData(
+        eventName: 'message',
+        arguments: EventArguments(['Hello']),
+      );
+      final EventPacketData data3 = EventPacketData(
+        eventName: 'different',
+        arguments: EventArguments(['Hello']),
+      );
+
+      expect(data1, equals(data2));
+      expect(data1, isNot(equals(data3)));
+    });
+
+    test('toString provides meaningful output', () {
+      final EventPacketData data = EventPacketData(
+        eventName: 'message',
+        arguments: EventArguments(['Hello', 42]),
+      );
+
+      final String str = data.toString();
+      expect(str, contains('EventPacketData'));
+      expect(str, contains('message'));
+      expect(str, contains('2 args'));
+    });
+  });
+
+  group('AckPacketData', () {
+    test('creates with arguments', () {
+      final AckPacketData data = AckPacketData(
+        arguments: EventArguments(['success', 200]),
+      );
+
+      expect(data.arguments.length, 2);
+      expect(data.arguments[0], 'success');
+      expect(data.arguments[1], 200);
+    });
+
+    test('fromList creates correctly', () {
+      final AckPacketData data = AckPacketData.fromList(['success', 200]);
+
+      expect(data.arguments.length, 2);
+      expect(data.arguments[0], 'success');
+      expect(data.arguments[1], 200);
+    });
+
+    test('empty factory creates correctly', () {
+      final AckPacketData data = AckPacketData.empty();
+
+      expect(data.arguments.isEmpty, isTrue);
+    });
+
+    test('single factory creates correctly', () {
+      final AckPacketData data = AckPacketData.single('ok');
+
+      expect(data.arguments.length, 1);
+      expect(data.arguments[0], 'ok');
+    });
+
+    test('success factory with no data', () {
+      final AckPacketData data = AckPacketData.success();
+
+      expect(data.arguments.isEmpty, isTrue);
+    });
+
+    test('success factory with data', () {
+      final AckPacketData data = AckPacketData.success('ok');
+
+      expect(data.arguments.length, 1);
+      expect(data.arguments[0], 'ok');
+    });
+
+    test('error factory creates correctly', () {
+      final AckPacketData data = AckPacketData.error('Something went wrong');
+
+      expect(data.arguments.length, 1);
+      expect(data.arguments[0], 'Something went wrong');
+    });
+
+    test('toList returns arguments', () {
+      final AckPacketData data = AckPacketData(
+        arguments: EventArguments(['success', 200]),
+      );
+
+      final List list = data.toList();
+
+      expect(list, ['success', 200]);
+    });
+
+    test('equality works correctly', () {
+      final AckPacketData data1 = AckPacketData(
+        arguments: EventArguments(['success']),
+      );
+      final AckPacketData data2 = AckPacketData(
+        arguments: EventArguments(['success']),
+      );
+      final AckPacketData data3 = AckPacketData(
+        arguments: EventArguments(['error']),
+      );
+
+      expect(data1, equals(data2));
+      expect(data1, isNot(equals(data3)));
+    });
+  });
+
+  group('ConnectErrorPacketData', () {
+    test('creates with message', () {
+      const ConnectErrorPacketData data = ConnectErrorPacketData(message: 'Authentication failed');
+
+      expect(data.message, 'Authentication failed');
+      expect(data.code, isNull);
+      expect(data.details, isNull);
+    });
+
+    test('creates with all fields', () {
+      final ConnectErrorPacketData data = ConnectErrorPacketData(
+        message: 'Authentication failed',
+        code: 'AUTH_ERROR',
+        details: {'reason': 'Invalid token'},
+      );
+
+      expect(data.message, 'Authentication failed');
+      expect(data.code, 'AUTH_ERROR');
+      expect(data.details, {'reason': 'Invalid token'});
+    });
+
+    test('fromMessage factory creates correctly', () {
+      final ConnectErrorPacketData data = ConnectErrorPacketData.fromMessage('Connection failed');
+
+      expect(data.message, 'Connection failed');
+      expect(data.code, isNull);
+    });
+
+    test('fromJson handles string', () {
+      final ConnectErrorPacketData data = ConnectErrorPacketData.fromJson('Authentication failed');
+
+      expect(data.message, 'Authentication failed');
+    });
+
+    test('fromJson handles map', () {
+      final ConnectErrorPacketData data = ConnectErrorPacketData.fromJson(
+        {
+          'message': 'Authentication failed',
+          'code': 'AUTH_ERROR',
+        },
+      );
+
+      expect(data.message, 'Authentication failed');
+      expect(data.code, 'AUTH_ERROR');
+    });
+
+    test('fromJson handles null', () {
+      final ConnectErrorPacketData data = ConnectErrorPacketData.fromJson(null);
+
+      expect(data.message, 'Unknown error');
+    });
+
+    test('toJson returns string for simple case', () {
+      const ConnectErrorPacketData data = ConnectErrorPacketData(message: 'Authentication failed');
+
+      final Object json = data.toJson();
+
+      expect(json, 'Authentication failed');
+    });
+
+    test('toJson returns map for complex case', () {
+      const ConnectErrorPacketData data = ConnectErrorPacketData(
+        message: 'Authentication failed',
+        code: 'AUTH_ERROR',
+      );
+
+      final Object json = data.toJson();
+
+      expect(json, isA>());
+      expect(
+        (json as Map)['message'],
+        'Authentication failed',
+      );
+      expect(json['code'], 'AUTH_ERROR');
+    });
+
+    test('equality works correctly', () {
+      const ConnectErrorPacketData data1 = ConnectErrorPacketData(message: 'Authentication failed');
+      const ConnectErrorPacketData data2 = ConnectErrorPacketData(message: 'Authentication failed');
+      const ConnectErrorPacketData data3 = ConnectErrorPacketData(message: 'Connection refused');
+
+      expect(data1, equals(data2));
+      expect(data1, isNot(equals(data3)));
+    });
+
+    test('toString provides meaningful output', () {
+      const ConnectErrorPacketData data = ConnectErrorPacketData(message: 'Authentication failed');
+
+      final String str = data.toString();
+      expect(str, contains('ConnectErrorPacketData'));
+      expect(str, contains('Authentication failed'));
+    });
+  });
+}
diff --git a/test/polling_transport_integration_test.dart b/test/polling_transport_integration_test.dart
new file mode 100644
index 0000000..0d8dd8b
--- /dev/null
+++ b/test/polling_transport_integration_test.dart
@@ -0,0 +1,170 @@
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:socket_io/socket_io.dart' as sio;
+import 'package:socket_io_common/socket_io_common.dart' show SEPARATOR;
+import 'package:test/test.dart';
+
+void main() {
+  group('Polling transport integration', () {
+    late HttpClient client;
+    late sio.Server server;
+    late Uri baseUri;
+
+    setUp(() async {
+      client = HttpClient();
+      server = sio.Server();
+
+      server.onConnection((final sio.Socket socket) {
+        socket.on('msg', (final sio.SocketIOEventData data) {
+          socket.emit('fromServer', 'Message received: $data');
+        });
+      });
+
+      await server.listen(0);
+      baseUri = Uri(
+        scheme: 'http',
+        host: '127.0.0.1',
+        port: server.port!,
+        path: '/socket.io/',
+      );
+    });
+
+    tearDown(() async {
+      client.close(force: true);
+      await server.close();
+    });
+
+    test('decodes record-separator-delimited Engine.IO v4 payloads', () async {
+      final String sid = await _openPollingSession(client, baseUri);
+
+      final HttpClientResponse connectResponse = await _postPollingPayload(client, baseUri, sid, '40');
+      expect(connectResponse.statusCode, equals(HttpStatus.ok));
+      await connectResponse.drain();
+
+      final HttpClientResponse eventResponse = await _postPollingPayload(
+        client,
+        baseUri,
+        sid,
+        '42["msg","first"]${SEPARATOR}42["msg","second"]',
+      );
+      expect(eventResponse.statusCode, equals(HttpStatus.ok));
+      await eventResponse.drain();
+
+      final String pollData = await _pollForMessages(client, baseUri, sid);
+      expect(pollData, contains('Message received: first'));
+      expect(pollData, contains('Message received: second'));
+    });
+
+    test('decodes concatenated polling payloads as a compatibility fallback', () async {
+      final String sid = await _openPollingSession(client, baseUri);
+
+      final HttpClientResponse connectResponse = await _postPollingPayload(client, baseUri, sid, '40');
+      expect(connectResponse.statusCode, equals(HttpStatus.ok));
+      await connectResponse.drain();
+
+      final HttpClientResponse eventResponse = await _postPollingPayload(
+        client,
+        baseUri,
+        sid,
+        '42["msg","first"]42["msg","second"]',
+      );
+      expect(eventResponse.statusCode, equals(HttpStatus.ok));
+      await eventResponse.drain();
+
+      final String pollData = await _pollForMessages(client, baseUri, sid);
+      expect(pollData, contains('Message received: first'));
+      expect(pollData, contains('Message received: second'));
+    });
+
+    test('answers polling CORS preflight requests', () async {
+      final HttpClientRequest request = await client.openUrl(
+        'OPTIONS',
+        _pollingUri(baseUri),
+      );
+      request.headers
+        ..add('origin', 'https://example.com')
+        ..add('Access-Control-Request-Method', 'POST')
+        ..add('Access-Control-Request-Headers', 'Content-Type');
+
+      final HttpClientResponse response = await request.close();
+      final String body = await utf8.decoder.bind(response).join();
+
+      expect(response.statusCode, equals(HttpStatus.ok));
+      expect(body, isEmpty);
+      expect(
+        response.headers.value('Access-Control-Allow-Origin'),
+        equals('https://example.com'),
+      );
+      expect(
+        response.headers.value('Access-Control-Allow-Credentials'),
+        equals('true'),
+      );
+      expect(
+        response.headers.value('Access-Control-Allow-Methods'),
+        contains('OPTIONS'),
+      );
+      expect(
+        response.headers.value('Access-Control-Allow-Headers'),
+        contains('Content-Type'),
+      );
+      expect(
+        response.headers.value(HttpHeaders.cacheControlHeader),
+        contains('no-cache'),
+      );
+    });
+  });
+}
+
+Future _openPollingSession(
+  final HttpClient client,
+  final Uri baseUri,
+) async {
+  final HttpClientRequest request = await client.getUrl(_pollingUri(baseUri));
+  final HttpClientResponse response = await request.close();
+  final String body = await utf8.decoder.bind(response).join();
+
+  expect(response.statusCode, equals(HttpStatus.ok));
+
+  final Match? sidMatch = RegExp(r'"sid":"([^"]+)"').firstMatch(body);
+  expect(sidMatch, isNotNull, reason: 'Missing sid in handshake: $body');
+
+  return sidMatch!.group(1)!;
+}
+
+Future _postPollingPayload(
+  final HttpClient client,
+  final Uri baseUri,
+  final String sid,
+  final String payload,
+) async {
+  final HttpClientRequest request = await client.postUrl(_pollingUri(baseUri, sid: sid));
+  request.headers.contentType = ContentType('text', 'plain', charset: 'utf-8');
+  request.write(payload);
+  return request.close();
+}
+
+Future _pollForMessages(
+  final HttpClient client,
+  final Uri baseUri,
+  final String sid,
+) async {
+  final HttpClientRequest request = await client.getUrl(_pollingUri(baseUri, sid: sid));
+  final HttpClientResponse response = await request.close();
+  final String body = await utf8.decoder.bind(response).join();
+
+  expect(response.statusCode, equals(HttpStatus.ok));
+  return body;
+}
+
+Uri _pollingUri(
+  final Uri baseUri, {
+  final String? sid,
+}) {
+  return baseUri.replace(queryParameters: {
+    'EIO': '4',
+    'transport': 'polling',
+    't': DateTime.now().microsecondsSinceEpoch.toString(),
+    if (sid != null) 'sid': sid,
+  });
+}
diff --git a/test/socket.test.dart b/test/socket.test.dart
index bc9b5f1..7e7acaf 100644
--- a/test/socket.test.dart
+++ b/test/socket.test.dart
@@ -1,36 +1,37 @@
-/// socket.test.dart
-///
-/// Purpose:
-///
-/// Description:
-///
-/// History:
-///    16/02/2017, Created by jumperchen
-///
-/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
+// socket.test.dart
+//
+// Purpose:
+//
+// Description:
+//
+// History:
+//    16/02/2017, Created by jumperchen
+//
+// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
 import 'package:test/test.dart';
-
 import 'package:socket_io/socket_io.dart';
 
 void main() {
   group('Socket IO', () {
     test('Start standalone server', () async {
-      var io = Server();
-      var nsp = io.of('/some');
-      nsp.on('connection', (client) {
+      final Server io = Server();
+
+      io.of('/some').onConnection((final Socket client) {
         print('connection /some');
-        client.on('msg', (data) {
+        client.on('msg', (final SocketIOEventData data) {
           print('data from /some => $data');
           client.emit('fromServer', 'ok 2');
         });
       });
-      io.on('connection', (client) {
+
+      io.onConnection((final Socket client) {
         print('connection default namespace');
-        client.on('msg', (data) {
+        client.on('msg', (final SocketIOEventData data) {
           print('data from default => $data');
           client.emit('fromServer', 'ok');
         });
       });
+
       await io.listen(3000);
     });
   });
diff --git a/test/socket_state_vo_test.dart b/test/socket_state_vo_test.dart
new file mode 100644
index 0000000..7534771
--- /dev/null
+++ b/test/socket_state_vo_test.dart
@@ -0,0 +1,132 @@
+import 'package:test/test.dart';
+import 'package:socket_io/src/value_objects/socket_state_vo.dart';
+
+void main() {
+  group('SocketState', () {
+    group('enum values', () {
+      test('should have correct string values', () {
+        expect(SocketState.connecting.value, equals('connecting'));
+        expect(SocketState.connected.value, equals('connected'));
+        expect(SocketState.disconnecting.value, equals('disconnecting'));
+        expect(SocketState.disconnected.value, equals('disconnected'));
+      });
+
+      test('should have correct toString() output', () {
+        expect(SocketState.connecting.toString(), equals('connecting'));
+        expect(SocketState.connected.toString(), equals('connected'));
+        expect(SocketState.disconnecting.toString(), equals('disconnecting'));
+        expect(SocketState.disconnected.toString(), equals('disconnected'));
+      });
+    });
+
+    group('fromString()', () {
+      test('should parse valid state strings', () {
+        expect(SocketState.fromString('connecting'), equals(SocketState.connecting));
+        expect(SocketState.fromString('connected'), equals(SocketState.connected));
+        expect(SocketState.fromString('disconnecting'), equals(SocketState.disconnecting));
+        expect(SocketState.fromString('disconnected'), equals(SocketState.disconnected));
+      });
+
+      test('should return null for invalid strings', () {
+        expect(SocketState.fromString('invalid'), isNull);
+        expect(SocketState.fromString(''), isNull);
+        expect(SocketState.fromString('CONNECTED'), isNull);
+        expect(SocketState.fromString(' connected'), isNull);
+      });
+    });
+
+    group('fromStringOrDefault()', () {
+      test('should parse valid state strings', () {
+        expect(
+          SocketState.fromStringOrDefault('connected', SocketState.disconnected),
+          equals(SocketState.connected),
+        );
+        expect(
+          SocketState.fromStringOrDefault('connecting', SocketState.disconnected),
+          equals(SocketState.connecting),
+        );
+      });
+
+      test('should return default for invalid strings', () {
+        expect(
+          SocketState.fromStringOrDefault('invalid', SocketState.disconnected),
+          equals(SocketState.disconnected),
+        );
+        expect(
+          SocketState.fromStringOrDefault('', SocketState.connected),
+          equals(SocketState.connected),
+        );
+      });
+    });
+
+    group('state check helpers', () {
+      test('isConnected should return true only for connected state', () {
+        expect(SocketState.connected.isConnected, isTrue);
+        expect(SocketState.connecting.isConnected, isFalse);
+        expect(SocketState.disconnecting.isConnected, isFalse);
+        expect(SocketState.disconnected.isConnected, isFalse);
+      });
+
+      test('isDisconnected should return true only for disconnected state', () {
+        expect(SocketState.disconnected.isDisconnected, isTrue);
+        expect(SocketState.connecting.isDisconnected, isFalse);
+        expect(SocketState.connected.isDisconnected, isFalse);
+        expect(SocketState.disconnecting.isDisconnected, isFalse);
+      });
+
+      test('isTransitioning should return true for connecting/disconnecting', () {
+        expect(SocketState.connecting.isTransitioning, isTrue);
+        expect(SocketState.disconnecting.isTransitioning, isTrue);
+        expect(SocketState.connected.isTransitioning, isFalse);
+        expect(SocketState.disconnected.isTransitioning, isFalse);
+      });
+
+      test('canCommunicate should return true only when connected', () {
+        expect(SocketState.connected.canCommunicate, isTrue);
+        expect(SocketState.connecting.canCommunicate, isFalse);
+        expect(SocketState.disconnecting.canCommunicate, isFalse);
+        expect(SocketState.disconnected.canCommunicate, isFalse);
+      });
+    });
+
+    group('exhaustive pattern matching', () {
+      test('should handle all states in switch', () {
+        String getStateMessage(final SocketState state) {
+          switch (state) {
+            case SocketState.connecting:
+              return 'Establishing connection...';
+            case SocketState.connected:
+              return 'Connected and ready';
+            case SocketState.disconnecting:
+              return 'Closing connection...';
+            case SocketState.disconnected:
+              return 'Not connected';
+          }
+        }
+
+        expect(getStateMessage(SocketState.connecting), equals('Establishing connection...'));
+        expect(getStateMessage(SocketState.connected), equals('Connected and ready'));
+        expect(getStateMessage(SocketState.disconnecting), equals('Closing connection...'));
+        expect(getStateMessage(SocketState.disconnected), equals('Not connected'));
+      });
+    });
+
+    group('state transitions', () {
+      test('should represent typical connection lifecycle', () {
+        final List lifecycle = [
+          SocketState.disconnected,
+          SocketState.connecting,
+          SocketState.connected,
+          SocketState.disconnecting,
+          SocketState.disconnected,
+        ];
+
+        expect(lifecycle[0].isDisconnected, isTrue);
+        expect(lifecycle[1].isTransitioning, isTrue);
+        expect(lifecycle[2].canCommunicate, isTrue);
+        expect(lifecycle[3].isTransitioning, isTrue);
+        expect(lifecycle[4].isDisconnected, isTrue);
+      });
+    });
+  });
+}
diff --git a/test/typed_event_emitter_test.dart b/test/typed_event_emitter_test.dart
new file mode 100644
index 0000000..9728925
--- /dev/null
+++ b/test/typed_event_emitter_test.dart
@@ -0,0 +1,188 @@
+/// Tests for TypedEventEmitter
+library;
+
+import 'package:test/test.dart';
+import 'package:socket_io/src/util/event_emitter.dart';
+
+void main() {
+  group('TypedEventEmitter', () {
+    late TypedEventEmitter emitter;
+
+    setUp(() {
+      emitter = TypedEventEmitter();
+    });
+
+    test('emits and receives string events', () {
+      String? received;
+      emitter.on('message', (final String data) {
+        received = data;
+      });
+      emitter.emit('message', 'Hello World');
+      expect(received, 'Hello World');
+    });
+
+    test('handles multiple listeners for same event', () {
+      final List received = [];
+      emitter.on('test', (final String data) => received.add('first: $data'));
+      emitter.on('test', (final String data) => received.add('second: $data'));
+      emitter.emit('test', 'data');
+      expect(received, ['first: data', 'second: data']);
+    });
+
+    test('once listener called only once', () {
+      int callCount = 0;
+      emitter.once('oneTime', (final String data) => callCount++);
+      emitter.emit('oneTime', 'first');
+      emitter.emit('oneTime', 'second');
+      expect(callCount, 1);
+    });
+
+    test('off removes specific handler', () {
+      final List received = [];
+      void handler(final String data) => received.add(data);
+      emitter.on('test', handler);
+      emitter.emit('test', 'before');
+      emitter.off('test', handler);
+      emitter.emit('test', 'after');
+      expect(received, ['before']);
+    });
+
+    test('off without handler removes all handlers', () {
+      int count = 0;
+      emitter.on('test', (final String _) => count++);
+      emitter.on('test', (final String _) => count++);
+      emitter.off('test');
+      emitter.emit('test', 'data');
+      expect(count, 0);
+    });
+
+    test('clearListeners removes all event listeners', () {
+      int count = 0;
+      emitter.on('event1', (final String _) => count++);
+      emitter.on('event2', (final String _) => count++);
+      emitter.clearListeners();
+      emitter.emit('event1', 'data');
+      emitter.emit('event2', 'data');
+      expect(count, 0);
+    });
+
+    test('hasListeners returns correct status', () {
+      expect(emitter.hasListeners('test'), false);
+      emitter.on('test', (final String _) {});
+      expect(emitter.hasListeners('test'), true);
+      emitter.off('test');
+      expect(emitter.hasListeners('test'), false);
+    });
+
+    test('hasListeners includes once listeners', () {
+      expect(emitter.hasListeners('test'), false);
+      emitter.once('test', (final String _) {});
+      expect(emitter.hasListeners('test'), true);
+    });
+  });
+
+  group('TypedEventEmitter', () {
+    late TypedEventEmitter emitter;
+
+    setUp(() {
+      emitter = TypedEventEmitter();
+    });
+
+    test('emits and receives int events', () {
+      int? received;
+      emitter.on('count', (final int data) {
+        received = data;
+      });
+      emitter.emit('count', 42);
+      expect(received, 42);
+    });
+
+    test('handles multiple int emissions', () {
+      final List received = [];
+      emitter.on('numbers', (final int data) => received.add(data));
+      emitter.emit('numbers', 1);
+      emitter.emit('numbers', 2);
+      emitter.emit('numbers', 3);
+      expect(received, [1, 2, 3]);
+    });
+  });
+
+  group('TypedEventEmitter>', () {
+    late TypedEventEmitter> emitter;
+
+    setUp(() {
+      emitter = TypedEventEmitter>();
+    });
+
+    test('emits and receives map events', () {
+      Map? received;
+      emitter.on('data', (final Map data) {
+        received = data;
+      });
+      final Map testData = {'key': 'value', 'num': 123};
+      emitter.emit('data', testData);
+      expect(received, testData);
+    });
+
+    test('handles complex nested maps', () {
+      Map? received;
+      emitter.on('nested', (final Map data) {
+        received = data;
+      });
+      final Map nested = {
+        'user': {'name': 'John', 'age': 30},
+        'items': [1, 2, 3],
+      };
+      emitter.emit('nested', nested);
+      expect(received, nested);
+    });
+  });
+
+  group('TypedEventEmitter edge cases', () {
+    test('handler can be removed during emission', () {
+      final TypedEventEmitter emitter = TypedEventEmitter();
+      int count = 0;
+
+      void handler(final String data) {
+        count++;
+        emitter.off('test', handler);
+      }
+
+      emitter.on('test', handler);
+      emitter.on('test', (final String _) => count++);
+
+      emitter.emit('test', 'data');
+      expect(count, 2);
+
+      emitter.emit('test', 'data');
+      expect(count, 3); // Only second handler called
+    });
+
+    test('multiple once handlers all called once', () {
+      final TypedEventEmitter emitter = TypedEventEmitter();
+      int count1 = 0;
+      int count2 = 0;
+
+      emitter.once('test', (final String _) => count1++);
+      emitter.once('test', (final String _) => count2++);
+
+      emitter.emit('test', 'data');
+      expect(count1, 1);
+      expect(count2, 1);
+
+      emitter.emit('test', 'data');
+      expect(count1, 1);
+      expect(count2, 1);
+    });
+
+    test('off on non-existent event does not throw', () {
+      final TypedEventEmitter emitter = TypedEventEmitter();
+      expect(() => emitter.off('nonExistent'), returnsNormally);
+    });
+
+    test('emit on event with no listeners does not throw', () {
+      final TypedEventEmitter emitter = TypedEventEmitter();
+      expect(() => emitter.emit('nonExistent', 'data'), returnsNormally);
+    });
+  });
+}
diff --git a/test/value_objects/connection_id_vo_test.dart b/test/value_objects/connection_id_vo_test.dart
new file mode 100644
index 0000000..6f1eea7
--- /dev/null
+++ b/test/value_objects/connection_id_vo_test.dart
@@ -0,0 +1,37 @@
+import 'package:test/test.dart';
+import 'package:socket_io/src/value_objects/connection_id_vo.dart';
+
+void main() {
+  group('ConnectionId', () {
+    test('creates valid ConnectionId from non-empty string', () {
+      final ConnectionId id = ConnectionId('test-id-123');
+      expect(id.value, equals('test-id-123'));
+      expect(id.toString(), equals('test-id-123'));
+    });
+
+    test('throws ArgumentError for empty string', () {
+      expect(() => ConnectionId(''), throwsArgumentError);
+    });
+
+    test('equality works correctly', () {
+      final ConnectionId id1 = ConnectionId('same-id');
+      final ConnectionId id2 = ConnectionId('same-id');
+      final ConnectionId id3 = ConnectionId('different-id');
+
+      expect(id1, equals(id2));
+      expect(id1, isNot(equals(id3)));
+    });
+
+    test('hashCode works correctly', () {
+      final ConnectionId id1 = ConnectionId('same-id');
+      final ConnectionId id2 = ConnectionId('same-id');
+
+      expect(id1.hashCode, equals(id2.hashCode));
+    });
+
+    test('unchecked constructor allows any value', () {
+      const ConnectionId id = ConnectionId.unchecked('');
+      expect(id.value, equals(''));
+    });
+  });
+}
diff --git a/test/value_objects/disconnect_reason_vo_test.dart b/test/value_objects/disconnect_reason_vo_test.dart
new file mode 100644
index 0000000..64359c5
--- /dev/null
+++ b/test/value_objects/disconnect_reason_vo_test.dart
@@ -0,0 +1,113 @@
+import 'package:test/test.dart';
+import 'package:socket_io/src/value_objects/disconnect_reason_vo.dart';
+
+void main() {
+  group('DisconnectReasonType', () {
+    test('enum has all expected values', () {
+      expect(DisconnectReasonType.values.length, equals(10));
+      expect(DisconnectReasonType.values, contains(DisconnectReasonType.clientDisconnect));
+      expect(DisconnectReasonType.values, contains(DisconnectReasonType.serverDisconnect));
+      expect(DisconnectReasonType.values, contains(DisconnectReasonType.pingTimeout));
+    });
+
+    test('value returns correct string', () {
+      expect(DisconnectReasonType.clientDisconnect.value, equals('client disconnect'));
+      expect(DisconnectReasonType.serverDisconnect.value, equals('server disconnect'));
+      expect(DisconnectReasonType.pingTimeout.value, equals('ping timeout'));
+      expect(DisconnectReasonType.transportClose.value, equals('transport close'));
+    });
+
+    test('fromString creates correct type', () {
+      expect(DisconnectReasonTypeHelper.fromString('client disconnect'), equals(DisconnectReasonType.clientDisconnect));
+      expect(DisconnectReasonTypeHelper.fromString('server disconnect'), equals(DisconnectReasonType.serverDisconnect));
+      expect(DisconnectReasonTypeHelper.fromString('ping timeout'), equals(DisconnectReasonType.pingTimeout));
+    });
+
+    test('fromString is case insensitive', () {
+      expect(DisconnectReasonTypeHelper.fromString('CLIENT DISCONNECT'), equals(DisconnectReasonType.clientDisconnect));
+      expect(DisconnectReasonTypeHelper.fromString('Ping Timeout'), equals(DisconnectReasonType.pingTimeout));
+    });
+
+    test('fromString throws for invalid reason', () {
+      expect(() => DisconnectReasonTypeHelper.fromString('invalid reason'), throwsArgumentError);
+    });
+  });
+
+  group('DisconnectReason', () {
+    test('creates from DisconnectReasonType', () {
+      const DisconnectReason reason = DisconnectReason(DisconnectReasonType.clientDisconnect);
+      expect(reason.type, equals(DisconnectReasonType.clientDisconnect));
+      expect(reason.value, equals('client disconnect'));
+      expect(reason.details, isNull);
+    });
+
+    test('creates with details', () {
+      const DisconnectReason reason = DisconnectReason(DisconnectReasonType.transportError, 'Connection lost');
+      expect(reason.type, equals(DisconnectReasonType.transportError));
+      expect(reason.details, equals('Connection lost'));
+      expect(reason.value, equals('Connection lost'));
+    });
+
+    test('creates from standard string', () {
+      final DisconnectReason reason = DisconnectReason.fromString('ping timeout');
+      expect(reason.type, equals(DisconnectReasonType.pingTimeout));
+      expect(reason.value, equals('ping timeout'));
+    });
+
+    test('creates from non-standard string', () {
+      final DisconnectReason reason = DisconnectReason.fromString('custom error');
+      expect(reason.type, equals(DisconnectReasonType.serverDisconnect));
+      expect(reason.details, equals('custom error'));
+      expect(reason.value, equals('custom error'));
+    });
+
+    test('has static constants', () {
+      expect(DisconnectReason.clientDisconnect.type, equals(DisconnectReasonType.clientDisconnect));
+      expect(DisconnectReason.serverDisconnect.type, equals(DisconnectReasonType.serverDisconnect));
+      expect(DisconnectReason.pingTimeout.type, equals(DisconnectReasonType.pingTimeout));
+      expect(DisconnectReason.transportClose.type, equals(DisconnectReasonType.transportClose));
+      expect(DisconnectReason.transportError.type, equals(DisconnectReasonType.transportError));
+    });
+
+    test('equality works correctly', () {
+      const DisconnectReason reason1 = DisconnectReason(DisconnectReasonType.clientDisconnect);
+      const DisconnectReason reason2 = DisconnectReason(DisconnectReasonType.clientDisconnect);
+      const DisconnectReason reason3 = DisconnectReason(DisconnectReasonType.serverDisconnect);
+
+      expect(reason1, equals(reason2));
+      expect(reason1, isNot(equals(reason3)));
+    });
+
+    test('equality considers details', () {
+      const DisconnectReason reason1 = DisconnectReason(DisconnectReasonType.transportError, 'Error 1');
+      const DisconnectReason reason2 = DisconnectReason(DisconnectReasonType.transportError, 'Error 1');
+      const DisconnectReason reason3 = DisconnectReason(DisconnectReasonType.transportError, 'Error 2');
+
+      expect(reason1, equals(reason2));
+      expect(reason1, isNot(equals(reason3)));
+    });
+
+    test('hashCode works correctly', () {
+      const DisconnectReason reason1 = DisconnectReason(DisconnectReasonType.clientDisconnect);
+      const DisconnectReason reason2 = DisconnectReason(DisconnectReasonType.clientDisconnect);
+
+      expect(reason1.hashCode, equals(reason2.hashCode));
+    });
+
+    test('toString returns value', () {
+      const DisconnectReason reason = DisconnectReason(DisconnectReasonType.pingTimeout);
+      expect(reason.toString(), equals('ping timeout'));
+    });
+
+    test('toString returns details when provided', () {
+      const DisconnectReason reason = DisconnectReason(DisconnectReasonType.serverDisconnect, 'Custom message');
+      expect(reason.toString(), equals('Custom message'));
+    });
+
+    test('all enum types covered', () {
+      for (final DisconnectReasonType type in DisconnectReasonType.values) {
+        expect(type.value, isNotEmpty);
+      }
+    });
+  });
+}
diff --git a/test/value_objects/error_code_vo_test.dart b/test/value_objects/error_code_vo_test.dart
new file mode 100644
index 0000000..fb042f7
--- /dev/null
+++ b/test/value_objects/error_code_vo_test.dart
@@ -0,0 +1,233 @@
+import 'package:test/test.dart';
+import 'package:socket_io/src/value_objects/error_code_vo.dart';
+
+void main() {
+  group('ErrorCode', () {
+    group('factory constructor', () {
+      test('creates numeric code from numeric string', () {
+        final ErrorCode code = ErrorCode('1001');
+        expect(code.value, equals('1001'));
+        expect(code.isNumeric, isTrue);
+        expect(code.isString, isFalse);
+        expect(code.numericValue, equals(1001));
+      });
+
+      test('creates string code from alphabetic string', () {
+        final ErrorCode code = ErrorCode('AUTH_ERROR');
+        expect(code.value, equals('AUTH_ERROR'));
+        expect(code.isNumeric, isFalse);
+        expect(code.isString, isTrue);
+        expect(code.numericValue, isNull);
+      });
+
+      test('normalizes string code to uppercase', () {
+        final ErrorCode code = ErrorCode('auth_error');
+        expect(code.value, equals('AUTH_ERROR'));
+      });
+
+      test('accepts string codes with hyphens', () {
+        final ErrorCode code = ErrorCode('AUTH-ERROR');
+        expect(code.value, equals('AUTH-ERROR'));
+      });
+
+      test('accepts string codes with numbers', () {
+        final ErrorCode code = ErrorCode('ERROR_404');
+        expect(code.value, equals('ERROR_404'));
+      });
+
+      test('throws for empty string', () {
+        expect(() => ErrorCode(''), throwsArgumentError);
+      });
+
+      test('throws for whitespace-only string', () {
+        expect(() => ErrorCode('   '), throwsArgumentError);
+      });
+
+      test('throws for invalid characters in string code', () {
+        expect(() => ErrorCode('error.code'), throwsArgumentError);
+        expect(() => ErrorCode('error code'), throwsArgumentError);
+        expect(() => ErrorCode('error@code'), throwsArgumentError);
+      });
+
+      test('trims whitespace from input', () {
+        final ErrorCode code = ErrorCode('  1001  ');
+        expect(code.value, equals('1001'));
+      });
+    });
+
+    group('numeric factory', () {
+      test('creates numeric code', () {
+        final ErrorCode code = ErrorCode.numeric(1001);
+        expect(code.value, equals('1001'));
+        expect(code.isNumeric, isTrue);
+        expect(code.numericValue, equals(1001));
+      });
+
+      test('accepts zero', () {
+        final ErrorCode code = ErrorCode.numeric(0);
+        expect(code.value, equals('0'));
+        expect(code.numericValue, equals(0));
+      });
+
+      test('throws for negative numbers', () {
+        expect(() => ErrorCode.numeric(-1), throwsArgumentError);
+      });
+    });
+
+    group('string factory', () {
+      test('creates string code', () {
+        final ErrorCode code = ErrorCode.string('AUTH_ERROR');
+        expect(code.value, equals('AUTH_ERROR'));
+        expect(code.isString, isTrue);
+        expect(code.numericValue, isNull);
+      });
+
+      test('normalizes to uppercase', () {
+        final ErrorCode code = ErrorCode.string('auth_error');
+        expect(code.value, equals('AUTH_ERROR'));
+      });
+
+      test('accepts codes with hyphens', () {
+        final ErrorCode code = ErrorCode.string('AUTH-ERROR');
+        expect(code.value, equals('AUTH-ERROR'));
+      });
+
+      test('accepts codes with numbers', () {
+        final ErrorCode code = ErrorCode.string('ERROR404');
+        expect(code.value, equals('ERROR404'));
+      });
+
+      test('throws for empty string', () {
+        expect(() => ErrorCode.string(''), throwsArgumentError);
+      });
+
+      test('throws for whitespace-only string', () {
+        expect(() => ErrorCode.string('   '), throwsArgumentError);
+      });
+
+      test('throws for invalid characters', () {
+        expect(() => ErrorCode.string('error.code'), throwsArgumentError);
+        expect(() => ErrorCode.string('error code'), throwsArgumentError);
+      });
+
+      test('trims whitespace', () {
+        final ErrorCode code = ErrorCode.string('  AUTH_ERROR  ');
+        expect(code.value, equals('AUTH_ERROR'));
+      });
+    });
+
+    group('unchecked constructor', () {
+      test('creates code without validation', () {
+        final ErrorCode code = ErrorCode.unchecked('any value');
+        expect(code.value, equals('any value'));
+        expect(code.isNumeric, isFalse);
+      });
+    });
+
+    group('common numeric codes', () {
+      test('connection codes are defined', () {
+        expect(ErrorCode.connectionTimeout.value, equals('1001'));
+        expect(ErrorCode.connectionRefused.value, equals('1002'));
+        expect(ErrorCode.connectionLost.value, equals('1003'));
+        expect(ErrorCode.connectionFailed.value, equals('1004'));
+      });
+
+      test('transport codes are defined', () {
+        expect(ErrorCode.transportError.value, equals('2001'));
+        expect(ErrorCode.transportClosed.value, equals('2002'));
+        expect(ErrorCode.transportTimeout.value, equals('2003'));
+        expect(ErrorCode.transportFailed.value, equals('2004'));
+      });
+
+      test('protocol codes are defined', () {
+        expect(ErrorCode.protocolError.value, equals('3001'));
+        expect(ErrorCode.invalidPacket.value, equals('3002'));
+      });
+
+      test('auth codes are defined', () {
+        expect(ErrorCode.authenticationFailed.value, equals('4001'));
+        expect(ErrorCode.authorizationFailed.value, equals('4002'));
+        expect(ErrorCode.invalidCredentials.value, equals('4003'));
+      });
+
+      test('unknown code is defined', () {
+        expect(ErrorCode.unknown.value, equals('9999'));
+      });
+    });
+
+    group('common string codes', () {
+      test('string codes are defined', () {
+        expect(ErrorCode.authError.value, equals('AUTH_ERROR'));
+        expect(ErrorCode.timeoutError.value, equals('TIMEOUT_ERROR'));
+        expect(ErrorCode.networkError.value, equals('NETWORK_ERROR'));
+        expect(ErrorCode.parseError.value, equals('PARSE_ERROR'));
+        expect(ErrorCode.invalidData.value, equals('INVALID_DATA'));
+      });
+    });
+
+    group('equality', () {
+      test('equal codes are equal', () {
+        final ErrorCode code1 = ErrorCode('1001');
+        final ErrorCode code2 = ErrorCode.numeric(1001);
+        expect(code1, equals(code2));
+      });
+
+      test('different codes are not equal', () {
+        final ErrorCode code1 = ErrorCode('1001');
+        final ErrorCode code2 = ErrorCode('1002');
+        expect(code1, isNot(equals(code2)));
+      });
+
+      test('numeric and string codes with same value are not equal', () {
+        final ErrorCode code1 = ErrorCode('AUTH_ERROR');
+        final ErrorCode code2 = ErrorCode.string('AUTH_ERROR');
+        expect(code1, equals(code2));
+      });
+    });
+
+    group('hashCode', () {
+      test('equal codes have same hashCode', () {
+        final ErrorCode code1 = ErrorCode('1001');
+        final ErrorCode code2 = ErrorCode.numeric(1001);
+        expect(code1.hashCode, equals(code2.hashCode));
+      });
+    });
+
+    group('toString', () {
+      test('provides meaningful representation', () {
+        final ErrorCode code = ErrorCode('1001');
+        expect(code.toString(), contains('ErrorCode'));
+        expect(code.toString(), contains('1001'));
+      });
+    });
+
+    group('edge cases', () {
+      test('handles large numeric codes', () {
+        final ErrorCode code = ErrorCode.numeric(99999);
+        expect(code.value, equals('99999'));
+        expect(code.numericValue, equals(99999));
+      });
+
+      test('handles single character string codes', () {
+        final ErrorCode code = ErrorCode.string('E');
+        expect(code.value, equals('E'));
+      });
+
+      test('handles long string codes', () {
+        final String longCode = 'VERY_LONG_ERROR_CODE_NAME_FOR_TESTING';
+        final ErrorCode code = ErrorCode.string(longCode);
+        expect(code.value, equals(longCode));
+      });
+
+      test('handles codes with only underscores', () {
+        final ErrorCode code = ErrorCode.string('___');
+        expect(code.value, equals('___'));
+      });
+
+      test('handles codes with only hyphens', () {
+        final ErrorCode code = ErrorCode.string('---');
+        expect(code.value, equals('---'));
+      });
+    });
+  });
+}
diff --git a/test/value_objects/event_arguments_vo_test.dart b/test/value_objects/event_arguments_vo_test.dart
new file mode 100644
index 0000000..c23643c
--- /dev/null
+++ b/test/value_objects/event_arguments_vo_test.dart
@@ -0,0 +1,355 @@
+import 'package:test/test.dart';
+import 'package:socket_io/src/models/event_data_models.dart';
+import 'package:socket_io/src/value_objects/event_arguments_vo.dart';
+
+void main() {
+  group('EventArguments', () {
+    group('factory constructors', () {
+      test('creates from list', () {
+        final List list = ['hello', 42, true];
+        final EventArguments args = EventArguments(list);
+
+        expect(args.length, equals(3));
+        expect(args[0], equals('hello'));
+        expect(args[1], equals(42));
+        expect(args[2], equals(true));
+      });
+
+      test('creates empty', () {
+        final EventArguments args = EventArguments.empty();
+
+        expect(args.isEmpty, isTrue);
+        expect(args.length, equals(0));
+      });
+
+      test('creates single', () {
+        final EventArguments args = EventArguments.single('test');
+
+        expect(args.length, equals(1));
+        expect(args[0], equals('test'));
+      });
+
+      test('creates multiple', () {
+        final EventArguments args = EventArguments.multiple([1, 2, 3]);
+
+        expect(args.length, equals(3));
+        expect(args[0], equals(1));
+      });
+
+      test('creates from EventData', () {
+        final List eventDataList = [
+          StringEventData('hello'),
+          NumericEventData(42),
+          BooleanEventData(true),
+        ];
+        final EventArguments args = EventArguments.fromEventData(eventDataList);
+
+        expect(args.length, equals(3));
+        expect(args[0], equals('hello'));
+        expect(args[1], equals(42));
+        expect(args[2], equals(true));
+      });
+
+      test('creates immutable copy', () {
+        final List list = ['test'];
+        final EventArguments args = EventArguments(list);
+
+        // Modify original list
+        list.add('new');
+
+        // EventArguments should not be affected
+        expect(args.length, equals(1));
+      });
+    });
+
+    group('basic properties', () {
+      test('length returns correct count', () {
+        expect(EventArguments.empty().length, equals(0));
+        expect(EventArguments(['a']).length, equals(1));
+        expect(EventArguments(['a', 'b']).length, equals(2));
+      });
+
+      test('isEmpty returns correct value', () {
+        expect(EventArguments.empty().isEmpty, isTrue);
+        expect(EventArguments(['a']).isEmpty, isFalse);
+      });
+
+      test('isNotEmpty returns correct value', () {
+        expect(EventArguments.empty().isNotEmpty, isFalse);
+        expect(EventArguments(['a']).isNotEmpty, isTrue);
+      });
+    });
+
+    group('indexer', () {
+      test('returns value at valid index', () {
+        final EventArguments args = EventArguments(['a', 'b', 'c']);
+
+        expect(args[0], equals('a'));
+        expect(args[1], equals('b'));
+        expect(args[2], equals('c'));
+      });
+
+      test('returns null for out of bounds index', () {
+        final EventArguments args = EventArguments(['a']);
+
+        expect(args[-1], isNull);
+        expect(args[1], isNull);
+        expect(args[100], isNull);
+      });
+    });
+
+    group('type-safe getters', () {
+      late EventArguments args;
+
+      setUp(() {
+        args = EventArguments([
+          'string',
+          42,
+          3.14,
+          true,
+          {'key': 'value'},
+          [1, 2, 3],
+          null,
+        ]);
+      });
+
+      test('getStringAt returns string', () {
+        expect(args.getStringAt(0), equals('string'));
+        expect(args.getStringAt(1), isNull); // Not a string
+        expect(args.getStringAt(10), isNull); // Out of bounds
+      });
+
+      test('getIntAt returns int', () {
+        expect(args.getIntAt(1), equals(42));
+        expect(args.getIntAt(0), isNull); // Not an int
+        expect(args.getIntAt(10), isNull); // Out of bounds
+      });
+
+      test('getIntAt converts num to int', () {
+        final EventArguments numArgs = EventArguments([3.14]);
+        expect(numArgs.getIntAt(0), equals(3));
+      });
+
+      test('getDoubleAt returns double', () {
+        expect(args.getDoubleAt(2), equals(3.14));
+        expect(args.getDoubleAt(0), isNull); // Not a number
+        expect(args.getDoubleAt(10), isNull); // Out of bounds
+      });
+
+      test('getDoubleAt converts num to double', () {
+        final EventArguments intArgs = EventArguments([42]);
+        expect(intArgs.getDoubleAt(0), equals(42.0));
+      });
+
+      test('getBoolAt returns bool', () {
+        expect(args.getBoolAt(3), equals(true));
+        expect(args.getBoolAt(0), isNull); // Not a bool
+        expect(args.getBoolAt(10), isNull); // Out of bounds
+      });
+
+      test('getMapAt returns map', () {
+        final Map? map = args.getMapAt(4);
+        expect(map, isNotNull);
+        expect(map!['key'], equals('value'));
+        expect(args.getMapAt(0), isNull); // Not a map
+        expect(args.getMapAt(10), isNull); // Out of bounds
+      });
+
+      test('getMapAt converts generic map', () {
+        final EventArguments mapArgs = EventArguments([
+          {'test': 123},
+        ]);
+        final Map? map = mapArgs.getMapAt(0);
+        expect(map, isNotNull);
+        expect(map!['test'], equals(123));
+      });
+
+      test('getListAt returns list', () {
+        final List? list = args.getListAt(5);
+        expect(list, isNotNull);
+        expect(list!.length, equals(3));
+        expect(args.getListAt(0), isNull); // Not a list
+        expect(args.getListAt(10), isNull); // Out of bounds
+      });
+
+      test('getListAt converts generic list', () {
+        final EventArguments listArgs = EventArguments([
+          [1, 2, 3],
+        ]);
+        final List? list = listArgs.getListAt(0);
+        expect(list, isNotNull);
+        expect(list!.length, equals(3));
+      });
+    });
+
+    group('conversion methods', () {
+      test('toEventData converts all arguments', () {
+        final EventArguments args = EventArguments(['test', 42, true]);
+        final List eventData = args.toEventData();
+
+        expect(eventData.length, equals(3));
+        expect(eventData[0], isA());
+        expect(eventData[1], isA());
+        expect(eventData[2], isA());
+      });
+
+      test('toList returns mutable copy', () {
+        final EventArguments args = EventArguments(['test']);
+        final List list = args.toList();
+
+        // Should be mutable
+        list.add('new');
+        expect(list.length, equals(2));
+
+        // Original should not be affected
+        expect(args.length, equals(1));
+      });
+
+      test('toJson returns list', () {
+        final EventArguments args = EventArguments(['test', 42]);
+        final List json = args.toJson();
+
+        expect(json, equals(['test', 42]));
+      });
+    });
+
+    group('equality', () {
+      test('equal arguments are equal', () {
+        final EventArguments args1 = EventArguments(['a', 1, true]);
+        final EventArguments args2 = EventArguments(['a', 1, true]);
+
+        expect(args1, equals(args2));
+        expect(args1.hashCode, equals(args2.hashCode));
+      });
+
+      test('different arguments are not equal', () {
+        final EventArguments args1 = EventArguments(['a', 1]);
+        final EventArguments args2 = EventArguments(['a', 2]);
+        final EventArguments args3 = EventArguments(['a']);
+
+        expect(args1, isNot(equals(args2)));
+        expect(args1, isNot(equals(args3)));
+      });
+
+      test('empty arguments are equal', () {
+        final EventArguments args1 = EventArguments.empty();
+        final EventArguments args2 = EventArguments.empty();
+
+        expect(args1, equals(args2));
+      });
+    });
+
+    group('toString', () {
+      test('includes argument count', () {
+        expect(EventArguments.empty().toString(), contains('0 args'));
+        expect(EventArguments(['a']).toString(), contains('1 args'));
+        expect(EventArguments(['a', 'b']).toString(), contains('2 args'));
+      });
+    });
+
+    group('extension methods', () {
+      late EventArguments args;
+
+      setUp(() {
+        args = EventArguments([1, 2, 3, 4, 5]);
+      });
+
+      test('map transforms arguments', () {
+        final List strings = args.map((final Object? o) => o.toString());
+        expect(strings, equals(['1', '2', '3', '4', '5']));
+      });
+
+      test('where filters arguments', () {
+        final List filtered = args.where((final Object? o) => o is int && o > 3);
+        expect(filtered, equals([4, 5]));
+      });
+
+      test('firstOrNull returns first or null', () {
+        expect(args.firstOrNull, equals(1));
+        expect(EventArguments.empty().firstOrNull, isNull);
+      });
+
+      test('lastOrNull returns last or null', () {
+        expect(args.lastOrNull, equals(5));
+        expect(EventArguments.empty().lastOrNull, isNull);
+      });
+
+      test('every checks all arguments', () {
+        expect(args.every((final Object? o) => o is int), isTrue);
+        expect(args.every((final Object? o) => o is int && o as int > 3), isFalse);
+      });
+
+      test('any checks for any matching argument', () {
+        expect(args.any((final Object? o) => o is int && o as int > 3), isTrue);
+        expect(args.any((final Object? o) => o is String), isFalse);
+      });
+
+      test('take returns first n arguments', () {
+        final EventArguments taken = args.take(3);
+        expect(taken.length, equals(3));
+        expect(taken[0], equals(1));
+        expect(taken[2], equals(3));
+      });
+
+      test('skip skips first n arguments', () {
+        final EventArguments skipped = args.skip(3);
+        expect(skipped.length, equals(2));
+        expect(skipped[0], equals(4));
+        expect(skipped[1], equals(5));
+      });
+    });
+
+    group('edge cases', () {
+      test('handles null values', () {
+        final EventArguments args = EventArguments([null, 'test', null]);
+
+        expect(args.length, equals(3));
+        expect(args[0], isNull);
+        expect(args[1], equals('test'));
+        expect(args[2], isNull);
+      });
+
+      test('handles mixed types', () {
+        final EventArguments args = EventArguments([
+          'string',
+          42,
+          3.14,
+          true,
+          {'key': 'value'},
+          [1, 2],
+          null,
+        ]);
+
+        expect(args.length, equals(7));
+        expect(args.getStringAt(0), equals('string'));
+        expect(args.getIntAt(1), equals(42));
+        expect(args.getDoubleAt(2), equals(3.14));
+        expect(args.getBoolAt(3), equals(true));
+        expect(args.getMapAt(4), isNotNull);
+        expect(args.getListAt(5), isNotNull);
+        expect(args[6], isNull);
+      });
+
+      test('handles nested structures', () {
+        final EventArguments args = EventArguments([
+          {
+            'nested': {'deep': 'value'},
+          },
+          [
+            [1, 2],
+            [3, 4],
+          ],
+        ]);
+
+        final Map? map = args.getMapAt(0);
+        expect(map, isNotNull);
+        expect(map!['nested'], isA());
+
+        final List? list = args.getListAt(1);
+        expect(list, isNotNull);
+        expect(list!.length, equals(2));
+        expect(list[0], isA());
+      });
+    });
+  });
+}
diff --git a/test/value_objects/event_name_vo_test.dart b/test/value_objects/event_name_vo_test.dart
new file mode 100644
index 0000000..26d5df6
--- /dev/null
+++ b/test/value_objects/event_name_vo_test.dart
@@ -0,0 +1,54 @@
+import 'package:test/test.dart';
+import 'package:socket_io/src/value_objects/event_name_vo.dart';
+
+void main() {
+  group('EventName', () {
+    test('creates valid EventName from non-empty string', () {
+      final EventName event = EventName('message');
+      expect(event.value, equals('message'));
+      expect(event.toString(), equals('message'));
+    });
+
+    test('accepts custom event names', () {
+      final EventName event = EventName('custom-event');
+      expect(event.value, equals('custom-event'));
+    });
+
+    test('throws ArgumentError for empty string', () {
+      expect(() => EventName(''), throwsArgumentError);
+    });
+
+    test('throws ArgumentError for reserved names', () {
+      expect(() => EventName('connect'), throwsArgumentError);
+      expect(() => EventName('disconnect'), throwsArgumentError);
+      expect(() => EventName('error'), throwsArgumentError);
+      expect(() => EventName('connect_error'), throwsArgumentError);
+    });
+
+    test('equality works correctly', () {
+      final EventName event1 = EventName('same-event');
+      final EventName event2 = EventName('same-event');
+      final EventName event3 = EventName('different-event');
+
+      expect(event1, equals(event2));
+      expect(event1, isNot(equals(event3)));
+    });
+
+    test('hashCode works correctly', () {
+      final EventName event1 = EventName('same-event');
+      final EventName event2 = EventName('same-event');
+
+      expect(event1.hashCode, equals(event2.hashCode));
+    });
+
+    test('unchecked constructor allows reserved names', () {
+      const EventName event = EventName.unchecked('connect');
+      expect(event.value, equals('connect'));
+    });
+
+    test('reserved names set is defined', () {
+      expect(EventName.reservedNames, isNotEmpty);
+      expect(EventName.reservedNames, contains('connect'));
+    });
+  });
+}
diff --git a/test/value_objects/namespace_name_vo_test.dart b/test/value_objects/namespace_name_vo_test.dart
new file mode 100644
index 0000000..3adaeed
--- /dev/null
+++ b/test/value_objects/namespace_name_vo_test.dart
@@ -0,0 +1,53 @@
+import 'package:test/test.dart';
+import 'package:socket_io/src/value_objects/namespace_name_vo.dart';
+
+void main() {
+  group('NamespaceName', () {
+    test('creates valid NamespaceName starting with /', () {
+      final NamespaceName ns = NamespaceName('/test');
+      expect(ns.value, equals('/test'));
+      expect(ns.toString(), equals('/test'));
+    });
+
+    test('accepts default namespace', () {
+      final NamespaceName ns = NamespaceName('/');
+      expect(ns.value, equals('/'));
+    });
+
+    test('accepts complex namespace paths', () {
+      final NamespaceName ns = NamespaceName('/chat/room-1');
+      expect(ns.value, equals('/chat/room-1'));
+    });
+
+    test('throws ArgumentError for empty string', () {
+      expect(() => NamespaceName(''), throwsArgumentError);
+    });
+
+    test('throws ArgumentError when not starting with /', () {
+      expect(() => NamespaceName('test'), throwsArgumentError);
+    });
+
+    test('throws ArgumentError for invalid characters', () {
+      expect(() => NamespaceName('/test space'), throwsArgumentError);
+      expect(() => NamespaceName('/test?query'), throwsArgumentError);
+    });
+
+    test('equality works correctly', () {
+      final NamespaceName ns1 = NamespaceName('/same');
+      final NamespaceName ns2 = NamespaceName('/same');
+      final NamespaceName ns3 = NamespaceName('/different');
+
+      expect(ns1, equals(ns2));
+      expect(ns1, isNot(equals(ns3)));
+    });
+
+    test('defaultNamespace constant is available', () {
+      expect(NamespaceName.defaultNamespace.value, equals('/'));
+    });
+
+    test('unchecked constructor allows any value', () {
+      const NamespaceName ns = NamespaceName.unchecked('invalid');
+      expect(ns.value, equals('invalid'));
+    });
+  });
+}
diff --git a/test/value_objects/packet_id_vo_test.dart b/test/value_objects/packet_id_vo_test.dart
new file mode 100644
index 0000000..81cce33
--- /dev/null
+++ b/test/value_objects/packet_id_vo_test.dart
@@ -0,0 +1,57 @@
+import 'package:test/test.dart';
+import 'package:socket_io/src/value_objects/packet_id_vo.dart';
+
+void main() {
+  group('PacketId', () {
+    test('creates valid PacketId from non-empty string', () {
+      final PacketId id = PacketId('123');
+      expect(id.value, equals('123'));
+      expect(id.toString(), equals('123'));
+    });
+
+    test('creates PacketId from integer', () {
+      final PacketId id = PacketId.fromInt(42);
+      expect(id.value, equals('42'));
+      expect(id.toInt(), equals(42));
+    });
+
+    test('throws ArgumentError for empty string', () {
+      expect(() => PacketId(''), throwsArgumentError);
+    });
+
+    test('throws ArgumentError for negative integer', () {
+      expect(() => PacketId.fromInt(-1), throwsArgumentError);
+    });
+
+    test('toInt returns null for non-numeric string', () {
+      final PacketId id = PacketId('abc');
+      expect(id.toInt(), isNull);
+    });
+
+    test('toInt returns correct value for numeric string', () {
+      final PacketId id = PacketId('999');
+      expect(id.toInt(), equals(999));
+    });
+
+    test('equality works correctly', () {
+      final PacketId id1 = PacketId('123');
+      final PacketId id2 = PacketId('123');
+      final PacketId id3 = PacketId('456');
+
+      expect(id1, equals(id2));
+      expect(id1, isNot(equals(id3)));
+    });
+
+    test('hashCode works correctly', () {
+      final PacketId id1 = PacketId('123');
+      final PacketId id2 = PacketId('123');
+
+      expect(id1.hashCode, equals(id2.hashCode));
+    });
+
+    test('unchecked constructor allows any value', () {
+      const PacketId id = PacketId.unchecked('');
+      expect(id.value, equals(''));
+    });
+  });
+}
diff --git a/test/value_objects/port_number_vo_test.dart b/test/value_objects/port_number_vo_test.dart
new file mode 100644
index 0000000..4458d27
--- /dev/null
+++ b/test/value_objects/port_number_vo_test.dart
@@ -0,0 +1,78 @@
+import 'package:test/test.dart';
+import 'package:socket_io/src/value_objects/port_number_vo.dart';
+
+void main() {
+  group('PortNumber', () {
+    test('creates valid PortNumber in range', () {
+      final PortNumber port = PortNumber(8080);
+      expect(port.value, equals(8080));
+      expect(port.toString(), equals('8080'));
+    });
+
+    test('accepts minimum port 1', () {
+      final PortNumber port = PortNumber(1);
+      expect(port.value, equals(1));
+    });
+
+    test('accepts maximum port 65535', () {
+      final PortNumber port = PortNumber(65535);
+      expect(port.value, equals(65535));
+    });
+
+    test('throws ArgumentError for port 0', () {
+      expect(() => PortNumber(0), throwsArgumentError);
+    });
+
+    test('throws ArgumentError for negative port', () {
+      expect(() => PortNumber(-1), throwsArgumentError);
+    });
+
+    test('throws ArgumentError for port > 65535', () {
+      expect(() => PortNumber(65536), throwsArgumentError);
+      expect(() => PortNumber(100000), throwsArgumentError);
+    });
+
+    test('has HTTP port constant', () {
+      expect(PortNumber.http.value, equals(80));
+    });
+
+    test('has HTTPS port constant', () {
+      expect(PortNumber.https.value, equals(443));
+    });
+
+    test('has dev port constant', () {
+      expect(PortNumber.dev.value, equals(3000));
+    });
+
+    test('has alternative HTTP port constant', () {
+      expect(PortNumber.altHttp.value, equals(8080));
+    });
+
+    test('equality works correctly', () {
+      final PortNumber port1 = PortNumber(8080);
+      final PortNumber port2 = PortNumber(8080);
+      final PortNumber port3 = PortNumber(3000);
+
+      expect(port1, equals(port2));
+      expect(port1, isNot(equals(port3)));
+    });
+
+    test('hashCode works correctly', () {
+      final PortNumber port1 = PortNumber(8080);
+      final PortNumber port2 = PortNumber(8080);
+
+      expect(port1.hashCode, equals(port2.hashCode));
+    });
+
+    test('unchecked constructor allows any value', () {
+      const PortNumber port = PortNumber.unchecked(0);
+      expect(port.value, equals(0));
+    });
+
+    test('common ports work', () {
+      expect(PortNumber(80), equals(PortNumber.http));
+      expect(PortNumber(443), equals(PortNumber.https));
+      expect(PortNumber(3000), equals(PortNumber.dev));
+    });
+  });
+}
diff --git a/test/value_objects/query_parameters_vo_test.dart b/test/value_objects/query_parameters_vo_test.dart
new file mode 100644
index 0000000..6baa9ef
--- /dev/null
+++ b/test/value_objects/query_parameters_vo_test.dart
@@ -0,0 +1,379 @@
+import 'package:test/test.dart';
+import 'package:socket_io/src/value_objects/query_parameters_vo.dart';
+
+void main() {
+  group('QueryParameters', () {
+    group('constructor', () {
+      test('creates from map with string values', () {
+        final Map params = {'token': 'abc123', 'room': 'chat'};
+        final QueryParameters query = QueryParameters(params);
+
+        expect(query.get('token'), 'abc123');
+        expect(query.get('room'), 'chat');
+        expect(query.length, 2);
+      });
+
+      test('creates from map with non-string values', () {
+        final Map params = {
+          'id': 123,
+          'active': true,
+          'ratio': 1.5,
+        };
+        final QueryParameters query = QueryParameters(params);
+
+        expect(query.get('id'), '123');
+        expect(query.get('active'), 'true');
+        expect(query.get('ratio'), '1.5');
+      });
+
+      test('throws on null values', () {
+        final Map params = {'token': null};
+
+        expect(
+          () => QueryParameters(params),
+          throwsArgumentError,
+        );
+      });
+
+      test('creates empty query parameters', () {
+        final QueryParameters query = QueryParameters.empty();
+
+        expect(query.isEmpty, true);
+        expect(query.length, 0);
+      });
+    });
+
+    group('fromQueryString', () {
+      test('parses simple query string', () {
+        final QueryParameters query = QueryParameters.fromQueryString('token=abc&room=chat');
+
+        expect(query.get('token'), 'abc');
+        expect(query.get('room'), 'chat');
+        expect(query.length, 2);
+      });
+
+      test('parses query string with leading question mark', () {
+        final QueryParameters query = QueryParameters.fromQueryString('?token=abc&room=chat');
+
+        expect(query.get('token'), 'abc');
+        expect(query.get('room'), 'chat');
+      });
+
+      test('parses query string with encoded values', () {
+        final QueryParameters query = QueryParameters.fromQueryString('name=John%20Doe&email=test%40example.com');
+
+        expect(query.get('name'), 'John Doe');
+        expect(query.get('email'), 'test@example.com');
+      });
+
+      test('handles empty values', () {
+        final QueryParameters query = QueryParameters.fromQueryString('token=&room=chat');
+
+        expect(query.get('token'), '');
+        expect(query.get('room'), 'chat');
+      });
+
+      test('handles empty string', () {
+        final QueryParameters query = QueryParameters.fromQueryString('');
+
+        expect(query.isEmpty, true);
+      });
+
+      test('handles malformed query strings', () {
+        final QueryParameters query = QueryParameters.fromQueryString('token&room=chat&');
+
+        expect(query.get('token'), '');
+        expect(query.get('room'), 'chat');
+      });
+    });
+
+    group('get and access methods', () {
+      late QueryParameters query;
+
+      setUp(() {
+        query = QueryParameters({
+          'token': 'abc123',
+          'room': 'chat',
+          'count': '5',
+        });
+      });
+
+      test('get returns value for existing key', () {
+        expect(query.get('token'), 'abc123');
+      });
+
+      test('get returns null for non-existing key', () {
+        expect(query.get('missing'), null);
+      });
+
+      test('getOrDefault returns value for existing key', () {
+        expect(query.getOrDefault('token', 'default'), 'abc123');
+      });
+
+      test('getOrDefault returns default for non-existing key', () {
+        expect(query.getOrDefault('missing', 'default'), 'default');
+      });
+
+      test('has returns true for existing key', () {
+        expect(query.has('token'), true);
+      });
+
+      test('has returns false for non-existing key', () {
+        expect(query.has('missing'), false);
+      });
+
+      test('keys returns all keys', () {
+        expect(query.keys, containsAll(['token', 'room', 'count']));
+        expect(query.keys.length, 3);
+      });
+
+      test('values returns all values', () {
+        expect(query.values, containsAll(['abc123', 'chat', '5']));
+        expect(query.values.length, 3);
+      });
+
+      test('entries returns all entries', () {
+        final List> entries = query.entries.toList();
+        expect(entries.length, 3);
+      });
+
+      test('length returns number of parameters', () {
+        expect(query.length, 3);
+      });
+
+      test('isEmpty returns false for non-empty', () {
+        expect(query.isEmpty, false);
+      });
+
+      test('isNotEmpty returns true for non-empty', () {
+        expect(query.isNotEmpty, true);
+      });
+
+      test('isEmpty returns true for empty', () {
+        final QueryParameters empty = QueryParameters.empty();
+        expect(empty.isEmpty, true);
+        expect(empty.isNotEmpty, false);
+      });
+    });
+
+    group('conversion methods', () {
+      late QueryParameters query;
+
+      setUp(() {
+        query = QueryParameters({'token': 'abc123', 'room': 'chat'});
+      });
+
+      test('toMap returns Map', () {
+        final Map map = query.toMap();
+
+        expect(map, isA>());
+        expect(map['token'], 'abc123');
+        expect(map['room'], 'chat');
+      });
+
+      test('toDynamicMap returns Map', () {
+        final Map map = query.toDynamicMap();
+
+        expect(map, isA>());
+        expect(map['token'], 'abc123');
+        expect(map['room'], 'chat');
+      });
+
+      test('toQueryString returns formatted string', () {
+        final String queryString = query.toQueryString();
+
+        expect(
+          queryString,
+          anyOf('token=abc123&room=chat', 'room=chat&token=abc123'),
+        );
+      });
+
+      test('toQueryString encodes special characters', () {
+        final QueryParameters special = QueryParameters({
+          'name': 'John Doe',
+          'email': 'test@example.com',
+        });
+
+        final String queryString = special.toQueryString();
+
+        expect(queryString, contains('John%20Doe'));
+        expect(queryString, contains('test%40example.com'));
+      });
+
+      test('toQueryString returns empty string for empty parameters', () {
+        final QueryParameters empty = QueryParameters.empty();
+        expect(empty.toQueryString(), '');
+      });
+    });
+
+    group('immutability methods', () {
+      late QueryParameters query;
+
+      setUp(() {
+        query = QueryParameters({'token': 'abc123', 'room': 'chat'});
+      });
+
+      test('withParameter adds new parameter', () {
+        final QueryParameters newQuery = query.withParameter('user', 'john');
+
+        expect(newQuery.get('user'), 'john');
+        expect(newQuery.get('token'), 'abc123');
+        expect(newQuery.length, 3);
+
+        // Original unchanged
+        expect(query.has('user'), false);
+        expect(query.length, 2);
+      });
+
+      test('withParameter replaces existing parameter', () {
+        final QueryParameters newQuery = query.withParameter('token', 'xyz789');
+
+        expect(newQuery.get('token'), 'xyz789');
+        expect(newQuery.length, 2);
+
+        // Original unchanged
+        expect(query.get('token'), 'abc123');
+      });
+
+      test('withoutParameter removes parameter', () {
+        final QueryParameters newQuery = query.withoutParameter('token');
+
+        expect(newQuery.has('token'), false);
+        expect(newQuery.get('room'), 'chat');
+        expect(newQuery.length, 1);
+
+        // Original unchanged
+        expect(query.has('token'), true);
+        expect(query.length, 2);
+      });
+
+      test('withoutParameter on non-existing key', () {
+        final QueryParameters newQuery = query.withoutParameter('missing');
+
+        expect(newQuery.length, 2);
+      });
+
+      test('merge combines two query parameters', () {
+        final QueryParameters other = QueryParameters({'user': 'john', 'id': '1'});
+        final QueryParameters merged = query.merge(other);
+
+        expect(merged.get('token'), 'abc123');
+        expect(merged.get('room'), 'chat');
+        expect(merged.get('user'), 'john');
+        expect(merged.get('id'), '1');
+        expect(merged.length, 4);
+      });
+
+      test('merge overwrites with other values on conflict', () {
+        final QueryParameters other = QueryParameters({'token': 'xyz789', 'user': 'john'});
+        final QueryParameters merged = query.merge(other);
+
+        expect(merged.get('token'), 'xyz789'); // Overwritten
+        expect(merged.get('user'), 'john');
+        expect(merged.length, 3);
+
+        // Original unchanged
+        expect(query.get('token'), 'abc123');
+      });
+    });
+
+    group('equality and hashCode', () {
+      test('equal query parameters are equal', () {
+        final QueryParameters query1 = QueryParameters({'token': 'abc', 'room': 'chat'});
+        final QueryParameters query2 = QueryParameters({'token': 'abc', 'room': 'chat'});
+
+        expect(query1, equals(query2));
+        expect(query1.hashCode, equals(query2.hashCode));
+      });
+
+      test('different query parameters are not equal', () {
+        final QueryParameters query1 = QueryParameters({'token': 'abc'});
+        final QueryParameters query2 = QueryParameters({'token': 'xyz'});
+
+        expect(query1, isNot(equals(query2)));
+      });
+
+      test('query parameters with different keys are not equal', () {
+        final QueryParameters query1 = QueryParameters({'token': 'abc'});
+        final QueryParameters query2 = QueryParameters({'key': 'abc'});
+
+        expect(query1, isNot(equals(query2)));
+      });
+
+      test('identical instances are equal', () {
+        final QueryParameters query = QueryParameters({'token': 'abc'});
+
+        expect(query, equals(query));
+      });
+
+      test('empty query parameters are equal', () {
+        final QueryParameters query1 = QueryParameters.empty();
+        final QueryParameters query2 = QueryParameters({});
+
+        expect(query1, equals(query2));
+      });
+    });
+
+    group('toString', () {
+      test('returns formatted string', () {
+        final QueryParameters query = QueryParameters({'token': 'abc', 'room': 'chat'});
+
+        final String str = query.toString();
+
+        expect(str, startsWith('QueryParameters('));
+        expect(str, endsWith(')'));
+      });
+
+      test('returns empty for empty parameters', () {
+        final QueryParameters empty = QueryParameters.empty();
+
+        expect(empty.toString(), 'QueryParameters()');
+      });
+    });
+
+    group('edge cases', () {
+      test('handles special characters in keys', () {
+        final QueryParameters query = QueryParameters({'key-name': 'value', 'key_name': 'value2'});
+
+        expect(query.get('key-name'), 'value');
+        expect(query.get('key_name'), 'value2');
+      });
+
+      test('handles empty string values', () {
+        final QueryParameters query = QueryParameters({'token': ''});
+
+        expect(query.get('token'), '');
+        expect(query.has('token'), true);
+      });
+
+      test('handles single parameter', () {
+        final QueryParameters query = QueryParameters({'only': 'one'});
+
+        expect(query.length, 1);
+        expect(query.get('only'), 'one');
+      });
+
+      test('round-trip conversion preserves data', () {
+        final Map original = {
+          'token': 'abc123',
+          'room': 'chat',
+          'count': '5',
+        };
+        final QueryParameters query = QueryParameters(original);
+        final Map roundTrip = query.toDynamicMap();
+
+        expect(roundTrip, equals(original));
+      });
+
+      test('query string round-trip preserves data', () {
+        const String original = 'token=abc&room=chat';
+        final QueryParameters query = QueryParameters.fromQueryString(original);
+        final String roundTrip = query.toQueryString();
+
+        // Note: order might differ, so parse and compare
+        final QueryParameters reparsed = QueryParameters.fromQueryString(roundTrip);
+        expect(reparsed, equals(query));
+      });
+    });
+  });
+}
diff --git a/test/value_objects/room_name_vo_test.dart b/test/value_objects/room_name_vo_test.dart
new file mode 100644
index 0000000..6dd5321
--- /dev/null
+++ b/test/value_objects/room_name_vo_test.dart
@@ -0,0 +1,42 @@
+import 'package:test/test.dart';
+import 'package:socket_io/src/value_objects/room_name_vo.dart';
+
+void main() {
+  group('RoomName', () {
+    test('creates valid RoomName from non-empty string', () {
+      final RoomName room = RoomName('room1');
+      expect(room.value, equals('room1'));
+      expect(room.toString(), equals('room1'));
+    });
+
+    test('accepts room names with special characters', () {
+      final RoomName room = RoomName('room-1_test');
+      expect(room.value, equals('room-1_test'));
+    });
+
+    test('throws ArgumentError for empty string', () {
+      expect(() => RoomName(''), throwsArgumentError);
+    });
+
+    test('equality works correctly', () {
+      final RoomName room1 = RoomName('same-room');
+      final RoomName room2 = RoomName('same-room');
+      final RoomName room3 = RoomName('different-room');
+
+      expect(room1, equals(room2));
+      expect(room1, isNot(equals(room3)));
+    });
+
+    test('hashCode works correctly', () {
+      final RoomName room1 = RoomName('same-room');
+      final RoomName room2 = RoomName('same-room');
+
+      expect(room1.hashCode, equals(room2.hashCode));
+    });
+
+    test('unchecked constructor allows any value', () {
+      const RoomName room = RoomName.unchecked('');
+      expect(room.value, equals(''));
+    });
+  });
+}
diff --git a/test/value_objects/timeout_duration_vo_test.dart b/test/value_objects/timeout_duration_vo_test.dart
new file mode 100644
index 0000000..6bed96b
--- /dev/null
+++ b/test/value_objects/timeout_duration_vo_test.dart
@@ -0,0 +1,76 @@
+import 'package:test/test.dart';
+import 'package:socket_io/src/value_objects/timeout_duration_vo.dart';
+
+void main() {
+  group('TimeoutDuration', () {
+    test('creates valid TimeoutDuration from Duration', () {
+      final TimeoutDuration timeout = TimeoutDuration(const Duration(seconds: 30));
+      expect(timeout.value, equals(const Duration(seconds: 30)));
+      expect(timeout.inSeconds, equals(30));
+      expect(timeout.inMilliseconds, equals(30000));
+    });
+
+    test('creates from milliseconds', () {
+      final TimeoutDuration timeout = TimeoutDuration.milliseconds(5000);
+      expect(timeout.inMilliseconds, equals(5000));
+      expect(timeout.inSeconds, equals(5));
+    });
+
+    test('creates from seconds', () {
+      final TimeoutDuration timeout = TimeoutDuration.seconds(10);
+      expect(timeout.inSeconds, equals(10));
+    });
+
+    test('creates from minutes', () {
+      final TimeoutDuration timeout = TimeoutDuration.minutes(2);
+      expect(timeout.inSeconds, equals(120));
+    });
+
+    test('throws ArgumentError for negative duration', () {
+      expect(() => TimeoutDuration(const Duration(seconds: -1)), throwsArgumentError);
+    });
+
+    test('throws ArgumentError for duration exceeding 1 hour', () {
+      expect(() => TimeoutDuration(const Duration(hours: 2)), throwsArgumentError);
+    });
+
+    test('allows duration up to 1 hour', () {
+      final TimeoutDuration timeout = TimeoutDuration(const Duration(hours: 1));
+      expect(timeout.inSeconds, equals(3600));
+    });
+
+    test('has default connection timeout', () {
+      expect(TimeoutDuration.defaultConnection.inSeconds, equals(20));
+    });
+
+    test('has default ping timeout', () {
+      expect(TimeoutDuration.defaultPing.inSeconds, equals(5));
+    });
+
+    test('equality works correctly', () {
+      final TimeoutDuration timeout1 = TimeoutDuration.seconds(30);
+      final TimeoutDuration timeout2 = TimeoutDuration.seconds(30);
+      final TimeoutDuration timeout3 = TimeoutDuration.seconds(60);
+
+      expect(timeout1, equals(timeout2));
+      expect(timeout1, isNot(equals(timeout3)));
+    });
+
+    test('hashCode works correctly', () {
+      final TimeoutDuration timeout1 = TimeoutDuration.seconds(30);
+      final TimeoutDuration timeout2 = TimeoutDuration.seconds(30);
+
+      expect(timeout1.hashCode, equals(timeout2.hashCode));
+    });
+
+    test('toString returns milliseconds', () {
+      final TimeoutDuration timeout = TimeoutDuration.seconds(5);
+      expect(timeout.toString(), equals('5000ms'));
+    });
+
+    test('unchecked constructor allows any value', () {
+      const TimeoutDuration timeout = TimeoutDuration.unchecked(Duration(hours: 10));
+      expect(timeout.value, equals(const Duration(hours: 10)));
+    });
+  });
+}
diff --git a/test/value_objects/transport_name_vo_test.dart b/test/value_objects/transport_name_vo_test.dart
new file mode 100644
index 0000000..6e367f8
--- /dev/null
+++ b/test/value_objects/transport_name_vo_test.dart
@@ -0,0 +1,78 @@
+import 'package:test/test.dart';
+import 'package:socket_io/src/value_objects/transport_name_vo.dart';
+
+void main() {
+  group('TransportType', () {
+    test('enum has all expected values', () {
+      expect(TransportType.values, contains(TransportType.websocket));
+      expect(TransportType.values, contains(TransportType.polling));
+      expect(TransportType.values, contains(TransportType.webtransport));
+    });
+
+    test('value returns correct string', () {
+      expect(TransportType.websocket.value, equals('websocket'));
+      expect(TransportType.polling.value, equals('polling'));
+      expect(TransportType.webtransport.value, equals('webtransport'));
+    });
+
+    test('fromString creates correct type', () {
+      expect(TransportTypeHelper.fromString('websocket'), equals(TransportType.websocket));
+      expect(TransportTypeHelper.fromString('polling'), equals(TransportType.polling));
+      expect(TransportTypeHelper.fromString('webtransport'), equals(TransportType.webtransport));
+    });
+
+    test('fromString handles aliases', () {
+      expect(TransportTypeHelper.fromString('ws'), equals(TransportType.websocket));
+    });
+
+    test('fromString throws for invalid type', () {
+      expect(() => TransportTypeHelper.fromString('invalid'), throwsArgumentError);
+    });
+  });
+
+  group('TransportName', () {
+    test('creates from TransportType', () {
+      const TransportName name = TransportName(TransportType.websocket);
+      expect(name.type, equals(TransportType.websocket));
+      expect(name.value, equals('websocket'));
+    });
+
+    test('creates from string', () {
+      final TransportName name = TransportName.fromString('polling');
+      expect(name.type, equals(TransportType.polling));
+      expect(name.value, equals('polling'));
+    });
+
+    test('has static constants', () {
+      expect(TransportName.websocket.type, equals(TransportType.websocket));
+      expect(TransportName.polling.type, equals(TransportType.polling));
+      expect(TransportName.webtransport.type, equals(TransportType.webtransport));
+    });
+
+    test('equality works correctly', () {
+      const TransportName name1 = TransportName(TransportType.websocket);
+      const TransportName name2 = TransportName(TransportType.websocket);
+      const TransportName name3 = TransportName(TransportType.polling);
+
+      expect(name1, equals(name2));
+      expect(name1, isNot(equals(name3)));
+    });
+
+    test('hashCode works correctly', () {
+      const TransportName name1 = TransportName(TransportType.websocket);
+      const TransportName name2 = TransportName(TransportType.websocket);
+
+      expect(name1.hashCode, equals(name2.hashCode));
+    });
+
+    test('toString returns value', () {
+      const TransportName name = TransportName(TransportType.websocket);
+      expect(name.toString(), equals('websocket'));
+    });
+
+    test('static constants work', () {
+      expect(TransportName.websocket.value, equals('websocket'));
+      expect(TransportName.polling.value, equals('polling'));
+    });
+  });
+}
diff --git a/test/value_objects/url_path_vo_test.dart b/test/value_objects/url_path_vo_test.dart
new file mode 100644
index 0000000..7dae1aa
--- /dev/null
+++ b/test/value_objects/url_path_vo_test.dart
@@ -0,0 +1,59 @@
+import 'package:test/test.dart';
+import 'package:socket_io/src/value_objects/url_path_vo.dart';
+
+void main() {
+  group('UrlPath', () {
+    test('creates valid UrlPath starting with /', () {
+      final UrlPath path = UrlPath('/api/v1');
+      expect(path.value, equals('/api/v1'));
+      expect(path.toString(), equals('/api/v1'));
+    });
+
+    test('accepts root path', () {
+      final UrlPath path = UrlPath('/');
+      expect(path.value, equals('/'));
+    });
+
+    test('accepts complex paths', () {
+      final UrlPath path = UrlPath('/socket.io/v4/');
+      expect(path.value, equals('/socket.io/v4/'));
+    });
+
+    test('throws ArgumentError for empty string', () {
+      expect(() => UrlPath(''), throwsArgumentError);
+    });
+
+    test('throws ArgumentError when not starting with /', () {
+      expect(() => UrlPath('api/v1'), throwsArgumentError);
+    });
+
+    test('has default Socket.IO path constant', () {
+      expect(UrlPath.defaultSocketIO.value, equals('/socket.io'));
+    });
+
+    test('has root path constant', () {
+      expect(UrlPath.root.value, equals('/'));
+    });
+
+    test('equality works correctly', () {
+      final UrlPath path1 = UrlPath('/same');
+      final UrlPath path2 = UrlPath('/same');
+      final UrlPath path3 = UrlPath('/different');
+
+      expect(path1, equals(path2));
+      expect(path1, isNot(equals(path3)));
+    });
+
+    test('hashCode works correctly', () {
+      final UrlPath path1 = UrlPath('/same');
+      final UrlPath path2 = UrlPath('/same');
+
+      expect(path1.hashCode, equals(path2.hashCode));
+    });
+
+    test('unchecked constructor allows any value', () {
+      const UrlPath path = UrlPath.unchecked('invalid');
+      expect(path.value, equals('invalid'));
+    });
+  });
+}
diff --git a/tool/check.sh b/tool/check.sh
new file mode 100755
index 0000000..77d5ec0
--- /dev/null
+++ b/tool/check.sh
@@ -0,0 +1,97 @@
+#!/bin/bash
+# Development check script - runs all quality checks
+
+set -e  # Exit on error
+
+WITH_POLLING_SMOKE=false
+for arg in "$@"; do
+  case "$arg" in
+    --with-polling-smoke)
+      WITH_POLLING_SMOKE=true
+      ;;
+    -h|--help)
+      echo "Usage: tool/check.sh [--with-polling-smoke]"
+      echo ""
+      echo "Options:"
+      echo "  --with-polling-smoke   Also run example/polling_smoke.dart against example server"
+      exit 0
+      ;;
+    *)
+      echo "Unknown option: $arg"
+      echo "Run: tool/check.sh --help"
+      exit 1
+      ;;
+  esac
+done
+
+echo "Running Dart quality checks..."
+echo ""
+
+# Colors
+GREEN='\033[0;32m'
+RED='\033[0;31m'
+YELLOW='\033[1;33m'
+NC='\033[0m' # No Color
+
+# Format check
+echo "Checking code formatting..."
+if dart format --output=none --set-exit-if-changed . ; then
+    echo -e "${GREEN}OK Code is properly formatted${NC}"
+else
+    echo -e "${RED}FAIL Code formatting issues found${NC}"
+    echo "  Run: dart format ."
+    exit 1
+fi
+echo ""
+
+# Analysis
+echo "Running static analysis..."
+if dart analyze ; then
+    echo -e "${GREEN}OK No analysis issues found${NC}"
+else
+    echo -e "${RED}FAIL Analysis issues found${NC}"
+    exit 1
+fi
+echo ""
+
+# Tests
+echo "Running tests..."
+if dart test ; then
+    echo -e "${GREEN}OK All tests passed${NC}"
+else
+    echo -e "${RED}FAIL Some tests failed${NC}"
+    exit 1
+fi
+echo ""
+
+# Optional polling smoke check
+if [ "$WITH_POLLING_SMOKE" = true ]; then
+    echo "Running optional polling smoke check..."
+
+    # Start example server in background
+    dart run example/example_server.dart >/tmp/tp_socket_io_example_server.log 2>&1 &
+    SERVER_PID=$!
+
+    cleanup_server() {
+      if kill -0 "$SERVER_PID" >/dev/null 2>&1; then
+        kill "$SERVER_PID" >/dev/null 2>&1 || true
+      fi
+    }
+    trap cleanup_server EXIT
+
+    # Give server a moment to start
+    sleep 2
+
+    if dart run example/polling_smoke.dart ; then
+        echo -e "${GREEN}OK Polling smoke check passed${NC}"
+    else
+        echo -e "${RED}FAIL Polling smoke check failed${NC}"
+        echo "  Server log: /tmp/tp_socket_io_example_server.log"
+        exit 1
+    fi
+    echo ""
+fi
+
+echo -e "${GREEN}SUCCESS All checks passed!${NC}"
+echo ""
+echo "Ready to commit!"
diff --git a/tool/coverage.sh b/tool/coverage.sh
new file mode 100755
index 0000000..e0d63f0
--- /dev/null
+++ b/tool/coverage.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+# Run tests with coverage
+
+set -e
+
+echo "🧪 Running tests with coverage..."
+echo ""
+
+# Run tests with coverage
+dart test --coverage=coverage
+
+# Install coverage tool if not already installed
+if ! dart pub global list | grep -q coverage ; then
+    echo "Installing coverage tool..."
+    dart pub global activate coverage
+fi
+
+# Format coverage
+echo ""
+echo "📊 Formatting coverage report..."
+dart pub global run coverage:format_coverage \
+    --lcov \
+    --in=coverage \
+    --out=coverage/lcov.info \
+    --report-on=lib
+
+echo ""
+echo "✅ Coverage report generated: coverage/lcov.info"
+echo ""
+echo "To view coverage in browser:"
+echo "  genhtml coverage/lcov.info -o coverage/html"
+echo "  open coverage/html/index.html"

From eb703896d8821d2966584a486a4902c506c48f48 Mon Sep 17 00:00:00 2001
From: brein 
Date: Mon, 4 May 2026 02:15:23 +0200
Subject: [PATCH 2/3] fix: remove duplicate legacy adapter implementation

---
 lib/src/adapter/adapter.dart | 236 -----------------------------------
 1 file changed, 236 deletions(-)
 delete mode 100644 lib/src/adapter/adapter.dart

diff --git a/lib/src/adapter/adapter.dart b/lib/src/adapter/adapter.dart
deleted file mode 100644
index 4d4a08f..0000000
--- a/lib/src/adapter/adapter.dart
+++ /dev/null
@@ -1,236 +0,0 @@
-/// adapter.dart
-///
-/// Purpose:
-///
-/// Description:
-///
-/// History:
-///    16/02/2017, Created by jumperchen
-///
-/// Copyright (C) 2017 Potix Corporation. All Rights Reserved.
-import 'dart:async';
-import 'package:socket_io/src/namespace.dart';
-import 'package:socket_io_common/src/parser/parser.dart';
-import 'package:socket_io/src/util/event_emitter.dart';
-
-abstract class Adapter {
-  Map nsps = {};
-  Map rooms = {};
-  Map sids = {};
-
-  void add(String id, String room, [dynamic Function([dynamic]) fn]);
-  void del(String id, String room, [dynamic Function([dynamic]) fn]);
-  void delAll(String id, [dynamic Function([dynamic]) fn]);
-  void broadcast(Map packet, [Map opts]);
-  void clients([List rooms, dynamic Function([dynamic]) fn]);
-  void clientRooms(String id, [dynamic Function(dynamic, [dynamic]) fn]);
-
-  static Adapter newInstance(String key, Namespace nsp) {
-    if ('default' == key) {
-      return _MemoryStoreAdapter(nsp);
-    }
-    throw UnimplementedError('not supported other adapter yet.');
-  }
-}
-
-class _MemoryStoreAdapter extends EventEmitter implements Adapter {
-  @override
-  Map nsps = {};
-  @override
-  Map rooms = {};
-
-  @override
-  Map sids = {};
-  late Encoder encoder;
-  late Namespace nsp;
-
-  _MemoryStoreAdapter(Namespace nsp) {
-    this.nsp = nsp;
-    encoder = nsp.server.encoder;
-  }
-
-  /// Adds a socket to a room.
-  ///
-  /// @param {String} socket id
-  /// @param {String} room name
-  /// @param {Function} callback
-  /// @api public
-
-  @override
-  void add(String id, String room, [dynamic Function([dynamic])? fn]) {
-    sids[id] = sids[id] ?? {};
-    sids[id]![room] = true;
-    rooms[room] = rooms[room] ?? _Room();
-    rooms[room]!.add(id);
-    if (fn != null) scheduleMicrotask(() => fn(null));
-  }
-
-  /// Removes a socket from a room.
-  ///
-  /// @param {String} socket id
-  /// @param {String} room name
-  /// @param {Function} callback
-  /// @api public
-  @override
-  void del(String id, String room, [dynamic Function([dynamic])? fn]) {
-    sids[id] = sids[id] ?? {};
-    sids[id]!.remove(room);
-    if (rooms.containsKey(room)) {
-      rooms[room]!.del(id);
-      if (rooms[room]!.length == 0) rooms.remove(room);
-    }
-
-    if (fn != null) scheduleMicrotask(() => fn(null));
-  }
-
-  /// Removes a socket from all rooms it's joined.
-  ///
-  /// @param {String} socket id
-  /// @param {Function} callback
-  /// @api public
-  @override
-  void delAll(String id, [dynamic Function([dynamic])? fn]) {
-    var rooms = sids[id];
-    if (rooms != null) {
-      for (var room in rooms.keys) {
-        if (this.rooms.containsKey(room)) {
-          this.rooms[room]!.del(id);
-          if (this.rooms[room]!.length == 0) this.rooms.remove(room);
-        }
-      }
-    }
-    sids.remove(id);
-
-    if (fn != null) scheduleMicrotask(() => fn(null));
-  }
-
-  /// Broadcasts a packet.
-  ///
-  /// Options:
-  ///  - `flags` {Object} flags for this packet
-  ///  - `except` {Array} sids that should be excluded
-  ///  - `rooms` {Array} list of rooms to broadcast to
-  ///
-  /// @param {Object} packet object
-  /// @api public
-  @override
-  void broadcast(Map packet, [Map? opts]) {
-    opts = opts ?? {};
-    List rooms = opts['rooms'] ?? [];
-    List except = opts['except'] ?? [];
-    Map flags = opts['flags'] ?? {};
-    var packetOpts = {
-      'preEncoded': true,
-      'volatile': flags['volatile'],
-      'compress': flags['compress']
-    };
-    var ids = {};
-    var socket;
-
-    packet['nsp'] = nsp.name;
-    encoder.encode(packet, (encodedPackets) {
-      if (rooms.isNotEmpty) {
-        for (var i = 0; i < rooms.length; i++) {
-          var room = this.rooms[rooms[i]];
-          if (room == null) continue;
-          var sockets = room.sockets;
-          for (var id in sockets.keys) {
-            if (sockets.containsKey(id)) {
-              if (ids[id] != null || except.contains(id)) continue;
-              socket = nsp.connected[id];
-              if (socket != null) {
-                socket.packet(encodedPackets, packetOpts);
-                ids[id] = true;
-              }
-            }
-          }
-        }
-      } else {
-        for (var id in sids.keys) {
-          if (except.contains(id)) continue;
-          socket = nsp.connected[id];
-          if (socket != null) socket.packet(encodedPackets, packetOpts);
-        }
-      }
-    });
-  }
-
-  /// Gets a list of clients by sid.
-  ///
-  /// @param {Array} explicit set of rooms to check.
-  /// @param {Function} callback
-  /// @api public
-  @override
-  void clients(
-      [List rooms = const [], dynamic Function([dynamic])? fn]) {
-    var ids = {};
-    var sids = [];
-    var socket;
-
-    if (rooms.isNotEmpty) {
-      for (var i = 0; i < rooms.length; i++) {
-        var room = this.rooms[rooms[i]];
-        if (room == null) continue;
-        var sockets = room.sockets;
-        for (var id in sockets.keys) {
-          if (sockets.containsKey(id)) {
-            if (ids[id] != null) continue;
-            socket = nsp.connected[id];
-            if (socket != null) {
-              sids.add(id);
-              ids[id] = true;
-            }
-          }
-        }
-      }
-    } else {
-      for (var id in this.sids.keys) {
-        socket = nsp.connected[id];
-        if (socket != null) sids.add(id);
-      }
-    }
-
-    if (fn != null) scheduleMicrotask(() => fn(sids));
-  }
-
-  /// Gets the list of rooms a given client has joined.
-  ///
-  /// @param {String} socket id
-  /// @param {Function} callback
-  /// @api public
-  @override
-  void clientRooms(String id, [dynamic Function(dynamic, [dynamic])? fn]) {
-    var rooms = sids[id];
-    if (fn != null) scheduleMicrotask(() => fn(null, rooms?.keys));
-  }
-}
-
-/// Room constructor.
-///
-/// @api private
-class _Room {
-  Map sockets = {};
-  int length = 0;
-
-  /// Adds a socket to a room.
-  ///
-  /// @param {String} socket id
-  /// @api private
-  void add(String id) {
-    if (!sockets.containsKey(id)) {
-      sockets[id] = true;
-      length++;
-    }
-  }
-
-  /// Removes a socket from a room.
-  ///
-  /// @param {String} socket id
-  /// @api private
-  void del(String id) {
-    if (sockets.containsKey(id)) {
-      sockets.remove(id);
-      length--;
-    }
-  }
-}

From ee2342ad72f0c7476d5df7cd2d91043711aba279 Mon Sep 17 00:00:00 2001
From: brein 
Date: Wed, 6 May 2026 21:54:57 +0200
Subject: [PATCH 3/3] docs: publish referenced documentation

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
 README.md                          |    1 -
 doc/ARCHITECTURE.md                |  803 +++++++++++++++++++++
 doc/CONTRIBUTING.md                |  686 ++++++++++++++++++
 doc/QUICK_REFERENCE.md             |  412 +++++++++++
 doc/TYPE_SAFETY_MIGRATION_GUIDE.md |  796 +++++++++++++++++++++
 doc/TYPE_SAFE_EXAMPLES.md          | 1051 ++++++++++++++++++++++++++++
 doc/UPSTREAM_PR.md                 |  425 +++++++++++
 7 files changed, 4173 insertions(+), 1 deletion(-)
 create mode 100644 doc/ARCHITECTURE.md
 create mode 100644 doc/CONTRIBUTING.md
 create mode 100644 doc/QUICK_REFERENCE.md
 create mode 100644 doc/TYPE_SAFETY_MIGRATION_GUIDE.md
 create mode 100644 doc/TYPE_SAFE_EXAMPLES.md
 create mode 100644 doc/UPSTREAM_PR.md

diff --git a/README.md b/README.md
index d63f85a..764ee01 100644
--- a/README.md
+++ b/README.md
@@ -417,7 +417,6 @@ For detailed API documentation, see:
 - [TYPE_SAFE_EXAMPLES.md](doc/TYPE_SAFE_EXAMPLES.md) - Comprehensive examples of the typed API
 - [TYPE_SAFETY_MIGRATION_GUIDE.md](doc/TYPE_SAFETY_MIGRATION_GUIDE.md) - Migration guide from legacy to typed API
 - [QUICK_REFERENCE.md](doc/QUICK_REFERENCE.md) - Quick reference guide
-- [UPGRADE_TO_V3.md](doc/archive/UPGRADE_TO_V3.md) - Upgrade guide for breaking changes
 
 ## Architecture
 
diff --git a/doc/ARCHITECTURE.md b/doc/ARCHITECTURE.md
new file mode 100644
index 0000000..15ebd34
--- /dev/null
+++ b/doc/ARCHITECTURE.md
@@ -0,0 +1,803 @@
+# Architecture Documentation
+
+**socket_io-dart** - Modern Socket.IO Server Implementation
+
+This document describes the architectural patterns, design decisions, and code organization of the socket_io library.
+
+---
+
+## Table of Contents
+
+1. [High-Level Architecture](#high-level-architecture)
+2. [Directory Structure](#directory-structure)
+3. [Core Concepts](#core-concepts)
+4. [Type System & Value Objects](#type-system--value-objects)
+5. [Domain Models](#domain-models)
+6. [Extension Methods Pattern](#extension-methods-pattern)
+7. [Sealed Classes & Pattern Matching](#sealed-classes--pattern-matching)
+8. [Backward Compatibility Strategy](#backward-compatibility-strategy)
+9. [Transport Layer](#transport-layer)
+10. [Event System](#event-system)
+11. [Testing Strategy](#testing-strategy)
+
+---
+
+## High-Level Architecture
+
+socket_io follows a **layered architecture** with clear separation of concerns:
+
+```
+┌─────────────────────────────────────────────────────┐
+│                   Public API                         │
+│              (Server, Socket, Namespace)             │
+├─────────────────────────────────────────────────────┤
+│                 Domain Layer                         │
+│        (Models, Value Objects, Business Logic)       │
+├─────────────────────────────────────────────────────┤
+│                Transport Layer                       │
+│        (WebSocket, Polling, JSONP Transports)        │
+├─────────────────────────────────────────────────────┤
+│                  Engine.IO Layer                     │
+│          (Low-level connection management)           │
+└─────────────────────────────────────────────────────┘
+```
+
+### Key Architectural Principles
+
+1. **Type Safety First**: Extensive use of value objects, sealed classes, and typed models
+2. **Dual API Pattern**: Backward compatibility with legacy API while providing modern typed alternatives
+3. **Domain-Driven Design**: Rich domain models with business logic encapsulation
+4. **Extension-based Utilities**: Non-intrusive functionality additions via extensions
+5. **Event-Driven Architecture**: Observer pattern for real-time communication
+6. **Adapter Pattern**: Pluggable room/namespace management
+
+---
+
+## Directory Structure
+
+```
+lib/
+├── src/
+│   ├── adapter/              # Room and namespace adapters
+│   │   └── adapter.dart      # Default memory adapter
+│   ├── constants/            # Shared constants
+│   │   └── socket_events.dart
+│   ├── engine/               # Engine.IO transport layer
+│   │   ├── engine.dart       # Engine.IO server
+│   │   ├── socket.dart       # Engine.IO socket
+│   │   └── transport/        # Transport implementations
+│   │       ├── polling_transport.dart
+│   │       ├── websocket_transport.dart
+│   │       ├── jsonp_transport.dart
+│   │       └── transports.dart
+│   ├── extensions/           # Extension methods
+│   │   ├── duration_extensions.dart
+│   │   ├── list_extensions.dart
+│   │   ├── map_extensions.dart
+│   │   ├── packet_extensions.dart
+│   │   ├── socket_extensions.dart
+│   │   └── string_extensions.dart
+│   ├── models/              # Domain models (26+ files)
+│   │   ├── packet_models.dart
+│   │   ├── server_options_models.dart
+│   │   ├── error_models.dart
+│   │   ├── callbacks_models.dart
+│   │   └── ... (20+ more)
+│   ├── value_objects/       # Value objects (14 types)
+│   │   ├── connection_id_vo.dart
+│   │   ├── room_name_vo.dart
+│   │   ├── event_name_vo.dart
+│   │   └── ... (11 more)
+│   ├── util/                # Utilities
+│   │   └── event_emitter.dart
+│   ├── client.dart          # Client connection handler
+│   ├── namespace.dart       # Namespace management
+│   ├── server.dart          # Socket.IO server
+│   └── socket.dart          # Socket connection
+└── socket_io.dart        # Public API exports
+
+test/                        # Test suite (748 tests)
+├── models/                  # Model tests
+├── value_objects/           # Value object tests
+└── ...
+```
+
+### Organization Principles
+
+- **models/**: Rich domain models with business logic
+- **value_objects/**: Immutable validated primitives
+- **extensions/**: Utility methods organized by type
+- **engine/**: Low-level transport and connection handling
+- **adapter/**: Room and namespace management strategies
+
+---
+
+## Core Concepts
+
+### 1. Server
+
+The `Server` class is the main entry point:
+
+```dart
+final server = Server();
+server.on('connection', (socket) {
+  // Handle socket connection
+});
+server.listen(3000);
+```
+
+**Responsibilities:**
+- Manages namespaces
+- Handles client connections
+- Configures transport options
+- Provides adapter configuration
+
+### 2. Namespace
+
+Namespaces create separate communication channels:
+
+```dart
+final chatNamespace = server.of('/chat');
+chatNamespace.on('connection', (socket) {
+  // Chat-specific logic
+});
+```
+
+**Responsibilities:**
+- Isolates message routing
+- Manages socket connections within namespace
+- Applies middleware
+- Manages rooms
+
+### 3. Socket
+
+Individual client connections:
+
+```dart
+socket.on('message', (data) {
+  socket.emit('response', ['received']);
+});
+```
+
+**Responsibilities:**
+- Event emission and reception
+- Room membership management
+- Binary data handling
+- Acknowledgment callbacks
+
+### 4. Adapter
+
+Manages rooms and broadcasts:
+
+```dart
+class Adapter {
+  void add(String id, String room);
+  void broadcast(Map packet, Map? opts);
+  void clients(List? rooms, ClientsCallback fn);
+}
+```
+
+**Default:** In-memory adapter  
+**Extensible:** Can implement Redis, MongoDB adapters
+
+---
+
+## Type System & Value Objects
+
+### Philosophy
+
+Value Objects provide **type safety** and **validation** at the boundaries:
+
+```dart
+// Instead of: String roomName
+final room = RoomName('chatRoom');  // Validated, non-empty
+
+// Instead of: int port  
+final port = PortNumber(3000);  // Validated range 1-65535
+
+// Instead of: String eventName
+final event = EventName('message');  // No reserved names
+```
+
+### Value Object Catalog
+
+| Value Object | Validation | Purpose |
+|--------------|------------|---------|
+| `ConnectionId` | Non-empty string | Socket connection identifier |
+| `RoomName` | Non-empty string | Room identifier |
+| `EventName` | No reserved names | Custom event names |
+| `NamespaceName` | Starts with `/` | Namespace identifier |
+| `PortNumber` | 1-65535 | Port validation |
+| `TimeoutDuration` | Non-negative | Timeout configuration |
+| `TransportName` | Valid transport type | Transport selection |
+| `UrlPath` | Starts with `/` | URL path validation |
+| `QueryParameters` | Key-value map | Type-safe query access |
+| `ErrorCode` | Numeric or string | Error identification |
+| `DisconnectReason` | Enum-based | Disconnect classification |
+| `SocketState` | Enum-based | Connection state |
+| `PacketId` | Non-empty | Packet identification |
+| `EventArguments` | Type-safe list | Event data container |
+
+### Value Object Pattern
+
+```dart
+class ConnectionId {
+  final String value;
+  
+  // Private constructor
+  const ConnectionId._(this.value);
+  
+  // Validated factory
+  factory ConnectionId(String id) {
+    if (id.isEmpty) {
+      throw ArgumentError('Connection ID cannot be empty');
+    }
+    return ConnectionId._(id);
+  }
+  
+  // Unchecked for trusted sources
+  const ConnectionId.unchecked(this.value);
+  
+  @override
+  bool operator ==(Object other) => 
+      other is ConnectionId && value == other.value;
+  
+  @override
+  int get hashCode => value.hashCode;
+  
+  @override
+  String toString() => value;
+}
+```
+
+---
+
+## Domain Models
+
+### Model Categories
+
+**1. Packet Models** (`packet_models.dart`)
+- `SocketIOPacket` (sealed class)
+- `ConnectPacket`, `DisconnectPacket`, `EventPacket`, `AckPacket`
+- Type-safe packet construction
+
+**2. Configuration Models**
+- `ServerOptionsModel`: Server configuration
+- `HandshakeDataModel`: Connection handshake data
+- `SocketDataModel`: Per-socket data storage
+
+**3. Error Models** (`error_models.dart`)
+- `SocketIOError` (sealed class)
+- `TransportErrorModel`, `ConnectionErrorModel`, `ValidationErrorModel`
+- Typed error handling
+
+**4. Transport Models**
+- `TransportData` (sealed class)
+- `StringTransportData`, `BinaryTransportData`, `JsonTransportData`
+
+**5. Room & Broadcast Models**
+- `RoomMembership`: Type-safe room tracking
+- `BroadcastOptions`: Broadcast configuration
+- `RoomFilter`: Room selection logic
+
+### Sealed Class Pattern
+
+Sealed classes enable **exhaustive pattern matching**:
+
+```dart
+sealed class SocketIOError implements Exception {
+  String get type;
+  String get message;
+}
+
+class TransportErrorModel extends SocketIOError { ... }
+class ConnectionErrorModel extends SocketIOError { ... }
+class ValidationErrorModel extends SocketIOError { ... }
+
+// Exhaustive switch - compiler ensures all cases covered
+void handleError(SocketIOError error) {
+  switch (error) {
+    case TransportErrorModel():
+      // Handle transport error
+    case ConnectionErrorModel():
+      // Handle connection error
+    case ValidationErrorModel():
+      // Handle validation error
+  }
+  // Compiler error if any case is missing!
+}
+```
+
+---
+
+## Extension Methods Pattern
+
+Extensions add utility methods **without modifying core classes**:
+
+### Examples
+
+**Packet Extensions** (`packet_extensions.dart`):
+```dart
+extension PacketExtensions on SocketIOPacket {
+  bool get isConnect => type == CONNECT;
+  bool get isDisconnect => type == DISCONNECT;
+  bool get isEvent => type == EVENT || type == BINARY_EVENT;
+  String get typeName { ... }
+  String get description { ... }
+}
+```
+
+**Map Extensions** (`map_extensions.dart`):
+```dart
+extension TypeSafeMapAccess on Map {
+  T? getTyped(String key) => this[key] as T?;
+  String? getString(String key) => getTyped(key);
+  int? getInt(String key) => getTyped(key);
+  Map? getMap(String key) => 
+      getTyped>(key);
+}
+```
+
+**Duration Extensions** (`duration_extensions.dart`):
+```dart
+extension DurationFormatting on Duration {
+  String toReadableString() { ... }
+  bool get isLongerThan(Duration other) { ... }
+}
+```
+
+### Extension Benefits
+
+✅ **Non-intrusive**: Don't modify original classes  
+✅ **Organized**: Group related utilities  
+✅ **Discoverable**: IDE auto-completion  
+✅ **Type-safe**: Compile-time checking  
+✅ **Testable**: Easy to unit test
+
+---
+
+## Sealed Classes & Pattern Matching
+
+### Why Sealed Classes?
+
+Dart 3.0 introduced sealed classes for **closed type hierarchies**:
+
+```dart
+sealed class TransportData {
+  const TransportData();
+}
+
+final class StringTransportData extends TransportData {
+  final String value;
+  const StringTransportData(this.value);
+}
+
+final class BinaryTransportData extends TransportData {
+  final List bytes;
+  const BinaryTransportData(this.bytes);
+}
+
+final class JsonTransportData extends TransportData {
+  final Map data;
+  const JsonTransportData(this.data);
+}
+```
+
+### Benefits
+
+1. **Exhaustive Checking**: Compiler ensures all cases handled
+2. **No Default Case Needed**: All subtypes known at compile time
+3. **Refactoring Safety**: Adding new subtype causes compile errors
+4. **IDE Support**: Better auto-completion and hints
+
+### Usage in socket_io
+
+- `SocketIOPacket`: All packet types
+- `SocketIOError`: All error types
+- `TransportData`: All data formats
+- `EventData`: All event data types
+- `CookieConfig`: Enabled/disabled states
+- `ValidationError`: All validation error types
+
+---
+
+## Backward Compatibility Strategy
+
+### Dual API Pattern
+
+The library maintains **two parallel APIs**:
+
+**Legacy API** (for backward compatibility):
+```dart
+socket.on('message', (data) {
+  final map = data as Map;
+  print(map['text']);
+});
+
+socket.handshake['query'];  // Map
+socket.data['userId'] = 123;  // Map
+socket.acks[id] = callback;  // Map
+```
+
+**Modern Typed API**:
+```dart
+socket.on('message', (data) {
+  final eventData = EventData.fromDynamic(data);
+  if (eventData is MapEventData) {
+    print(eventData.value['text']);
+  }
+});
+
+socket.handshakeData?.query;  // QueryParameters
+socket.socketData.set('userId', 123);  // SocketDataModel
+socket.acksTyped[id] = callback;  // Map
+```
+
+### Implementation Strategy
+
+**Dual Fields**:
+```dart
+class Socket {
+  // Legacy (kept for BC)
+  Map? handshake;
+  Map data = {};
+  Map acks = {};
+  
+  // Modern typed (new)
+  HandshakeDataModel? handshakeData;
+  SocketDataModel socketData = SocketDataModel();
+  Map acksTyped = {};
+}
+```
+
+**Synchronization**:
+```dart
+// Both APIs work with same underlying data
+data = socketData.toMap();  // Sync old → new
+socketData.fromMap(data);   // Sync new → old
+```
+
+### Migration Path
+
+1. **Phase 1** (Current): Both APIs available
+2. **Phase 2** (Future): Deprecate old API
+3. **Phase 3** (Major version): Remove old API
+
+---
+
+## Transport Layer
+
+### Transport Hierarchy
+
+```
+Transport (abstract)
+├── WebSocketTransport    - Full-duplex, lowest latency
+├── PollingTransport      - HTTP long-polling fallback
+└── JSONPTransport        - Legacy browser support
+```
+
+### Transport Selection
+
+**Client-driven** with server preferences:
+
+```dart
+final server = Server(options: {
+  'transports': ['websocket', 'polling'],  // Preference order
+});
+```
+
+**Automatic fallback**:
+1. Try WebSocket
+2. Fall back to Polling if WebSocket unavailable
+3. Use JSONP for legacy browsers if needed
+
+### Transport Interface
+
+```dart
+abstract class Transport {
+  void send(List> packets);
+  void close();
+  void onPacket(Map packet);
+  void onClose();
+}
+```
+
+### Engine.IO Integration
+
+socket_io builds on **Engine.IO** for transport management:
+
+```
+Socket.IO (Application Protocol)
+      ↓
+  SocketIOPacket encoding/decoding
+      ↓
+Engine.IO (Transport Protocol)
+      ↓
+WebSocket | HTTP Polling | JSONP
+```
+
+---
+
+## Event System
+
+### EventEmitter Pattern
+
+Core event system based on **Observer pattern**:
+
+```dart
+class EventEmitter {
+  // Event → List of handlers
+  HashMap> _events;
+  
+  void on(String event, EventHandler handler) { ... }
+  void once(String event, EventHandler handler) { ... }
+  void emit(String event, [dynamic data]) { ... }
+  void off(String event, [EventHandler? handler]) { ... }
+}
+```
+
+### Event Flow
+
+```
+Client Event
+    ↓
+Transport.onPacket()
+    ↓
+Engine.Socket.onPacket()
+    ↓
+Client.onpacket()
+    ↓
+Socket.emit(eventName, data)
+    ↓
+User Handler
+```
+
+### Reserved Events
+
+Defined in `SocketEvents.blacklisted`:
+- `connect` / `connection`
+- `disconnect`
+- `error`
+- `newListener`
+- `removeListener`
+
+Custom events **cannot** use these names (validated by `EventName` value object).
+
+---
+
+## Testing Strategy
+
+### Test Organization
+
+```
+test/
+├── models/                 # Domain model tests
+│   ├── packet_models_test.dart
+│   ├── error_models_test.dart
+│   └── ... (15 more)
+├── value_objects/          # Value object tests
+│   ├── connection_id_vo_test.dart
+│   ├── room_name_vo_test.dart
+│   └── ... (12 more)
+├── adapter_broadcast_models_test.dart
+├── namespace_config_models_test.dart
+└── typed_event_emitter_test.dart
+```
+
+### Testing Principles
+
+1. **Unit Tests**: Every value object and model tested independently
+2. **Property-Based**: Validation rules thoroughly tested
+3. **Edge Cases**: Null, empty, invalid inputs
+4. **Type Safety**: Ensure compile-time type checking works
+5. **Backward Compatibility**: Both APIs tested
+
+### Test Coverage
+
+- **748 tests** across 34 test files
+- **Value Objects**: 100% coverage of validation rules
+- **Models**: Factory methods, equality, serialization
+- **Extensions**: All utility methods tested
+- **Error Handling**: All error types and factories
+
+### Example Test Pattern
+
+```dart
+group('ConnectionId', () {
+  test('creates valid ConnectionId from non-empty string', () {
+    final id = ConnectionId('test-id');
+    expect(id.value, equals('test-id'));
+  });
+  
+  test('throws ArgumentError for empty string', () {
+    expect(() => ConnectionId(''), throwsArgumentError);
+  });
+  
+  test('equality works correctly', () {
+    final id1 = ConnectionId('test');
+    final id2 = ConnectionId('test');
+    expect(id1, equals(id2));
+  });
+  
+  test('hashCode works correctly', () {
+    final id1 = ConnectionId('test');
+    final id2 = ConnectionId('test');
+    expect(id1.hashCode, equals(id2.hashCode));
+  });
+});
+```
+
+---
+
+## Key Design Patterns
+
+### 1. Factory Pattern
+
+Used extensively for packet creation:
+
+```dart
+// Factory methods for different packet types
+EventPacket.typed(eventName: 'message', data: ...);
+AckPacket.typed(id: '123', data: ...);
+ConnectPacket.typed(namespace: '/chat');
+```
+
+### 2. Builder Pattern
+
+Complex object construction:
+
+```dart
+HandshakeDataBuilder()
+  .headers(request.headers)
+  .time(DateTime.now())
+  .address(remoteAddress)
+  .secure(true)
+  .build();
+```
+
+### 3. Adapter Pattern
+
+Pluggable room/namespace management:
+
+```dart
+abstract class Adapter {
+  void add(String id, String room);
+  void broadcast(Map packet, Map? opts);
+}
+
+// Default implementation
+class _MemoryStoreAdapter extends Adapter { ... }
+
+// Future: RedisAdapter, MongoAdapter, etc.
+```
+
+### 4. Observer Pattern
+
+Event system foundation:
+
+```dart
+server.on('connection', (socket) { ... });
+socket.on('message', (data) { ... });
+```
+
+### 5. Strategy Pattern
+
+Transport selection and fallback:
+
+```dart
+final transports = [
+  WebSocketTransport(),
+  PollingTransport(),
+  JSONPTransport(),
+];
+```
+
+---
+
+## Extension Points
+
+### Custom Adapters
+
+Implement `Adapter` interface for distributed setups:
+
+```dart
+class RedisAdapter extends Adapter {
+  @override
+  void broadcast(Map packet, Map? opts) {
+    redis.publish(channel, packet);
+  }
+}
+
+server.adapter = 'redis';
+Adapter.register('redis', (nsp) => RedisAdapter(nsp));
+```
+
+### Custom Transports
+
+Extend `Transport` for new protocols:
+
+```dart
+class CustomTransport extends Transport {
+  @override
+  void send(List packets) { ... }
+}
+```
+
+### Middleware
+
+Add custom authentication, logging, rate limiting:
+
+```dart
+namespace.use((socket, next) {
+  if (isAuthenticated(socket)) {
+    next(null);
+  } else {
+    next('Authentication required');
+  }
+});
+```
+
+---
+
+## Performance Considerations
+
+### Memory Efficiency
+
+- **Value Objects**: Immutable, can be cached/reused
+- **Sealed Classes**: Optimized pattern matching
+- **Event Handlers**: Lazy initialization of maps
+
+### Type Safety Benefits
+
+- **Compile-time checks**: Catch errors early
+- **No runtime casts**: Faster execution
+- **Better tree-shaking**: Unused code eliminated
+
+### Scalability
+
+- **Adapter pattern**: Enables horizontal scaling with Redis
+- **Namespace isolation**: Prevents cross-talk
+- **Room-based broadcasting**: Efficient message routing
+
+---
+
+## Future Architecture Plans
+
+### Planned Improvements
+
+1. **Distributed Adapters**: Redis, MongoDB support
+2. **Binary Protocol**: More efficient encoding
+3. **Streaming**: Large file transfer support
+4. **State Management**: Persistent session state
+5. **Metrics**: Built-in performance monitoring
+
+### API Evolution
+
+Following **deprecation policy**:
+
+```
+v2.x: Dual API (current)
+v3.x: Deprecate legacy API
+v4.x: Remove legacy API (breaking)
+```
+
+---
+
+## Conclusion
+
+socket_io's architecture balances:
+
+✅ **Modern Dart**: Leverages Dart 3.0+ features  
+✅ **Type Safety**: Value objects and sealed classes  
+✅ **Backward Compatibility**: Smooth migration path  
+✅ **Extensibility**: Adapters, middleware, custom transports  
+✅ **Performance**: Efficient event routing and transport selection  
+✅ **Maintainability**: Clear separation of concerns, comprehensive tests
+
+The architecture supports both immediate production use and long-term evolution.
+
+---
+
+**Document Version:** 1.0  
+**Last Updated:** 2025-10-11  
+**Maintainers:** socket_io team
diff --git a/doc/CONTRIBUTING.md b/doc/CONTRIBUTING.md
new file mode 100644
index 0000000..7def395
--- /dev/null
+++ b/doc/CONTRIBUTING.md
@@ -0,0 +1,686 @@
+# Contributing to socket_io
+
+Thank you for your interest in contributing to socket_io! This document provides guidelines and instructions for contributing to the project.
+
+---
+
+## Table of Contents
+
+1. [Code of Conduct](#code-of-conduct)
+2. [Getting Started](#getting-started)
+3. [Development Setup](#development-setup)
+4. [Coding Standards](#coding-standards)
+5. [Testing Requirements](#testing-requirements)
+6. [Pull Request Process](#pull-request-process)
+7. [Architecture Guidelines](#architecture-guidelines)
+8. [Common Tasks](#common-tasks)
+9. [Release Process](#release-process)
+
+---
+
+## Code of Conduct
+
+### Our Standards
+
+- **Be respectful** and constructive in all interactions
+- **Welcome newcomers** and help them get started
+- **Focus on what is best** for the community and project
+- **Show empathy** towards other community members
+- **Accept constructive criticism** gracefully
+
+### Unacceptable Behavior
+
+- Harassment, discrimination, or offensive comments
+- Trolling, insulting, or derogatory remarks
+- Public or private harassment
+- Publishing others' private information without permission
+
+---
+
+## Getting Started
+
+### Prerequisites
+
+- **Dart SDK**: >= 3.0.0 < 4.0.0
+- **Git**: For version control
+- **IDE**: VS Code, IntelliJ IDEA, or Android Studio (recommended)
+
+### Fork and Clone
+
+1. **Fork** the repository on GitHub
+2. **Clone** your fork locally:
+   ```bash
+   git clone https://github.com/YOUR_USERNAME/socket.io-dart.git
+   cd socket.io-dart
+   ```
+
+3. **Add upstream** remote:
+   ```bash
+   git remote add upstream https://github.com/rikulo/socket.io-dart.git
+   ```
+
+4. **Install dependencies**:
+   ```bash
+   dart pub get
+   ```
+
+---
+
+## Development Setup
+
+### Install Development Tools
+
+```bash
+# Format checker
+dart format --version
+
+# Analyzer
+dart analyze --version
+
+# Test runner
+dart test --version
+```
+
+### Verify Setup
+
+Run all quality checks:
+
+```bash
+# Format check
+dart format . --output none --set-exit-if-changed
+
+# Analysis
+dart analyze
+
+# Tests
+dart test
+```
+
+All commands should complete successfully.
+
+---
+
+## Coding Standards
+
+### File Naming
+
+- **Snake case** for files: `socket_options_models.dart`
+- **PascalCase** for classes: `SocketOptionsModel`
+- **camelCase** for variables and functions: `connectionId`, `buildHandshake()`
+
+### Code Style
+
+**Follow official Dart style guide**: https://dart.dev/guides/language/effective-dart/style
+
+#### Formatting
+
+```bash
+# Format all Dart files
+dart format .
+```
+
+Our configuration (in `analysis_options.yaml`):
+- **Page width**: 120 characters
+- **Trailing commas**: Preserved for better diffs
+
+#### Naming Conventions
+
+```dart
+// Classes: PascalCase
+class ConnectionManager { }
+
+// Variables: camelCase
+final connectionId = ConnectionId('123');
+
+// Constants: lowerCamelCase
+const defaultPort = 3000;
+
+// Private: prefix with _
+String _privateField;
+void _privateMethod() { }
+```
+
+#### Import Organization
+
+```dart
+// 1. Dart imports
+import 'dart:async';
+import 'dart:io';
+
+// 2. Package imports
+import 'package:logging/logging.dart';
+import 'package:socket_io_common/socket_io_common.dart';
+
+// 3. Relative imports
+import 'models/packet_models.dart';
+import 'value_objects/connection_id_vo.dart';
+```
+
+#### Comments
+
+```dart
+/// Public API documentation (triple-slash)
+/// 
+/// Detailed description of the class/method.
+/// 
+/// Example:
+/// ```dart
+/// final socket = Socket(...);
+/// socket.emit('event', ['data']);
+/// ```
+class Socket {
+  // Implementation comments (double-slash)
+  void _privateMethod() {
+    // Explain complex logic here
+  }
+}
+```
+
+### Type Safety
+
+#### Always Specify Types
+
+```dart
+// ✅ Good
+final String name = 'socket';
+final List rooms = [];
+final Map data = {};
+
+// ❌ Avoid
+var name = 'socket';  // Don't use var
+final rooms = [];     // Missing type
+```
+
+#### Avoid Dynamic
+
+```dart
+// ✅ Good - Use specific types
+void handleEvent(EventData data) { }
+final Map query = {};
+
+// ❌ Avoid - Dynamic is not type-safe
+void handleEvent(dynamic data) { }
+final Map query = {};  // Only if truly needed
+```
+
+#### Null Safety
+
+```dart
+// ✅ Good - Explicit nullable types
+String? optionalValue;
+final String requiredValue = optionalValue ?? 'default';
+
+// ✅ Good - Late initialization only when necessary
+late final String lateValue;
+
+// ❌ Avoid - Use nullable instead of late when possible
+late String? confusing;  // Rarely needed
+```
+
+---
+
+## Testing Requirements
+
+### Test Coverage
+
+**All new code must have tests** covering:
+
+1. **Happy paths**: Normal usage scenarios
+2. **Edge cases**: Null, empty, boundary values
+3. **Error cases**: Invalid inputs, exceptions
+4. **Type safety**: Ensure compile-time checking
+
+### Test Organization
+
+```dart
+group('FeatureName', () {
+  group('Method/Aspect', () {
+    test('specific behavior description', () {
+      // Arrange
+      final instance = MyClass();
+      
+      // Act
+      final result = instance.method();
+      
+      // Assert
+      expect(result, equals(expected));
+    });
+  });
+});
+```
+
+### Running Tests
+
+```bash
+# Run all tests
+dart test
+
+# Run specific test file
+dart test test/models/packet_models_test.dart
+
+# Run with coverage
+dart test --coverage=coverage
+```
+
+### Test Naming
+
+```dart
+// ✅ Good - Descriptive test names
+test('creates valid ConnectionId from non-empty string', () { });
+test('throws ArgumentError when ID is empty', () { });
+test('equality works correctly for same values', () { });
+
+// ❌ Avoid - Vague test names
+test('test1', () { });
+test('works', () { });
+```
+
+### Example Test
+
+```dart
+import 'package:test/test.dart';
+import 'package:socket_io/socket_io.dart';
+
+void main() {
+  group('ConnectionId', () {
+    test('creates valid ConnectionId from non-empty string', () {
+      final id = ConnectionId('test-123');
+      expect(id.value, equals('test-123'));
+    });
+    
+    test('throws ArgumentError for empty string', () {
+      expect(() => ConnectionId(''), throwsArgumentError);
+    });
+    
+    test('equality works correctly', () {
+      final id1 = ConnectionId('test');
+      final id2 = ConnectionId('test');
+      final id3 = ConnectionId('other');
+      
+      expect(id1, equals(id2));
+      expect(id1, isNot(equals(id3)));
+    });
+    
+    test('hashCode is consistent', () {
+      final id1 = ConnectionId('test');
+      final id2 = ConnectionId('test');
+      
+      expect(id1.hashCode, equals(id2.hashCode));
+    });
+  });
+}
+```
+
+---
+
+## Pull Request Process
+
+### Before Creating a PR
+
+1. **Sync with upstream**:
+   ```bash
+   git fetch upstream
+   git rebase upstream/main
+   ```
+
+2. **Run all checks**:
+   ```bash
+   dart format .
+   dart analyze
+   dart test
+   ```
+
+3. **Commit with clear messages**:
+   ```bash
+   git commit -m "feat: add binary data detection
+   
+   - Implement _containsBinaryData() method
+   - Add recursive checking for nested structures
+   - Update tests to cover new functionality"
+   ```
+
+### Commit Message Format
+
+```
+: 
+
+
+
+