@cordy/electro 1.2.3 → 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 +111 -83
- package/dist/index.mjs +86 -23
- package/package.json +1 -1
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 (
|
|
160
|
-
for (const
|
|
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 = {
|
|
@@ -511,32 +564,33 @@ var Feature = class {
|
|
|
511
564
|
initial: FeatureStatus.NONE,
|
|
512
565
|
name: `feature "${config.id}"`
|
|
513
566
|
});
|
|
567
|
+
const fid = config.id;
|
|
514
568
|
this.context = {
|
|
515
569
|
getService: () => {
|
|
516
|
-
throw new Error(
|
|
570
|
+
throw new Error(`[${fid}] ctx.getService() is not available — feature has not been initialized yet`);
|
|
517
571
|
},
|
|
518
572
|
getTask: () => {
|
|
519
|
-
throw new Error(
|
|
573
|
+
throw new Error(`[${fid}] ctx.getTask() is not available — feature has not been initialized yet`);
|
|
520
574
|
},
|
|
521
575
|
getFeature: () => {
|
|
522
|
-
throw new Error(
|
|
576
|
+
throw new Error(`[${fid}] ctx.getFeature() is not available — feature has not been initialized yet`);
|
|
523
577
|
},
|
|
524
578
|
events: {
|
|
525
579
|
publish: () => {
|
|
526
|
-
throw new Error(
|
|
580
|
+
throw new Error(`[${fid}] ctx.events.publish() is not available — feature has not been initialized yet`);
|
|
527
581
|
},
|
|
528
582
|
on: () => {
|
|
529
|
-
throw new Error(
|
|
583
|
+
throw new Error(`[${fid}] ctx.events.on() is not available — feature has not been initialized yet`);
|
|
530
584
|
}
|
|
531
585
|
},
|
|
532
586
|
getWindow: () => {
|
|
533
|
-
throw new Error(
|
|
587
|
+
throw new Error(`[${fid}] ctx.getWindow() is not available — feature has not been initialized yet`);
|
|
534
588
|
},
|
|
535
589
|
createView: () => {
|
|
536
|
-
throw new Error(
|
|
590
|
+
throw new Error(`[${fid}] ctx.createView() is not available — feature has not been initialized yet`);
|
|
537
591
|
},
|
|
538
592
|
getView: () => {
|
|
539
|
-
throw new Error(
|
|
593
|
+
throw new Error(`[${fid}] ctx.getView() is not available — feature has not been initialized yet`);
|
|
540
594
|
},
|
|
541
595
|
logger: this.logger,
|
|
542
596
|
signal: this.controller.signal
|
|
@@ -581,12 +635,6 @@ var Feature = class {
|
|
|
581
635
|
for (const dep of features) if (dep.serviceManager) deps.set(dep.id, dep.serviceManager);
|
|
582
636
|
const accessor = new ServiceAccessor(this.serviceManager, deps);
|
|
583
637
|
this.context.getService = ((name) => accessor.get(name));
|
|
584
|
-
this.serviceManager.startup();
|
|
585
|
-
this.taskManager = new TaskManager(this.context);
|
|
586
|
-
for (const task of this.config.tasks ?? []) this.taskManager.register(task);
|
|
587
|
-
this.context.getTask = ((name) => {
|
|
588
|
-
return new TaskHandle(this.taskManager.getTaskInstance(name), this.context);
|
|
589
|
-
});
|
|
590
638
|
const declaredDeps = new Set(this.config.dependencies ?? []);
|
|
591
639
|
this.context.getFeature = ((name) => {
|
|
592
640
|
if (!declaredDeps.has(name)) throw new Error(`Feature "${name}" is not a declared dependency of "${this.id}"`);
|
|
@@ -624,6 +672,12 @@ var Feature = class {
|
|
|
624
672
|
return inst.view();
|
|
625
673
|
};
|
|
626
674
|
}
|
|
675
|
+
this.serviceManager.startup();
|
|
676
|
+
this.taskManager = new TaskManager(this.context);
|
|
677
|
+
for (const task of this.config.tasks ?? []) this.taskManager.register(task);
|
|
678
|
+
this.context.getTask = ((name) => {
|
|
679
|
+
return new TaskHandle(this.taskManager.getTaskInstance(name), this.context);
|
|
680
|
+
});
|
|
627
681
|
}
|
|
628
682
|
};
|
|
629
683
|
|
|
@@ -712,8 +766,9 @@ var FeatureManager = class {
|
|
|
712
766
|
feature.transition(FeatureStatus.READY);
|
|
713
767
|
} catch (err) {
|
|
714
768
|
feature.transition(FeatureStatus.ERROR);
|
|
715
|
-
|
|
716
|
-
|
|
769
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
770
|
+
this.logger.error(id, `initialize failed: ${message}`);
|
|
771
|
+
if (feature.config.critical) throw new Error(`Critical feature "${id}" failed to initialize: ${message}`, { cause: err });
|
|
717
772
|
}
|
|
718
773
|
}
|
|
719
774
|
/**
|
|
@@ -738,8 +793,9 @@ var FeatureManager = class {
|
|
|
738
793
|
feature.transition(FeatureStatus.ACTIVATED);
|
|
739
794
|
} catch (err) {
|
|
740
795
|
feature.transition(FeatureStatus.ERROR);
|
|
741
|
-
|
|
742
|
-
|
|
796
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
797
|
+
this.logger.error(id, `activate failed: ${message}`);
|
|
798
|
+
if (feature.config.critical) throw new Error(`Critical feature "${id}" failed to activate: ${message}`, { cause: err });
|
|
743
799
|
}
|
|
744
800
|
}
|
|
745
801
|
/**
|
|
@@ -755,7 +811,8 @@ var FeatureManager = class {
|
|
|
755
811
|
feature.transition(FeatureStatus.DEACTIVATED);
|
|
756
812
|
} catch (err) {
|
|
757
813
|
feature.transition(FeatureStatus.ERROR);
|
|
758
|
-
|
|
814
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
815
|
+
this.logger.error(id, `deactivate failed: ${message}`);
|
|
759
816
|
}
|
|
760
817
|
}
|
|
761
818
|
async destroy(id) {
|
|
@@ -767,7 +824,8 @@ var FeatureManager = class {
|
|
|
767
824
|
feature.transition(FeatureStatus.DESTROYED);
|
|
768
825
|
} catch (err) {
|
|
769
826
|
feature.transition(FeatureStatus.ERROR);
|
|
770
|
-
|
|
827
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
828
|
+
this.logger.error(id, `destroy failed: ${message}`);
|
|
771
829
|
}
|
|
772
830
|
}
|
|
773
831
|
/** Public: re-activate a DEACTIVATED or ERROR feature. */
|
|
@@ -986,6 +1044,7 @@ var Runtime = class {
|
|
|
986
1044
|
eventBus;
|
|
987
1045
|
windowManager;
|
|
988
1046
|
viewManager;
|
|
1047
|
+
eventBridge = null;
|
|
989
1048
|
constructor(config) {
|
|
990
1049
|
this.state = new StateMachine({
|
|
991
1050
|
transitions: RUNTIME_TRANSITIONS,
|
|
@@ -1002,6 +1061,7 @@ var Runtime = class {
|
|
|
1002
1061
|
this.viewManager = new ViewManager();
|
|
1003
1062
|
const viewRegistry = typeof __ELECTRO_VIEW_REGISTRY__ !== "undefined" ? __ELECTRO_VIEW_REGISTRY__ : [];
|
|
1004
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);
|
|
1005
1065
|
this.featureManager.setWindowManager(this.windowManager);
|
|
1006
1066
|
this.featureManager.setViewManager(this.viewManager);
|
|
1007
1067
|
if (config?.features) this.featureManager.register(config.features);
|
|
@@ -1013,9 +1073,11 @@ var Runtime = class {
|
|
|
1013
1073
|
async start() {
|
|
1014
1074
|
this.state.transition(RuntimeState.STARTING);
|
|
1015
1075
|
try {
|
|
1076
|
+
this.eventBridge?.start();
|
|
1016
1077
|
await this.featureManager.bootstrap();
|
|
1017
1078
|
this.state.transition(RuntimeState.RUNNING);
|
|
1018
1079
|
} catch (err) {
|
|
1080
|
+
this.eventBridge?.stop();
|
|
1019
1081
|
this.state.transition(RuntimeState.FAILED);
|
|
1020
1082
|
throw err;
|
|
1021
1083
|
}
|
|
@@ -1023,6 +1085,7 @@ var Runtime = class {
|
|
|
1023
1085
|
async shutdown() {
|
|
1024
1086
|
this.state.assertState(RuntimeState.RUNNING);
|
|
1025
1087
|
this.state.transition(RuntimeState.STOPPING);
|
|
1088
|
+
this.eventBridge?.stop();
|
|
1026
1089
|
await this.featureManager.shutdown();
|
|
1027
1090
|
this.viewManager.destroyAll();
|
|
1028
1091
|
this.windowManager.destroyAll();
|
|
@@ -1619,4 +1682,4 @@ var PolicyEngine = class {
|
|
|
1619
1682
|
};
|
|
1620
1683
|
|
|
1621
1684
|
//#endregion
|
|
1622
|
-
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 };
|