@electrojs/runtime 1.0.7 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -252,6 +252,7 @@ declare function scanModules(rootModule: Constructor): AppDefinition;
252
252
  * @Injectable()
253
253
  * class DatabaseService implements LifecycleTarget {
254
254
  * async onInit() { await this.connect(); }
255
+ * async onStart() { await this.startReplication(); }
255
256
  * async onShutdown() { await this.disconnect(); }
256
257
  * }
257
258
  * ```
@@ -263,7 +264,9 @@ declare function scanModules(rootModule: Constructor): AppDefinition;
263
264
  interface LifecycleTarget {
264
265
  /** Called during initialization phase. Use for setting up connections, state, etc. */
265
266
  onInit?(): void | Promise<void>;
266
- /** Called after all modules have been initialized. Use for cross-module coordination. */
267
+ /** Called after the Electron app is ready. Use for startup side effects such as windows, jobs, and launch flows. */
268
+ onStart?(): void | Promise<void>;
269
+ /** Called after startup work completes. Use for final coordination before the kernel becomes `started`. */
267
270
  onReady?(): void | Promise<void>;
268
271
  /** Called during graceful shutdown. Use for releasing resources, closing connections. */
269
272
  onShutdown?(): void | Promise<void>;
@@ -274,6 +277,8 @@ interface LifecycleTarget {
274
277
  * Represents the current phase of a module's lifecycle.
275
278
  *
276
279
  * Transitions follow a strict state machine: creating -> ready -> started -> stopping -> stopped.
280
+ * Modules may also transition directly from `ready` to `stopped` if the kernel is
281
+ * shut down after initialization but before startup completes.
277
282
  * Any state can also transition to "failed".
278
283
  */
279
284
  type ModuleStatus = "creating" | "ready" | "started" | "stopping" | "stopped" | "failed";
@@ -386,6 +391,7 @@ interface AppKernelOptions {
386
391
  * @example
387
392
  * ```ts
388
393
  * const kernel = AppKernel.create(AppModule);
394
+ * await kernel.initialize();
389
395
  * await kernel.start();
390
396
  *
391
397
  * // ... app is running ...
@@ -394,10 +400,10 @@ interface AppKernelOptions {
394
400
  * ```
395
401
  *
396
402
  * @remarks
397
- * - Calling {@link start} when already started is a no-op.
398
- * - If startup fails, the kernel automatically rolls back initialized modules and
403
+ * - Calling {@link initialize} when already initialized is a no-op.
404
+ * - Calling {@link start} from `idle` first performs {@link initialize} for backward compatibility.
405
+ * - If initialization or startup fails, the kernel automatically rolls back initialized modules and
399
406
  * transitions to `failed`.
400
- * - {@link shutdown} can only be called from the `started` state.
401
407
  */
402
408
  declare class AppKernel {
403
409
  private state;
@@ -407,289 +413,250 @@ declare class AppKernel {
407
413
  private readonly logger;
408
414
  private readonly stateListeners;
409
415
  private readonly disposers;
416
+ private initializeTask?;
417
+ private startTask?;
418
+ private shutdownTask?;
410
419
  private constructor();
411
420
  /**
412
421
  * Creates a new kernel instance for the given root module.
413
- * The kernel starts in the `idle` state -- call {@link start} to bootstrap the application.
422
+ * The kernel starts in the `idle` state -- call {@link initialize} and/or {@link start} to bootstrap the application.
414
423
  */
415
424
  static create(rootModule: Constructor, options?: AppKernelOptions): AppKernel;
416
425
  /**
417
- * Bootstraps and starts the application.
426
+ * Builds the application graph and runs the initialization lifecycle.
418
427
  *
419
428
  * Performs module scanning, validation, DI container creation, module instantiation,
420
- * capability installation, and runs `onInit` / `onReady` lifecycle hooks.
421
- * On failure, automatically rolls back and transitions to `failed`.
429
+ * capability installation, and runs `onInit`.
422
430
  *
423
- * @remarks No-op if already started. Must be called from `idle` state.
431
+ * @remarks Safe to call before `app.whenReady()`. No-op if already initialized or started.
424
432
  * @throws {BootstrapError} On invalid module graph or decorator metadata.
425
433
  * @throws {LifecycleError} On lifecycle hook failure.
426
434
  */
435
+ initialize(): Promise<void>;
436
+ /**
437
+ * Starts the application after initialization.
438
+ *
439
+ * Runs `onStart` and `onReady` lifecycle hooks. Bridge calls from renderer processes
440
+ * become available at the beginning of the `starting` phase.
441
+ *
442
+ * @remarks No-op if already started. Calling from `idle` first performs {@link initialize}.
443
+ * @throws {LifecycleError} On lifecycle hook failure.
444
+ */
427
445
  start(): Promise<void>;
428
446
  /**
429
447
  * Gracefully shuts down the application.
430
448
  *
431
- * Runs `onShutdown` / `onDispose` lifecycle hooks in reverse module order,
432
- * then disposes framework services (jobs, windows, views).
449
+ * Runs `onShutdown` / `onDispose` for started kernels, or only `onDispose` if the
450
+ * kernel was initialized but never started.
433
451
  *
434
- * @remarks No-op if `idle` or already `stopped`. Throws if not in `started` state.
435
- * @throws {LifecycleError} If the kernel is not in the `started` state.
452
+ * @remarks No-op if `idle`, `stopped`, or already `failed`.
453
+ * @throws {LifecycleError} If shutdown is requested while startup is still in progress.
436
454
  */
437
455
  shutdown(): Promise<void>;
438
456
  /** Returns the current kernel lifecycle state. */
439
457
  getState(): KernelState;
440
458
  /** Convenience check: returns `true` only when the kernel is in the `started` state. */
441
459
  isStarted(): boolean;
442
- /** Returns the scanned app definition, or `undefined` if the kernel has not been started yet. */
460
+ /** Returns the scanned app definition, or `undefined` if the kernel has not been initialized yet. */
443
461
  getDefinition(): AppDefinition | undefined;
444
- /** Returns the live module references, or an empty array if the kernel has not been started yet. */
462
+ /** Returns the live module references, or an empty array if the kernel has not been initialized yet. */
445
463
  getModuleRefs(): readonly ModuleRef[];
446
464
  /** @internal Hook for subsystems that need to react to kernel state changes. */
447
465
  onStateChange(callback: (state: KernelState) => void): void;
466
+ private performInitialize;
467
+ private performStart;
468
+ private performShutdown;
469
+ private getPreviousRunningState;
448
470
  private registerFrameworkServices;
449
471
  private disposeServices;
450
472
  private transitionTo;
451
473
  private safeDisposeServices;
452
474
  }
453
475
  //#endregion
454
- //#region src/container/inject.d.ts
476
+ //#region src/desktop/view-manager.d.ts
455
477
  /**
456
- * Resolve a dependency from the current injection context.
478
+ * Framework-internal registry that tracks all `@View()` providers in the application.
457
479
  *
458
- * This is the primary way framework-managed classes obtain their dependencies.
459
- * It reads the active {@link Injector} from `AsyncLocalStorage` and delegates to
460
- * {@link Injector.get}.
480
+ * Handles registration (including injecting the source URL into the base class)
481
+ * and provides lookup by view ID. Consumers typically interact with views through
482
+ * their {@link ViewProvider} subclass rather than this manager directly.
461
483
  *
462
- * @remarks
463
- * `inject()` is only available inside a framework-managed execution scope:
464
- * - **Construction** -- property initializers of `@Injectable()`, `@Module()`, `@View()`, or `@Window()` classes
465
- * - **Lifecycle hooks** -- `onInit`, `onReady`, `onShutdown`, `onDispose`
466
- * - **Capability handlers** -- methods decorated with `@command`, `@query`, `@signal`, or `@job`
484
+ * @internal
485
+ */
486
+ declare class ViewManager {
487
+ private readonly views;
488
+ /**
489
+ * Register a view provider and apply its `@View()` source URL.
490
+ *
491
+ * @remarks Silently skips providers that do not have view metadata.
492
+ */
493
+ register(providerRef: ProviderRef): void;
494
+ /** Look up a registered view provider by its view ID. */
495
+ get(viewId: string): ProviderRef | undefined;
496
+ /** Return all registered view providers. */
497
+ list(): readonly ProviderRef[];
498
+ /** Clear the registry. Does not destroy any underlying web contents. */
499
+ dispose(): Promise<void>;
500
+ }
501
+ //#endregion
502
+ //#region src/desktop/view-provider.d.ts
503
+ declare const VIEW_STATE: unique symbol;
504
+ interface ViewAuthoringSurface {
505
+ readonly contentView: WebContentsView | undefined;
506
+ readonly webContents: WebContents | undefined;
507
+ load(): Promise<void>;
508
+ setBounds(bounds: Rectangle): void;
509
+ setBackgroundColor(color: string): void;
510
+ focus(): void;
511
+ setWindowButtonVisibility(visible: boolean): void;
512
+ }
513
+ /**
514
+ * Abstract base class for view providers decorated with `@View()`.
467
515
  *
468
- * Calling it anywhere else (e.g. in a `setTimeout` callback or a plain function)
469
- * throws {@link DIError} with code `ELECTRO_DI_NO_INJECTION_CONTEXT`.
516
+ * Extend this class to define a renderable view in your application. The framework
517
+ * injects the source URL from the `@View()` decorator, and your subclass gains access
518
+ * to methods for loading content, positioning, and styling.
519
+ *
520
+ * Views are created with strict security defaults: `sandbox: true`,
521
+ * `contextIsolation: true`, and `nodeIntegration: false`.
522
+ *
523
+ * @remarks The `WebContentsView` is lazily created on the first call to {@link load}.
470
524
  *
471
525
  * @example
472
526
  * ```ts
473
- * @Injectable()
474
- * class MyService {
475
- * private readonly config = inject(AppConfig);
527
+ * @View({ source: 'view:main', access: ['app:getVersion'] })
528
+ * class MainView extends ViewProvider {
529
+ * async onStart() {
530
+ * await this.load();
531
+ * this.setBounds({ x: 0, y: 0, width: 800, height: 600 });
532
+ * }
476
533
  * }
477
534
  * ```
478
- *
479
- * @throws {@link DIError} if no injection context is active or the token cannot be resolved.
480
535
  */
481
- declare function inject<T>(token: InjectionToken<T>): T;
536
+ declare abstract class ViewProvider {
537
+ private [VIEW_STATE];
538
+ /** The underlying Electron `WebContentsView`, or `undefined` if not yet created. */
539
+ get contentView(): WebContentsView | undefined;
540
+ /** The `WebContents` associated with this view, or `undefined` if not yet created. */
541
+ get webContents(): WebContents | undefined;
542
+ /**
543
+ * Create the underlying `WebContentsView` (if needed) and load the configured source URL.
544
+ *
545
+ * The view is lazily created on the first call. Subsequent calls reload the source.
546
+ * If the previous view was destroyed (e.g. parent window closed), a new one is created.
547
+ * No-ops if no source URL has been configured.
548
+ */
549
+ load(): Promise<void>;
550
+ /** Set the position and size of this view within its parent window. */
551
+ setBounds(bounds: Rectangle): void;
552
+ /** Set the background color of the view (e.g. `"#ffffff"` or `"transparent"`). */
553
+ setBackgroundColor(color: string): void;
554
+ /** Give keyboard focus to this view's web contents. */
555
+ focus(): void;
556
+ /** Show or hide the native window traffic-light buttons for this view (macOS). */
557
+ setWindowButtonVisibility(visible: boolean): void;
558
+ private createView;
559
+ private static resolveSource;
560
+ /** @internal Called by the framework to set the view source from @View metadata. */
561
+ static __setSource(instance: ViewProvider, source: string): void;
562
+ /** @internal Called by the framework to set web preferences from @View metadata. */
563
+ static __setWebPreferences(instance: ViewProvider, webPreferences: WebPreferences): void;
564
+ }
482
565
  //#endregion
483
- //#region src/container/injection-context.d.ts
566
+ //#region src/desktop/window-manager.d.ts
484
567
  /**
485
- * Ambient injection context backed by `AsyncLocalStorage`.
486
- *
487
- * The framework uses `InjectionContext` to make the current {@link Injector} available
488
- * to {@link inject} calls without requiring explicit injector references. It is set
489
- * automatically during provider construction, lifecycle hooks, and capability handlers.
568
+ * Framework-internal registry that tracks all `@Window()` providers in the application.
490
569
  *
491
- * @remarks
492
- * This is a framework-internal API. Application code should use {@link inject} instead
493
- * of interacting with `InjectionContext` directly.
570
+ * Handles registration (including injecting `@Window()` configuration into the base class)
571
+ * and provides lookup by window ID. Consumers typically interact with windows through
572
+ * their {@link WindowProvider} subclass rather than this manager directly.
494
573
  *
495
574
  * @internal
496
575
  */
497
- declare const InjectionContext: {
498
- /**
499
- * Execute `fn` within the scope of the given injector.
500
- * Any {@link inject} call made synchronously inside `fn` will resolve from this injector.
501
- */
502
- readonly run: <T>(injector: Injector, fn: () => T) => T;
576
+ declare class WindowManager {
577
+ private readonly windows;
503
578
  /**
504
- * Execute `fn` within the scope of the given injector, additionally tracking `owner`
505
- * as the object currently being constructed or invoked.
579
+ * Register a window provider and apply its `@Window()` configuration.
580
+ *
581
+ * @remarks Silently skips providers that do not have window metadata.
506
582
  */
507
- readonly runOwned: <T>(injector: Injector, owner: object, fn: () => T) => T; /** Return the injector for the current execution context, or `undefined` if none is active. */
508
- readonly current: () => Injector | undefined; /** Return the owner object of the current execution context, or `undefined` if not set. */
509
- readonly currentOwner: () => object | undefined; /** Whether a framework-managed injection context is currently active. */
510
- readonly isActive: () => boolean;
511
- };
512
- //#endregion
513
- //#region src/modules/registry.d.ts
514
- /**
515
- * Serializable point-in-time view of a provider's registration state.
516
- * Produced by {@link ModuleRegistry.snapshot} for runtime introspection and tooling.
517
- */
518
- interface ProviderSnapshot {
519
- readonly id: string;
520
- readonly target: string;
521
- readonly kind: "provider" | "view" | "window";
522
- readonly scope: string;
523
- /** Bridge channels this provider exposes (commands and queries). */
524
- readonly bridgeChannels: readonly string[];
525
- /** Signal IDs this provider subscribes to. */
526
- readonly signalIds: readonly string[];
527
- /** Scheduled job IDs registered by this provider. */
528
- readonly jobIds: readonly string[];
583
+ register(providerRef: ProviderRef): void;
584
+ /** Look up a registered window provider by its window ID. */
585
+ get(windowId: string): ProviderRef | undefined;
586
+ /** Return all registered window providers. */
587
+ list(): readonly ProviderRef[];
588
+ /** Close all managed windows and clear the registry. */
589
+ dispose(): Promise<void>;
529
590
  }
530
- /**
531
- * Serializable point-in-time view of a module's registration and lifecycle state.
532
- * Produced by {@link ModuleRegistry.snapshot} for runtime introspection and tooling (e.g. CLI module graph).
533
- */
534
- interface ModuleSnapshot {
535
- readonly id: string;
536
- readonly target: string;
537
- readonly status: ModuleStatus;
538
- /** IDs of modules this module imports. */
539
- readonly imports: readonly string[];
540
- /** Class names of providers this module exports. */
541
- readonly exports: readonly string[];
542
- readonly providers: readonly ProviderSnapshot[];
591
+ //#endregion
592
+ //#region src/desktop/window-provider.d.ts
593
+ declare const WINDOW_STATE: unique symbol;
594
+ interface WindowAuthoringSurface {
595
+ readonly window: BaseWindow | undefined;
596
+ create(): void;
597
+ mount(view: Pick<ViewProvider, "contentView">): void;
598
+ show(): void;
599
+ hide(): void;
600
+ focus(): void;
601
+ close(): void;
602
+ getBounds(): Rectangle | undefined;
543
603
  }
544
604
  /**
545
- * Central registry of all loaded modules in the application.
605
+ * Abstract base class for window providers decorated with `@Window()`.
546
606
  *
547
- * Provides lookup by class constructor or module ID, and produces serializable
548
- * snapshots for runtime introspection (e.g. CLI `inspect` commands, debug tooling).
549
- * Populated once during bootstrap by the capability installer.
607
+ * Extend this class to define a window in your application. The framework injects
608
+ * configuration from the `@Window()` decorator metadata, and your subclass gains
609
+ * access to lifecycle methods like {@link create}, {@link show}, {@link mount}, and {@link close}.
610
+ *
611
+ * @remarks The native `BaseWindow` instance is only available after calling {@link create}.
612
+ * Calling lifecycle methods before `create()` is safe (they no-op) but the window
613
+ * will not exist on screen.
550
614
  *
551
615
  * @example
552
616
  * ```ts
553
- * const registry = injector.get(ModuleRegistry);
554
- * const snapshot = registry.snapshot(); // serializable module graph
617
+ * @Window({ width: 800, height: 600, show: false })
618
+ * class MainWindow extends WindowProvider {
619
+ * async onStart() {
620
+ * this.create();
621
+ * this.mount(this.mainView);
622
+ * this.show();
623
+ * }
624
+ * }
555
625
  * ```
556
626
  */
557
- declare class ModuleRegistry {
558
- private readonly modulesByTarget;
559
- private readonly modulesById;
560
- /** @internal Populate the registry from bootstrap. Called once by the capability installer. */
561
- load(moduleRefs: readonly ModuleRef[]): void;
562
- /** Looks up a module by its decorated class constructor. */
563
- getByTarget(target: Constructor): ModuleRef | undefined;
564
- /** Looks up a module by its unique string identifier. */
565
- getById(moduleId: string): ModuleRef | undefined;
566
- /** Returns all registered modules. */
567
- getAll(): readonly ModuleRef[];
568
- /** Produces a serializable snapshot of the entire module graph for introspection. */
569
- snapshot(): readonly ModuleSnapshot[];
627
+ declare abstract class WindowProvider {
628
+ private [WINDOW_STATE];
629
+ /** The underlying Electron `BaseWindow` instance, or `undefined` if not yet created. */
630
+ get window(): BaseWindow | undefined;
631
+ /** Create the native `BaseWindow` using configuration from the `@Window()` decorator. */
632
+ create(): void;
633
+ /**
634
+ * Attach a view's content to this window.
635
+ *
636
+ * The view's `WebContentsView` is added as a child view of the window's content area.
637
+ * No-ops silently if the window or view has not been created yet.
638
+ */
639
+ mount(view: ViewProvider): void;
640
+ /** Show the window. No-ops if the window has not been created. */
641
+ show(): void;
642
+ /** Hide the window without destroying it. */
643
+ hide(): void;
644
+ /** Bring the window to the front and give it focus. */
645
+ focus(): void;
646
+ /** Close and destroy the native window, releasing its resources. */
647
+ close(): void;
648
+ /** Return the window's current screen bounds, or `undefined` if not created. */
649
+ getBounds(): Rectangle | undefined;
650
+ /** @internal Called by the framework to set the window configuration from @Window metadata. */
651
+ static __setConfiguration(instance: WindowProvider, configuration?: BaseWindowConstructorOptions): void;
570
652
  }
571
653
  //#endregion
572
- //#region src/modules/validator.d.ts
654
+ //#region src/jobs/context.d.ts
573
655
  /**
574
- * Validates a scanned {@link AppDefinition} for structural correctness before bootstrap.
656
+ * Execution context passed to every job handler invocation.
575
657
  *
576
- * Checks performed:
577
- * - Unique module IDs
578
- * - Exports reference declared providers
579
- * - No cyclic module imports
580
- * - Unique view/window IDs
581
- * - Unique bridge channel names
582
- * - Unique job IDs
583
- * - Provider role exclusivity (a class cannot be both a view and a window)
584
- * - View access references point to existing bridge channels
585
- *
586
- * @throws {BootstrapError} On the first validation failure encountered.
587
- */
588
- declare function validateAppDefinition(definition: AppDefinition): void;
589
- //#endregion
590
- //#region src/signals/relay.d.ts
591
- /**
592
- * Callback invoked by {@link PublicationRelay} whenever a signal is published.
593
- *
594
- * Typically used by the renderer registry to forward signals from the main process
595
- * to renderer processes that have opted in via `@View({ signals: [...] })`.
596
- */
597
- type SignalPublishListener = (signalId: string, payload: unknown) => void;
598
- //#endregion
599
- //#region src/signals/context.d.ts
600
- /**
601
- * Metadata object passed to {@link ContextualSignalHandler} callbacks when a signal is published.
602
- *
603
- * Provides contextual information about the publication event. Handlers that need
604
- * access to this metadata should declare two parameters (context, payload) so the
605
- * {@link SignalBus} treats them as contextual handlers.
606
- */
607
- declare class SignalContext {
608
- /** Epoch timestamp (milliseconds) captured at the moment the signal was dispatched to this handler. */
609
- readonly timestamp: number;
610
- }
611
- //#endregion
612
- //#region src/signals/bus.d.ts
613
- /**
614
- * A callback that receives only the signal payload.
615
- *
616
- * Use this form when the handler does not need access to the {@link SignalContext}.
617
- */
618
- type SignalListener<T = unknown> = (payload: T) => void | Promise<void>;
619
- /**
620
- * A callback that receives both a {@link SignalContext} and the signal payload.
621
- *
622
- * Use this form when the handler needs metadata about the publication (e.g. timestamp).
623
- */
624
- type ContextualSignalHandler<T = unknown> = (context: SignalContext, payload: T) => void | Promise<void>;
625
- /**
626
- * Union of the two supported handler signatures for {@link SignalBus.subscribe}.
627
- *
628
- * The bus distinguishes between the two forms by arity: handlers with two or more
629
- * parameters are treated as {@link ContextualSignalHandler}, all others as {@link SignalListener}.
630
- */
631
- type SignalHandler<T = unknown> = SignalListener<T> | ContextualSignalHandler<T>;
632
- /**
633
- * Fire-and-forget publish/subscribe signal bus for decoupled inter-module communication.
634
- *
635
- * Handlers are dispatched asynchronously via `queueMicrotask`, so `publish()` never blocks
636
- * the caller. Each handler runs inside the dependency-injection context that was active at
637
- * the time `subscribe()` was called, which means `inject()` works correctly inside handlers.
638
- *
639
- * @example
640
- * ```ts
641
- * // Subscribe to a signal (returns an unsubscribe function)
642
- * const unsub = signalBus.subscribe('user:login', (payload) => {
643
- * console.log('User logged in:', payload.userId);
644
- * });
645
- *
646
- * // Publish a signal — all handlers run asynchronously
647
- * signalBus.publish('user:login', { userId: '42' });
648
- *
649
- * // Unsubscribe when no longer needed
650
- * unsub();
651
- * ```
652
- *
653
- * @example
654
- * ```ts
655
- * // Contextual handler with access to SignalContext
656
- * signalBus.subscribe('data:sync', (context, payload) => {
657
- * console.log('Signal published at:', context.timestamp);
658
- * });
659
- * ```
660
- */
661
- declare class SignalBus {
662
- private readonly handlers;
663
- private readonly relay;
664
- /**
665
- * Register a handler for the given signal.
666
- *
667
- * The handler is bound to the current injection context at call time, so
668
- * `inject()` resolves correctly even though the handler runs asynchronously.
669
- *
670
- * @returns A dispose function that removes the subscription.
671
- */
672
- subscribe<T>(signalId: string, handler: SignalHandler<T>): () => void;
673
- /**
674
- * Publish a signal to all registered handlers.
675
- *
676
- * Handlers are invoked asynchronously via `queueMicrotask` and their errors are
677
- * logged but never propagated to the publisher. The signal is also forwarded to
678
- * any connected {@link PublicationRelay} listeners (e.g. renderer processes).
679
- *
680
- * @remarks Payload is optional for signals that carry no data (`SignalBus.publish<void>('app:ready')`).
681
- */
682
- publish<T = void>(signalId: string, payload?: T): void;
683
- /** @internal Connect a relay listener (used by renderer registry). */
684
- connectRelay(listener: SignalPublishListener): () => void;
685
- }
686
- //#endregion
687
- //#region src/jobs/context.d.ts
688
- /**
689
- * Execution context passed to every job handler invocation.
690
- *
691
- * Provides cooperative cancellation and progress reporting. The handler should
692
- * periodically check {@link isCanceled} and abort work when it returns `true`.
658
+ * Provides cooperative cancellation and progress reporting. The handler should
659
+ * periodically check {@link isCanceled} and abort work when it returns `true`.
693
660
  *
694
661
  * @example
695
662
  * ```ts
@@ -819,242 +786,398 @@ declare class JobRegistry {
819
786
  private executeJob;
820
787
  }
821
788
  //#endregion
822
- //#region src/desktop/renderer-session.d.ts
789
+ //#region src/modules/registry.d.ts
823
790
  /**
824
- * Tracks the capabilities of a single renderer process (identified by `webContentsId`).
791
+ * Serializable point-in-time view of a provider's registration state.
792
+ * Produced by {@link ModuleRegistry.snapshot} for runtime introspection and tooling.
793
+ */
794
+ interface ProviderSnapshot {
795
+ readonly id: string;
796
+ readonly target: string;
797
+ readonly kind: "provider" | "view" | "window";
798
+ readonly scope: string;
799
+ /** Bridge channels this provider exposes (commands and queries). */
800
+ readonly bridgeChannels: readonly string[];
801
+ /** Signal IDs this provider subscribes to. */
802
+ readonly signalIds: readonly string[];
803
+ /** Scheduled job IDs registered by this provider. */
804
+ readonly jobIds: readonly string[];
805
+ }
806
+ /**
807
+ * Serializable point-in-time view of a module's registration and lifecycle state.
808
+ * Produced by {@link ModuleRegistry.snapshot} for runtime introspection and tooling (e.g. CLI module graph).
809
+ */
810
+ interface ModuleSnapshot {
811
+ readonly id: string;
812
+ readonly target: string;
813
+ readonly status: ModuleStatus;
814
+ /** IDs of modules this module imports. */
815
+ readonly imports: readonly string[];
816
+ /** Class names of providers this module exports. */
817
+ readonly exports: readonly string[];
818
+ readonly providers: readonly ProviderSnapshot[];
819
+ }
820
+ /**
821
+ * Central registry of all loaded modules in the application.
825
822
  *
826
- * Each session records which bridge channels the renderer may invoke and which signals
827
- * it is allowed to receive, as declared in the `@View()` decorator metadata. The
828
- * {@link BridgeAccessGuard} and signal relay use this information to enforce per-view
829
- * access control.
823
+ * Provides lookup by class constructor or module ID, and produces serializable
824
+ * snapshots for runtime introspection (e.g. CLI `inspect` commands, debug tooling).
825
+ * Populated once during bootstrap by the capability installer.
830
826
  *
831
- * @internal Created and managed by {@link RendererRegistry}.
827
+ * @example
828
+ * ```ts
829
+ * const registry = injector.get(ModuleRegistry);
830
+ * const snapshot = registry.snapshot(); // serializable module graph
831
+ * ```
832
832
  */
833
- declare class RendererSession {
834
- /** The Electron `webContents.id` that uniquely identifies this renderer process. */
835
- readonly rendererId: number;
836
- /** The view ID this renderer is associated with. */
837
- readonly viewId: string;
838
- /** Set of bridge channel names this renderer is allowed to invoke. */
839
- readonly access: ReadonlySet<string>;
840
- /** Set of signal IDs this renderer is allowed to receive. */
841
- readonly allowedSignals: ReadonlySet<string>;
842
- constructor(/** The Electron `webContents.id` that uniquely identifies this renderer process. */
843
-
844
- rendererId: number, viewDef: ViewDefinition);
845
- /** Check whether this renderer has access to the given bridge channel. */
846
- hasAccess(channel: string): boolean;
847
- /** Check whether this renderer is allowed to receive the given signal. */
848
- canReceiveSignal(signalId: string): boolean;
833
+ declare class ModuleRegistry {
834
+ private readonly modulesByTarget;
835
+ private readonly modulesById;
836
+ /** @internal Populate the registry from bootstrap. Called once by the capability installer. */
837
+ load(moduleRefs: readonly ModuleRef[]): void;
838
+ /** Looks up a module by its decorated class constructor. */
839
+ getByTarget(target: Constructor): ModuleRef | undefined;
840
+ /** Looks up a module by its unique string identifier. */
841
+ getById(moduleId: string): ModuleRef | undefined;
842
+ /** Returns all registered modules. */
843
+ getAll(): readonly ModuleRef[];
844
+ /** Produces a serializable snapshot of the entire module graph for introspection. */
845
+ snapshot(): readonly ModuleSnapshot[];
849
846
  }
850
847
  //#endregion
851
- //#region src/desktop/renderer-registry.d.ts
848
+ //#region src/signals/relay.d.ts
852
849
  /**
853
- * Maps Electron `webContentsId` values to {@link RendererSession} instances.
850
+ * Callback invoked by {@link PublicationRelay} whenever a signal is published.
854
851
  *
855
- * Used by the bridge layer to look up which renderer process is making an IPC request
856
- * and determine what channels and signals it is allowed to access. View definitions are
857
- * registered at bootstrap time; sessions are created lazily when a renderer first
858
- * communicates.
852
+ * Typically used by the renderer registry to forward signals from the main process
853
+ * to renderer processes that have opted in via `@View({ signals: [...] })`.
854
+ */
855
+ type SignalPublishListener = (signalId: string, payload: unknown) => void;
856
+ //#endregion
857
+ //#region src/signals/context.d.ts
858
+ /**
859
+ * Metadata object passed to {@link ContextualSignalHandler} callbacks when a signal is published.
859
860
  *
860
- * @internal
861
+ * Provides contextual information about the publication event. Handlers that need
862
+ * access to this metadata should declare two parameters (context, payload) so the
863
+ * {@link SignalBus} treats them as contextual handlers.
861
864
  */
862
- declare class RendererRegistry {
863
- private readonly sessions;
864
- private readonly viewDefinitions;
865
- /** Store a view definition so that sessions can be created for it later. */
866
- registerView(viewDef: ViewDefinition): void;
867
- /**
868
- * Return the existing session for a `webContentsId`, or create one if this is
869
- * the first request from that renderer.
870
- *
871
- * @throws {BridgeError} If the `viewId` does not match any registered view definition.
872
- */
873
- getOrCreateSession(webContentsId: number, viewId: string): RendererSession;
874
- /** Look up an existing session by its `webContentsId`, or `undefined` if none exists. */
875
- getSession(webContentsId: number): RendererSession | undefined;
876
- /** Remove a session when its renderer process is destroyed. */
877
- removeSession(webContentsId: number): void;
878
- /** Return all active renderer sessions. */
879
- getAllSessions(): readonly RendererSession[];
865
+ declare class SignalContext {
866
+ /** Epoch timestamp (milliseconds) captured at the moment the signal was dispatched to this handler. */
867
+ readonly timestamp: number;
880
868
  }
881
869
  //#endregion
882
- //#region src/desktop/view-manager.d.ts
870
+ //#region src/signals/bus.d.ts
883
871
  /**
884
- * Framework-internal registry that tracks all `@View()` providers in the application.
872
+ * A callback that receives only the signal payload.
885
873
  *
886
- * Handles registration (including injecting the source URL into the base class)
887
- * and provides lookup by view ID. Consumers typically interact with views through
888
- * their {@link ViewProvider} subclass rather than this manager directly.
874
+ * Use this form when the handler does not need access to the {@link SignalContext}.
875
+ */
876
+ type SignalListener<T = unknown> = (payload: T) => void | Promise<void>;
877
+ /**
878
+ * A callback that receives both a {@link SignalContext} and the signal payload.
889
879
  *
890
- * @internal
880
+ * Use this form when the handler needs metadata about the publication (e.g. timestamp).
891
881
  */
892
- declare class ViewManager {
893
- private readonly views;
882
+ type ContextualSignalHandler<T = unknown> = (context: SignalContext, payload: T) => void | Promise<void>;
883
+ /**
884
+ * Union of the two supported handler signatures for {@link SignalBus.subscribe}.
885
+ *
886
+ * The bus distinguishes between the two forms by arity: handlers with two or more
887
+ * parameters are treated as {@link ContextualSignalHandler}, all others as {@link SignalListener}.
888
+ */
889
+ type SignalHandler<T = unknown> = SignalListener<T> | ContextualSignalHandler<T>;
890
+ /**
891
+ * Fire-and-forget publish/subscribe signal bus for decoupled inter-module communication.
892
+ *
893
+ * Handlers are dispatched asynchronously via `queueMicrotask`, so `publish()` never blocks
894
+ * the caller. Each handler runs inside the dependency-injection context that was active at
895
+ * the time `subscribe()` was called, which means `inject()` works correctly inside handlers.
896
+ *
897
+ * @example
898
+ * ```ts
899
+ * // Subscribe to a signal (returns an unsubscribe function)
900
+ * const unsub = signalBus.subscribe('user:login', (payload) => {
901
+ * console.log('User logged in:', payload.userId);
902
+ * });
903
+ *
904
+ * // Publish a signal — all handlers run asynchronously
905
+ * signalBus.publish('user:login', { userId: '42' });
906
+ *
907
+ * // Unsubscribe when no longer needed
908
+ * unsub();
909
+ * ```
910
+ *
911
+ * @example
912
+ * ```ts
913
+ * // Contextual handler with access to SignalContext
914
+ * signalBus.subscribe('data:sync', (context, payload) => {
915
+ * console.log('Signal published at:', context.timestamp);
916
+ * });
917
+ * ```
918
+ */
919
+ declare class SignalBus {
920
+ private readonly handlers;
921
+ private readonly relay;
894
922
  /**
895
- * Register a view provider and apply its `@View()` source URL.
923
+ * Register a handler for the given signal.
896
924
  *
897
- * @remarks Silently skips providers that do not have view metadata.
925
+ * The handler is bound to the current injection context at call time, so
926
+ * `inject()` resolves correctly even though the handler runs asynchronously.
927
+ *
928
+ * @returns A dispose function that removes the subscription.
898
929
  */
899
- register(providerRef: ProviderRef): void;
900
- /** Look up a registered view provider by its view ID. */
901
- get(viewId: string): ProviderRef | undefined;
902
- /** Return all registered view providers. */
903
- list(): readonly ProviderRef[];
904
- /** Clear the registry. Does not destroy any underlying web contents. */
905
- dispose(): Promise<void>;
930
+ subscribe<T>(signalId: string, handler: SignalListener<T>): () => void;
931
+ subscribe<T>(signalId: string, handler: ContextualSignalHandler<T>): () => void;
932
+ /**
933
+ * Publish a signal to all registered handlers.
934
+ *
935
+ * Handlers are invoked asynchronously via `queueMicrotask` and their errors are
936
+ * logged but never propagated to the publisher. The signal is also forwarded to
937
+ * any connected {@link PublicationRelay} listeners (e.g. renderer processes).
938
+ *
939
+ * @remarks Payload is optional for signals that carry no data (`SignalBus.publish<void>('app:ready')`).
940
+ */
941
+ publish<T = void>(signalId: string, payload?: T): void;
942
+ /** @internal Connect a relay listener (used by renderer registry). */
943
+ connectRelay(listener: SignalPublishListener): () => void;
906
944
  }
907
945
  //#endregion
908
- //#region src/desktop/view-provider.d.ts
909
- declare const VIEW_STATE: unique symbol;
910
- interface ViewAuthoringSurface {
911
- readonly contentView: WebContentsView | undefined;
912
- readonly webContents: WebContents | undefined;
913
- load(): Promise<void>;
914
- setBounds(bounds: Rectangle): void;
915
- setBackgroundColor(color: string): void;
916
- focus(): void;
917
- setWindowButtonVisibility(visible: boolean): void;
946
+ //#region src/contracts/authoring.d.ts
947
+ /**
948
+ * Codegen contract types for `@electrojs/runtime`.
949
+ *
950
+ * These interfaces and types are used by `@electrojs/codegen` to generate
951
+ * type-safe authoring APIs. The codegen augments the empty registry interfaces
952
+ * via `declare module "@electro"` to populate them with app-specific types.
953
+ *
954
+ * @module contracts/authoring
955
+ */
956
+ /** Maps `"moduleId:methodId"` to the method's type reference. Augmented by codegen. */
957
+ interface ModuleMethodMap {}
958
+ /** Maps module IDs to their method type objects. Augmented by codegen. */
959
+ interface ModuleApiRegistry {}
960
+ /** Maps signal keys to their payload types. Augmented by codegen. */
961
+ interface ModuleSignalPayloadMap {}
962
+ /** Maps module IDs to their job ID unions. Augmented by codegen. */
963
+ interface ModuleJobRegistry {}
964
+ /** Maps injectable class names to their class types. Augmented by codegen. */
965
+ interface InjectableClassRegistry {}
966
+ /** Maps window IDs to their `@Window()` class types. Augmented by codegen. */
967
+ interface WindowClassRegistry {}
968
+ /** Maps view IDs to their `@View()` class types. Augmented by codegen. */
969
+ interface ViewClassRegistry {}
970
+ /** Union of all registered module IDs. Derived from `ModuleApiRegistry` keys. */
971
+ type ModuleRegistryId = keyof ModuleApiRegistry;
972
+ type JobIdFor<TModuleId extends ModuleRegistryId> = Extract<ModuleJobRegistry[TModuleId], string>;
973
+ type SignalId = Extract<keyof ModuleSignalPayloadMap, string>;
974
+ type VoidSignalId = { [TKey in SignalId]: ModuleSignalPayloadMap[TKey] extends void ? TKey : never }[SignalId];
975
+ type NonVoidSignalId = Exclude<SignalId, VoidSignalId>;
976
+ /** A class decorated with `@Module()`. Used in generated registry arrays. */
977
+ type ModuleClass = Constructor;
978
+ /** A class decorated with `@View()`. Used in generated registry arrays. */
979
+ type ViewClass = Constructor;
980
+ /** A class decorated with `@Window()`. Used in generated registry arrays. */
981
+ type WindowClass = Constructor;
982
+ /**
983
+ * Shape of the static kernel definition object generated by codegen.
984
+ *
985
+ * Used by the generated `electroAppDefinition` constant which can be
986
+ * passed to `AppKernel.create()` for fully typed bootstrapping.
987
+ */
988
+ interface AppKernelDefinition {
989
+ readonly root: Constructor;
990
+ readonly modules: readonly Constructor[];
991
+ readonly windows: readonly Constructor[];
992
+ readonly views: readonly Constructor[];
993
+ }
994
+ interface TypedSignalBus {
995
+ publish<TSignalId extends VoidSignalId>(signalId: TSignalId): void;
996
+ publish<TSignalId extends NonVoidSignalId>(signalId: TSignalId, payload: ModuleSignalPayloadMap[TSignalId]): void;
997
+ subscribe<TSignalId extends SignalId>(signalId: TSignalId, handler: SignalListener<ModuleSignalPayloadMap[TSignalId]>): () => void;
998
+ subscribe<TSignalId extends SignalId>(signalId: TSignalId, handler: ContextualSignalHandler<ModuleSignalPayloadMap[TSignalId]>): () => void;
999
+ }
1000
+ interface TypedJobRegistry<TJobId extends string = string> {
1001
+ ensure(jobId: TJobId): void;
1002
+ start(jobId: TJobId): void;
1003
+ run(jobId: TJobId, ...args: unknown[]): Promise<unknown>;
1004
+ stop(jobId: TJobId): Promise<void>;
1005
+ cancel(jobId: TJobId): void;
1006
+ getStatus(jobId: TJobId): JobRuntimeState | undefined;
1007
+ list(): readonly JobRuntimeState[];
918
1008
  }
919
1009
  /**
920
- * Abstract base class for view providers decorated with `@View()`.
1010
+ * Base type augmented onto module and provider classes by codegen.
921
1011
  *
922
- * Extend this class to define a renderable view in your application. The framework
923
- * injects the source URL from the `@View()` decorator, and your subclass gains access
924
- * to methods for loading content, positioning, and styling.
1012
+ * Codegen generates `interface MyModule extends ModuleAuthoringApi<"myModuleId"> {}`
1013
+ * which gives the class access to typed methods based on the module's registered
1014
+ * commands, queries, signals, and jobs.
925
1015
  *
926
- * Views are created with strict security defaults: `sandbox: true`,
927
- * `contextIsolation: true`, and `nodeIntegration: false`.
1016
+ */
1017
+ interface ModuleAuthoringApi<TModuleId extends ModuleRegistryId> {
1018
+ readonly moduleId: TModuleId;
1019
+ readonly api: ModuleApiRegistry[TModuleId];
1020
+ readonly signals: TypedSignalBus;
1021
+ readonly jobs: TypedJobRegistry<JobIdFor<TModuleId>>;
1022
+ readonly modules: ModuleRegistry;
1023
+ readonly windows: WindowManager;
1024
+ readonly views: ViewManager;
1025
+ readonly logger: ElectroLogger;
1026
+ }
1027
+ /**
1028
+ * Base type augmented onto `@Window()` classes by codegen.
928
1029
  *
929
- * @remarks The `WebContentsView` is lazily created on the first call to {@link load}.
1030
+ * Provides typed window management capabilities when extended by codegen-generated
1031
+ * interface augmentations.
1032
+ */
1033
+ interface WindowAuthoringApi extends WindowAuthoringSurface {
1034
+ readonly logger: ElectroLogger;
1035
+ }
1036
+ /**
1037
+ * Base type augmented onto `@View()` classes by codegen.
1038
+ *
1039
+ * Provides typed view management capabilities when extended by codegen-generated
1040
+ * interface augmentations.
1041
+ */
1042
+ interface ViewAuthoringApi extends ViewAuthoringSurface {
1043
+ readonly logger: ElectroLogger;
1044
+ }
1045
+ //#endregion
1046
+ //#region src/container/inject.d.ts
1047
+ /**
1048
+ * Resolve a dependency from the current injection context.
1049
+ *
1050
+ * This is the primary way framework-managed classes obtain their dependencies.
1051
+ * It reads the active {@link Injector} from `AsyncLocalStorage` and delegates to
1052
+ * {@link Injector.get}.
1053
+ *
1054
+ * @remarks
1055
+ * `inject()` is only available inside a framework-managed execution scope:
1056
+ * - **Construction** -- property initializers of `@Injectable()`, `@Module()`, `@View()`, or `@Window()` classes
1057
+ * - **Lifecycle hooks** -- `onInit`, `onStart`, `onReady`, `onShutdown`, `onDispose`
1058
+ * - **Capability handlers** -- methods decorated with `@command`, `@query`, `@signal`, or `@job`
1059
+ *
1060
+ * Calling it anywhere else (e.g. in a `setTimeout` callback or a plain function)
1061
+ * throws {@link DIError} with code `ELECTRO_DI_NO_INJECTION_CONTEXT`.
930
1062
  *
931
1063
  * @example
932
1064
  * ```ts
933
- * @View({ source: 'view:main', access: ['app:getVersion'] })
934
- * class MainView extends ViewProvider {
935
- * async onReady() {
936
- * await this.load();
937
- * this.setBounds({ x: 0, y: 0, width: 800, height: 600 });
938
- * }
1065
+ * @Injectable()
1066
+ * class MyService {
1067
+ * private readonly config = inject(AppConfig);
939
1068
  * }
940
1069
  * ```
1070
+ *
1071
+ * @throws {@link DIError} if no injection context is active or the token cannot be resolved.
941
1072
  */
942
- declare abstract class ViewProvider {
943
- private [VIEW_STATE];
944
- /** The underlying Electron `WebContentsView`, or `undefined` if not yet created. */
945
- get contentView(): WebContentsView | undefined;
946
- /** The `WebContents` associated with this view, or `undefined` if not yet created. */
947
- get webContents(): WebContents | undefined;
948
- /**
949
- * Create the underlying `WebContentsView` (if needed) and load the configured source URL.
950
- *
951
- * The view is lazily created on the first call. Subsequent calls reload the source.
952
- * If the previous view was destroyed (e.g. parent window closed), a new one is created.
953
- * No-ops if no source URL has been configured.
954
- */
955
- load(): Promise<void>;
956
- /** Set the position and size of this view within its parent window. */
957
- setBounds(bounds: Rectangle): void;
958
- /** Set the background color of the view (e.g. `"#ffffff"` or `"transparent"`). */
959
- setBackgroundColor(color: string): void;
960
- /** Give keyboard focus to this view's web contents. */
961
- focus(): void;
962
- /** Show or hide the native window traffic-light buttons for this view (macOS). */
963
- setWindowButtonVisibility(visible: boolean): void;
964
- private createView;
965
- private static resolveSource;
966
- /** @internal Called by the framework to set the view source from @View metadata. */
967
- static __setSource(instance: ViewProvider, source: string): void;
968
- /** @internal Called by the framework to set web preferences from @View metadata. */
969
- static __setWebPreferences(instance: ViewProvider, webPreferences: WebPreferences): void;
970
- }
1073
+ declare function inject<T>(token: InjectionToken<T>): T extends SignalBus ? TypedSignalBus : T;
971
1074
  //#endregion
972
- //#region src/desktop/window-manager.d.ts
1075
+ //#region src/container/injection-context.d.ts
973
1076
  /**
974
- * Framework-internal registry that tracks all `@Window()` providers in the application.
1077
+ * Ambient injection context backed by `AsyncLocalStorage`.
975
1078
  *
976
- * Handles registration (including injecting `@Window()` configuration into the base class)
977
- * and provides lookup by window ID. Consumers typically interact with windows through
978
- * their {@link WindowProvider} subclass rather than this manager directly.
1079
+ * The framework uses `InjectionContext` to make the current {@link Injector} available
1080
+ * to {@link inject} calls without requiring explicit injector references. It is set
1081
+ * automatically during provider construction, lifecycle hooks, and capability handlers.
1082
+ *
1083
+ * @remarks
1084
+ * This is a framework-internal API. Application code should use {@link inject} instead
1085
+ * of interacting with `InjectionContext` directly.
979
1086
  *
980
1087
  * @internal
981
1088
  */
982
- declare class WindowManager {
983
- private readonly windows;
1089
+ declare const InjectionContext: {
984
1090
  /**
985
- * Register a window provider and apply its `@Window()` configuration.
986
- *
987
- * @remarks Silently skips providers that do not have window metadata.
1091
+ * Execute `fn` within the scope of the given injector.
1092
+ * Any {@link inject} call made synchronously inside `fn` will resolve from this injector.
988
1093
  */
989
- register(providerRef: ProviderRef): void;
990
- /** Look up a registered window provider by its window ID. */
991
- get(windowId: string): ProviderRef | undefined;
992
- /** Return all registered window providers. */
993
- list(): readonly ProviderRef[];
994
- /** Close all managed windows and clear the registry. */
995
- dispose(): Promise<void>;
996
- }
1094
+ readonly run: <T>(injector: Injector, fn: () => T) => T;
1095
+ /**
1096
+ * Execute `fn` within the scope of the given injector, additionally tracking `owner`
1097
+ * as the object currently being constructed or invoked.
1098
+ */
1099
+ readonly runOwned: <T>(injector: Injector, owner: object, fn: () => T) => T; /** Return the injector for the current execution context, or `undefined` if none is active. */
1100
+ readonly current: () => Injector | undefined; /** Return the owner object of the current execution context, or `undefined` if not set. */
1101
+ readonly currentOwner: () => object | undefined; /** Whether a framework-managed injection context is currently active. */
1102
+ readonly isActive: () => boolean;
1103
+ };
997
1104
  //#endregion
998
- //#region src/desktop/window-provider.d.ts
999
- declare const WINDOW_STATE: unique symbol;
1000
- interface WindowAuthoringSurface {
1001
- readonly window: BaseWindow | undefined;
1002
- create(): void;
1003
- mount(view: Pick<ViewProvider, "contentView">): void;
1004
- show(): void;
1005
- hide(): void;
1006
- focus(): void;
1007
- close(): void;
1008
- getBounds(): Rectangle | undefined;
1009
- }
1105
+ //#region src/modules/validator.d.ts
1010
1106
  /**
1011
- * Abstract base class for window providers decorated with `@Window()`.
1107
+ * Validates a scanned {@link AppDefinition} for structural correctness before bootstrap.
1012
1108
  *
1013
- * Extend this class to define a window in your application. The framework injects
1014
- * configuration from the `@Window()` decorator metadata, and your subclass gains
1015
- * access to lifecycle methods like {@link create}, {@link show}, {@link mount}, and {@link close}.
1109
+ * Checks performed:
1110
+ * - Unique module IDs
1111
+ * - Exports reference declared providers
1112
+ * - No cyclic module imports
1113
+ * - Unique view/window IDs
1114
+ * - Unique bridge channel names
1115
+ * - Unique job IDs
1116
+ * - Provider role exclusivity (a class cannot be both a view and a window)
1117
+ * - View access references point to existing bridge channels
1016
1118
  *
1017
- * @remarks The native `BaseWindow` instance is only available after calling {@link create}.
1018
- * Calling lifecycle methods before `create()` is safe (they no-op) but the window
1019
- * will not exist on screen.
1119
+ * @throws {BootstrapError} On the first validation failure encountered.
1120
+ */
1121
+ declare function validateAppDefinition(definition: AppDefinition): void;
1122
+ //#endregion
1123
+ //#region src/desktop/renderer-session.d.ts
1124
+ /**
1125
+ * Tracks the capabilities of a single renderer process (identified by `webContentsId`).
1020
1126
  *
1021
- * @example
1022
- * ```ts
1023
- * @Window({ width: 800, height: 600, show: false })
1024
- * class MainWindow extends WindowProvider {
1025
- * async onReady() {
1026
- * this.create();
1027
- * this.mount(this.mainView);
1028
- * this.show();
1029
- * }
1030
- * }
1031
- * ```
1127
+ * Each session records which bridge channels the renderer may invoke and which signals
1128
+ * it is allowed to receive, as declared in the `@View()` decorator metadata. The
1129
+ * {@link BridgeAccessGuard} and signal relay use this information to enforce per-view
1130
+ * access control.
1131
+ *
1132
+ * @internal Created and managed by {@link RendererRegistry}.
1032
1133
  */
1033
- declare abstract class WindowProvider {
1034
- private [WINDOW_STATE];
1035
- /** The underlying Electron `BaseWindow` instance, or `undefined` if not yet created. */
1036
- get window(): BaseWindow | undefined;
1037
- /** Create the native `BaseWindow` using configuration from the `@Window()` decorator. */
1038
- create(): void;
1134
+ declare class RendererSession {
1135
+ /** The Electron `webContents.id` that uniquely identifies this renderer process. */
1136
+ readonly rendererId: number;
1137
+ /** The view ID this renderer is associated with. */
1138
+ readonly viewId: string;
1139
+ /** Set of bridge channel names this renderer is allowed to invoke. */
1140
+ readonly access: ReadonlySet<string>;
1141
+ /** Set of signal IDs this renderer is allowed to receive. */
1142
+ readonly allowedSignals: ReadonlySet<string>;
1143
+ constructor(/** The Electron `webContents.id` that uniquely identifies this renderer process. */
1144
+
1145
+ rendererId: number, viewDef: ViewDefinition);
1146
+ /** Check whether this renderer has access to the given bridge channel. */
1147
+ hasAccess(channel: string): boolean;
1148
+ /** Check whether this renderer is allowed to receive the given signal. */
1149
+ canReceiveSignal(signalId: string): boolean;
1150
+ }
1151
+ //#endregion
1152
+ //#region src/desktop/renderer-registry.d.ts
1153
+ /**
1154
+ * Maps Electron `webContentsId` values to {@link RendererSession} instances.
1155
+ *
1156
+ * Used by the bridge layer to look up which renderer process is making an IPC request
1157
+ * and determine what channels and signals it is allowed to access. View definitions are
1158
+ * registered at bootstrap time; sessions are created lazily when a renderer first
1159
+ * communicates.
1160
+ *
1161
+ * @internal
1162
+ */
1163
+ declare class RendererRegistry {
1164
+ private readonly sessions;
1165
+ private readonly viewDefinitions;
1166
+ /** Store a view definition so that sessions can be created for it later. */
1167
+ registerView(viewDef: ViewDefinition): void;
1039
1168
  /**
1040
- * Attach a view's content to this window.
1169
+ * Return the existing session for a `webContentsId`, or create one if this is
1170
+ * the first request from that renderer.
1041
1171
  *
1042
- * The view's `WebContentsView` is added as a child view of the window's content area.
1043
- * No-ops silently if the window or view has not been created yet.
1172
+ * @throws {BridgeError} If the `viewId` does not match any registered view definition.
1044
1173
  */
1045
- mount(view: ViewProvider): void;
1046
- /** Show the window. No-ops if the window has not been created. */
1047
- show(): void;
1048
- /** Hide the window without destroying it. */
1049
- hide(): void;
1050
- /** Bring the window to the front and give it focus. */
1051
- focus(): void;
1052
- /** Close and destroy the native window, releasing its resources. */
1053
- close(): void;
1054
- /** Return the window's current screen bounds, or `undefined` if not created. */
1055
- getBounds(): Rectangle | undefined;
1056
- /** @internal Called by the framework to set the window configuration from @Window metadata. */
1057
- static __setConfiguration(instance: WindowProvider, configuration?: BaseWindowConstructorOptions): void;
1174
+ getOrCreateSession(webContentsId: number, viewId: string): RendererSession;
1175
+ /** Look up an existing session by its `webContentsId`, or `undefined` if none exists. */
1176
+ getSession(webContentsId: number): RendererSession | undefined;
1177
+ /** Remove a session when its renderer process is destroyed. */
1178
+ removeSession(webContentsId: number): void;
1179
+ /** Return all active renderer sessions. */
1180
+ getAllSessions(): readonly RendererSession[];
1058
1181
  }
1059
1182
  //#endregion
1060
1183
  //#region src/bridge/access-guard.d.ts
@@ -1062,7 +1185,7 @@ declare abstract class WindowProvider {
1062
1185
  * Enforces access control for bridge IPC calls.
1063
1186
  *
1064
1187
  * Checks two conditions before a bridge request is allowed through:
1065
- * 1. The application kernel must be in the `"started"` state.
1188
+ * 1. The application kernel must be in the `"starting"` or `"started"` state.
1066
1189
  * 2. The requesting renderer's {@link RendererSession} must have the target channel
1067
1190
  * in its access set (as declared in `@View({ access: [...] })`).
1068
1191
  *
@@ -1073,7 +1196,7 @@ declare class BridgeAccessGuard {
1073
1196
  /** Update the tracked kernel state. Called by the kernel during lifecycle transitions. */
1074
1197
  setKernelState(state: KernelState): void;
1075
1198
  /**
1076
- * Assert that the kernel is in the `"started"` state.
1199
+ * Assert that the kernel is in a state where bridge calls are allowed.
1077
1200
  *
1078
1201
  * @throws {BridgeError} If the kernel is not ready to handle requests.
1079
1202
  */
@@ -1147,7 +1270,8 @@ interface BridgeResponse {
1147
1270
  /**
1148
1271
  * Routes IPC requests from renderer processes to the appropriate {@link BridgeHandler}.
1149
1272
  *
1150
- * For each incoming request the dispatcher verifies that the kernel is ready,
1273
+ * For each incoming request the dispatcher verifies that the kernel currently
1274
+ * accepts bridge calls,
1151
1275
  * that the requesting renderer has an active session, and that the session grants
1152
1276
  * access to the requested channel. If all checks pass the corresponding handler
1153
1277
  * is invoked; otherwise a serialized error is returned. Errors never propagate as
@@ -1165,7 +1289,7 @@ declare class BridgeDispatcher {
1165
1289
  /**
1166
1290
  * Process an IPC request from a renderer identified by its `webContentsId`.
1167
1291
  *
1168
- * Performs kernel-readiness, session-existence, and channel-access checks before
1292
+ * Performs kernel bridge-readiness, session-existence, and channel-access checks before
1169
1293
  * delegating to the registered handler. All errors are caught and returned as
1170
1294
  * serialized error objects in the response.
1171
1295
  */
@@ -1278,7 +1402,7 @@ declare class BridgeError extends RuntimeError {
1278
1402
  * The original error is attached as {@link Error.cause}.
1279
1403
  */
1280
1404
  static handlerFailed(channel: string, cause: unknown): BridgeError;
1281
- /** A bridge call arrived before the kernel finished starting or after it began shutting down. */
1405
+ /** A bridge call arrived before startup entered the `starting` phase or after shutdown began. */
1282
1406
  static kernelNotReady(): BridgeError;
1283
1407
  /** A bridge request came from a `webContents` that is not associated with any known view. */
1284
1408
  static unknownRenderer(webContentsId: number): BridgeError;
@@ -1310,7 +1434,7 @@ declare class DIError extends RuntimeError {
1310
1434
  *
1311
1435
  * @remarks
1312
1436
  * `inject()` is only available during construction (property initializers),
1313
- * lifecycle hooks (`onInit`/`onReady`/`onShutdown`/`onDispose`),
1437
+ * lifecycle hooks (`onInit`/`onStart`/`onReady`/`onShutdown`/`onDispose`),
1314
1438
  * and capability handlers (`@command`, `@query`, `@signal`, `@job`).
1315
1439
  */
1316
1440
  static noInjectionContext(): DIError;
@@ -1346,7 +1470,7 @@ declare class JobError extends RuntimeError {
1346
1470
  declare class LifecycleError extends RuntimeError {
1347
1471
  private constructor();
1348
1472
  /**
1349
- * A lifecycle hook (`onInit`, `onReady`, etc.) threw during the startup sequence.
1473
+ * A lifecycle hook (`onInit`, `onStart`, `onReady`, etc.) threw during the startup sequence.
1350
1474
  *
1351
1475
  * @remarks
1352
1476
  * The original error is attached as {@link Error.cause}.
@@ -1374,103 +1498,4 @@ declare class SignalError extends RuntimeError {
1374
1498
  static invalidSignalId(signalId: unknown): SignalError;
1375
1499
  }
1376
1500
  //#endregion
1377
- //#region src/contracts/authoring.d.ts
1378
- /**
1379
- * Codegen contract types for `@electrojs/runtime`.
1380
- *
1381
- * These interfaces and types are used by `@electrojs/codegen` to generate
1382
- * type-safe authoring APIs. The codegen augments the empty registry interfaces
1383
- * via `declare module "@electro"` to populate them with app-specific types.
1384
- *
1385
- * @module contracts/authoring
1386
- */
1387
- /** Maps `"moduleId:methodId"` to the method's type reference. Augmented by codegen. */
1388
- interface ModuleMethodMap {}
1389
- /** Maps module IDs to their method type objects. Augmented by codegen. */
1390
- interface ModuleApiRegistry {}
1391
- /** Maps signal keys to their payload types. Augmented by codegen. */
1392
- interface ModuleSignalPayloadMap {}
1393
- /** Maps module IDs to their job ID unions. Augmented by codegen. */
1394
- interface ModuleJobRegistry {}
1395
- /** Maps injectable class names to their class types. Augmented by codegen. */
1396
- interface InjectableClassRegistry {}
1397
- /** Maps window IDs to their `@Window()` class types. Augmented by codegen. */
1398
- interface WindowClassRegistry {}
1399
- /** Maps view IDs to their `@View()` class types. Augmented by codegen. */
1400
- interface ViewClassRegistry {}
1401
- /** Union of all registered module IDs. Derived from `ModuleApiRegistry` keys. */
1402
- type ModuleRegistryId = keyof ModuleApiRegistry;
1403
- type JobIdFor<TModuleId extends ModuleRegistryId> = Extract<ModuleJobRegistry[TModuleId], string>;
1404
- type SignalId = Extract<keyof ModuleSignalPayloadMap, string>;
1405
- type VoidSignalId = { [TKey in SignalId]: ModuleSignalPayloadMap[TKey] extends void ? TKey : never }[SignalId];
1406
- type NonVoidSignalId = Exclude<SignalId, VoidSignalId>;
1407
- /** A class decorated with `@Module()`. Used in generated registry arrays. */
1408
- type ModuleClass = Constructor;
1409
- /** A class decorated with `@View()`. Used in generated registry arrays. */
1410
- type ViewClass = Constructor;
1411
- /** A class decorated with `@Window()`. Used in generated registry arrays. */
1412
- type WindowClass = Constructor;
1413
- /**
1414
- * Shape of the static kernel definition object generated by codegen.
1415
- *
1416
- * Used by the generated `electroAppDefinition` constant which can be
1417
- * passed to `AppKernel.create()` for fully typed bootstrapping.
1418
- */
1419
- interface AppKernelDefinition {
1420
- readonly root: Constructor;
1421
- readonly modules: readonly Constructor[];
1422
- readonly windows: readonly Constructor[];
1423
- readonly views: readonly Constructor[];
1424
- }
1425
- interface TypedSignalBus {
1426
- publish<TSignalId extends VoidSignalId>(signalId: TSignalId): void;
1427
- publish<TSignalId extends NonVoidSignalId>(signalId: TSignalId, payload: ModuleSignalPayloadMap[TSignalId]): void;
1428
- subscribe<TSignalId extends SignalId>(signalId: TSignalId, handler: SignalHandler<ModuleSignalPayloadMap[TSignalId]>): () => void;
1429
- }
1430
- interface TypedJobRegistry<TJobId extends string = string> {
1431
- ensure(jobId: TJobId): void;
1432
- start(jobId: TJobId): void;
1433
- run(jobId: TJobId, ...args: unknown[]): Promise<unknown>;
1434
- stop(jobId: TJobId): Promise<void>;
1435
- cancel(jobId: TJobId): void;
1436
- getStatus(jobId: TJobId): JobRuntimeState | undefined;
1437
- list(): readonly JobRuntimeState[];
1438
- }
1439
- /**
1440
- * Base type augmented onto module and provider classes by codegen.
1441
- *
1442
- * Codegen generates `interface MyModule extends ModuleAuthoringApi<"myModuleId"> {}`
1443
- * which gives the class access to typed methods based on the module's registered
1444
- * commands, queries, signals, and jobs.
1445
- *
1446
- */
1447
- interface ModuleAuthoringApi<TModuleId extends ModuleRegistryId> {
1448
- readonly moduleId: TModuleId;
1449
- readonly api: ModuleApiRegistry[TModuleId];
1450
- readonly signals: TypedSignalBus;
1451
- readonly jobs: TypedJobRegistry<JobIdFor<TModuleId>>;
1452
- readonly modules: ModuleRegistry;
1453
- readonly windows: WindowManager;
1454
- readonly views: ViewManager;
1455
- readonly logger: ElectroLogger;
1456
- }
1457
- /**
1458
- * Base type augmented onto `@Window()` classes by codegen.
1459
- *
1460
- * Provides typed window management capabilities when extended by codegen-generated
1461
- * interface augmentations.
1462
- */
1463
- interface WindowAuthoringApi extends WindowAuthoringSurface {
1464
- readonly logger: ElectroLogger;
1465
- }
1466
- /**
1467
- * Base type augmented onto `@View()` classes by codegen.
1468
- *
1469
- * Provides typed view management capabilities when extended by codegen-generated
1470
- * interface augmentations.
1471
- */
1472
- interface ViewAuthoringApi extends ViewAuthoringSurface {
1473
- readonly logger: ElectroLogger;
1474
- }
1475
- //#endregion
1476
1501
  export { type AppDefinition, AppKernel, type AppKernelDefinition, type AppKernelOptions, BootstrapError, BridgeAccessGuard, type BridgeClientConfig, BridgeDispatcher, BridgeError, BridgeHandler, type BridgeMethodDefinition, type BridgeMethodKind, type BridgeRequest, type BridgeResponse, type ContextualSignalHandler, DIError, type ElectroIpcRenderer, type ElectroLogBindings, type ElectroLogContext, type ElectroLogLevel, type ElectroLogger, IPC_CHANNELS, type InjectableClassRegistry, InjectionContext, Injector, JobContext, type JobDefinition, JobError, JobRegistry, type JobRuntimeState, type JobStatus, type KernelState, LifecycleError, type LifecycleTarget, type ModuleApiRegistry, type ModuleAuthoringApi, type ModuleClass, type ModuleDefinition, type ModuleJobRegistry, type ModuleMethodMap, ModuleRef, ModuleRegistry, type ModuleRegistryId, type ModuleSignalPayloadMap, type ModuleSnapshot, type ModuleStatus, type PreloadBridgeApi, type ProviderDefinition, type ProviderKind, ProviderRef, type ProviderSnapshot, RendererRegistry, RendererSession, RuntimeError, SignalBus, SignalContext, SignalError, type SignalHandler, type SignalHandlerDefinition, type SignalListener, type TypedJobRegistry, type TypedSignalBus, type ViewAuthoringApi, type ViewClass, type ViewClassRegistry, type ViewDefinition, ViewManager, ViewProvider, type WindowAuthoringApi, type WindowClass, type WindowClassRegistry, type WindowDefinition, WindowManager, WindowProvider, createBridgeClient, createConsoleLogger, inject, scanModules, serializeBridgeError, validateAppDefinition };