# System Patterns This document outlines the system architecture, key technical decisions, and design patterns used in the G1 TypeScript Common Packages repository. ## System Architecture The repository follows a monorepo architecture using pnpm workspaces: ``` g1-ts-common-packages/ ├── packages/ # All packages are stored here │ ├── sse-client/ # Server-Sent Events client package │ └── [future-packages]/ # Additional packages will be added here ├── scripts/ # Utility scripts for the repository ├── docs/ # Documentation └── .github/workflows/ # CI/CD workflows ``` Each package within the monorepo is designed to be: - **Independent**: Can be used without other packages from the monorepo - **Focused**: Solves a specific problem domain - **Well-documented**: Includes comprehensive documentation - **Well-tested**: Includes unit tests ## Key Technical Decisions ### 1. Monorepo Structure - **Decision**: Use a monorepo structure with pnpm workspaces - **Rationale**: - Simplifies management of multiple related packages - Enables sharing of configuration and tooling - Facilitates coordinated changes across packages - Reduces duplication of dependencies ### 2. Package Scope - **Decision**: Use the `@g1` scope for all packages - **Rationale**: - Provides namespace isolation - Makes it clear that packages belong to Generation One - Prevents name collisions with other packages ### 3. Package Versioning - **Decision**: Start packages at version 0.2.0 - **Rationale**: - Follows Generation One's standard starting version - Allows for pre-1.0 breaking changes as packages mature ### 4. Publishing Strategy - **Decision**: Publish packages to Gitea registry - **Rationale**: - Keeps packages within Generation One's infrastructure - Provides access control for private packages (if needed in the future) - Simplifies CI/CD integration ### 5. TypeScript First - **Decision**: Use TypeScript for all packages - **Rationale**: - Provides type safety - Improves developer experience - Enables better tooling and IDE support - Reduces runtime errors ## Design Patterns ### SSE Client Package The SSE Client package demonstrates several design patterns: #### 1. Singleton Pattern - Used in the `SSEConnectionManager` to ensure only one instance manages all connections - Provides a global point of access to the connection manager ```typescript export class SSEConnectionManager { private static instance: SSEConnectionManager; private constructor() {} public static getInstance(): SSEConnectionManager { if (!SSEConnectionManager.instance) { SSEConnectionManager.instance = new SSEConnectionManager(); } return SSEConnectionManager.instance; } // ... } ``` #### 2. Factory Pattern - Helper functions like `getSSEConnection` act as factories to create or retrieve SSE client instances - Simplifies the creation and management of SSE connections ```typescript export function getSSEConnection(url: string, id: string, options: SSEClientOptions = {}): SSEClient { return SSEConnectionManager.getInstance().getConnection(url, id, options); } ``` #### 3. Observer Pattern - The SSE client implements an event-based system where clients can subscribe to events - Uses the browser's `EventTarget` or a custom implementation in Node.js ```typescript public on(eventName: string, listener: (event: any) => void): SSEClient { if (!this.eventListeners.has(eventName)) { this.eventListeners.set(eventName, new Set()); } this.eventListeners.get(eventName)!.add(listener); // ... return this; } ``` #### 4. Options Pattern - Uses an options object with defaults for configuration - Allows for flexible and extensible configuration ```typescript export interface SSEClientOptions { headers?: Record; withCredentials?: boolean; heartbeatTimeout?: number; // ... } const DEFAULT_OPTIONS: Required> = { withCredentials: true, heartbeatTimeout: 21600000, // 6 hours // ... }; ``` #### 5. Exponential Backoff Pattern - Implements exponential backoff for reconnection attempts - Helps prevent overwhelming the server during outages ```typescript private async reconnect(): Promise { this.retryCount++; // Calculate delay with exponential backoff const delayMs = Math.min( this.options.initialRetryDelay * Math.pow(2, this.retryCount - 1), this.options.maxRetryDelay ); // ... } ``` ## Critical Implementation Paths ### SSE Client Connection Flow 1. **Initialization**: Create an SSE client with URL and options 2. **Connection**: Call `connect()` to establish the connection 3. **Event Handling**: Register event listeners with `on(eventName, listener)` 4. **Error Handling**: Automatic reconnection with exponential backoff 5. **Cleanup**: Close connection with `close()` or use automatic cleanup ### SSE Client Connection Management 1. **Get/Create Connection**: Use `getSSEConnection(url, id, options)` to get or create a connection 2. **Connection Reuse**: Connections are reused based on the provided ID 3. **Connection Cleanup**: Close specific connections with `closeSSEConnection(id)` or all with `closeAllSSEConnections()` 4. **Automatic Cleanup**: Set up with `setupSSECleanup()` to close connections on page unload ## Component Relationships - **SSEClient**: Core class that handles the SSE connection and events - **SSEConnectionManager**: Singleton that manages multiple SSE connections - **Helper Functions**: Provide simplified access to the connection manager - `getSSEConnection`: Gets or creates a connection - `closeSSEConnection`: Closes a specific connection - `closeAllSSEConnections`: Closes all connections - `setupSSECleanup`: Sets up automatic cleanup - **Debug Utilities**: Provide debug logging capabilities - `debugLog`, `debugInfo`, `debugWarn`, `debugError`