diff --git a/illustration.png b/illustration.png index d3e31b9..691edc3 100644 Binary files a/illustration.png and b/illustration.png differ diff --git a/package.json b/package.json index c92654c..ddaaf00 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@devisfuture/electron-modular", - "version": "1.1.12", + "version": "1.1.14", "description": "Core module system, DI container, IPC handlers, and window utilities for Electron main process.", "type": "module", "main": "./dist/index.js", diff --git a/src/@core/bootstrap/bootstrap.ts b/src/@core/bootstrap/bootstrap.ts index 79c6ca5..2d76ec1 100644 --- a/src/@core/bootstrap/bootstrap.ts +++ b/src/@core/bootstrap/bootstrap.ts @@ -1,3 +1,16 @@ +/** + * @fileoverview Bootstrap entry point for initializing all application modules. + * + * This file orchestrates the module initialization process: + * 1. Validates that modules have the @RgModule decorator + * 2. Initializes each module (registers providers, imports, windows, IPC handlers) + * 3. Instantiates the module class + * 4. Resolves module dependencies + * 5. Initializes IPC handlers with window factory functions + * + * @module @core/bootstrap/bootstrap + */ + import type { RgModuleMetadata } from "../types/module-metadata.js"; import type { Constructor } from "../types/constructor.js"; import { ModuleDecoratorMissingError } from "../errors/index.js"; @@ -6,6 +19,25 @@ import { initializeModule } from "./initialize-module.js"; import { container } from "../container.js"; import { initializeIpcHandlers } from "./initialize-ipc/handlers.js"; +/** + * Bootstraps an array of modules in the application. + * + * This is the main entry point for initializing the dependency injection container + * and setting up all modules. It processes each module sequentially to ensure + * proper dependency resolution order. + * + * @param modulesClass - Array of module class constructors to bootstrap + * @throws {ModuleDecoratorMissingError} If a module is missing the @RgModule decorator + * + * @example + * ```typescript + * await bootstrapModules([ + * UserModule, + * ResourcesModule, + * AuthModule + * ]); + * ``` + */ export const bootstrapModules = async ( modulesClass: Constructor[], ): Promise => { diff --git a/src/@core/bootstrap/initialize-ipc/handlers.ts b/src/@core/bootstrap/initialize-ipc/handlers.ts index 1fe26d6..d37d33d 100644 --- a/src/@core/bootstrap/initialize-ipc/handlers.ts +++ b/src/@core/bootstrap/initialize-ipc/handlers.ts @@ -1,3 +1,12 @@ +/** + * @fileoverview IPC handler initialization with window factory functions. + * + * Creates window factory functions and initializes IPC handlers by calling their onInit methods. + * Each IPC handler receives a getWindow function that returns window factories keyed by hash. + * + * @module @core/bootstrap/initialize-ipc/handlers + */ + import type { BrowserWindow } from "electron"; import type { Constructor } from "../../types/constructor.js"; import type { TIpcHandlerInterface } from "../../types/ipc-handler.js"; @@ -10,6 +19,13 @@ import { createWindowWithParams } from "./window-creator.js"; import { createWindowInstance } from "./window-instance-creator.js"; import { attachWindowEventListeners } from "./window-event-listeners.js"; +/** + * Creates a window factory that can instantiate BrowserWindows. + * + * @param moduleClass - The module context + * @param windowMetadata - Window metadata including options and hash + * @returns Window factory with create method + */ const createWindowFactory = ( moduleClass: Constructor, windowMetadata: TMetadataWindow | undefined, @@ -47,6 +63,12 @@ const createWindowFactory = ( }; }; +/** + * Creates a getWindow function for retrieving window factories by hash. + * + * @param moduleClass - The module context + * @returns Function that returns window factories by hash + */ const createGetWindowFunction = (moduleClass: Constructor) => { return (name?: string): TWindowFactory => { if (!name) { @@ -64,6 +86,19 @@ const createGetWindowFunction = (moduleClass: Constructor) => { }; }; +/** + * Initializes all IPC handlers for a module. + * + * Process: + * 1. Creates a getWindow function for the module + * 2. Resolves each IPC handler instance from the container + * 3. Calls onInit on each handler with the getWindow function + * + * This allows IPC handlers to access window factories and create windows dynamically. + * + * @param moduleClass - The module class owning these IPC handlers + * @param metadata - Module metadata containing ipc array + */ export const initializeIpcHandlers = async ( moduleClass: Constructor, metadata: RgModuleMetadata, diff --git a/src/@core/bootstrap/initialize-ipc/window-creator.ts b/src/@core/bootstrap/initialize-ipc/window-creator.ts index 224aba1..47b6309 100644 --- a/src/@core/bootstrap/initialize-ipc/window-creator.ts +++ b/src/@core/bootstrap/initialize-ipc/window-creator.ts @@ -1,9 +1,25 @@ +/** + * @fileoverview Window creation with parameter merging. + * + * Handles merging of base window metadata with runtime parameters to create BrowserWindows. + * Supports deep merging of nested configuration objects. + * + * @module @core/bootstrap/initialize-ipc/window-creator + */ + import type { BrowserWindow } from "electron"; import type { TParamsCreateWindow } from "../../control-window/types.js"; import { createWindow } from "../../control-window/create.js"; +/** Plain JavaScript object type */ type TPlainObject = Record; +/** + * Type guard to check if a value is a plain object. + * + * @param value - Value to check + * @returns true if value is a plain object + */ const isPlainObject = (value: unknown): value is TPlainObject => { return ( typeof value === "object" && @@ -13,6 +29,15 @@ const isPlainObject = (value: unknown): value is TPlainObject => { ); }; +/** + * Deep merges two objects recursively. + * + * Plain objects are merged recursively, other values from source override target. + * + * @param target - Target object + * @param source - Source object with values to merge + * @returns Merged object + */ const mergeDeep = (target: T, source: T): T => { const output: TPlainObject = { ...target }; @@ -30,6 +55,24 @@ const mergeDeep = (target: T, source: T): T => { return output as T; }; +/** + * Creates a BrowserWindow with merged metadata and runtime parameters. + * + * Base metadata from @WindowManager is deep merged with optional runtime params. + * This allows dynamic window configuration while maintaining sensible defaults. + * + * @param baseMetadata - Base window metadata from decorator + * @param params - Optional runtime parameters to merge + * @returns Created BrowserWindow instance + * + * @example + * ```typescript + * const window = createWindowWithParams( + * { hash: 'window:main', options: { width: 800 } }, + * { options: { height: 600 } } + * ); + * ``` + */ export const createWindowWithParams = ( baseMetadata: W, params?: W, diff --git a/src/@core/bootstrap/initialize-ipc/window-event-listeners.ts b/src/@core/bootstrap/initialize-ipc/window-event-listeners.ts index 548acff..597352a 100644 --- a/src/@core/bootstrap/initialize-ipc/window-event-listeners.ts +++ b/src/@core/bootstrap/initialize-ipc/window-event-listeners.ts @@ -1,18 +1,40 @@ +/** + * @fileoverview Window lifecycle event listener attachment. + * + * Automatically attaches lifecycle hooks from window manager instances to BrowserWindow + * and WebContents events. Supports: + * - BrowserWindow events (onFocus, onClose, etc.) + * - WebContents events (onWebContentsDidFinishLoad, etc.) + * + * Event handlers are named with 'on' prefix and CamelCase, which are converted to kebab-case event names. + * + * @module @core/bootstrap/initialize-ipc/window-event-listeners + */ + import type { BrowserWindow, WebContents } from "electron"; import type { TWindowManagerWithHandlers } from "../../types/window-manager.js"; +/** Event emitter interface (BrowserWindow or WebContents) */ type TEventEmitter = Pick< BrowserWindow | WebContents, "on" | "off" | "removeListener" >; +/** Tracks attached listeners and cleanup functions for each window */ type TWindowListenerEntry = { instance: TWindowManagerWithHandlers; cleanup: Array<() => void>; }; +/** WeakMap to track listeners per window instance */ const windowListeners = new WeakMap(); +/** + * Extracts all method names from an object's prototype chain. + * + * @param instance - Object to inspect + * @returns Array of method names + */ const getPrototypeMethodNames = (instance: object): string[] => { const names = new Set(); let proto = Object.getPrototypeOf(instance); @@ -25,6 +47,17 @@ const getPrototypeMethodNames = (instance: object): string[] => { return Array.from(names); }; +/** + * Converts a method name to an Electron event name. + * + * Examples: + * - onFocus -> focus + * - onWebContentsDidFinishLoad -> did-finish-load + * - onMaximize -> maximize + * + * @param h - Handler method name + * @returns Kebab-case event name + */ const toEventName = (h: string): string => { const c = h.replace(/^(onWindow|onWebContents|on)/, ""); return c @@ -33,6 +66,19 @@ const toEventName = (h: string): string => { .toLowerCase(); }; +/** + * Attaches event handlers from window manager instance to an event emitter. + * + * Handlers with 0-1 parameters receive only the BrowserWindow instance. + * Handlers with 2+ parameters receive original Electron event arguments plus the window. + * + * @param emitter - BrowserWindow or WebContents to attach listeners to + * @param win - BrowserWindow instance to pass to handlers + * @param inst - Window manager instance containing handler methods + * @param names - Method names to consider + * @param filter - Function to filter which methods to attach + * @returns Array of cleanup functions to remove listeners + */ const attachHandlersToEmitter = ( emitter: TEventEmitter, win: BrowserWindow, @@ -60,6 +106,27 @@ const attachHandlersToEmitter = ( return cleanups; }; +/** + * Attaches all lifecycle event listeners from a window manager to a BrowserWindow. + * + * Process: + * 1. Checks if listeners are already attached to avoid duplicates + * 2. Extracts all methods starting with 'on' from window manager + * 3. Separates BrowserWindow events from WebContents events + * 4. Attaches handlers to appropriate emitters + * 5. Registers cleanup on window close + * + * @param win - BrowserWindow instance + * @param inst - Window manager instance with lifecycle hooks + * + * @example + * ```typescript + * const window = new BrowserWindow(); + * const manager = new MyWindowManager(); + * attachWindowEventListeners(window, manager); + * // Now manager.onFocus() will be called when window focuses + * ``` + */ export const attachWindowEventListeners = ( win: BrowserWindow, inst: TWindowManagerWithHandlers, diff --git a/src/@core/bootstrap/initialize-ipc/window-instance-creator.ts b/src/@core/bootstrap/initialize-ipc/window-instance-creator.ts index 829b0dd..5e10331 100644 --- a/src/@core/bootstrap/initialize-ipc/window-instance-creator.ts +++ b/src/@core/bootstrap/initialize-ipc/window-instance-creator.ts @@ -1,8 +1,34 @@ +/** + * @fileoverview Window manager instance creation. + * + * Instantiates window manager classes with their dependencies resolved from the container. + * Uses reflection to discover constructor dependencies automatically. + * + * @module @core/bootstrap/initialize-ipc/window-instance-creator + */ + import type { Constructor } from "../../types/constructor.js"; import type { TWindowManagerWithHandlers } from "../../types/window-manager.js"; import { container } from "../../container.js"; import { getDependencyTokens } from "../../utils/dependency-tokens.js"; +/** + * Creates an instance of a window manager class with resolved dependencies. + * + * Process: + * 1. Extracts constructor parameter types using reflection + * 2. Resolves each dependency from the container + * 3. Instantiates the window manager with resolved dependencies + * + * @param moduleClass - The module context for dependency resolution + * @param windowClass - The window manager class constructor + * @returns Instantiated window manager or undefined if class is invalid + * + * @example + * ```typescript + * const instance = await createWindowInstance(UserModule, UserWindow); + * ``` + */ export const createWindowInstance = async < T extends TWindowManagerWithHandlers, >( diff --git a/src/@core/bootstrap/initialize-module.ts b/src/@core/bootstrap/initialize-module.ts index da23754..efac2b2 100644 --- a/src/@core/bootstrap/initialize-module.ts +++ b/src/@core/bootstrap/initialize-module.ts @@ -1,3 +1,15 @@ +/** + * @fileoverview Module initialization coordinator. + * + * Orchestrates the registration of all module components: + * - Providers (services and factories) + * - Imported modules + * - Window managers + * - IPC handlers + * + * @module @core/bootstrap/initialize-module + */ + import type { RgModuleMetadata } from "../types/module-metadata.js"; import type { Constructor } from "../types/constructor.js"; import { container } from "../container.js"; @@ -6,6 +18,21 @@ import { registerImports } from "./register-imports.js"; import { registerWindows } from "./register-windows.js"; import { registerIpcHandlers } from "./register-ipc-handlers.js"; +/** + * Initializes a module by registering all its components in the container. + * + * Process: + * 1. Adds module to container with metadata + * 2. Registers all providers + * 3. Recursively initializes imported modules + * 4. Registers window managers + * 5. Registers IPC handlers + * + * All registration steps run in parallel for performance. + * + * @param moduleClass - The module class constructor + * @param metadata - Module metadata from @RgModule decorator + */ export const initializeModule = async ( moduleClass: Constructor, metadata: RgModuleMetadata, diff --git a/src/@core/bootstrap/instantiate-module.ts b/src/@core/bootstrap/instantiate-module.ts index a558244..0e48da3 100644 --- a/src/@core/bootstrap/instantiate-module.ts +++ b/src/@core/bootstrap/instantiate-module.ts @@ -1,7 +1,33 @@ +/** + * @fileoverview Module instantiation logic. + * + * Handles the creation of module instances with automatic dependency resolution. + * Uses reflection to discover constructor dependencies and resolves them from the container. + * + * @module @core/bootstrap/instantiate-module + */ + import type { Constructor } from "../types/constructor.js"; import { container } from "../container.js"; import { getDependencyTokens } from "../utils/dependency-tokens.js"; +/** + * Instantiates a module class with its resolved dependencies. + * + * Process: + * 1. Extracts constructor parameter types using reflection + * 2. Resolves each dependency from the container + * 3. Creates a new instance with resolved dependencies + * 4. Registers the instance in the global container + * + * @param moduleClass - The module class constructor to instantiate + * @returns The instantiated module instance + * + * @example + * ```typescript + * const instance = await instantiateModule(UserModule); + * ``` + */ export const instantiateModule = async ( moduleClass: Constructor, ): Promise => { diff --git a/src/@core/bootstrap/register-imports.ts b/src/@core/bootstrap/register-imports.ts index 18238f6..d730eb4 100644 --- a/src/@core/bootstrap/register-imports.ts +++ b/src/@core/bootstrap/register-imports.ts @@ -1,6 +1,24 @@ +/** + * @fileoverview Module import registration. + * + * Handles recursive initialization of imported modules to establish + * the dependency graph. Imported modules are initialized before the + * importing module can access their exported providers. + * + * @module @core/bootstrap/register-imports + */ + import type { RgModuleMetadata } from "../types/module-metadata.js"; import { initializeModule } from "./initialize-module.js"; +/** + * Recursively initializes all modules imported by the current module. + * + * This ensures that all imported modules are fully initialized before + * the importing module tries to resolve their exported providers. + * + * @param metadata - Module metadata containing imports array + */ export const registerImports = async ( metadata: RgModuleMetadata, ): Promise => { diff --git a/src/@core/bootstrap/register-ipc-handlers.ts b/src/@core/bootstrap/register-ipc-handlers.ts index 36e85d3..7710ff2 100644 --- a/src/@core/bootstrap/register-ipc-handlers.ts +++ b/src/@core/bootstrap/register-ipc-handlers.ts @@ -1,7 +1,26 @@ +/** + * @fileoverview IPC handler registration. + * + * Registers all IPC handler classes as providers in the container. + * The handlers are instantiated later during initialization when their + * dependencies can be resolved. + * + * @module @core/bootstrap/register-ipc-handlers + */ + import type { Constructor } from "../types/constructor.js"; import type { RgModuleMetadata } from "../types/module-metadata.js"; import { container } from "../container.js"; +/** + * Registers all IPC handler classes defined in a module. + * + * Each IPC handler is registered as a provider using the class as the token. + * The actual instantiation and onInit call happens later in the bootstrap process. + * + * @param moduleClass - The module class owning these IPC handlers + * @param metadata - Module metadata containing ipc array + */ export const registerIpcHandlers = async ( moduleClass: Constructor, metadata: RgModuleMetadata, diff --git a/src/@core/bootstrap/register-providers.ts b/src/@core/bootstrap/register-providers.ts index ef5c36c..f713020 100644 --- a/src/@core/bootstrap/register-providers.ts +++ b/src/@core/bootstrap/register-providers.ts @@ -1,8 +1,27 @@ +/** + * @fileoverview Provider registration logic. + * + * Handles registration of all provider types: + * - Class providers (simple constructors) + * - Factory providers (useFactory) + * - Value providers (useValue) + * - Class providers with custom tokens (useClass) + * - Existing providers (useExisting) + * + * @module @core/bootstrap/register-providers + */ + import type { Constructor } from "../types/constructor.js"; import type { RgModuleMetadata } from "../types/module-metadata.js"; import { InvalidProviderError } from "../errors/index.js"; import { container } from "../container.js"; +/** + * Type guard to check if a provider is a provider object (not a simple class). + * + * @param provider - Provider to check + * @returns true if provider is an object with a 'provide' property + */ const isProviderObject = ( provider: unknown, ): provider is { provide: unknown } => { @@ -11,6 +30,17 @@ const isProviderObject = ( ); }; +/** + * Registers all providers defined in a module's metadata. + * + * Handles two provider formats: + * 1. Simple class constructors - registered with the class as the token + * 2. Provider objects - registered with custom token and configuration + * + * @param moduleClass - The module class owning these providers + * @param metadata - Module metadata containing providers array + * @throws {InvalidProviderError} If a provider is neither a function nor a valid provider object + */ export const registerProviders = async ( moduleClass: Constructor, metadata: RgModuleMetadata, diff --git a/src/@core/bootstrap/register-windows.ts b/src/@core/bootstrap/register-windows.ts index 78c066c..20765c7 100644 --- a/src/@core/bootstrap/register-windows.ts +++ b/src/@core/bootstrap/register-windows.ts @@ -1,8 +1,26 @@ +/** + * @fileoverview Window manager registration. + * + * Registers window managers as providers with their hash as the token. + * This allows IPC handlers to retrieve window factories using the hash string. + * + * @module @core/bootstrap/register-windows + */ + import type { Constructor } from "../types/constructor.js"; import type { RgModuleMetadata } from "../types/module-metadata.js"; import type { WindowManagerOptions } from "../types/window-manager.js"; import { container } from "../container.js"; +/** + * Registers all window managers defined in a module. + * + * Each window manager is registered with its hash as the provider token, + * storing both the window metadata and the window class constructor. + * + * @param moduleClass - The module class owning these window managers + * @param metadata - Module metadata containing windows array + */ export const registerWindows = async ( moduleClass: Constructor, metadata: RgModuleMetadata, diff --git a/src/@core/bootstrap/settings.ts b/src/@core/bootstrap/settings.ts index a21a404..ccf38b5 100644 --- a/src/@core/bootstrap/settings.ts +++ b/src/@core/bootstrap/settings.ts @@ -1,23 +1,71 @@ +/** + * @fileoverview Application settings management. + * + * Provides functions to initialize and retrieve application-wide settings including: + * - CSP (Content Security Policy) connect sources + * - Localhost port for development + * - Build output folder paths + * + * Settings must be initialized before modules are bootstrapped. + * + * @module @core/bootstrap/settings + */ + import { SettingsNotInitializedError } from "../errors/index.js"; +/** + * Folder configuration for build outputs. + */ export type TFolderSettings = { distRenderer: string; distMain: string; }; +/** + * Application-wide settings configuration. + */ export type TSettings = { cspConnectSources?: string[]; localhostPort: string; folders: TFolderSettings; }; +/** Key for storing settings in the internal map */ const KEY = "settings" as const; + +/** Internal settings storage */ const settings = new Map(); +/** + * Initializes application settings. + * + * Must be called before bootstrapping modules. Settings are stored globally + * and used throughout the application lifecycle. + * + * @param options - Application settings configuration + * + * @example + * ```typescript + * initSettings({ + * cspConnectSources: ['https://api.example.com'], + * localhostPort: '3000', + * folders: { + * distRenderer: 'dist-renderer', + * distMain: 'dist-main' + * } + * }); + * ``` + */ export const initSettings = (options: TSettings): void => { settings.set(KEY, options); }; +/** + * Retrieves the current application settings. + * + * @returns The application settings + * @throws {SettingsNotInitializedError} If settings haven't been initialized + */ export const getSettings = (): TSettings => { const cachedSettings = settings.get(KEY); diff --git a/src/@core/container.ts b/src/@core/container.ts index 6d009ef..ab51399 100644 --- a/src/@core/container.ts +++ b/src/@core/container.ts @@ -1,3 +1,17 @@ +/** + * @fileoverview Dependency injection container for managing modules and providers. + * + * The Container class is the core of the dependency injection system. It: + * - Registers and manages modules + * - Resolves dependencies automatically + * - Instantiates services with their dependencies + * - Handles different provider types (class, factory, value, existing) + * - Caches instances for singleton behavior + * - Supports module imports and exports for cross-module dependencies + * + * @module @core/container + */ + import type { Constructor } from "./types/constructor.js"; import type { RgModuleMetadata } from "./types/module-metadata.js"; import type { @@ -13,17 +27,43 @@ import { ProviderNotFoundError, } from "./errors/index.js"; +/** + * Internal module data structure storing providers and exports. + */ type TModuleData = { providers: Map; exports: Set; }; +/** + * Dependency injection container for managing modules and their providers. + * + * The container handles the entire lifecycle of dependency resolution: + * 1. Module registration + * 2. Provider registration + * 3. Dependency resolution + * 4. Instance creation and caching + */ export class Container { + /** Stores module data including providers and exports for each registered module */ private readonly modules = new Map(); + + /** Stores metadata for each module (imports, providers, IPC handlers, windows) */ private readonly moduleMetadata = new Map(); + + /** Global instance cache for singleton providers */ private readonly instances = new Map(); + + /** Resolution cache to optimize repeated dependency lookups */ private readonly resolutionCache = new Map(); + /** + * Adds a new module to the container. + * + * @param moduleClass - The module class constructor + * @param metadata - Module metadata containing providers and exports + * @returns true if module was added, false if it already exists + */ addModule( moduleClass: Constructor, metadata: Pick, @@ -39,6 +79,12 @@ export class Container { return true; } + /** + * Stores complete metadata for a module. + * + * @param moduleClass - The module class constructor + * @param metadata - Complete module metadata + */ setModuleMetadata( moduleClass: Constructor, metadata: RgModuleMetadata, @@ -46,10 +92,23 @@ export class Container { this.moduleMetadata.set(moduleClass, metadata); } + /** + * Checks if a module is registered in the container. + * + * @param moduleClass - The module class constructor to check + * @returns true if module exists + */ hasModule(moduleClass: Constructor): boolean { return this.modules.has(moduleClass); } + /** + * Generates a unique cache key for module-provider combinations. + * + * @param moduleClass - The module class constructor + * @param token - The provider token (Symbol, string, or constructor) + * @returns Cache key in format "ModuleName:TokenName" + */ private getCacheKey(moduleClass: Constructor, token: TProviderToken): string { const tokenKey = typeof token === "string" || typeof token === "symbol" @@ -58,6 +117,14 @@ export class Container { return `${moduleClass.name}:${tokenKey}`; } + /** + * Registers a provider instance for a module. + * + * @param moduleClass - The module class constructor + * @param provider - The provider token or class + * @param instance - Optional instance to register (for already-instantiated providers) + * @throws {ModuleNotRegisteredError} If the module hasn't been registered + */ addProvider( moduleClass: Constructor, provider: TProviderToken, @@ -71,6 +138,13 @@ export class Container { moduleData.providers.set(provider, instance ?? provider); } + /** + * Retrieves a provider from a module's provider map. + * + * @param moduleClass - The module class constructor + * @param token - The provider token to retrieve + * @returns The provider instance or undefined if not found + */ getProvider( moduleClass: Constructor, token: TProviderToken, @@ -83,38 +157,77 @@ export class Container { return moduleData.providers.get(token) as T | undefined; } + /** + * Gets the set of exported providers for a module. + * + * @param moduleClass - The module class constructor + * @returns Set of exported provider tokens + */ getModuleExports(moduleClass: Constructor): Set { const moduleData = this.modules.get(moduleClass); return moduleData?.exports ?? new Set(); } + /** + * Retrieves complete metadata for a module. + * + * @param moduleClass - The module class constructor + * @returns Module metadata or undefined if not found + */ getModuleMetadata(moduleClass: Constructor): RgModuleMetadata | undefined { return this.moduleMetadata.get(moduleClass); } + /** + * Registers an instance in the global instance cache. + * Used for singleton providers that should be shared across modules. + * + * @param token - The provider token + * @param instance - The instance to cache + */ registerInstance(token: TProviderToken, instance: unknown): void { this.instances.set(token, instance); } + /** + * Resolves a dependency by token within a module context. + * + * This is the main dependency resolution method that: + * 1. Checks resolution cache for previous lookups + * 2. Checks global instance cache + * 3. Looks up provider in current module + * 4. Falls back to imported modules + * 5. Instantiates the provider if needed + * 6. Caches the result + * + * @param moduleClass - The module requesting the dependency + * @param token - The provider token to resolve + * @returns Resolved instance or undefined + * @throws {ProviderNotFoundError} If provider cannot be found + */ async resolve( moduleClass: Constructor, token: TProviderToken, ): Promise { const cacheKey = this.getCacheKey(moduleClass, token); + // Check resolution cache first if (this.resolutionCache.has(cacheKey)) { return this.resolutionCache.get(cacheKey) as T; } + // Check global instance cache if (this.instances.has(token)) { const instance = this.instances.get(token) as T; this.resolutionCache.set(cacheKey, instance); return instance; } + // Get provider from current module const provider = this.getProvider(moduleClass, token); if (!provider) { + // Try to resolve from imported modules const resolvedFromImports = await this.resolveFromImports( moduleClass, token, @@ -125,6 +238,7 @@ export class Container { return resolvedFromImports; } + // If token is not the module itself, throw error if (token !== moduleClass) { throw new ProviderNotFoundError(String(token), moduleClass.name); } @@ -132,6 +246,7 @@ export class Container { return undefined; } + // Instantiate the provider const instance = await this.instantiateProvider( moduleClass, token, @@ -145,6 +260,16 @@ export class Container { return instance; } + /** + * Attempts to resolve a dependency from imported modules. + * + * When a dependency is not found in the current module, this method + * searches through all imported modules for exported providers matching the token. + * + * @param moduleClass - The module requesting the dependency + * @param token - The provider token to resolve + * @returns Resolved instance or undefined + */ private async resolveFromImports( moduleClass: Constructor, token: TProviderToken, @@ -155,9 +280,11 @@ export class Container { return undefined; } + // Search through all imported modules for (const importedModuleClass of moduleMetadata.imports) { const exportedProviders = this.getModuleExports(importedModuleClass); + // Check if the imported module exports this token if (exportedProviders.has(token)) { const exportedProvider = this.getProvider(importedModuleClass, token); @@ -170,6 +297,21 @@ export class Container { return undefined; } + /** + * Instantiates a provider based on its type. + * + * Handles different provider types: + * - Factory providers (useFactory) + * - Class providers (useClass) + * - Value providers (useValue) + * - Existing providers (useExisting) + * - Direct class constructors + * + * @param moduleClass - The module context + * @param token - The provider token + * @param provider - The provider definition + * @returns Instantiated provider + */ private async instantiateProvider( moduleClass: Constructor, token: TProviderToken, @@ -180,6 +322,7 @@ export class Container { provider !== null && "provide" in provider; + // Handle factory providers (useFactory) if ( isObj && "useFactory" in provider && @@ -192,6 +335,7 @@ export class Container { ); } + // Handle class providers (useClass) if ( isObj && "useClass" in provider && @@ -204,12 +348,14 @@ export class Container { ); } + // Handle value providers (useValue) if (isObj && "useValue" in provider) { const val = (provider as TValueProvider).useValue; this.instances.set(token, val); return val as T; } + // Handle existing providers (useExisting) - alias to another provider if (isObj && "useExisting" in provider) { const instance = await this.resolve( moduleClass, @@ -221,6 +367,7 @@ export class Container { return instance; } + // Handle direct class constructors if (typeof provider === "function") { return this.instantiateClassConstructor( moduleClass, @@ -232,6 +379,14 @@ export class Container { return provider as T; } + /** + * Instantiates a factory provider by calling its factory function with dependencies. + * + * @param moduleClass - The module context + * @param token - The provider token + * @param provider - The factory provider configuration + * @returns Instantiated provider + */ private async instantiateFactoryProvider( moduleClass: Constructor, token: TProviderToken, @@ -247,6 +402,14 @@ export class Container { return instance as T; } + /** + * Instantiates a class provider with its dependencies. + * + * @param moduleClass - The module context + * @param token - The provider token + * @param provider - The class provider configuration + * @returns Instantiated provider + */ private async instantiateClassProvider( moduleClass: Constructor, token: TProviderToken, @@ -263,6 +426,16 @@ export class Container { return instance as T; } + /** + * Instantiates a class constructor directly with auto-resolved dependencies. + * + * Uses reflection to get constructor parameter types and resolves them automatically. + * + * @param moduleClass - The module context + * @param token - The provider token + * @param providerClass - The class constructor + * @returns Instantiated provider + */ private async instantiateClassConstructor( moduleClass: Constructor, token: TProviderToken, @@ -278,6 +451,13 @@ export class Container { return instance as T; } + /** + * Resolves an array of dependency tokens to their instances. + * + * @param moduleClass - The module context + * @param dependencies - Array of dependency tokens to resolve + * @returns Array of resolved instances + */ private async resolveDependencies( moduleClass: Constructor, dependencies: TProviderToken[], @@ -288,4 +468,8 @@ export class Container { } } +/** + * Global singleton instance of the dependency injection container. + * This is the main entry point for all container operations. + */ export const container = new Container(); diff --git a/src/@core/control-window/cache.ts b/src/@core/control-window/cache.ts index 1b58a9c..7ec03c9 100644 --- a/src/@core/control-window/cache.ts +++ b/src/@core/control-window/cache.ts @@ -1,3 +1,18 @@ +/** + * @fileoverview Window cache storage. + * + * Maintains a global Map of cached BrowserWindow instances keyed by their hash. + * Cached windows are hidden instead of destroyed when closed, allowing for fast re-opening. + * + * @module @core/control-window/cache + */ + import type { TCache } from "./types.js"; +/** + * Global cache for BrowserWindow instances. + * + * Windows with isCache=true are stored here and hidden on close instead of destroyed. + * This improves performance when reopening frequently used windows. + */ export const cacheWindows = new Map(); diff --git a/src/@core/control-window/create.ts b/src/@core/control-window/create.ts index 30e2a0a..08afb76 100644 --- a/src/@core/control-window/create.ts +++ b/src/@core/control-window/create.ts @@ -1,3 +1,16 @@ +/** + * @fileoverview BrowserWindow creation and configuration. + * + * Handles creation of BrowserWindows with: + * - Automatic preload script injection + * - CSP (Content Security Policy) setup + * - Window caching for performance + * - Development vs production URL loading + * - Hash-based routing support + * + * @module @core/control-window/create + */ + import { BrowserWindow, session, app } from "electron"; import path from "node:path"; import type { TParamsCreateWindow } from "./types.js"; @@ -5,6 +18,17 @@ import { cacheWindows } from "./cache.js"; import { getWindow } from "./receive.js"; import { getSettings } from "../bootstrap/settings.js"; +/** + * Sets up Content Security Policy for the default session. + * + * Configures CSP headers for all responses to enhance security by: + * - Restricting resource loading to self and specified sources + * - Allowing inline styles (needed for many UI frameworks) + * - Conditionally allowing inline scripts in development + * + * @param sources - Additional connect-src sources from settings + * @param dev - Whether running in development mode + */ const setupCSP = (sources: string[], dev: boolean): void => { const connectSrc = sources.length > 0 ? ` ${sources.join(" ")}` : ""; const csp = @@ -21,6 +45,32 @@ const setupCSP = (sources: string[], dev: boolean): void => { }); }; +/** + * Creates a BrowserWindow with automatic configuration. + * + * Features: + * - Returns cached window if hash matches and isCache=true + * - Sets up preload script from configured distMain folder + * - Configures CSP for cached windows + * - Loads development server URL or production file + * - Hides instead of destroys cached windows on close + * + * @param params - Window creation parameters + * @param params.hash - Unique window identifier (used for routing and caching) + * @param params.options - BrowserWindowConstructorOptions to customize the window + * @param params.isCache - Whether to cache this window instance + * @param params.loadURL - Custom URL to load (overrides default behavior) + * @returns Created or cached BrowserWindow instance + * + * @example + * ```typescript + * const window = createWindow({ + * hash: 'window:main', + * isCache: true, + * options: { width: 800, height: 600 } + * }); + * ``` + */ export const createWindow = ({ hash, options, @@ -41,7 +91,7 @@ export const createWindow = ({ if (!settings.localhostPort) console.warn( - 'Warning: You have to add an environment variable called "process.env.LOCALHOST_ELECTRON_SERVER_PORT"!', + 'Warning: You have to add an environment variable for example called "process.env.LOCALHOST_ELECTRON_SERVER_PORT"!', ); if (hash && isCache) { diff --git a/src/@core/control-window/destroy.ts b/src/@core/control-window/destroy.ts index a1c7355..1776ccd 100644 --- a/src/@core/control-window/destroy.ts +++ b/src/@core/control-window/destroy.ts @@ -1,5 +1,27 @@ +/** + * @fileoverview Window destruction utility. + * + * Provides functionality to destroy all BrowserWindows in the application. + * Useful for cleanup during app shutdown or testing. + * + * @module @core/control-window/destroy + */ + import { BrowserWindow } from "electron"; +/** + * Destroys all existing BrowserWindow instances. + * + * Iterates through all windows and destroys them if not already destroyed. + * This clears all cached windows and frees resources. + * + * Typically called during app shutdown: + * ```typescript + * app.on('before-quit', () => { + * destroyWindows(); + * }); + * ``` + */ export const destroyWindows = (): void => { const windows = BrowserWindow.getAllWindows(); diff --git a/src/@core/control-window/receive.ts b/src/@core/control-window/receive.ts index 0dd28b1..3ff143d 100644 --- a/src/@core/control-window/receive.ts +++ b/src/@core/control-window/receive.ts @@ -1,6 +1,33 @@ +/** + * @fileoverview Window retrieval utility. + * + * Provides functionality to retrieve cached BrowserWindow instances by their hash. + * + * @module @core/control-window/receive + */ + import type { BrowserWindow } from "electron"; import { cacheWindows } from "./cache.js"; +/** + * Retrieves a cached BrowserWindow by its hash. + * + * Returns undefined if: + * - Window is not cached + * - Window has been destroyed + * - Hash does not exist in cache + * + * @param name - Window hash identifier + * @returns BrowserWindow instance or undefined + * + * @example + * ```typescript + * const mainWindow = getWindow('window:main'); + * if (mainWindow) { + * mainWindow.show(); + * } + * ``` + */ export const getWindow = ( name: N, ): BrowserWindow | undefined => { diff --git a/src/@core/control-window/types.ts b/src/@core/control-window/types.ts index 8660abc..b98d2f2 100644 --- a/src/@core/control-window/types.ts +++ b/src/@core/control-window/types.ts @@ -1,9 +1,28 @@ +/** + * @fileoverview Type definitions for window creation and management. + * + * Defines types for: + * - Window creation parameters + * - Window caching + * - Route parameters + * + * @module @core/control-window/types + */ + import { BrowserWindow, type BrowserWindowConstructorOptions } from "electron"; +/** + * Route parameters for dynamic window paths. + */ export type TParamsRoute = { [key: string]: string; }; +/** + * Parameters for creating a BrowserWindow. + * + * @template N - String literal type for the window hash + */ export type TParamsCreateWindow = { hash?: N; isCache?: boolean; @@ -12,6 +31,9 @@ export type TParamsCreateWindow = { loadURL?: string; }; +/** + * Type for window cache mapping hashes to BrowserWindow instances. + */ export type TCache = { [key in string]: BrowserWindow; }; diff --git a/src/@core/decorators/inject.ts b/src/@core/decorators/inject.ts index 3d6acf4..7f0b144 100644 --- a/src/@core/decorators/inject.ts +++ b/src/@core/decorators/inject.ts @@ -1,10 +1,42 @@ +/** + * @fileoverview @Inject decorator for custom dependency injection. + * + * The @Inject decorator allows injecting dependencies by custom tokens (Symbols) + * instead of relying on TypeScript's reflected types. This is useful for: + * - Provider pattern (injecting interfaces instead of concrete classes) + * - Avoiding circular dependencies + * - Runtime token-based injection + * + * @module @core/decorators/inject + */ + import "reflect-metadata/lite"; import type { TProviderToken } from "../types/provider.js"; +/** Maps parameter index to provider token */ type TInjectTokensMetadata = Record; +/** Metadata key for storing inject tokens */ const INJECT_TOKENS_METADATA_KEY = "RgInjectTokens"; +/** + * Parameter decorator for injecting dependencies by custom token. + * + * Use this when you need to inject by Symbol token instead of class type. + * + * @param token - Provider token (Symbol, string, or class) to inject + * @returns ParameterDecorator function + * + * @example + * ```typescript + * @Injectable() + * export class UserService { + * constructor( + * @Inject(REST_API_PROVIDER) private restApi: TRestApiProvider + * ) {} + * } + * ``` + */ export const Inject = (token: TProviderToken): ParameterDecorator => { return (target, propertyKey, parameterIndex) => { if (propertyKey !== undefined) { @@ -20,6 +52,14 @@ export const Inject = (token: TProviderToken): ParameterDecorator => { }; }; +/** + * Retrieves injected tokens metadata from a class constructor. + * + * Used by the container to resolve dependencies specified via @Inject decorator. + * + * @param target - Class constructor to get injection tokens from + * @returns Map of parameter index to provider token + */ export const getInjectedTokens = (target: Function): TInjectTokensMetadata => { return Reflect.getMetadata(INJECT_TOKENS_METADATA_KEY, target) ?? {}; }; diff --git a/src/@core/decorators/injectable.ts b/src/@core/decorators/injectable.ts index 4e5d42f..2bf7e92 100644 --- a/src/@core/decorators/injectable.ts +++ b/src/@core/decorators/injectable.ts @@ -1,5 +1,32 @@ +/** + * @fileoverview @Injectable decorator for marking classes as injectable services. + * + * Classes decorated with @Injectable can be: + * - Registered as providers in modules + * - Automatically instantiated by the DI container + * - Injected into other services + * + * @module @core/decorators/injectable + */ + import "reflect-metadata/lite"; +/** + * Decorator that marks a class as injectable into the DI container. + * + * The container will automatically resolve constructor dependencies + * when instantiating this class. + * + * @returns ClassDecorator function + * + * @example + * ```typescript + * @Injectable() + * export class UserService { + * constructor(private restApiService: RestApiService) {} + * } + * ``` + */ export const Injectable = (): ClassDecorator => { return (target: Function) => { Reflect.defineMetadata("Injectable", true, target); diff --git a/src/@core/decorators/ipc-handler.ts b/src/@core/decorators/ipc-handler.ts index 6296127..19bd632 100644 --- a/src/@core/decorators/ipc-handler.ts +++ b/src/@core/decorators/ipc-handler.ts @@ -1,5 +1,38 @@ +/** + * @fileoverview @IpcHandler decorator for marking IPC handler classes. + * + * Classes decorated with @IpcHandler: + * - Are registered as providers in their module + * - Have their onInit method called during module bootstrap + * - Receive a getWindow function to access window factories + * + * @module @core/decorators/ipc-handler + */ + import "reflect-metadata/lite"; +/** + * Decorator that marks a class as an IPC handler. + * + * IPC handlers implement the TIpcHandlerInterface and typically: + * - Set up ipcMain listeners + * - Handle communication between main and renderer processes + * - Access window factories to send messages to windows + * + * @returns ClassDecorator function + * + * @example + * ```typescript + * @IpcHandler() + * export class UserIpc implements TIpcHandlerInterface { + * async onInit({ getWindow }: TParamOnInit) { + * ipcMain.on('user:fetch', async (event, userId) => { + * // Handle IPC message + * }); + * } + * } + * ``` + */ export const IpcHandler = (): ClassDecorator => { return (target: Function) => { Reflect.defineMetadata("IpcHandler", true, target); diff --git a/src/@core/decorators/rg-module.ts b/src/@core/decorators/rg-module.ts index d61488e..6e0218f 100644 --- a/src/@core/decorators/rg-module.ts +++ b/src/@core/decorators/rg-module.ts @@ -1,6 +1,37 @@ +/** + * @fileoverview @RgModule decorator for defining application modules. + * + * The @RgModule decorator marks a class as a module and attaches metadata defining: + * - Imported modules + * - Providers (services and factories) + * - IPC handlers + * - Window managers + * - Exported providers + * + * @module @core/decorators/rg-module + */ + import "reflect-metadata/lite"; import type { RgModuleMetadata } from "../types/module-metadata.js"; +/** + * Decorator that marks a class as a module and attaches module metadata. + * + * @param options - Module configuration including imports, providers, IPC, windows, and exports + * @returns ClassDecorator function + * + * @example + * ```typescript + * @RgModule({ + * imports: [RestApiModule], + * providers: [UserService], + * ipc: [UserIpc], + * windows: [UserWindow], + * exports: [UserService] + * }) + * export class UserModule {} + * ``` + */ export const RgModule = (options: RgModuleMetadata): ClassDecorator => { return (target: Function) => { Reflect.defineMetadata("RgModule", options, target); diff --git a/src/@core/decorators/window-manager.ts b/src/@core/decorators/window-manager.ts index 1f9ebd9..3b08f3e 100644 --- a/src/@core/decorators/window-manager.ts +++ b/src/@core/decorators/window-manager.ts @@ -1,6 +1,47 @@ +/** + * @fileoverview @WindowManager decorator for defining BrowserWindow managers. + * + * The @WindowManager decorator: + * - Defines window configuration (hash, caching, BrowserWindow options) + * - Registers the class as a window factory provider + * - Enables lifecycle hooks (onFocus, onWebContentsDidFinishLoad, etc.) + * + * @module @core/decorators/window-manager + */ + import "reflect-metadata/lite"; import type { TParamsCreateWindow } from "../control-window/types.js"; +/** + * Decorator that marks a class as a BrowserWindow manager. + * + * Window managers: + * - Define window configuration and lifecycle + * - Can implement lifecycle hooks as methods (onFocus, onClose, etc.) + * - Are registered as providers keyed by their hash + * - Can inject services for window initialization logic + * + * @param options - Window configuration including hash, caching, and BrowserWindow options + * @returns ClassDecorator function + * + * @example + * ```typescript + * @WindowManager({ + * hash: 'window:user-profile', + * isCache: true, + * options: { + * width: 600, + * height: 400, + * resizable: false + * } + * }) + * export class UserWindow implements TWindowManager { + * onWebContentsDidFinishLoad(window: BrowserWindow): void { + * // Initialize when content loads + * } + * } + * ``` + */ export const WindowManager =

( options: TParamsCreateWindow

, ): ClassDecorator => { diff --git a/src/@core/errors/index.ts b/src/@core/errors/index.ts index 570f872..cb86af7 100644 --- a/src/@core/errors/index.ts +++ b/src/@core/errors/index.ts @@ -1,3 +1,23 @@ +/** + * @fileoverview Custom error classes for the dependency injection system. + * + * Provides specific error types for: + * - Module registration issues + * - Provider resolution failures + * - Decorator validation + * - Settings initialization + * + * All errors extend BaseError which sets the error name for better debugging. + * + * @module @core/errors + */ + +/** + * Base error class with custom name support. + * + * All framework errors extend this class to provide consistent error handling + * with descriptive error names. + */ class BaseError extends Error { constructor(msg: string, name: string) { super(msg); @@ -5,6 +25,13 @@ class BaseError extends Error { } } +/** + * Thrown when attempting to access a module that hasn't been registered. + * + * This typically indicates a programming error where a module is referenced + * before bootstrapModules() is called or the module wasn't included in the + * bootstrap array. + */ export class ModuleNotRegisteredError extends BaseError { constructor(m: string) { super( @@ -14,6 +41,14 @@ export class ModuleNotRegisteredError extends BaseError { } } +/** + * Thrown when a provider cannot be resolved for a given token. + * + * This usually means: + * - The provider wasn't registered in the module + * - The provider wasn't exported by an imported module + * - There's a typo in the provider token + */ export class ProviderNotFoundError extends BaseError { constructor(t: string, m: string) { super( @@ -23,6 +58,12 @@ export class ProviderNotFoundError extends BaseError { } } +/** + * Thrown when a module class is missing the @RgModule decorator. + * + * All modules passed to bootstrapModules() must be decorated with @RgModule + * to define their metadata (providers, imports, exports, etc.). + */ export class ModuleDecoratorMissingError extends BaseError { constructor(m: string) { super( @@ -32,6 +73,13 @@ export class ModuleDecoratorMissingError extends BaseError { } } +/** + * Thrown when a provider definition is invalid. + * + * Providers must be either: + * - A class constructor + * - A provider object with 'provide' property + */ export class InvalidProviderError extends BaseError { constructor(m: string) { super( @@ -41,6 +89,18 @@ export class InvalidProviderError extends BaseError { } } +/** + * Thrown when attempting to access settings before initialization. + * + * Call initSettings() before bootstrapModules() to configure the application: + * ```typescript + * initSettings({ + * localhostPort: '3000', + * folders: { distRenderer: 'dist-renderer', distMain: 'dist-main' } + * }); + * await bootstrapModules([...]); + * ``` + */ export class SettingsNotInitializedError extends BaseError { constructor() { super( diff --git a/src/@core/types/constructor.ts b/src/@core/types/constructor.ts index d9ed91e..b5aab6d 100644 --- a/src/@core/types/constructor.ts +++ b/src/@core/types/constructor.ts @@ -1 +1,15 @@ +/** + * @fileoverview Generic constructor type definition. + * + * Provides a type-safe representation of class constructors used throughout + * the dependency injection system. + * + * @module @core/types/constructor + */ + +/** + * Represents a class constructor that can be instantiated with any arguments. + * + * @template T - The type of instance the constructor creates + */ export type Constructor = new (...args: any[]) => T; diff --git a/src/@core/types/index.ts b/src/@core/types/index.ts index 1e2421d..cfca497 100644 --- a/src/@core/types/index.ts +++ b/src/@core/types/index.ts @@ -1,3 +1,11 @@ +/** + * @fileoverview Central export point for all type definitions. + * + * Re-exports all types used in the dependency injection and window management system. + * + * @module @core/types + */ + export type { Constructor } from "./constructor.js"; export type { TIpcHandlerInterface, TParamOnInit } from "./ipc-handler.js"; export type { RgModuleMetadata } from "./module-metadata.js"; diff --git a/src/@core/types/ipc-handler.ts b/src/@core/types/ipc-handler.ts index b9de6cc..7e232b3 100644 --- a/src/@core/types/ipc-handler.ts +++ b/src/@core/types/ipc-handler.ts @@ -1,9 +1,29 @@ +/** + * @fileoverview IPC handler interface definition. + * + * Defines the interface that IPC handler classes must implement. + * + * @module @core/types/ipc-handler + */ + import type { TWindowFactory } from "./window-factory.js"; +/** + * Parameters passed to IPC handler onInit method. + * + * @template N - String literal type for window hash + * @property getWindow - Function to retrieve window factories by hash + */ export type TParamOnInit = { getWindow: (name?: N) => TWindowFactory; }; +/** + * Interface that IPC handler classes must implement. + * + * IPC handlers are initialized during module bootstrap and receive + * a getWindow function to access window factories. + */ export type TIpcHandlerInterface = { onInit: (data: TParamOnInit) => void; }; diff --git a/src/@core/types/module-metadata.ts b/src/@core/types/module-metadata.ts index 4581e71..6f5f4e9 100644 --- a/src/@core/types/module-metadata.ts +++ b/src/@core/types/module-metadata.ts @@ -1,7 +1,24 @@ +/** + * @fileoverview Module metadata type definition. + * + * Defines the structure of metadata attached to modules by the @RgModule decorator. + * + * @module @core/types/module-metadata + */ + import type { TIpcHandlerInterface } from "./ipc-handler.js"; import type { Constructor } from "./constructor.js"; import type { TProvider, TProviderToken } from "./provider.js"; +/** + * Metadata for a module decorated with @RgModule. + * + * @property imports - Modules to import and make their exports available + * @property ipc - IPC handler classes to initialize + * @property windows - Window manager classes to register + * @property providers - Services and factories available in this module + * @property exports - Providers to make available to importing modules + */ export type RgModuleMetadata = { imports?: Constructor[]; ipc?: (new (...args: any[]) => TIpcHandlerInterface)[]; diff --git a/src/@core/types/provider.ts b/src/@core/types/provider.ts index 490f487..85b4773 100644 --- a/src/@core/types/provider.ts +++ b/src/@core/types/provider.ts @@ -1,29 +1,76 @@ +/** + * @fileoverview Provider type definitions for dependency injection. + * + * Defines all supported provider types: + * - Class providers (with custom token) + * - Factory providers (function-based instantiation) + * - Value providers (pre-instantiated values) + * - Existing providers (aliases to other providers) + * - Simple class constructors + * + * @module @core/types/provider + */ + import type { Constructor } from "./constructor.js"; +/** + * Token used to identify a provider in the DI container. + * Can be a class constructor, string, or Symbol. + */ export type TProviderToken = Constructor | string | symbol; +/** + * Provider that uses a custom class with explicit token. + * + * Use when you want to register a class with a different token than the class itself. + */ export type TClassProvider = { provide: TProviderToken; useClass: Constructor; inject?: TProviderToken[]; }; +/** + * Provider that uses a factory function to create instances. + * + * Use when instance creation requires custom logic or conditional configuration. + */ export type TFactoryProvider = { provide: TProviderToken; useFactory: (...args: any[]) => T; inject?: TProviderToken[]; }; +/** + * Provider that uses a pre-instantiated value. + * + * Use for configuration objects, constants, or pre-created instances. + */ export type TValueProvider = { provide: TProviderToken; useValue: T; }; +/** + * Provider that creates an alias to another provider. + * + * Use when you want multiple tokens to resolve to the same instance. + */ export type TExistingProvider = { provide: TProviderToken; useExisting: TProviderToken; }; +/** + * Union type of all supported provider formats. + * + * Can be: + * - Direct class constructor + * - Class provider with custom token + * - Factory provider + * - Value provider + * - Existing provider (alias) + */ export type TProvider = | Constructor | TClassProvider diff --git a/src/@core/types/window-factory.ts b/src/@core/types/window-factory.ts index 420bf4c..bf684d3 100644 --- a/src/@core/types/window-factory.ts +++ b/src/@core/types/window-factory.ts @@ -1,10 +1,29 @@ +/** + * @fileoverview Window factory type definitions. + * + * Defines types for window creation factories provided to IPC handlers. + * + * @module @core/types/window-factory + */ + import { BrowserWindow } from "electron"; import type { TParamsCreateWindow } from "../control-window/types.js"; +/** + * Function type for creating BrowserWindow instances. + * + * @param options - Optional parameters to customize window creation + * @returns Promise resolving to BrowserWindow or undefined + */ export type TWindowCreate = ( - options?: TParamsCreateWindow + options?: TParamsCreateWindow, ) => Promise; +/** + * Window factory interface provided to IPC handlers. + * + * Allows IPC handlers to create windows dynamically with optional parameters. + */ export type TWindowFactory = { create: TWindowCreate; }; diff --git a/src/@core/types/window-manager.ts b/src/@core/types/window-manager.ts index 9c6e5e2..e345b3c 100644 --- a/src/@core/types/window-manager.ts +++ b/src/@core/types/window-manager.ts @@ -1,12 +1,34 @@ +/** + * @fileoverview Window manager type definitions. + * + * Defines types for window manager configuration and lifecycle hooks. + * + * @module @core/types/window-manager + */ + import type { BrowserWindowConstructorOptions } from "electron"; import type { TParamsCreateWindow } from "../control-window/types.js"; +/** + * Window manager configuration options. + * + * Extends window creation parameters with required BrowserWindow options. + */ export type WindowManagerOptions = TParamsCreateWindow & { options: BrowserWindowConstructorOptions; }; +/** + * Generic window event handler function type. + */ type TWindowEventHandler = (...args: any[]) => void; +/** + * Type for window manager classes with lifecycle hook methods. + * + * Methods starting with 'on' are automatically attached as event listeners. + * Examples: onFocus, onClose, onWebContentsDidFinishLoad + */ export type TWindowManagerWithHandlers = { [key: `on${string}`]: TWindowEventHandler | undefined; }; diff --git a/src/@core/types/window-metadata.ts b/src/@core/types/window-metadata.ts index 33c2a8a..71df1b5 100644 --- a/src/@core/types/window-metadata.ts +++ b/src/@core/types/window-metadata.ts @@ -1,6 +1,20 @@ +/** + * @fileoverview Window metadata type definition. + * + * Defines the structure of window metadata stored in the container. + * + * @module @core/types/window-metadata + */ + import type { TParamsCreateWindow } from "../control-window/types.js"; import type { Constructor } from "./constructor.js"; +/** + * Metadata for a registered window manager. + * + * Combines window configuration with the window manager class constructor. + * This is stored in the container and used to create window factories. + */ export type TMetadataWindow = { metadata: TParamsCreateWindow; windowClass: Constructor; diff --git a/src/@core/utils/dependency-tokens.ts b/src/@core/utils/dependency-tokens.ts index cc5f9a9..5da002d 100644 --- a/src/@core/utils/dependency-tokens.ts +++ b/src/@core/utils/dependency-tokens.ts @@ -1,10 +1,44 @@ +/** + * @fileoverview Dependency token resolution utility. + * + * Extracts constructor parameter types using TypeScript's reflection metadata + * and merges them with manually injected tokens from @Inject decorator. + * + * @module @core/utils/dependency-tokens + */ + import "reflect-metadata/lite"; import type { Constructor } from "../types/constructor.js"; import type { TProviderToken } from "../types/provider.js"; import { getInjectedTokens } from "../decorators/inject.js"; +/** Cache to avoid repeated reflection lookups */ const dependencyTokensCache = new WeakMap(); +/** + * Extracts dependency tokens from a class constructor. + * + * Process: + * 1. Checks cache for previous results + * 2. Gets constructor parameter types from TypeScript metadata + * 3. Gets manually injected tokens from @Inject decorator + * 4. Merges both, with @Inject tokens taking precedence + * 5. Caches the result + * + * @param target - Class constructor to extract dependencies from + * @returns Array of provider tokens for each constructor parameter + * + * @example + * ```typescript + * // For class: constructor(private userService: UserService) + * const tokens = getDependencyTokens(MyClass); + * // Returns: [UserService] + * + * // For class: constructor(@Inject(API_TOKEN) private api: TApi) + * const tokens = getDependencyTokens(MyClass); + * // Returns: [API_TOKEN] + * ``` + */ export const getDependencyTokens = (target: Constructor): TProviderToken[] => { const cached = dependencyTokensCache.get(target); if (cached !== undefined) { diff --git a/src/config.ts b/src/config.ts index a90ab9f..a4c28d6 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,3 +1,18 @@ +/** + * @fileoverview Configuration defaults for build output folders. + * + * Defines the default folder structure for Electron application build outputs. + * These defaults can be overridden using the `initSettings` function. + * + * @module config + */ + +/** + * Default folder configuration for build outputs. + * + * @property {string} distRenderer - Default folder for renderer process build output + * @property {string} distMain - Default folder for main process build output + */ export const folders = { distRenderer: "dist-renderer", distMain: "dist-main",