cyclecad 2.0.0 → 2.1.0
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/IMPLEMENTATION_GUIDE.md +502 -0
- package/INTEGRATION-GUIDE.md +377 -0
- package/MODULES_PHASES_6_7.md +780 -0
- package/app/index.html +106 -2
- package/app/js/brep-kernel.js +1353 -455
- package/app/js/help-module.js +1437 -0
- package/app/js/kernel.js +364 -40
- package/app/js/modules/animation-module.js +967 -0
- package/app/js/modules/assembly-module.js +47 -3
- package/app/js/modules/cam-module.js +1067 -0
- package/app/js/modules/collaboration-module.js +1102 -0
- package/app/js/modules/data-module.js +1656 -0
- package/app/js/modules/drawing-module.js +54 -8
- package/app/js/modules/formats-module.js +1173 -0
- package/app/js/modules/inspection-module.js +937 -0
- package/app/js/modules/mesh-module.js +968 -0
- package/app/js/modules/operations-module.js +40 -7
- package/app/js/modules/plugin-module.js +957 -0
- package/app/js/modules/rendering-module.js +1306 -0
- package/app/js/modules/scripting-module.js +955 -0
- package/app/js/modules/simulation-module.js +60 -3
- package/app/js/modules/sketch-module.js +1032 -90
- package/app/js/modules/step-module.js +47 -6
- package/app/js/modules/surface-module.js +728 -0
- package/app/js/modules/version-module.js +1410 -0
- package/app/js/modules/viewport-module.js +95 -8
- package/app/test-agent-v2.html +881 -1316
- package/docs/ARCHITECTURE.html +838 -1408
- package/docs/DEVELOPER-GUIDE.md +1504 -0
- package/docs/TUTORIAL.md +740 -0
- package/package.json +1 -1
- package/.github/scripts/cad-diff.js +0 -590
- package/.github/workflows/cad-diff.yml +0 -117
package/app/js/kernel.js
CHANGED
|
@@ -1,49 +1,191 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* @file kernel.js
|
|
3
|
+
* @description cycleCAD Microkernel — A lightweight, modular architecture for pluggable CAD components.
|
|
4
|
+
* Manages module lifecycle, dependencies, lazy loading, hot-swapping, and inter-module communication
|
|
5
|
+
* via events and commands.
|
|
3
6
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
+
* @version 1.0.0
|
|
8
|
+
* @author cycleCAD Team
|
|
9
|
+
* @license MIT
|
|
10
|
+
* @see {@link https://github.com/vvlars-cmd/cyclecad}
|
|
7
11
|
*
|
|
8
12
|
* @module kernel
|
|
9
|
-
* @
|
|
13
|
+
* @requires nothing (foundational — no dependencies)
|
|
14
|
+
*
|
|
15
|
+
* Architecture:
|
|
16
|
+
* ┌──────────────────────────────────────────────┐
|
|
17
|
+
* │ KERNEL (tiny & modular) │
|
|
18
|
+
* │ Registry · Bus · Loader · Memory · API │
|
|
19
|
+
* └──────────────────────────────────────────────┘
|
|
20
|
+
*
|
|
21
|
+
* Design Patterns:
|
|
22
|
+
* - Module Registry: Central catalog of all loadable modules
|
|
23
|
+
* - State Management: Shared kernel.state with watchers
|
|
24
|
+
* - Event Bus: Pub/sub for inter-module communication
|
|
25
|
+
* - Lazy Loading: Modules load on-demand when commands execute
|
|
26
|
+
* - Memory Management: Automatic LRU eviction when budget exceeded
|
|
27
|
+
* - Dependency Resolution: Recursive load of dependencies before activation
|
|
28
|
+
*
|
|
29
|
+
* Usage Example:
|
|
30
|
+
* ```javascript
|
|
31
|
+
* import kernel from './kernel.js';
|
|
32
|
+
*
|
|
33
|
+
* // Register a module
|
|
34
|
+
* kernel.register({
|
|
35
|
+
* id: 'my-module',
|
|
36
|
+
* name: 'My Module',
|
|
37
|
+
* version: '1.0.0',
|
|
38
|
+
* category: 'engine',
|
|
39
|
+
* dependencies: ['viewport'],
|
|
40
|
+
* memoryEstimate: 10,
|
|
41
|
+
* async load(kernel) { console.log('Loading...'); },
|
|
42
|
+
* async activate(kernel) { console.log('Activated'); },
|
|
43
|
+
* async deactivate(kernel) { console.log('Deactivated'); },
|
|
44
|
+
* async unload(kernel) { console.log('Unloaded'); },
|
|
45
|
+
* provides: {
|
|
46
|
+
* commands: {
|
|
47
|
+
* 'my-module.greet': (kernel) => (name) => `Hello, ${name}!`
|
|
48
|
+
* }
|
|
49
|
+
* }
|
|
50
|
+
* });
|
|
51
|
+
*
|
|
52
|
+
* // Execute a command (auto-loads module if needed)
|
|
53
|
+
* const result = await kernel.exec('my-module.greet', {name: 'Alice'});
|
|
54
|
+
*
|
|
55
|
+
* // Listen for events
|
|
56
|
+
* kernel.on('module:loaded', (data) => {
|
|
57
|
+
* console.log('Module loaded:', data.id);
|
|
58
|
+
* });
|
|
59
|
+
*
|
|
60
|
+
* // Watch shared state
|
|
61
|
+
* kernel.state.watch('selectedPart', (newVal, oldVal) => {
|
|
62
|
+
* console.log(`Selection changed from ${oldVal} to ${newVal}`);
|
|
63
|
+
* });
|
|
64
|
+
* ```
|
|
10
65
|
*/
|
|
11
66
|
|
|
67
|
+
// ═══════════════════════════════════════════════════════
|
|
68
|
+
// MODULE STATES
|
|
69
|
+
// ═══════════════════════════════════════════════════════
|
|
70
|
+
|
|
12
71
|
/**
|
|
13
|
-
* Module lifecycle states
|
|
72
|
+
* Module lifecycle states enum
|
|
73
|
+
*
|
|
74
|
+
* State transitions:
|
|
75
|
+
* REGISTERED → LOADING → INACTIVE/ACTIVE ↔ INACTIVE → UNLOADING → UNLOADED
|
|
76
|
+
* ↓ (error) ↓ (error)
|
|
77
|
+
* ERROR (requires manual recovery) ERROR
|
|
78
|
+
*
|
|
14
79
|
* @enum {string}
|
|
80
|
+
* @const
|
|
15
81
|
*/
|
|
16
82
|
const ModuleState = {
|
|
83
|
+
/** Initial state after registration, not yet loaded */
|
|
17
84
|
REGISTERED: 'registered',
|
|
85
|
+
/** Currently loading (resolving dependencies, running load hook) */
|
|
18
86
|
LOADING: 'loading',
|
|
87
|
+
/** Loaded and activated, commands available */
|
|
19
88
|
ACTIVE: 'active',
|
|
89
|
+
/** Loaded but not activated, takes memory but no commands */
|
|
20
90
|
INACTIVE: 'inactive',
|
|
91
|
+
/** Currently unloading (running unload hook, freeing memory) */
|
|
21
92
|
UNLOADING: 'unloading',
|
|
93
|
+
/** Fully unloaded, memory freed, requires full reload to use again */
|
|
22
94
|
UNLOADED: 'unloaded',
|
|
95
|
+
/** Load or activation failed, error message stored in metadata */
|
|
23
96
|
ERROR: 'error',
|
|
24
97
|
};
|
|
25
98
|
|
|
99
|
+
// ═══════════════════════════════════════════════════════
|
|
100
|
+
// KERNEL CLASS
|
|
101
|
+
// ═══════════════════════════════════════════════════════
|
|
102
|
+
|
|
26
103
|
/**
|
|
27
|
-
* Kernel class —
|
|
104
|
+
* Kernel class — Central manager for all modules and inter-module communication
|
|
105
|
+
*
|
|
106
|
+
* The Kernel is the tiny heart of cycleCAD, handling:
|
|
107
|
+
* - Module registration, loading, activation, deactivation, unloading
|
|
108
|
+
* - Lazy loading on command execution
|
|
109
|
+
* - Dependency resolution (recursive load of dependencies)
|
|
110
|
+
* - Module state tracking (REGISTERED → LOADING → ACTIVE ↔ INACTIVE → UNLOADING → UNLOADED)
|
|
111
|
+
* - Shared state management with watchers
|
|
112
|
+
* - Event bus for pub/sub communication
|
|
113
|
+
* - Command registry and dispatch
|
|
114
|
+
* - Memory management with LRU eviction
|
|
115
|
+
* - Hot-swap capability (swap old module for new one)
|
|
116
|
+
*
|
|
117
|
+
* @class Kernel
|
|
118
|
+
* @param {Object} [config={}] - Configuration options
|
|
119
|
+
* @param {number} [config.memoryBudget=512] - Total memory budget in MB
|
|
120
|
+
* @param {boolean} [config.autoGC=true] - Enable automatic garbage collection
|
|
28
121
|
*/
|
|
29
122
|
class Kernel {
|
|
30
123
|
constructor(config = {}) {
|
|
124
|
+
/**
|
|
125
|
+
* Kernel configuration
|
|
126
|
+
* @type {Object}
|
|
127
|
+
* @property {number} memoryBudget - Total memory budget in MB (default 512)
|
|
128
|
+
* @property {boolean} autoGC - Auto-GC enabled (default true)
|
|
129
|
+
*/
|
|
31
130
|
this.config = {
|
|
32
131
|
memoryBudget: 512, // MB
|
|
33
132
|
autoGC: true,
|
|
34
133
|
...config,
|
|
35
134
|
};
|
|
36
135
|
|
|
37
|
-
//
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
136
|
+
// ═══════════════════════════════════════════════════════
|
|
137
|
+
// REGISTRIES
|
|
138
|
+
// ═══════════════════════════════════════════════════════
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Module registry: id → {definition, state, instance, metadata}
|
|
142
|
+
* @type {Map<string, Object>}
|
|
143
|
+
*/
|
|
144
|
+
this.modules = new Map();
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Command registry: 'module.cmd' → handler function
|
|
148
|
+
* @type {Map<string, Function>}
|
|
149
|
+
*/
|
|
150
|
+
this.commands = new Map();
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Keyboard shortcut bindings: 'Ctrl+B' → 'module.cmd'
|
|
154
|
+
* @type {Map<string, string>}
|
|
155
|
+
*/
|
|
156
|
+
this.shortcuts = new Map();
|
|
157
|
+
|
|
158
|
+
// ═══════════════════════════════════════════════════════
|
|
159
|
+
// EVENT BUS
|
|
160
|
+
// ═══════════════════════════════════════════════════════
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Event listeners: event → Set of handler functions
|
|
164
|
+
* Supports wildcards (e.g., 'module:*' matches 'module:loaded')
|
|
165
|
+
* @type {Map<string, Set<Function>>}
|
|
166
|
+
*/
|
|
167
|
+
this.eventListeners = new Map();
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* One-time listeners: event → Map(handler → true)
|
|
171
|
+
* Auto-unsubscribed after first fire
|
|
172
|
+
* @type {Map<string, Map<Function, boolean>>}
|
|
173
|
+
*/
|
|
174
|
+
this.onceListeners = new Map();
|
|
175
|
+
|
|
176
|
+
// ═══════════════════════════════════════════════════════
|
|
177
|
+
// SHARED STATE
|
|
178
|
+
// ═══════════════════════════════════════════════════════
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Shared kernel state — accessible and watchable by all modules
|
|
182
|
+
* Usage: kernel.state.set('selectedPart', partId);
|
|
183
|
+
* kernel.state.watch('selectedPart', (newVal, oldVal) => {...});
|
|
184
|
+
*
|
|
185
|
+
* @type {Object}
|
|
186
|
+
* @property {Map} _values - Stored state values (key → value)
|
|
187
|
+
* @property {Map} _watchers - Watchers (key → Set of handler functions)
|
|
188
|
+
*/
|
|
47
189
|
this.state = {
|
|
48
190
|
_values: new Map(),
|
|
49
191
|
_watchers: new Map(), // key → Set of handlers
|
|
@@ -81,7 +223,22 @@ class Kernel {
|
|
|
81
223
|
all: () => Object.fromEntries(this.state._values),
|
|
82
224
|
};
|
|
83
225
|
|
|
84
|
-
//
|
|
226
|
+
// ═══════════════════════════════════════════════════════
|
|
227
|
+
// MEMORY MANAGER
|
|
228
|
+
// ═══════════════════════════════════════════════════════
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Memory management system with LRU eviction policy
|
|
232
|
+
* Tracks memory usage across modules and auto-evicts LRU inactive modules
|
|
233
|
+
* if budget exceeded (when pressure > 0.8)
|
|
234
|
+
*
|
|
235
|
+
* @type {Object}
|
|
236
|
+
* @property {Map} _estimates - Module ID → estimated memory in MB
|
|
237
|
+
* @property {Function} usage() - Get total memory used
|
|
238
|
+
* @property {Function} pressure() - Get pressure ratio (0-1, where 1.0 = at budget)
|
|
239
|
+
* @property {number} budget - Total memory budget in MB
|
|
240
|
+
* @property {Function} gc() - Run garbage collection (auto evicts if needed)
|
|
241
|
+
*/
|
|
85
242
|
this.memory = {
|
|
86
243
|
_estimates: new Map(), // moduleId → MB
|
|
87
244
|
|
|
@@ -117,10 +274,62 @@ class Kernel {
|
|
|
117
274
|
console.log('[Kernel] Initialized with memory budget:', this.config.memoryBudget, 'MB');
|
|
118
275
|
}
|
|
119
276
|
|
|
277
|
+
// ═══════════════════════════════════════════════════════
|
|
278
|
+
// MODULE LIFECYCLE METHODS
|
|
279
|
+
// ═══════════════════════════════════════════════════════
|
|
280
|
+
|
|
120
281
|
/**
|
|
121
|
-
* Register a module definition
|
|
282
|
+
* Register a module definition with the kernel
|
|
283
|
+
*
|
|
284
|
+
* The module is not loaded until explicitly requested or when one of its
|
|
285
|
+
* commands is executed. This allows lazy loading of heavy modules.
|
|
286
|
+
*
|
|
122
287
|
* @param {Object} definition - Module definition object
|
|
123
|
-
* @
|
|
288
|
+
* @param {string} definition.id - Unique module identifier (e.g., 'viewport', 'sketch')
|
|
289
|
+
* @param {string} definition.name - Human-readable name (e.g., 'Sketch Engine')
|
|
290
|
+
* @param {string} definition.version - Semantic version string (e.g., '1.0.0')
|
|
291
|
+
* @param {string} definition.category - Module category: 'engine'|'tool'|'data'|'service'|'core'
|
|
292
|
+
* @param {string[]} [definition.dependencies] - Array of module IDs this depends on
|
|
293
|
+
* @param {number} definition.memoryEstimate - Estimated memory usage in MB
|
|
294
|
+
* @param {string[]} [definition.replaces] - Module IDs this replaces (for hot-swap)
|
|
295
|
+
* @param {Function} [definition.load] - Async load hook: async load(kernel) {...}
|
|
296
|
+
* @param {Function} [definition.activate] - Async activate hook: async activate(kernel) {...}
|
|
297
|
+
* @param {Function} [definition.deactivate] - Async deactivate hook: async deactivate(kernel) {...}
|
|
298
|
+
* @param {Function} [definition.unload] - Async unload hook: async unload(kernel) {...}
|
|
299
|
+
* @param {Object} definition.provides - Provided API
|
|
300
|
+
* @param {Object} [definition.provides.commands] - Commands: {cmd: handler(kernel)}
|
|
301
|
+
* @param {Object} [definition.provides.ui] - UI components and shortcuts
|
|
302
|
+
* @returns {boolean} Registration success
|
|
303
|
+
* @throws {Error} If module ID already registered
|
|
304
|
+
*
|
|
305
|
+
* @example
|
|
306
|
+
* kernel.register({
|
|
307
|
+
* id: 'my-module',
|
|
308
|
+
* name: 'My Module',
|
|
309
|
+
* version: '1.0.0',
|
|
310
|
+
* category: 'tool',
|
|
311
|
+
* dependencies: ['viewport'],
|
|
312
|
+
* memoryEstimate: 15,
|
|
313
|
+
* async load(kernel) {
|
|
314
|
+
* console.log('Module loading...');
|
|
315
|
+
* },
|
|
316
|
+
* async activate(kernel) {
|
|
317
|
+
* console.log('Module activated');
|
|
318
|
+
* },
|
|
319
|
+
* async deactivate(kernel) {
|
|
320
|
+
* console.log('Module deactivated');
|
|
321
|
+
* },
|
|
322
|
+
* async unload(kernel) {
|
|
323
|
+
* console.log('Module unloaded, memory freed');
|
|
324
|
+
* },
|
|
325
|
+
* provides: {
|
|
326
|
+
* commands: {
|
|
327
|
+
* 'my-module.doSomething': (kernel) => (param) => {
|
|
328
|
+
* return `Did something with ${param}`;
|
|
329
|
+
* }
|
|
330
|
+
* }
|
|
331
|
+
* }
|
|
332
|
+
* });
|
|
124
333
|
*/
|
|
125
334
|
register(definition) {
|
|
126
335
|
if (!definition.id || !definition.name) {
|
|
@@ -198,10 +407,25 @@ class Kernel {
|
|
|
198
407
|
}
|
|
199
408
|
|
|
200
409
|
/**
|
|
201
|
-
* Load a module
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
*
|
|
410
|
+
* Load a module asynchronously
|
|
411
|
+
*
|
|
412
|
+
* Handles complete lifecycle: resolves dependencies recursively,
|
|
413
|
+
* marks state as LOADING, calls module.load() hook, transitions to INACTIVE.
|
|
414
|
+
* Idempotent — safe to call multiple times.
|
|
415
|
+
*
|
|
416
|
+
* State transition: REGISTERED → LOADING → INACTIVE
|
|
417
|
+
*
|
|
418
|
+
* @async
|
|
419
|
+
* @param {string} moduleId - Module identifier to load
|
|
420
|
+
* @returns {Promise<boolean>} True if load successful, false on error
|
|
421
|
+
* @emits module:loaded - When module enters INACTIVE state
|
|
422
|
+
* @emits module:error - If load fails
|
|
423
|
+
*
|
|
424
|
+
* @example
|
|
425
|
+
* const loaded = await kernel.load('viewport');
|
|
426
|
+
* if (loaded) {
|
|
427
|
+
* console.log('Viewport module is ready to activate');
|
|
428
|
+
* }
|
|
205
429
|
*/
|
|
206
430
|
async load(moduleId) {
|
|
207
431
|
const module = this.modules.get(moduleId);
|
|
@@ -253,9 +477,27 @@ class Kernel {
|
|
|
253
477
|
}
|
|
254
478
|
|
|
255
479
|
/**
|
|
256
|
-
* Activate a module (load first if needed
|
|
257
|
-
*
|
|
258
|
-
*
|
|
480
|
+
* Activate a module (load first if needed)
|
|
481
|
+
*
|
|
482
|
+
* Ensures module is loaded, then calls activate hook and registers commands.
|
|
483
|
+
* Only ACTIVE modules have their commands available in the command registry.
|
|
484
|
+
* Idempotent — safe to call multiple times.
|
|
485
|
+
*
|
|
486
|
+
* State transition: REGISTERED → LOADING → INACTIVE → ACTIVE
|
|
487
|
+
* (or jump to ACTIVE if already INACTIVE)
|
|
488
|
+
*
|
|
489
|
+
* @async
|
|
490
|
+
* @param {string} moduleId - Module identifier to activate
|
|
491
|
+
* @returns {Promise<boolean>} True if activation successful, false on error
|
|
492
|
+
* @emits module:activated - When module reaches ACTIVE state
|
|
493
|
+
* @emits module:error - If activation fails
|
|
494
|
+
*
|
|
495
|
+
* @example
|
|
496
|
+
* const activated = await kernel.activate('viewport');
|
|
497
|
+
* if (activated) {
|
|
498
|
+
* // viewport.fitAll command is now available
|
|
499
|
+
* await kernel.exec('viewport.fitAll');
|
|
500
|
+
* }
|
|
259
501
|
*/
|
|
260
502
|
async activate(moduleId) {
|
|
261
503
|
const module = this.modules.get(moduleId);
|
|
@@ -480,11 +722,31 @@ class Kernel {
|
|
|
480
722
|
}
|
|
481
723
|
|
|
482
724
|
/**
|
|
483
|
-
* Execute a named command
|
|
484
|
-
*
|
|
485
|
-
*
|
|
486
|
-
*
|
|
487
|
-
*
|
|
725
|
+
* Execute a named command by name
|
|
726
|
+
*
|
|
727
|
+
* If command exists and module is active, executes immediately.
|
|
728
|
+
* Otherwise, auto-loads and auto-activates the command's module first.
|
|
729
|
+
* This enables transparent lazy loading — caller doesn't care if module is loaded.
|
|
730
|
+
*
|
|
731
|
+
* Command names follow pattern: 'module.command' (e.g., 'viewport.fitAll')
|
|
732
|
+
*
|
|
733
|
+
* @async
|
|
734
|
+
* @param {string} commandName - Fully-qualified command name (e.g., 'viewport.fitAll', 'brep.makeBox')
|
|
735
|
+
* @param {Object} [params={}] - Command parameters (passed directly to handler)
|
|
736
|
+
* @returns {Promise<any>} Command result (return value of handler function)
|
|
737
|
+
* @throws {Error} If command not found after module load/activate
|
|
738
|
+
* @throws {Error} If module fails to load or activate
|
|
739
|
+
*
|
|
740
|
+
* @example
|
|
741
|
+
* // Direct execution (module auto-loads if needed)
|
|
742
|
+
* const result = await kernel.exec('viewport.fitAll');
|
|
743
|
+
*
|
|
744
|
+
* // With parameters
|
|
745
|
+
* const box = await kernel.exec('brep.makeBox', {
|
|
746
|
+
* width: 10,
|
|
747
|
+
* height: 20,
|
|
748
|
+
* depth: 30
|
|
749
|
+
* });
|
|
488
750
|
*/
|
|
489
751
|
async exec(commandName, params = {}) {
|
|
490
752
|
// Check if command exists
|
|
@@ -549,10 +811,30 @@ class Kernel {
|
|
|
549
811
|
return await this.exec(commandName, { fromShortcut: true });
|
|
550
812
|
}
|
|
551
813
|
|
|
814
|
+
// ═══════════════════════════════════════════════════════
|
|
815
|
+
// EVENT BUS METHODS
|
|
816
|
+
// ═══════════════════════════════════════════════════════
|
|
817
|
+
|
|
552
818
|
/**
|
|
553
|
-
* Subscribe to an event
|
|
554
|
-
*
|
|
555
|
-
*
|
|
819
|
+
* Subscribe to an event (persistent listener)
|
|
820
|
+
*
|
|
821
|
+
* Listener fires every time the event is emitted.
|
|
822
|
+
* Supports wildcard patterns: 'module:*' matches 'module:loaded', 'module:error', etc.
|
|
823
|
+
*
|
|
824
|
+
* @param {string} event - Event name or wildcard pattern (e.g., 'module:loaded', 'module:*')
|
|
825
|
+
* @param {Function} handler - Handler function: handler(data) {...}
|
|
826
|
+
* @returns {void}
|
|
827
|
+
*
|
|
828
|
+
* @example
|
|
829
|
+
* // Listen for specific event
|
|
830
|
+
* kernel.on('module:loaded', (data) => {
|
|
831
|
+
* console.log('Module loaded:', data.id);
|
|
832
|
+
* });
|
|
833
|
+
*
|
|
834
|
+
* // Listen for wildcard (all module events)
|
|
835
|
+
* kernel.on('module:*', (data) => {
|
|
836
|
+
* console.log('Module event:', data);
|
|
837
|
+
* });
|
|
556
838
|
*/
|
|
557
839
|
on(event, handler) {
|
|
558
840
|
if (!this.eventListeners.has(event)) {
|
|
@@ -587,9 +869,24 @@ class Kernel {
|
|
|
587
869
|
}
|
|
588
870
|
|
|
589
871
|
/**
|
|
590
|
-
* Emit an event
|
|
591
|
-
*
|
|
592
|
-
*
|
|
872
|
+
* Emit an event to all subscribers
|
|
873
|
+
*
|
|
874
|
+
* Triggers both direct listeners (exact event match) and wildcard listeners
|
|
875
|
+
* (pattern match on event prefix). All handlers called synchronously in order.
|
|
876
|
+
*
|
|
877
|
+
* Wildcard matching: 'module:loaded' triggers listeners for 'module:*'
|
|
878
|
+
*
|
|
879
|
+
* @param {string} event - Event name to emit (e.g., 'module:loaded')
|
|
880
|
+
* @param {Object} [data={}] - Event data (passed to all handler functions)
|
|
881
|
+
* @throws {Error} Suppressed — errors in handlers are caught and logged
|
|
882
|
+
* @returns {void}
|
|
883
|
+
*
|
|
884
|
+
* @example
|
|
885
|
+
* // Emit event with data
|
|
886
|
+
* kernel.emit('module:loaded', {
|
|
887
|
+
* id: 'viewport',
|
|
888
|
+
* version: '1.0.0'
|
|
889
|
+
* });
|
|
593
890
|
*/
|
|
594
891
|
emit(event, data = {}) {
|
|
595
892
|
// Direct listeners
|
|
@@ -647,9 +944,36 @@ class Kernel {
|
|
|
647
944
|
}
|
|
648
945
|
}
|
|
649
946
|
|
|
947
|
+
// ═══════════════════════════════════════════════════════
|
|
948
|
+
// INSPECTION METHODS
|
|
949
|
+
// ═══════════════════════════════════════════════════════
|
|
950
|
+
|
|
650
951
|
/**
|
|
651
|
-
* Get detailed kernel status
|
|
652
|
-
*
|
|
952
|
+
* Get detailed kernel status and statistics
|
|
953
|
+
*
|
|
954
|
+
* Returns comprehensive status of all modules, commands, state, and memory.
|
|
955
|
+
* Useful for debugging and monitoring kernel health.
|
|
956
|
+
*
|
|
957
|
+
* @returns {Object} Status report
|
|
958
|
+
* @returns {Object} .modules - Module counts by state
|
|
959
|
+
* @returns {number} .modules.registered - Number of registered modules
|
|
960
|
+
* @returns {number} .modules.active - Number of active modules
|
|
961
|
+
* @returns {number} .modules.inactive - Number of inactive (loaded but not active) modules
|
|
962
|
+
* @returns {number} .modules.error - Number of modules in error state
|
|
963
|
+
* @returns {number} .commands - Total registered commands
|
|
964
|
+
* @returns {number} .shortcuts - Total keyboard shortcuts
|
|
965
|
+
* @returns {Object} .memory - Memory usage information
|
|
966
|
+
* @returns {number} .memory.usage - Current usage in MB
|
|
967
|
+
* @returns {number} .memory.budget - Total budget in MB
|
|
968
|
+
* @returns {string} .memory.pressure - Pressure as percentage (0-100%)
|
|
969
|
+
* @returns {Object} .state - Shared state information
|
|
970
|
+
* @returns {number} .state.keys - Number of shared state keys
|
|
971
|
+
* @returns {number} .state.watchers - Number of active state watchers
|
|
972
|
+
*
|
|
973
|
+
* @example
|
|
974
|
+
* const status = kernel.status();
|
|
975
|
+
* console.log(`${status.modules.active} modules active, ${status.memory.pressure} pressure`);
|
|
976
|
+
* // Output: "3 modules active, 25.5% pressure"
|
|
653
977
|
*/
|
|
654
978
|
status() {
|
|
655
979
|
return {
|