@hamak/microkernel-impl 0.4.7 → 0.4.8
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/package.json +4 -1
- package/.turbo/turbo-build.log +0 -1
- package/CHANGELOG.md +0 -41
- package/project.json +0 -24
- package/src/index.ts +0 -5
- package/src/runtime/di.ts +0 -102
- package/src/runtime/graph-utils.ts +0 -50
- package/src/runtime/host.ts +0 -265
- package/src/runtime/loader.ts +0 -72
- package/src/runtime/registries.ts +0 -4
- package/src/ui/adapter.ts +0 -2
- package/tsconfig.lib.es2015.json +0 -23
- package/tsconfig.lib.json +0 -24
package/package.json
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hamak/microkernel-impl",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.8",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Microkernel Implementation - Core microkernel functionality",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
8
|
"sideEffects": false,
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
9
12
|
"repository": {
|
|
10
13
|
"type": "git",
|
|
11
14
|
"url": "https://github.com/amah/app-framework.git",
|
package/.turbo/turbo-build.log
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
$ tsc -p tsconfig.lib.json && tsc -p tsconfig.lib.es2015.json
|
package/CHANGELOG.md
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
## 0.4.0 (2025-11-10)
|
|
2
|
-
|
|
3
|
-
### 🚀 Features
|
|
4
|
-
|
|
5
|
-
- implement notification plugin with UI and backend components ([c19ffcf](https://github.com/amah/app-framework/commit/c19ffcf))
|
|
6
|
-
- add ES2015 build support and fix TypeScript config for logging packages ([be5e45e](https://github.com/amah/app-framework/commit/be5e45e))
|
|
7
|
-
- complete logging system build and add optional console interception ([f390bc6](https://github.com/amah/app-framework/commit/f390bc6))
|
|
8
|
-
- implement core pluggable logging system (Phase 1) ([2abdc1a](https://github.com/amah/app-framework/commit/2abdc1a))
|
|
9
|
-
|
|
10
|
-
### 🩹 Fixes
|
|
11
|
-
|
|
12
|
-
- add notification packages to workspaces ([97a234d](https://github.com/amah/app-framework/commit/97a234d))
|
|
13
|
-
|
|
14
|
-
### ❤️ Thank You
|
|
15
|
-
|
|
16
|
-
- Amah
|
|
17
|
-
- Claude
|
|
18
|
-
|
|
19
|
-
## 0.3.0 (2025-11-06)
|
|
20
|
-
|
|
21
|
-
### 🚀 Features
|
|
22
|
-
|
|
23
|
-
- migrate from Turbo to Nx 22 with comprehensive monorepo setup ([e63801e](https://github.com/amah/app-framework/commit/e63801e))
|
|
24
|
-
- add Nx Release for automated dependency management ([01d474f](https://github.com/amah/app-framework/commit/01d474f))
|
|
25
|
-
- migrate from Turbo to Nx 22 monorepo orchestration ([d374271](https://github.com/amah/app-framework/commit/d374271))
|
|
26
|
-
- add configurable main padding and resizable sidebar to DashboardLayout ([c1d25bf](https://github.com/amah/app-framework/commit/c1d25bf))
|
|
27
|
-
- add debug logging and version management system ([ea514fc](https://github.com/amah/app-framework/commit/ea514fc))
|
|
28
|
-
- **ui-store:** add STORE_EXTENSIONS_TOKEN for DI-based middleware/reducer registration ([e855bdd](https://github.com/amah/app-framework/commit/e855bdd))
|
|
29
|
-
- Rename package scope from @amk to @hamak and configure npm publishing ([b6040b5](https://github.com/amah/app-framework/commit/b6040b5))
|
|
30
|
-
- Add hybrid local/CI-CD development workflow with bun link ([d09f528](https://github.com/amah/app-framework/commit/d09f528))
|
|
31
|
-
- Add Turborepo for intelligent build orchestration and fix test type errors ([ba41db8](https://github.com/amah/app-framework/commit/ba41db8))
|
|
32
|
-
- Add Redux store integration with ui-store package and demo ([e5aafa8](https://github.com/amah/app-framework/commit/e5aafa8))
|
|
33
|
-
|
|
34
|
-
### 🩹 Fixes
|
|
35
|
-
|
|
36
|
-
- move git config to top-level release.git in nx.json ([1bb2187](https://github.com/amah/app-framework/commit/1bb2187))
|
|
37
|
-
|
|
38
|
-
### ❤️ Thank You
|
|
39
|
-
|
|
40
|
-
- Amah
|
|
41
|
-
- Claude
|
package/project.json
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@hamak/microkernel-impl",
|
|
3
|
-
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
|
|
4
|
-
"sourceRoot": "packages/microkernel/microkernel-impl/src",
|
|
5
|
-
"projectType": "library",
|
|
6
|
-
"targets": {
|
|
7
|
-
"build": {
|
|
8
|
-
"executor": "nx:run-commands",
|
|
9
|
-
"outputs": ["{projectRoot}/dist"],
|
|
10
|
-
"options": {
|
|
11
|
-
"command": "tsc -p tsconfig.lib.json && tsc -p tsconfig.lib.es2015.json",
|
|
12
|
-
"cwd": "{projectRoot}"
|
|
13
|
-
}
|
|
14
|
-
},
|
|
15
|
-
"clean": {
|
|
16
|
-
"executor": "nx:run-commands",
|
|
17
|
-
"options": {
|
|
18
|
-
"command": "rm -rf dist",
|
|
19
|
-
"cwd": "{projectRoot}"
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
},
|
|
23
|
-
"tags": ["type:library", "scope:microkernel"]
|
|
24
|
-
}
|
package/src/index.ts
DELETED
package/src/runtime/di.ts
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import type { Provider, Token } from '@hamak/microkernel-api';
|
|
2
|
-
|
|
3
|
-
const INJECT_KEY = Symbol('di:inject');
|
|
4
|
-
|
|
5
|
-
export function Injectable(deps: Token[] = []): ClassDecorator {
|
|
6
|
-
return (t: any) => {
|
|
7
|
-
(t as any)[INJECT_KEY] = deps;
|
|
8
|
-
};
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface ContainerConfig {
|
|
12
|
-
debug?: boolean;
|
|
13
|
-
pluginContext?: string;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export class Container {
|
|
17
|
-
private parent?: Container;
|
|
18
|
-
private providers = new Map<Token, Provider>();
|
|
19
|
-
private instances = new Map<Token, any>();
|
|
20
|
-
private config: ContainerConfig;
|
|
21
|
-
|
|
22
|
-
constructor(parent?: Container, config: ContainerConfig = {}) {
|
|
23
|
-
this.parent = parent;
|
|
24
|
-
this.config = config;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
createChild(): Container {
|
|
28
|
-
return new Container(this, this.config);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
provide<T>(prov: Provider<T>): this {
|
|
32
|
-
if (this.config.debug) {
|
|
33
|
-
const tokenName = this.getTokenName(prov.provide);
|
|
34
|
-
const context = this.config.pluginContext ? `[${this.config.pluginContext}] ` : '';
|
|
35
|
-
console.log(`🔧 ${context}Providing token: ${tokenName}`);
|
|
36
|
-
}
|
|
37
|
-
this.providers.set(prov.provide, prov);
|
|
38
|
-
return this;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
resolve<T>(token: Token<T>): T {
|
|
42
|
-
if (this.instances.has(token)) {
|
|
43
|
-
if (this.config.debug) {
|
|
44
|
-
const tokenName = this.getTokenName(token);
|
|
45
|
-
const context = this.config.pluginContext ? `[${this.config.pluginContext}] ` : '';
|
|
46
|
-
console.log(`✓ ${context}Resolving token (cached): ${tokenName}`);
|
|
47
|
-
}
|
|
48
|
-
return this.instances.get(token);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (this.providers.has(token)) {
|
|
52
|
-
if (this.config.debug) {
|
|
53
|
-
const tokenName = this.getTokenName(token);
|
|
54
|
-
const context = this.config.pluginContext ? `[${this.config.pluginContext}] ` : '';
|
|
55
|
-
console.log(`🔍 ${context}Resolving token: ${tokenName}`);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const prov: any = this.providers.get(token)!;
|
|
59
|
-
let value: any;
|
|
60
|
-
|
|
61
|
-
if ('useValue' in prov) {
|
|
62
|
-
value = prov.useValue;
|
|
63
|
-
} else if ('useClass' in prov) {
|
|
64
|
-
const C = prov.useClass;
|
|
65
|
-
const deps: Token[] = (C as any)[INJECT_KEY] || [];
|
|
66
|
-
const args = deps.map((d) => this.resolve(d));
|
|
67
|
-
value = new C(...args);
|
|
68
|
-
} else {
|
|
69
|
-
const deps: Token[] = prov.deps || [];
|
|
70
|
-
const args = deps.map((d) => this.resolve(d));
|
|
71
|
-
value = prov.useFactory(...args);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
this.instances.set(token, value);
|
|
75
|
-
return value;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (this.parent) {
|
|
79
|
-
return this.parent.resolve(token);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
throw new Error(`No provider for token: ${token.toString?.() ?? String(token)}`);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
setPluginContext(pluginName: string): void {
|
|
86
|
-
this.config.pluginContext = pluginName;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
clearPluginContext(): void {
|
|
90
|
-
this.config.pluginContext = undefined;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
private getTokenName(token: Token): string {
|
|
94
|
-
if (typeof token === 'symbol') {
|
|
95
|
-
return token.toString();
|
|
96
|
-
}
|
|
97
|
-
if (typeof token === 'function') {
|
|
98
|
-
return token.name || 'AnonymousClass';
|
|
99
|
-
}
|
|
100
|
-
return String(token);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Dependency graph utilities for topological sorting
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export interface DependencyNode {
|
|
6
|
-
name: string;
|
|
7
|
-
dependsOn?: string[];
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Performs topological sort on items based on their dependencies
|
|
12
|
-
* @param items Items to sort (must have name and optional dependsOn properties)
|
|
13
|
-
* @returns Sorted items in dependency order
|
|
14
|
-
* @throws Error if circular dependency is detected
|
|
15
|
-
*/
|
|
16
|
-
export function topologicalSort<T extends DependencyNode>(items: T[]): T[] {
|
|
17
|
-
const index = new Map(items.map(i => [i.name, i]));
|
|
18
|
-
const visited = new Set<string>();
|
|
19
|
-
const result: T[] = [];
|
|
20
|
-
|
|
21
|
-
const visit = (item: T, path: Set<string> = new Set()) => {
|
|
22
|
-
if (visited.has(item.name)) return;
|
|
23
|
-
|
|
24
|
-
// Cycle detection
|
|
25
|
-
if (path.has(item.name)) {
|
|
26
|
-
throw new Error(
|
|
27
|
-
`Circular dependency detected: ${[...path, item.name].join(' -> ')}`
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
path.add(item.name);
|
|
32
|
-
|
|
33
|
-
(item.dependsOn ?? []).forEach(depName => {
|
|
34
|
-
const dep = index.get(depName);
|
|
35
|
-
if (!dep) {
|
|
36
|
-
console.warn(
|
|
37
|
-
`Dependency not found: ${depName} (required by ${item.name})`
|
|
38
|
-
);
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
visit(dep, new Set(path));
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
visited.add(item.name);
|
|
45
|
-
result.push(item);
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
items.forEach(item => visit(item));
|
|
49
|
-
return result;
|
|
50
|
-
}
|
package/src/runtime/host.ts
DELETED
|
@@ -1,265 +0,0 @@
|
|
|
1
|
-
import type { ActivateContext, IChildHost, IHost, PluginManifest, Provider, CommandRegistry, ViewRegistry, Hooks } from '@hamak/microkernel-api';
|
|
2
|
-
import { Container, type ContainerConfig } from './di';
|
|
3
|
-
import { createCommandRegistry, createHooks, createViewRegistry } from './registries';
|
|
4
|
-
import { PluginRegistry } from './loader';
|
|
5
|
-
import type { InitializationContext, PluginModule } from '@hamak/microkernel-spi';
|
|
6
|
-
|
|
7
|
-
export interface HostConfig {
|
|
8
|
-
/** Enable debug logging for plugin lifecycle and DI operations */
|
|
9
|
-
debug?: boolean;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
type SharedRegistries = {
|
|
13
|
-
commands: CommandRegistry;
|
|
14
|
-
views: ViewRegistry;
|
|
15
|
-
hooks: Hooks;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
function createSharedRegistries(): SharedRegistries {
|
|
19
|
-
return {
|
|
20
|
-
commands: createCommandRegistry(),
|
|
21
|
-
views: createViewRegistry(),
|
|
22
|
-
hooks: createHooks(),
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function createInitContext(
|
|
27
|
-
container: Container,
|
|
28
|
-
registries: SharedRegistries,
|
|
29
|
-
env?: Record<string, any>
|
|
30
|
-
): InitializationContext {
|
|
31
|
-
return {
|
|
32
|
-
provide: (prov: Provider) => container.provide(prov),
|
|
33
|
-
resolve: <T>(t: any) => container.resolve(t),
|
|
34
|
-
commands: {
|
|
35
|
-
register: (id: string, h: (...a: any[]) => any) => registries.commands.register(id, h)
|
|
36
|
-
},
|
|
37
|
-
views: {
|
|
38
|
-
register: (slot: string, vf: any) => registries.views.register(slot, vf)
|
|
39
|
-
},
|
|
40
|
-
hooks: {
|
|
41
|
-
on: (ev: string, fn: (...a: any[]) => void) => registries.hooks.on(ev, fn),
|
|
42
|
-
emit: (ev: string, ...a: any[]) => registries.hooks.emit(ev, ...a)
|
|
43
|
-
},
|
|
44
|
-
env,
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function createActivateContext(
|
|
49
|
-
container: Container,
|
|
50
|
-
registries: SharedRegistries,
|
|
51
|
-
env?: Record<string, any>
|
|
52
|
-
): ActivateContext {
|
|
53
|
-
return {
|
|
54
|
-
resolve: <T>(t: any) => container.resolve(t),
|
|
55
|
-
commands: registries.commands,
|
|
56
|
-
views: registries.views,
|
|
57
|
-
hooks: registries.hooks,
|
|
58
|
-
env,
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export class Host implements IHost {
|
|
63
|
-
readonly root: Container;
|
|
64
|
-
private readonly _registry: PluginRegistry;
|
|
65
|
-
private env?: Record<string, any>;
|
|
66
|
-
private config: HostConfig;
|
|
67
|
-
rootActivationCtx?: ActivateContext;
|
|
68
|
-
|
|
69
|
-
constructor(initialProviders: Provider[] = [], env?: Record<string, any>, config: HostConfig = {}){
|
|
70
|
-
const containerConfig: ContainerConfig = { debug: config.debug };
|
|
71
|
-
this.root = new Container(undefined, containerConfig);
|
|
72
|
-
initialProviders.forEach(p=>this.root.provide(p));
|
|
73
|
-
this._registry = new PluginRegistry();
|
|
74
|
-
this.env = env;
|
|
75
|
-
this.config = config;
|
|
76
|
-
|
|
77
|
-
if (this.config.debug) {
|
|
78
|
-
console.log('🚀 Microkernel Host created with debug logging enabled');
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
async loadPlugins(manifests: Array<string|PluginManifest>): Promise<void>{
|
|
83
|
-
await this._registry.loadAllAtRoot(manifests);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Register a plugin programmatically (useful for testing/inline plugins)
|
|
88
|
-
* @param name Plugin name
|
|
89
|
-
* @param manifest Plugin manifest
|
|
90
|
-
* @param module Plugin module implementation
|
|
91
|
-
*/
|
|
92
|
-
registerPlugin(name: string, manifest: PluginManifest, module: PluginModule): void {
|
|
93
|
-
this._registry.register(name, manifest, module);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Get plugin manifest by name
|
|
98
|
-
* @param name Plugin name
|
|
99
|
-
* @returns Plugin manifest or undefined if not found
|
|
100
|
-
*/
|
|
101
|
-
getPlugin(name: string): PluginManifest | undefined {
|
|
102
|
-
return this._registry.get(name)?.manifest;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* List all loaded plugins
|
|
107
|
-
* @returns Array of plugin manifests
|
|
108
|
-
*/
|
|
109
|
-
listPlugins(): PluginManifest[] {
|
|
110
|
-
return this._registry.list();
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
async bootstrapAllAtRoot(): Promise<void>{
|
|
114
|
-
const registries = createSharedRegistries();
|
|
115
|
-
const initCtx = createInitContext(this.root, registries, this.env);
|
|
116
|
-
const plugins = this._registry.getModulesWithNamesInOrder('all');
|
|
117
|
-
|
|
118
|
-
if (this.config.debug) {
|
|
119
|
-
console.log('\n📋 Plugin initialization order:', plugins.map(p => p.name).join(' → '));
|
|
120
|
-
console.log('\n--- INITIALIZATION PHASE ---\n');
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Initialize phase with error handling
|
|
124
|
-
for(const { name, module } of plugins) {
|
|
125
|
-
try {
|
|
126
|
-
if (this.config.debug) {
|
|
127
|
-
console.log(`⚙️ Initializing plugin: ${name}`);
|
|
128
|
-
this.root.setPluginContext(name);
|
|
129
|
-
}
|
|
130
|
-
await module.initialize?.(initCtx);
|
|
131
|
-
if (this.config.debug) {
|
|
132
|
-
console.log(`✅ Plugin initialized: ${name}\n`);
|
|
133
|
-
this.root.clearPluginContext();
|
|
134
|
-
}
|
|
135
|
-
} catch (error) {
|
|
136
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
137
|
-
const initError = new Error(`Plugin initialization failed: ${err.message}`);
|
|
138
|
-
(initError as any).cause = error;
|
|
139
|
-
if (this.config.debug) {
|
|
140
|
-
console.error(`❌ Plugin initialization failed: ${name}`, err);
|
|
141
|
-
}
|
|
142
|
-
throw initError;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
if (this.config.debug) {
|
|
147
|
-
console.log('--- ACTIVATION PHASE ---\n');
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const actCtx = createActivateContext(this.root, registries, this.env);
|
|
151
|
-
this.rootActivationCtx = actCtx;
|
|
152
|
-
|
|
153
|
-
// Activation phase with error handling
|
|
154
|
-
for(const { name, module } of plugins) {
|
|
155
|
-
try {
|
|
156
|
-
if (this.config.debug) {
|
|
157
|
-
console.log(`🔌 Activating plugin: ${name}`);
|
|
158
|
-
this.root.setPluginContext(name);
|
|
159
|
-
}
|
|
160
|
-
await module.activate?.(actCtx);
|
|
161
|
-
if (this.config.debug) {
|
|
162
|
-
console.log(`✅ Plugin activated: ${name}\n`);
|
|
163
|
-
this.root.clearPluginContext();
|
|
164
|
-
}
|
|
165
|
-
} catch (error) {
|
|
166
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
167
|
-
console.error(`Plugin activation failed:`, err);
|
|
168
|
-
if (this.config.debug) {
|
|
169
|
-
console.error(`❌ Plugin activation failed: ${name}`, err);
|
|
170
|
-
}
|
|
171
|
-
// Continue with other plugins rather than failing completely
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
if (this.config.debug) {
|
|
176
|
-
console.log('🎉 All plugins bootstrapped successfully!\n');
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
actCtx.hooks.emit('host:activated');
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
createChildHost(overrides: Provider[] = [], env?: Record<string, any>): IChildHost {
|
|
183
|
-
const child = this.root.createChild();
|
|
184
|
-
overrides.forEach(p=>child.provide(p));
|
|
185
|
-
const childEnv = { ...(this.env||{}), ...(env||{}) };
|
|
186
|
-
return new ChildHost(child, this._registry, childEnv, this.config);
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
class ChildHost implements IChildHost {
|
|
191
|
-
constructor(
|
|
192
|
-
private readonly container: Container,
|
|
193
|
-
private readonly registry: PluginRegistry,
|
|
194
|
-
private readonly env?: Record<string, any>,
|
|
195
|
-
private readonly config: HostConfig = {}
|
|
196
|
-
) {}
|
|
197
|
-
|
|
198
|
-
async bootstrap(pluginNames: string[]|'all'='all'): Promise<void>{
|
|
199
|
-
const registries = createSharedRegistries();
|
|
200
|
-
const initCtx = createInitContext(this.container, registries, this.env);
|
|
201
|
-
const plugins = this.registry.getModulesWithNamesInOrder(pluginNames);
|
|
202
|
-
|
|
203
|
-
if (this.config.debug) {
|
|
204
|
-
console.log('\n📋 Child host plugin initialization order:', plugins.map(p => p.name).join(' → '));
|
|
205
|
-
console.log('\n--- CHILD INITIALIZATION PHASE ---\n');
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// Initialize phase with error handling
|
|
209
|
-
for(const { name, module } of plugins) {
|
|
210
|
-
try {
|
|
211
|
-
if (this.config.debug) {
|
|
212
|
-
console.log(`⚙️ Initializing plugin: ${name}`);
|
|
213
|
-
this.container.setPluginContext(name);
|
|
214
|
-
}
|
|
215
|
-
await module.initialize?.(initCtx);
|
|
216
|
-
if (this.config.debug) {
|
|
217
|
-
console.log(`✅ Plugin initialized: ${name}\n`);
|
|
218
|
-
this.container.clearPluginContext();
|
|
219
|
-
}
|
|
220
|
-
} catch (error) {
|
|
221
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
222
|
-
const initError = new Error(`Plugin initialization failed: ${err.message}`);
|
|
223
|
-
(initError as any).cause = error;
|
|
224
|
-
if (this.config.debug) {
|
|
225
|
-
console.error(`❌ Plugin initialization failed: ${name}`, err);
|
|
226
|
-
}
|
|
227
|
-
throw initError;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
if (this.config.debug) {
|
|
232
|
-
console.log('--- CHILD ACTIVATION PHASE ---\n');
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
const actCtx = createActivateContext(this.container, registries, this.env);
|
|
236
|
-
|
|
237
|
-
// Activation phase with error handling
|
|
238
|
-
for(const { name, module } of plugins) {
|
|
239
|
-
try {
|
|
240
|
-
if (this.config.debug) {
|
|
241
|
-
console.log(`🔌 Activating plugin: ${name}`);
|
|
242
|
-
this.container.setPluginContext(name);
|
|
243
|
-
}
|
|
244
|
-
await module.activate?.(actCtx);
|
|
245
|
-
if (this.config.debug) {
|
|
246
|
-
console.log(`✅ Plugin activated: ${name}\n`);
|
|
247
|
-
this.container.clearPluginContext();
|
|
248
|
-
}
|
|
249
|
-
} catch (error) {
|
|
250
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
251
|
-
console.error(`Plugin activation failed:`, err);
|
|
252
|
-
if (this.config.debug) {
|
|
253
|
-
console.error(`❌ Plugin activation failed: ${name}`, err);
|
|
254
|
-
}
|
|
255
|
-
// Continue with other plugins rather than failing completely
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
if (this.config.debug) {
|
|
260
|
-
console.log('🎉 Child host plugins bootstrapped successfully!\n');
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
actCtx.hooks.emit('child:activated');
|
|
264
|
-
}
|
|
265
|
-
}
|
package/src/runtime/loader.ts
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import type { PluginManifest } from '@hamak/microkernel-api';
|
|
2
|
-
import type { PluginModule } from '@hamak/microkernel-spi';
|
|
3
|
-
import { topologicalSort } from './graph-utils';
|
|
4
|
-
|
|
5
|
-
class PluginRecord {
|
|
6
|
-
constructor(public manifest: PluginManifest, public module?: PluginModule) {}
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export class PluginRegistry {
|
|
10
|
-
private byName = new Map<string, PluginRecord>();
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Get a plugin record by name
|
|
14
|
-
*/
|
|
15
|
-
get(name: string) {
|
|
16
|
-
return this.byName.get(name);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* List all loaded plugin manifests
|
|
21
|
-
*/
|
|
22
|
-
list() {
|
|
23
|
-
return [...this.byName.values()].map(r => r.manifest);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Register a plugin programmatically (useful for testing/inline plugins)
|
|
28
|
-
*/
|
|
29
|
-
register(name: string, manifest: PluginManifest, module: PluginModule): void {
|
|
30
|
-
this.byName.set(name, new PluginRecord(manifest, module));
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Load plugins from manifest URLs or objects
|
|
35
|
-
*/
|
|
36
|
-
async loadAllAtRoot(manifestInputs: Array<string | PluginManifest>) {
|
|
37
|
-
const mfs: PluginManifest[] = [];
|
|
38
|
-
for (const m of manifestInputs) {
|
|
39
|
-
if (typeof m === 'string') {
|
|
40
|
-
mfs.push(await fetch(m).then(r => r.json()));
|
|
41
|
-
} else {
|
|
42
|
-
mfs.push(m);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
const sorted = topologicalSort(mfs);
|
|
46
|
-
for (const m of sorted) {
|
|
47
|
-
const url = new URL(m.entry, (m.baseUrl ?? location.origin) + '/').toString();
|
|
48
|
-
const mod: PluginModule = await import(/* @vite-ignore */ url);
|
|
49
|
-
this.byName.set(m.name, new PluginRecord(m, mod));
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Get plugin modules in dependency order
|
|
55
|
-
*/
|
|
56
|
-
getModulesInOrder(names: string[] | 'all' = 'all') {
|
|
57
|
-
const t = names === 'all' ? [...this.byName.keys()] : names;
|
|
58
|
-
const order = topologicalSort(t.map(n => this.byName.get(n)!.manifest));
|
|
59
|
-
return order.map(m => this.byName.get(m.name)!.module!).filter(Boolean);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Get plugin modules with their names in dependency order
|
|
64
|
-
*/
|
|
65
|
-
getModulesWithNamesInOrder(names: string[] | 'all' = 'all'): Array<{ name: string; module: PluginModule }> {
|
|
66
|
-
const t = names === 'all' ? [...this.byName.keys()] : names;
|
|
67
|
-
const order = topologicalSort(t.map(n => this.byName.get(n)!.manifest));
|
|
68
|
-
return order
|
|
69
|
-
.map(m => ({ name: m.name, module: this.byName.get(m.name)!.module! }))
|
|
70
|
-
.filter(item => item.module);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import type { CommandRegistry, Hooks, ViewRegistry } from '@hamak/microkernel-api';
|
|
2
|
-
export function createHooks(): Hooks { const map = new Map<string, Set<(...a:any[])=>void>>(); return { on(e: string,f: (...a: any[]) => void){ if(!map.has(e)) map.set(e,new Set()); map.get(e)!.add(f); }, off(e: string,f: (...a: any[]) => void){ map.get(e)?.delete(f); }, emit(e: string,...a: any[]){ for(const fn of map.get(e) ?? []) fn(...a); } }; }
|
|
3
|
-
export function createCommandRegistry(): CommandRegistry { const h=new Map<string,(...a:any[])=>any>(); return { register(id: string,fn: (...a:any[])=>any){h.set(id,fn);}, run(id: string,...a: any[]){const fn=h.get(id); if(!fn) throw new Error(`Command not found: ${id}`); return fn(...a);}, has(id: string){return h.has(id);} }; }
|
|
4
|
-
export function createViewRegistry(): ViewRegistry { const s=new Map<string,any[]>(); return { register(slot: string,v: any){ const arr=s.get(slot)??[]; arr.push(v); s.set(slot,arr); }, list(slot: string){ return [...(s.get(slot)??[])]; } }; }
|
package/src/ui/adapter.ts
DELETED
package/tsconfig.lib.es2015.json
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "./tsconfig.lib.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"target": "ES2015",
|
|
5
|
-
"lib": [
|
|
6
|
-
"ES2015",
|
|
7
|
-
"DOM"
|
|
8
|
-
],
|
|
9
|
-
"outDir": "./dist/es2015",
|
|
10
|
-
"declaration": false,
|
|
11
|
-
"declarationMap": false,
|
|
12
|
-
"sourceMap": false,
|
|
13
|
-
"downlevelIteration": true,
|
|
14
|
-
"composite": false
|
|
15
|
-
},
|
|
16
|
-
"include": [
|
|
17
|
-
"src/**/*.ts"
|
|
18
|
-
],
|
|
19
|
-
"exclude": [
|
|
20
|
-
"node_modules",
|
|
21
|
-
"dist"
|
|
22
|
-
]
|
|
23
|
-
}
|
package/tsconfig.lib.json
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2020",
|
|
4
|
-
"lib": ["ES2020", "DOM"],
|
|
5
|
-
"module": "ESNext",
|
|
6
|
-
"moduleResolution": "Bundler",
|
|
7
|
-
"rootDir": "./src",
|
|
8
|
-
"outDir": "./dist",
|
|
9
|
-
"declaration": true,
|
|
10
|
-
"declarationMap": true,
|
|
11
|
-
"sourceMap": true,
|
|
12
|
-
"strict": true,
|
|
13
|
-
"esModuleInterop": true,
|
|
14
|
-
"skipLibCheck": true,
|
|
15
|
-
"types": []
|
|
16
|
-
},
|
|
17
|
-
"include": [
|
|
18
|
-
"src/**/*.ts"
|
|
19
|
-
],
|
|
20
|
-
"exclude": [
|
|
21
|
-
"node_modules",
|
|
22
|
-
"dist"
|
|
23
|
-
]
|
|
24
|
-
}
|