@cordy/electro 1.2.4 → 1.2.5

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
@@ -16,90 +16,9 @@ declare enum FeatureStatus {
16
16
  ERROR = "error"
17
17
  }
18
18
  //#endregion
19
- //#region src/core/view/types.d.ts
20
- type ViewId = string;
21
- /** Runtime view registry entry — injected by CLI via __ELECTRO_VIEW_REGISTRY__. */
22
- interface ViewRegistryEntry {
23
- id: ViewId;
24
- hasRenderer: boolean;
25
- webPreferences?: Record<string, unknown>;
26
- }
27
- /** A WebContentsView augmented with `load()` for renderer-linked views. */
28
- type ElectroView = WebContentsView & {
29
- load(): Promise<void>;
30
- };
31
- /** Public interface — hides the View class. */
32
- interface ViewInstance {
33
- readonly id: ViewId;
34
- /** Create the WebContentsView. Idempotent: returns existing if alive. */
35
- create(): ElectroView;
36
- /** Return the live ElectroView, or null if not yet created / destroyed. */
37
- view(): ElectroView | null;
38
- /** Destroy the WebContentsView and clear refs. */
39
- destroy(): void;
40
- }
41
- //#endregion
42
- //#region src/core/view/manager.d.ts
43
- /**
44
- * ViewManager — global registry for view instances.
45
- *
46
- * Views are registered at runtime startup and accessible from any feature.
47
- * Views are idempotent: `get(id).create()` returns the same instance if alive.
48
- */
49
- declare class ViewManager {
50
- private readonly instances;
51
- register(view: ViewInstance): void;
52
- get(id: ViewId): ViewInstance | null;
53
- destroyAll(): void;
54
- }
55
- //#endregion
56
- //#region src/core/window/types.d.ts
57
- type WindowId = string;
58
- interface WindowConfig<TApi = void> {
59
- id: WindowId;
60
- options?: BaseWindowConstructorOptions;
61
- api?: (window: BaseWindow) => TApi;
62
- }
63
- /** Base window interface (without API methods). */
64
- interface WindowBase {
65
- readonly id: WindowId;
66
- /** Create the BaseWindow. Idempotent: returns existing if alive. */
67
- create(): BaseWindow;
68
- /** The live BaseWindow, or null if not yet created / destroyed. */
69
- readonly window: BaseWindow | null;
70
- /** Destroy the BaseWindow and clear refs. */
71
- destroy(): void;
72
- }
73
- /**
74
- * Public interface — API methods are mixed directly onto the object.
75
- * Access API methods directly: `window.show()` instead of `window.api?.show()`.
76
- */
77
- type CreatedWindow<TApi = void> = WindowBase & (TApi extends void ? {} : TApi) & {
78
- /** @internal Phantom type for API type inference. */readonly __apiType?: TApi;
79
- };
80
- /** Type-erased interface for managers. */
81
- interface WindowInstance {
82
- readonly id: WindowId;
83
- create(): BaseWindow;
84
- readonly window: BaseWindow | null;
85
- destroy(): void;
86
- }
87
- //#endregion
88
- //#region src/core/window/manager.d.ts
89
- /**
90
- * WindowManager — global registry for window instances.
91
- *
92
- * Windows are registered at runtime startup and accessible from any feature.
93
- */
94
- declare class WindowManager {
95
- private readonly instances;
96
- register(window: WindowInstance): void;
97
- get(id: WindowId): WindowInstance | null;
98
- destroyAll(): void;
99
- }
100
- //#endregion
101
19
  //#region src/core/event-bus/types.d.ts
102
20
  type EventHandler = (payload: unknown) => void;
21
+ type EventInterceptor = (channel: string, payload: unknown) => void;
103
22
  type EventSubscription = {
104
23
  channel: string;
105
24
  handler: EventHandler;
@@ -129,7 +48,10 @@ interface CreatedEvent<T = void> extends EventInstance {
129
48
  //#region src/core/event-bus/event-bus.d.ts
130
49
  declare class EventBus {
131
50
  private readonly subscriptions;
51
+ private readonly interceptors;
132
52
  publish(channel: string, payload?: unknown): void;
53
+ /** Register an interceptor that receives every published event. Returns an unsubscribe function. */
54
+ addInterceptor(fn: EventInterceptor): () => void;
133
55
  subscribe(channel: string, handler: EventHandler, ownerId: string): () => void;
134
56
  removeByOwner(ownerId: string): void;
135
57
  }
@@ -376,6 +298,89 @@ declare class StateMachine<TState extends string> {
376
298
  onTransition(cb: TransitionListener<TState>): () => void;
377
299
  }
378
300
  //#endregion
301
+ //#region src/core/view/types.d.ts
302
+ type ViewId = string;
303
+ /** Runtime view registry entry — injected by CLI via __ELECTRO_VIEW_REGISTRY__. */
304
+ interface ViewRegistryEntry {
305
+ id: ViewId;
306
+ hasRenderer: boolean;
307
+ features?: readonly string[];
308
+ webPreferences?: Record<string, unknown>;
309
+ }
310
+ /** A WebContentsView augmented with `load()` for renderer-linked views. */
311
+ type ElectroView = WebContentsView & {
312
+ load(): Promise<void>;
313
+ };
314
+ /** Public interface — hides the View class. */
315
+ interface ViewInstance {
316
+ readonly id: ViewId;
317
+ /** Create the WebContentsView. Idempotent: returns existing if alive. */
318
+ create(): ElectroView;
319
+ /** Return the live ElectroView, or null if not yet created / destroyed. */
320
+ view(): ElectroView | null;
321
+ /** Destroy the WebContentsView and clear refs. */
322
+ destroy(): void;
323
+ }
324
+ //#endregion
325
+ //#region src/core/view/manager.d.ts
326
+ /**
327
+ * ViewManager — global registry for view instances.
328
+ *
329
+ * Views are registered at runtime startup and accessible from any feature.
330
+ * Views are idempotent: `get(id).create()` returns the same instance if alive.
331
+ */
332
+ declare class ViewManager {
333
+ private readonly instances;
334
+ register(view: ViewInstance): void;
335
+ get(id: ViewId): ViewInstance | null;
336
+ destroyAll(): void;
337
+ }
338
+ //#endregion
339
+ //#region src/core/window/types.d.ts
340
+ type WindowId = string;
341
+ interface WindowConfig<TApi = void> {
342
+ id: WindowId;
343
+ options?: BaseWindowConstructorOptions;
344
+ api?: (window: BaseWindow) => TApi;
345
+ }
346
+ /** Base window interface (without API methods). */
347
+ interface WindowBase {
348
+ readonly id: WindowId;
349
+ /** Create the BaseWindow. Idempotent: returns existing if alive. */
350
+ create(): BaseWindow;
351
+ /** The live BaseWindow, or null if not yet created / destroyed. */
352
+ readonly window: BaseWindow | null;
353
+ /** Destroy the BaseWindow and clear refs. */
354
+ destroy(): void;
355
+ }
356
+ /**
357
+ * Public interface — API methods are mixed directly onto the object.
358
+ * Access API methods directly: `window.show()` instead of `window.api?.show()`.
359
+ */
360
+ type CreatedWindow<TApi = void> = WindowBase & (TApi extends void ? {} : TApi) & {
361
+ /** @internal Phantom type for API type inference. */readonly __apiType?: TApi;
362
+ };
363
+ /** Type-erased interface for managers. */
364
+ interface WindowInstance {
365
+ readonly id: WindowId;
366
+ create(): BaseWindow;
367
+ readonly window: BaseWindow | null;
368
+ destroy(): void;
369
+ }
370
+ //#endregion
371
+ //#region src/core/window/manager.d.ts
372
+ /**
373
+ * WindowManager — global registry for window instances.
374
+ *
375
+ * Windows are registered at runtime startup and accessible from any feature.
376
+ */
377
+ declare class WindowManager {
378
+ private readonly instances;
379
+ register(window: WindowInstance): void;
380
+ get(id: WindowId): WindowInstance | null;
381
+ destroyAll(): void;
382
+ }
383
+ //#endregion
379
384
  //#region src/core/feature/manager.d.ts
380
385
  declare class FeatureManager {
381
386
  private logger;
@@ -679,6 +684,28 @@ declare class EventAccessor {
679
684
  on(event: string, handler: EventHandler): () => void;
680
685
  }
681
686
  //#endregion
687
+ //#region src/core/event-bus/bridge.d.ts
688
+ /**
689
+ * Forwards EventBus publishes to renderer views via IPC.
690
+ *
691
+ * Respects deny-by-default policy: only views whose `features` array
692
+ * includes the publishing feature's ID receive the event.
693
+ *
694
+ * IPC channel format: `electro:event:{featureId}:{eventName}`
695
+ */
696
+ declare class EventBridge {
697
+ private readonly eventBus;
698
+ private readonly viewManager;
699
+ private readonly viewFeatures;
700
+ private removeInterceptor;
701
+ constructor(eventBus: EventBus, viewManager: ViewManager, viewRegistry: readonly ViewRegistryEntry[]);
702
+ /** Start forwarding events to eligible views. */
703
+ start(): void;
704
+ /** Stop forwarding and clean up. */
705
+ stop(): void;
706
+ private forward;
707
+ }
708
+ //#endregion
682
709
  //#region src/core/event-bus/helpers.d.ts
683
710
  /**
684
711
  * Creates a typed event definition.
@@ -746,6 +773,7 @@ declare class Runtime {
746
773
  private readonly eventBus;
747
774
  private readonly windowManager;
748
775
  private readonly viewManager;
776
+ private readonly eventBridge;
749
777
  constructor(config?: RuntimeConfig);
750
778
  register(features: FeatureConfig<any> | FeatureConfig<any>[]): void;
751
779
  start(): Promise<void>;
@@ -820,4 +848,4 @@ declare class PolicyEngine {
820
848
  getViewNames(): string[];
821
849
  }
822
850
  //#endregion
823
- export { type BaseContext, type CreatedEvent, type CreatedService, type CreatedTask, type CreatedWindow, type DefineConfigInput, type DefineRuntimeInput, type DefineViewInput, type ElectroConfig, type ElectroView, EventAccessor, type EventHandler, type EventId, type EventInstance, type EventSubscription, type FeatureConfig, type FeatureContext, type FeatureHandle, type FeatureId, type FeatureMap, FeatureStatus, type LogEntry, type LogHandler, type LoggerContext, PolicyDecision, PolicyEngine, type PolicyResult, Runtime, type RuntimeConfig, type RuntimeDefinition, RuntimeState, type ServiceConfig, type ServiceId, type ServiceInfo, type ServiceOwnerMap, ServiceScope, ServiceStatus, type StopMode, type TaskConfig, type TaskExecutionContext, type TaskHandle, type TaskId, TaskOverlapStrategy, type TaskOwnerMap, TaskRetryStrategy, TaskStatus, type TaskStatusInfo, type TypedContext, type ViewDefinition, type ViewId, type ViewInstance, type ViewMap, type ViewRegistryEntry, type WindowApiMap, type WindowConfig, type WindowId, type WindowInstance, createEvent, createFeature, createRuntime, createService, createTask, createWindow, defineConfig, defineRuntime, defineView };
851
+ export { type BaseContext, type CreatedEvent, type CreatedService, type CreatedTask, type CreatedWindow, type DefineConfigInput, type DefineRuntimeInput, type DefineViewInput, type ElectroConfig, type ElectroView, EventAccessor, EventBridge, type EventHandler, type EventId, type EventInstance, type EventInterceptor, type EventSubscription, type FeatureConfig, type FeatureContext, type FeatureHandle, type FeatureId, type FeatureMap, FeatureStatus, type LogEntry, type LogHandler, type LoggerContext, PolicyDecision, PolicyEngine, type PolicyResult, Runtime, type RuntimeConfig, type RuntimeDefinition, RuntimeState, type ServiceConfig, type ServiceId, type ServiceInfo, type ServiceOwnerMap, ServiceScope, ServiceStatus, type StopMode, type TaskConfig, type TaskExecutionContext, type TaskHandle, type TaskId, TaskOverlapStrategy, type TaskOwnerMap, TaskRetryStrategy, TaskStatus, type TaskStatusInfo, type TypedContext, type ViewDefinition, type ViewId, type ViewInstance, type ViewMap, type ViewRegistryEntry, type WindowApiMap, type WindowConfig, type WindowId, type WindowInstance, createEvent, createFeature, createRuntime, createService, createTask, createWindow, defineConfig, defineRuntime, defineView };
package/dist/index.mjs CHANGED
@@ -96,6 +96,51 @@ var EventAccessor = class {
96
96
  }
97
97
  };
98
98
 
99
+ //#endregion
100
+ //#region src/core/event-bus/bridge.ts
101
+ /**
102
+ * Forwards EventBus publishes to renderer views via IPC.
103
+ *
104
+ * Respects deny-by-default policy: only views whose `features` array
105
+ * includes the publishing feature's ID receive the event.
106
+ *
107
+ * IPC channel format: `electro:event:{featureId}:{eventName}`
108
+ */
109
+ var EventBridge = class {
110
+ viewFeatures;
111
+ removeInterceptor = null;
112
+ constructor(eventBus, viewManager, viewRegistry) {
113
+ this.eventBus = eventBus;
114
+ this.viewManager = viewManager;
115
+ this.viewFeatures = /* @__PURE__ */ new Map();
116
+ for (const entry of viewRegistry) if (entry.features && entry.features.length > 0) this.viewFeatures.set(entry.id, new Set(entry.features));
117
+ }
118
+ /** Start forwarding events to eligible views. */
119
+ start() {
120
+ if (this.removeInterceptor) return;
121
+ this.removeInterceptor = this.eventBus.addInterceptor((channel, payload) => this.forward(channel, payload));
122
+ }
123
+ /** Stop forwarding and clean up. */
124
+ stop() {
125
+ this.removeInterceptor?.();
126
+ this.removeInterceptor = null;
127
+ }
128
+ forward(channel, payload) {
129
+ const colonIdx = channel.indexOf(":");
130
+ if (colonIdx === -1) return;
131
+ const featureId = channel.slice(0, colonIdx);
132
+ const ipcChannel = `electro:event:${channel}`;
133
+ for (const [viewId, allowed] of this.viewFeatures) {
134
+ if (!allowed.has(featureId)) continue;
135
+ const view = this.viewManager.get(viewId)?.view();
136
+ if (!view || view.webContents.isDestroyed()) continue;
137
+ try {
138
+ view.webContents.send(ipcChannel, payload);
139
+ } catch {}
140
+ }
141
+ }
142
+ };
143
+
99
144
  //#endregion
100
145
  //#region src/core/event-bus/helpers.ts
101
146
  function createEvent(id, defaults) {
@@ -154,10 +199,18 @@ let RuntimeState = /* @__PURE__ */ function(RuntimeState) {
154
199
  //#region src/core/event-bus/event-bus.ts
155
200
  var EventBus = class {
156
201
  subscriptions = /* @__PURE__ */ new Map();
202
+ interceptors = /* @__PURE__ */ new Set();
157
203
  publish(channel, payload) {
158
204
  const subs = this.subscriptions.get(channel);
159
- if (!subs) return;
160
- for (const sub of subs) sub.handler(payload);
205
+ if (subs) for (const sub of subs) sub.handler(payload);
206
+ for (const interceptor of this.interceptors) interceptor(channel, payload);
207
+ }
208
+ /** Register an interceptor that receives every published event. Returns an unsubscribe function. */
209
+ addInterceptor(fn) {
210
+ this.interceptors.add(fn);
211
+ return () => {
212
+ this.interceptors.delete(fn);
213
+ };
161
214
  }
162
215
  subscribe(channel, handler, ownerId) {
163
216
  const sub = {
@@ -991,6 +1044,7 @@ var Runtime = class {
991
1044
  eventBus;
992
1045
  windowManager;
993
1046
  viewManager;
1047
+ eventBridge = null;
994
1048
  constructor(config) {
995
1049
  this.state = new StateMachine({
996
1050
  transitions: RUNTIME_TRANSITIONS,
@@ -1007,6 +1061,7 @@ var Runtime = class {
1007
1061
  this.viewManager = new ViewManager();
1008
1062
  const viewRegistry = typeof __ELECTRO_VIEW_REGISTRY__ !== "undefined" ? __ELECTRO_VIEW_REGISTRY__ : [];
1009
1063
  for (const entry of viewRegistry) this.viewManager.register(new View(entry));
1064
+ if (viewRegistry.some((v) => v.features && v.features.length > 0)) this.eventBridge = new EventBridge(this.eventBus, this.viewManager, viewRegistry);
1010
1065
  this.featureManager.setWindowManager(this.windowManager);
1011
1066
  this.featureManager.setViewManager(this.viewManager);
1012
1067
  if (config?.features) this.featureManager.register(config.features);
@@ -1018,9 +1073,11 @@ var Runtime = class {
1018
1073
  async start() {
1019
1074
  this.state.transition(RuntimeState.STARTING);
1020
1075
  try {
1076
+ this.eventBridge?.start();
1021
1077
  await this.featureManager.bootstrap();
1022
1078
  this.state.transition(RuntimeState.RUNNING);
1023
1079
  } catch (err) {
1080
+ this.eventBridge?.stop();
1024
1081
  this.state.transition(RuntimeState.FAILED);
1025
1082
  throw err;
1026
1083
  }
@@ -1028,6 +1085,7 @@ var Runtime = class {
1028
1085
  async shutdown() {
1029
1086
  this.state.assertState(RuntimeState.RUNNING);
1030
1087
  this.state.transition(RuntimeState.STOPPING);
1088
+ this.eventBridge?.stop();
1031
1089
  await this.featureManager.shutdown();
1032
1090
  this.viewManager.destroyAll();
1033
1091
  this.windowManager.destroyAll();
@@ -1624,4 +1682,4 @@ var PolicyEngine = class {
1624
1682
  };
1625
1683
 
1626
1684
  //#endregion
1627
- export { EventAccessor, FeatureStatus, PolicyDecision, PolicyEngine, Runtime, RuntimeState, ServiceScope, ServiceStatus, TaskOverlapStrategy, TaskRetryStrategy, TaskStatus, createEvent, createFeature, createRuntime, createService, createTask, createWindow, defineConfig, defineRuntime, defineView };
1685
+ export { EventAccessor, EventBridge, FeatureStatus, PolicyDecision, PolicyEngine, Runtime, RuntimeState, ServiceScope, ServiceStatus, TaskOverlapStrategy, TaskRetryStrategy, TaskStatus, createEvent, createFeature, createRuntime, createService, createTask, createWindow, defineConfig, defineRuntime, defineView };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cordy/electro",
3
- "version": "1.2.4",
3
+ "version": "1.2.5",
4
4
  "description": "Electron framework — feature-first runtime, codegen, and Vite-based build system",
5
5
  "license": "MIT",
6
6
  "type": "module",