1111 lines
28 KiB
Markdown
1111 lines
28 KiB
Markdown
# Network Library Phase 0 Implementation Plan
|
|
|
|
This document outlines the implementation plan for Phase 0 of the network library, focusing on core components and event system integration.
|
|
|
|
## Implementation Goals
|
|
|
|
1. Create utils directory with @suaveplan/events integration
|
|
2. Implement core NetworkNode methods
|
|
3. Implement NetworkSystem core methods
|
|
4. Add API versioning infrastructure
|
|
5. Establish testing patterns
|
|
|
|
## Architectural Overview
|
|
|
|
Phase 0 will establish the foundation for the modular network library. The core components and their relationships are illustrated in the following diagram:
|
|
|
|
```mermaid
|
|
classDiagram
|
|
class EventEmitter {
|
|
<<@suaveplan/events>>
|
|
+on(event, listener, context?)
|
|
+once(event, listener, context?)
|
|
+off(event, listener, context?)
|
|
+emit(event, ...args)
|
|
}
|
|
|
|
class NetworkEventEmitter {
|
|
-_emitter: EventEmitter
|
|
+on(event, listener, context?)
|
|
+once(event, listener, context?)
|
|
+off(event, listener, context?)
|
|
+emit(event, ...args)
|
|
+onNodeConnected(handler)
|
|
+onNodeDisconnected(handler)
|
|
+onMessageSent(handler)
|
|
}
|
|
|
|
class NetworkNode {
|
|
-_id: string
|
|
-_level: number
|
|
-_parent: NetworkNode | null
|
|
-_children: Map~string, NetworkNode~
|
|
-_siblings: Map~string, NetworkNode~
|
|
-_events: NetworkEventEmitter
|
|
+connect()
|
|
+disconnect()
|
|
+addChild(node)
|
|
+removeChild(nodeId)
|
|
+sendMessage(message)
|
|
}
|
|
|
|
class NetworkSystem {
|
|
-_nodes: Map~string, NetworkNode~
|
|
-_config: NetworkSystemConfig
|
|
-_events: NetworkEventEmitter
|
|
+createNode(id, capabilities)
|
|
+getNode(id)
|
|
+removeNode(id)
|
|
+placeNodeInHierarchy(node)
|
|
}
|
|
|
|
class VersionInfo {
|
|
+major: number
|
|
+minor: number
|
|
+patch: number
|
|
+isCompatibleWith(version)
|
|
}
|
|
|
|
class IPlugin {
|
|
<<interface>>
|
|
+id: string
|
|
+onRegister(entity)
|
|
+onUnregister(entity)
|
|
}
|
|
|
|
class PluginRegistry {
|
|
-_plugins: Map~string, IPlugin~
|
|
+register(plugin)
|
|
+unregister(pluginId)
|
|
+getPlugin(pluginId)
|
|
}
|
|
|
|
NetworkNode --> NetworkEventEmitter : uses
|
|
NetworkSystem --> NetworkEventEmitter : uses
|
|
NetworkSystem --> NetworkNode : manages
|
|
NetworkNode --> PluginRegistry : has
|
|
NetworkEventEmitter --> EventEmitter : wraps
|
|
NetworkNode ..> IPlugin : supports
|
|
PluginRegistry o-- IPlugin : contains
|
|
```
|
|
|
|
## Implementation Steps
|
|
|
|
### 1. Create Utils Directory with Event Integration
|
|
|
|
#### 1.1 Create Event Constants
|
|
|
|
**File: `src/utils/event-constants.ts`**
|
|
|
|
```typescript
|
|
/**
|
|
* Standard event names used throughout the network library
|
|
*/
|
|
export enum NetworkEvents {
|
|
// Node lifecycle events
|
|
NODE_CREATED = 'node:created',
|
|
NODE_CONNECTED = 'node:connected',
|
|
NODE_DISCONNECTED = 'node:disconnected',
|
|
NODE_UPDATED = 'node:updated',
|
|
NODE_REMOVED = 'node:removed',
|
|
|
|
// Message events
|
|
MESSAGE_SENT = 'message:sent',
|
|
MESSAGE_RECEIVED = 'message:received',
|
|
MESSAGE_ROUTED = 'message:routed',
|
|
MESSAGE_DROPPED = 'message:dropped',
|
|
|
|
// Hierarchy events
|
|
HIERARCHY_CHANGED = 'hierarchy:changed',
|
|
PARENT_CHANGED = 'hierarchy:parent-changed',
|
|
CHILD_ADDED = 'hierarchy:child-added',
|
|
CHILD_REMOVED = 'hierarchy:child-removed',
|
|
SIBLING_ADDED = 'hierarchy:sibling-added',
|
|
SIBLING_REMOVED = 'hierarchy:sibling-removed',
|
|
LEVEL_CHANGED = 'hierarchy:level-changed',
|
|
|
|
// Plugin events
|
|
PLUGIN_REGISTERED = 'plugin:registered',
|
|
PLUGIN_UNREGISTERED = 'plugin:unregistered',
|
|
|
|
// System events
|
|
SYSTEM_ERROR = 'system:error',
|
|
SYSTEM_WARNING = 'system:warning',
|
|
SYSTEM_INFO = 'system:info'
|
|
}
|
|
```
|
|
|
|
#### 1.2 Create Network Event Emitter
|
|
|
|
**File: `src/utils/network-event-emitter.ts`**
|
|
|
|
```typescript
|
|
import { EventEmitter } from '@suaveplan/events';
|
|
import { NetworkEvents } from './event-constants';
|
|
|
|
/**
|
|
* NetworkEventEmitter provides a standardized event handling system for the network library.
|
|
* It wraps the @suaveplan/events EventEmitter with network-specific features.
|
|
*/
|
|
export class NetworkEventEmitter {
|
|
private _emitter: EventEmitter;
|
|
|
|
constructor(emitter?: EventEmitter) {
|
|
this._emitter = emitter || new EventEmitter();
|
|
}
|
|
|
|
/**
|
|
* Registers a listener for the specified event
|
|
*/
|
|
on(event: string, listener: Function, context?: any): this {
|
|
this._emitter.on(event, listener, context);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Registers a one-time listener for the specified event
|
|
*/
|
|
once(event: string, listener: Function, context?: any): this {
|
|
this._emitter.once(event, listener, context);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Removes a listener for the specified event
|
|
*/
|
|
off(event: string, listener?: Function, context?: any): this {
|
|
this._emitter.off(event, listener, context);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Emits an event with the provided arguments
|
|
*/
|
|
emit(event: string, ...args: any[]): boolean {
|
|
return this._emitter.emit(event, ...args);
|
|
}
|
|
|
|
/**
|
|
* Returns all registered event names
|
|
*/
|
|
eventNames(): string[] {
|
|
return this._emitter.eventNames();
|
|
}
|
|
|
|
/**
|
|
* Returns the number of listeners for a given event
|
|
*/
|
|
listenerCount(event: string): number {
|
|
return this._emitter.listenerCount(event);
|
|
}
|
|
|
|
// Convenience methods for common events
|
|
|
|
/**
|
|
* Registers a listener for the NODE_CONNECTED event
|
|
*/
|
|
onNodeConnected(listener: Function, context?: any): this {
|
|
return this.on(NetworkEvents.NODE_CONNECTED, listener, context);
|
|
}
|
|
|
|
/**
|
|
* Registers a listener for the NODE_DISCONNECTED event
|
|
*/
|
|
onNodeDisconnected(listener: Function, context?: any): this {
|
|
return this.on(NetworkEvents.NODE_DISCONNECTED, listener, context);
|
|
}
|
|
|
|
/**
|
|
* Registers a listener for the MESSAGE_SENT event
|
|
*/
|
|
onMessageSent(listener: Function, context?: any): this {
|
|
return this.on(NetworkEvents.MESSAGE_SENT, listener, context);
|
|
}
|
|
|
|
/**
|
|
* Registers a listener for the MESSAGE_RECEIVED event
|
|
*/
|
|
onMessageReceived(listener: Function, context?: any): this {
|
|
return this.on(NetworkEvents.MESSAGE_RECEIVED, listener, context);
|
|
}
|
|
}
|
|
```
|
|
|
|
#### 1.3 Create Utils Index
|
|
|
|
**File: `src/utils/index.ts`**
|
|
|
|
```typescript
|
|
export * from './event-constants';
|
|
export * from './network-event-emitter';
|
|
```
|
|
|
|
### 2. Update Types for Event System Integration
|
|
|
|
**File: `src/types.ts` (additions)**
|
|
|
|
```typescript
|
|
// Add these interfaces to the existing types.ts file
|
|
|
|
/**
|
|
* Interface for event emitters, matching the core functionality of @suaveplan/events
|
|
*/
|
|
export interface IEventEmitter {
|
|
on(event: string, listener: Function, context?: any): any;
|
|
once(event: string, listener: Function, context?: any): any;
|
|
off(event: string, listener?: Function, context?: any): any;
|
|
emit(event: string, ...args: any[]): boolean;
|
|
eventNames?(): string[];
|
|
listenerCount?(event: string): number;
|
|
}
|
|
|
|
/**
|
|
* Interface for NetworkEventEmitter, extending the base IEventEmitter
|
|
*/
|
|
export interface INetworkEventEmitter extends IEventEmitter {
|
|
onNodeConnected(listener: Function, context?: any): any;
|
|
onNodeDisconnected(listener: Function, context?: any): any;
|
|
onMessageSent(listener: Function, context?: any): any;
|
|
onMessageReceived(listener: Function, context?: any): any;
|
|
}
|
|
|
|
// Add to NodeState enum if it doesn't already exist
|
|
export enum NodeState {
|
|
CREATED = 'created',
|
|
CONNECTING = 'connecting',
|
|
CONNECTED = 'connected',
|
|
DISCONNECTING = 'disconnecting',
|
|
DISCONNECTED = 'disconnected',
|
|
ERROR = 'error'
|
|
}
|
|
|
|
// Add API version interface
|
|
export interface APIVersion {
|
|
major: number;
|
|
minor: number;
|
|
patch: number;
|
|
}
|
|
```
|
|
|
|
### 3. Implement Basic Plugin System Interfaces
|
|
|
|
**File: `src/plugins/plugin-registry.ts`**
|
|
|
|
```typescript
|
|
import { IPlugin } from '../types';
|
|
|
|
/**
|
|
* PluginRegistry manages the registration and lifecycle of plugins.
|
|
*/
|
|
export class PluginRegistry {
|
|
private _plugins: Map<string, IPlugin>;
|
|
private _entity: any; // The entity (node or system) this registry belongs to
|
|
|
|
constructor(entity: any) {
|
|
this._plugins = new Map();
|
|
this._entity = entity;
|
|
}
|
|
|
|
/**
|
|
* Registers a plugin with the entity
|
|
*/
|
|
register(plugin: IPlugin): boolean {
|
|
if (this._plugins.has(plugin.id)) {
|
|
return false;
|
|
}
|
|
|
|
this._plugins.set(plugin.id, plugin);
|
|
plugin.onRegister(this._entity);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Unregisters a plugin from the entity
|
|
*/
|
|
unregister(pluginId: string): boolean {
|
|
const plugin = this._plugins.get(pluginId);
|
|
if (!plugin) {
|
|
return false;
|
|
}
|
|
|
|
plugin.onUnregister(this._entity);
|
|
this._plugins.delete(pluginId);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Returns a plugin by ID
|
|
*/
|
|
getPlugin<T extends IPlugin>(pluginId: string): T | undefined {
|
|
return this._plugins.get(pluginId) as T | undefined;
|
|
}
|
|
|
|
/**
|
|
* Checks if a plugin is registered
|
|
*/
|
|
hasPlugin(pluginId: string): boolean {
|
|
return this._plugins.has(pluginId);
|
|
}
|
|
|
|
/**
|
|
* Returns all registered plugins
|
|
*/
|
|
getAllPlugins(): IPlugin[] {
|
|
return Array.from(this._plugins.values());
|
|
}
|
|
}
|
|
```
|
|
|
|
### 4. Implement API Versioning Infrastructure
|
|
|
|
#### 4.1 Create Version Info Class
|
|
|
|
**File: `src/versioning/version-info.ts`**
|
|
|
|
```typescript
|
|
import { APIVersion } from '../types';
|
|
|
|
/**
|
|
* VersionInfo represents a semantic version and provides
|
|
* compatibility checking functionality.
|
|
*/
|
|
export class VersionInfo implements APIVersion {
|
|
readonly major: number;
|
|
readonly minor: number;
|
|
readonly patch: number;
|
|
|
|
constructor(major: number, minor: number, patch: number) {
|
|
this.major = major;
|
|
this.minor = minor;
|
|
this.patch = patch;
|
|
}
|
|
|
|
/**
|
|
* Creates a VersionInfo from a version string (e.g., "1.2.3")
|
|
*/
|
|
static fromString(version: string): VersionInfo {
|
|
const parts = version.split('.').map(p => parseInt(p, 10));
|
|
return new VersionInfo(
|
|
parts[0] || 0,
|
|
parts[1] || 0,
|
|
parts[2] || 0
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Converts the version to a string representation
|
|
*/
|
|
toString(): string {
|
|
return `${this.major}.${this.minor}.${this.patch}`;
|
|
}
|
|
|
|
/**
|
|
* Checks if this version is compatible with another version
|
|
* Compatible means same major version and minor version >= required
|
|
*/
|
|
isCompatibleWith(other: VersionInfo): boolean {
|
|
// Major version must match for compatibility
|
|
if (this.major !== other.major) {
|
|
return false;
|
|
}
|
|
|
|
// Minor version must be >= the required version
|
|
return this.minor >= other.minor;
|
|
}
|
|
|
|
/**
|
|
* Checks if this version is newer than another version
|
|
*/
|
|
isNewerThan(other: VersionInfo): boolean {
|
|
if (this.major > other.major) return true;
|
|
if (this.major < other.major) return false;
|
|
|
|
if (this.minor > other.minor) return true;
|
|
if (this.minor < other.minor) return false;
|
|
|
|
return this.patch > other.patch;
|
|
}
|
|
}
|
|
```
|
|
|
|
#### 4.2 Create Version Manager
|
|
|
|
**File: `src/versioning/version-manager.ts`**
|
|
|
|
```typescript
|
|
import { VersionInfo } from './version-info';
|
|
|
|
/**
|
|
* VersionManager handles API versioning and compatibility checking
|
|
*/
|
|
export class VersionManager {
|
|
static readonly CURRENT_VERSION = new VersionInfo(0, 1, 0);
|
|
|
|
/**
|
|
* Checks if a version is compatible with the current library version
|
|
*/
|
|
static isCompatible(version: VersionInfo): boolean {
|
|
return VersionManager.CURRENT_VERSION.isCompatibleWith(version);
|
|
}
|
|
|
|
/**
|
|
* Returns a list of versions between from and to (inclusive)
|
|
* This can be used for migration paths
|
|
*/
|
|
static getMigrationPath(fromVersion: VersionInfo, toVersion: VersionInfo): VersionInfo[] {
|
|
const result: VersionInfo[] = [];
|
|
|
|
// If versions are incompatible (different major), return empty path
|
|
if (fromVersion.major !== toVersion.major) {
|
|
return result;
|
|
}
|
|
|
|
// Add all minor versions between from and to
|
|
for (let minor = fromVersion.minor; minor <= toVersion.minor; minor++) {
|
|
result.push(new VersionInfo(fromVersion.major, minor, 0));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Returns the stability classification for the current version
|
|
*/
|
|
static getStabilityClassification(): 'experimental' | 'stable' | 'deprecated' {
|
|
// Phase 0 is experimental
|
|
return 'experimental';
|
|
}
|
|
}
|
|
```
|
|
|
|
#### 4.3 Create Versioning Index
|
|
|
|
**File: `src/versioning/index.ts`**
|
|
|
|
```typescript
|
|
export * from './version-info';
|
|
export * from './version-manager';
|
|
```
|
|
|
|
### 5. Implement Core NetworkNode Methods
|
|
|
|
**File: `src/node/node.ts` (update)**
|
|
|
|
```typescript
|
|
import {
|
|
NodeState,
|
|
NodeCapabilities,
|
|
NodePerformance,
|
|
NetworkMessage,
|
|
MessagePriority,
|
|
LevelConfig,
|
|
MessageRoutingConfig,
|
|
NetworkSystemConfig,
|
|
INetworkEventEmitter
|
|
} from '../types';
|
|
import { NetworkEventEmitter, NetworkEvents } from '../utils';
|
|
import { PluginRegistry } from '../plugins/plugin-registry';
|
|
import { IPlugin } from '../types';
|
|
|
|
export class NetworkNode {
|
|
private _id: string;
|
|
private _state: NodeState = NodeState.CREATED;
|
|
private _level: number;
|
|
private _parent: NetworkNode | null = null;
|
|
private _children: Map<string, NetworkNode> = new Map();
|
|
private _siblings: Map<string, NetworkNode> = new Map();
|
|
private _capabilities: NodeCapabilities;
|
|
private _events: INetworkEventEmitter;
|
|
private _plugins: PluginRegistry;
|
|
|
|
constructor(
|
|
id: string,
|
|
level: number,
|
|
capabilities: NodeCapabilities,
|
|
eventEmitter?: INetworkEventEmitter
|
|
) {
|
|
this._id = id;
|
|
this._level = level;
|
|
this._capabilities = capabilities;
|
|
this._events = eventEmitter || new NetworkEventEmitter();
|
|
this._plugins = new PluginRegistry(this);
|
|
|
|
// Emit node created event
|
|
this._events.emit(NetworkEvents.NODE_CREATED, this);
|
|
}
|
|
|
|
/**
|
|
* Returns the node's ID
|
|
*/
|
|
get id(): string {
|
|
return this._id;
|
|
}
|
|
|
|
/**
|
|
* Returns the node's hierarchy level
|
|
*/
|
|
get level(): number {
|
|
return this._level;
|
|
}
|
|
|
|
/**
|
|
* Returns the node's parent node, or null if it has no parent
|
|
*/
|
|
get parent(): NetworkNode | null {
|
|
return this._parent;
|
|
}
|
|
|
|
/**
|
|
* Returns the node's children as an array
|
|
*/
|
|
get children(): NetworkNode[] {
|
|
return Array.from(this._children.values());
|
|
}
|
|
|
|
/**
|
|
* Returns the node's siblings as an array
|
|
*/
|
|
get siblings(): NetworkNode[] {
|
|
return Array.from(this._siblings.values());
|
|
}
|
|
|
|
/**
|
|
* Returns the node's capabilities
|
|
*/
|
|
get capabilities(): NodeCapabilities {
|
|
return this._capabilities;
|
|
}
|
|
|
|
/**
|
|
* Returns the node's current state
|
|
*/
|
|
get state(): NodeState {
|
|
return this._state;
|
|
}
|
|
|
|
/**
|
|
* Returns the number of children
|
|
*/
|
|
get childCount(): number {
|
|
return this._children.size;
|
|
}
|
|
|
|
/**
|
|
* Connects the node to the network
|
|
*/
|
|
connect(): boolean {
|
|
if (this._state !== NodeState.CREATED && this._state !== NodeState.DISCONNECTED) {
|
|
return false;
|
|
}
|
|
|
|
this._state = NodeState.CONNECTING;
|
|
this._events.emit(NetworkEvents.NODE_UPDATED, this);
|
|
|
|
// Simulate successful connection for Phase 0
|
|
this._state = NodeState.CONNECTED;
|
|
this._events.emit(NetworkEvents.NODE_CONNECTED, this);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Disconnects the node from the network
|
|
*/
|
|
disconnect(): boolean {
|
|
if (this._state !== NodeState.CONNECTED && this._state !== NodeState.CONNECTING) {
|
|
return false;
|
|
}
|
|
|
|
this._state = NodeState.DISCONNECTING;
|
|
this._events.emit(NetworkEvents.NODE_UPDATED, this);
|
|
|
|
// Simulate successful disconnection for Phase 0
|
|
this._state = NodeState.DISCONNECTED;
|
|
this._events.emit(NetworkEvents.NODE_DISCONNECTED, this);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Checks if the node can accept more children
|
|
*/
|
|
canAcceptChildren(): boolean {
|
|
// For Phase 0, just check if level > 0 (clients can't have children)
|
|
return this._level > 0;
|
|
}
|
|
|
|
/**
|
|
* Adds a child node to this node
|
|
*/
|
|
addChild(node: NetworkNode): boolean {
|
|
if (!this.canAcceptChildren() || this._children.has(node.id) || node.id === this._id) {
|
|
return false;
|
|
}
|
|
|
|
// Remove from previous parent if exists
|
|
if (node.parent) {
|
|
node.parent.removeChild(node.id);
|
|
}
|
|
|
|
// Add as child
|
|
this._children.set(node.id, node);
|
|
this._setParentForNode(node);
|
|
|
|
// Emit events
|
|
this._events.emit(NetworkEvents.CHILD_ADDED, this, node);
|
|
this._events.emit(NetworkEvents.HIERARCHY_CHANGED, this);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Removes a child node from this node
|
|
*/
|
|
removeChild(nodeId: string): boolean {
|
|
const node = this._children.get(nodeId);
|
|
if (!node) {
|
|
return false;
|
|
}
|
|
|
|
// Remove child reference
|
|
this._children.delete(nodeId);
|
|
|
|
// Clear parent reference
|
|
this._clearParentForNode(node);
|
|
|
|
// Emit events
|
|
this._events.emit(NetworkEvents.CHILD_REMOVED, this, node);
|
|
this._events.emit(NetworkEvents.HIERARCHY_CHANGED, this);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Adds a sibling node to this node
|
|
*/
|
|
addSibling(node: NetworkNode): boolean {
|
|
if (node.id === this._id || this._siblings.has(node.id)) {
|
|
return false;
|
|
}
|
|
|
|
// Add sibling relationship (bidirectional)
|
|
this._siblings.set(node.id, node);
|
|
node._siblings.set(this._id, this);
|
|
|
|
// Emit events
|
|
this._events.emit(NetworkEvents.SIBLING_ADDED, this, node);
|
|
this._events.emit(NetworkEvents.HIERARCHY_CHANGED, this);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Removes a sibling node from this node
|
|
*/
|
|
removeSibling(nodeId: string): boolean {
|
|
const node = this._siblings.get(nodeId);
|
|
if (!node) {
|
|
return false;
|
|
}
|
|
|
|
// Remove sibling relationship (bidirectional)
|
|
this._siblings.delete(nodeId);
|
|
node._siblings.delete(this._id);
|
|
|
|
// Emit events
|
|
this._events.emit(NetworkEvents.SIBLING_REMOVED, this, node);
|
|
this._events.emit(NetworkEvents.HIERARCHY_CHANGED, this);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Sends a message from this node
|
|
* Phase 0 implementation just emits an event
|
|
*/
|
|
async sendMessage(message: NetworkMessage): Promise<void> {
|
|
// For Phase 0, just emit the event
|
|
this._events.emit(NetworkEvents.MESSAGE_SENT, message, this);
|
|
}
|
|
|
|
/**
|
|
* Updates the node's level
|
|
*/
|
|
updateLevel(newLevel: number): boolean {
|
|
if (newLevel === this._level) {
|
|
return false;
|
|
}
|
|
|
|
const oldLevel = this._level;
|
|
this._level = newLevel;
|
|
|
|
// Emit events
|
|
this._events.emit(NetworkEvents.LEVEL_CHANGED, this, oldLevel, newLevel);
|
|
this._events.emit(NetworkEvents.NODE_UPDATED, this);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Registers a plugin with this node
|
|
*/
|
|
registerPlugin(plugin: IPlugin): boolean {
|
|
const result = this._plugins.register(plugin);
|
|
if (result) {
|
|
this._events.emit(NetworkEvents.PLUGIN_REGISTERED, this, plugin);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Removes a plugin from this node
|
|
*/
|
|
removePlugin(pluginId: string): boolean {
|
|
const plugin = this._plugins.getPlugin(pluginId);
|
|
const result = this._plugins.unregister(pluginId);
|
|
if (result && plugin) {
|
|
this._events.emit(NetworkEvents.PLUGIN_UNREGISTERED, this, plugin);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Gets a plugin by ID
|
|
*/
|
|
getPlugin<T extends IPlugin>(pluginId: string): T | undefined {
|
|
return this._plugins.getPlugin<T>(pluginId);
|
|
}
|
|
|
|
/**
|
|
* Sets the parent for a node
|
|
*/
|
|
private _setParentForNode(node: NetworkNode): void {
|
|
node._parent = this;
|
|
node._events.emit(NetworkEvents.PARENT_CHANGED, node, null, this);
|
|
}
|
|
|
|
/**
|
|
* Clears the parent for a node
|
|
*/
|
|
private _clearParentForNode(node: NetworkNode): void {
|
|
const oldParent = node._parent;
|
|
node._parent = null;
|
|
node._events.emit(NetworkEvents.PARENT_CHANGED, node, oldParent, null);
|
|
}
|
|
}
|
|
```
|
|
|
|
### 6. Implement NetworkSystem Core Methods
|
|
|
|
**File: `src/network/network.ts` (update)**
|
|
|
|
```typescript
|
|
import { NetworkSystemConfig, NodeCapabilities, INetworkEventEmitter, NodeState } from '../types';
|
|
import { NetworkNode } from '../node/node';
|
|
import { NetworkEventEmitter, NetworkEvents } from '../utils';
|
|
import { PluginRegistry } from '../plugins/plugin-registry';
|
|
import { IPlugin } from '../types';
|
|
import { VersionManager } from '../versioning';
|
|
|
|
export class NetworkSystem {
|
|
private _nodes: Map<string, NetworkNode> = new Map();
|
|
private _config: NetworkSystemConfig;
|
|
private _events: INetworkEventEmitter;
|
|
private _plugins: PluginRegistry;
|
|
|
|
constructor(config: Partial<NetworkSystemConfig> = {}, eventEmitter?: INetworkEventEmitter) {
|
|
this._config = this._createDefaultConfig(config);
|
|
this._events = eventEmitter || new NetworkEventEmitter();
|
|
this._plugins = new PluginRegistry(this);
|
|
}
|
|
|
|
/**
|
|
* Creates a new node and adds it to the system
|
|
*/
|
|
createNode(
|
|
id: string,
|
|
capabilities: NodeCapabilities
|
|
): NetworkNode {
|
|
// Determine initial level based on capabilities
|
|
const level = this._determineInitialLevel(capabilities);
|
|
|
|
// Create the node
|
|
const node = new NetworkNode(id, level, capabilities, this._events);
|
|
|
|
// Add to nodes map
|
|
this._nodes.set(id, node);
|
|
|
|
// Setup event handlers
|
|
this._setupNodeEventHandlers(node);
|
|
|
|
// Emit event
|
|
this._events.emit(NetworkEvents.NODE_CREATED, node);
|
|
|
|
return node;
|
|
}
|
|
|
|
/**
|
|
* Gets a node by ID
|
|
*/
|
|
getNode(id: string): NetworkNode | undefined {
|
|
return this._nodes.get(id);
|
|
}
|
|
|
|
/**
|
|
* Removes a node from the system
|
|
*/
|
|
removeNode(id: string): boolean {
|
|
const node = this._nodes.get(id);
|
|
if (!node) {
|
|
return false;
|
|
}
|
|
|
|
// Disconnect the node if connected
|
|
if (node.state === NodeState.CONNECTED) {
|
|
node.disconnect();
|
|
}
|
|
|
|
// Remove parent-child relationships
|
|
if (node.parent) {
|
|
node.parent.removeChild(node.id);
|
|
}
|
|
|
|
// Remove child-parent relationships
|
|
for (const child of node.children) {
|
|
node.removeChild(child.id);
|
|
}
|
|
|
|
// Remove sibling relationships
|
|
for (const sibling of node.siblings) {
|
|
node.removeSibling(sibling.id);
|
|
}
|
|
|
|
// Remove from nodes map
|
|
this._nodes.delete(id);
|
|
|
|
// Emit event
|
|
this._events.emit(NetworkEvents.NODE_REMOVED, node);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Gets all nodes in the system
|
|
*/
|
|
getAllNodes(): NetworkNode[] {
|
|
return Array.from(this._nodes.values());
|
|
}
|
|
|
|
/**
|
|
* Gets all nodes at a specific level
|
|
*/
|
|
getNodesByLevel(level: number): NetworkNode[] {
|
|
return this.getAllNodes().filter(node => node.level === level);
|
|
}
|
|
|
|
/**
|
|
* Finds the best parent for a node based on hierarchy
|
|
*/
|
|
findBestParent(node: NetworkNode): NetworkNode | null {
|
|
// For Phase 0, just find a node at the next higher level with available capacity
|
|
const potentialParents = this.getNodesByLevel(node.level + 1)
|
|
.filter(parent => parent.canAcceptChildren());
|
|
|
|
if (potentialParents.length === 0) {
|
|
return null;
|
|
}
|
|
|
|
// For now, just return the first available parent
|
|
// In future phases, this will use metrics to find optimal parent
|
|
return potentialParents[0];
|
|
}
|
|
|
|
/**
|
|
* Places a node in the hierarchy by finding an appropriate parent
|
|
*/
|
|
placeNodeInHierarchy(node: NetworkNode): boolean {
|
|
// Find best parent
|
|
const parent = this.findBestParent(node);
|
|
|
|
// If no parent available and not at top level, cannot place in hierarchy
|
|
if (!parent && node.level < this._config.maxLevels - 1) {
|
|
return false;
|
|
}
|
|
|
|
// If parent found, add as child
|
|
if (parent) {
|
|
return parent.addChild(node);
|
|
}
|
|
|
|
// If no parent and at top level, node is at top of hierarchy
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Registers a plugin with the system
|
|
*/
|
|
registerPlugin(plugin: IPlugin): boolean {
|
|
const result = this._plugins.register(plugin);
|
|
if (result) {
|
|
this._events.emit(NetworkEvents.PLUGIN_REGISTERED, this, plugin);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Removes a plugin from the system
|
|
*/
|
|
removePlugin(pluginId: string): boolean {
|
|
const plugin = this._plugins.getPlugin(pluginId);
|
|
const result = this._plugins.unregister(pluginId);
|
|
if (result && plugin) {
|
|
this._events.emit(NetworkEvents.PLUGIN_UNREGISTERED, this, plugin);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Gets a plugin by ID
|
|
*/
|
|
getPlugin<T extends IPlugin>(pluginId: string): T | undefined {
|
|
return this._plugins.getPlugin<T>(pluginId);
|
|
}
|
|
|
|
/**
|
|
* Returns the current library version
|
|
*/
|
|
getVersion() {
|
|
return VersionManager.CURRENT_VERSION;
|
|
}
|
|
|
|
/**
|
|
* Creates a default configuration
|
|
*/
|
|
private _createDefaultConfig(partial: Partial<NetworkSystemConfig>): NetworkSystemConfig {
|
|
return {
|
|
maxLevels: partial.maxLevels || 5,
|
|
defaultLevel: partial.defaultLevel || 0,
|
|
levelConfigs: partial.levelConfigs || DEFAULT_LEVEL_CONFIGS,
|
|
enableVoting: partial.enableVoting ?? false,
|
|
enableStateSync: partial.enableStateSync ?? false,
|
|
enableMetrics: partial.enableMetrics ?? false,
|
|
enableFailover: partial.enableFailover ?? false,
|
|
transportFallbackOrder: partial.transportFallbackOrder || [],
|
|
connectionTimeout: partial.connectionTimeout || 30000,
|
|
connectionRetries: partial.connectionRetries || 3,
|
|
heartbeatInterval: partial.heartbeatInterval || 5000
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Determines the initial level for a node based on capabilities
|
|
*/
|
|
private _determineInitialLevel(capabilities: NodeCapabilities): number {
|
|
// For Phase 0, just use default level
|
|
// In future phases, this will examine capabilities
|
|
return this._config.defaultLevel;
|
|
}
|
|
|
|
/**
|
|
* Sets up event handlers for a node
|
|
*/
|
|
private _setupNodeEventHandlers(node: NetworkNode): void {
|
|
// For Phase 0, just listen for basic events
|
|
// In future phases, this will be more comprehensive
|
|
}
|
|
}
|
|
|
|
export const DEFAULT_LEVEL_CONFIGS = [
|
|
{
|
|
level: 0,
|
|
name: "Client",
|
|
maxChildren: 0,
|
|
maxSiblings: Infinity,
|
|
minPromotionScore: 0.7,
|
|
demotionThreshold: 0
|
|
},
|
|
{
|
|
level: 1,
|
|
name: "Host",
|
|
maxChildren: 20,
|
|
maxSiblings: 10,
|
|
minPromotionScore: 0.8,
|
|
demotionThreshold: 0.4
|
|
},
|
|
{
|
|
level: 2,
|
|
name: "Tier Host",
|
|
maxChildren: 10,
|
|
maxSiblings: 5,
|
|
minPromotionScore: 0.9,
|
|
demotionThreshold: 0.5
|
|
},
|
|
{
|
|
level: 3,
|
|
name: "Region Host",
|
|
maxChildren: 5,
|
|
maxSiblings: 10,
|
|
minPromotionScore: 0.95,
|
|
demotionThreshold: 0.6
|
|
},
|
|
{
|
|
level: 4,
|
|
name: "Country Host",
|
|
maxChildren: 10,
|
|
maxSiblings: 0,
|
|
minPromotionScore: 1.0,
|
|
demotionThreshold: 0.7
|
|
}
|
|
];
|
|
```
|
|
|
|
### 7. Update Index File with Exports
|
|
|
|
**File: `src/index.ts` (update)**
|
|
|
|
```typescript
|
|
// Core components
|
|
export * from './node';
|
|
export * from './network';
|
|
export * from './utils';
|
|
export * from './versioning';
|
|
export * from './plugins/plugin-registry';
|
|
|
|
// Types
|
|
export * from './types';
|
|
```
|
|
|
|
## Testing Strategy
|
|
|
|
For Phase 0, we'll implement the following tests:
|
|
|
|
1. **Unit Tests**:
|
|
- NetworkNode class tests
|
|
- NetworkSystem class tests
|
|
- NetworkEventEmitter tests
|
|
- VersionInfo and VersionManager tests
|
|
- PluginRegistry tests
|
|
|
|
2. **Integration Tests**:
|
|
- Event propagation through hierarchy
|
|
- Plugin registration and interaction with nodes
|
|
- System configuration and node placement
|
|
|
|
3. **Edge Cases**:
|
|
- Node removal and hierarchy reorganization
|
|
- Version compatibility checking
|
|
- Plugin dependency resolution
|
|
|
|
## Next Steps
|
|
|
|
After implementing Phase 0, we'll proceed to Phase 1, which will focus on:
|
|
|
|
1. Implementing the transport layer
|
|
2. Enhancing the relay capabilities
|
|
3. Building out the message routing system
|
|
4. Adding metrics and host selection
|
|
|
|
## Conclusion
|
|
|
|
This implementation plan establishes the foundational components of the network library with a focus on modularity, event handling, and plugin support. By following this plan, we'll create a solid base that supports all future phases of development while maintaining compatibility and extensibility.
|