bun_plugins 1.2.4 → 1.2.6
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/README.md +2 -2
- package/dist/{src/PluginManager.d.ts → PluginManager.d.ts} +9 -16
- package/dist/PluginManager.d.ts.map +1 -0
- package/dist/{src/PluginManager.js → PluginManager.js} +188 -187
- package/dist/PluginManager.js.map +1 -0
- package/dist/{src/index.d.ts → index.d.ts} +1 -2
- package/dist/index.d.ts.map +1 -0
- package/dist/{src/index.js → index.js} +0 -2
- package/dist/index.js.map +1 -0
- package/dist/{src/managers → managers}/ContextFactory.d.ts +1 -1
- package/dist/managers/ContextFactory.d.ts.map +1 -0
- package/dist/{src/managers → managers}/ContextFactory.js +29 -52
- package/dist/managers/ContextFactory.js.map +1 -0
- package/dist/managers/DependencyManager.d.ts.map +1 -0
- package/dist/{src/managers → managers}/DependencyManager.js +5 -7
- package/dist/managers/DependencyManager.js.map +1 -0
- package/dist/{src/managers → managers}/HooksManager.d.ts +1 -4
- package/dist/managers/HooksManager.d.ts.map +1 -0
- package/dist/{src/managers → managers}/HooksManager.js +8 -36
- package/dist/managers/HooksManager.js.map +1 -0
- package/dist/managers/ResourceManager.d.ts.map +1 -0
- package/dist/{src/managers → managers}/ResourceManager.js +3 -3
- package/dist/managers/ResourceManager.js.map +1 -0
- package/dist/{src/storage → storage}/JsonPluginStorage.d.ts +1 -2
- package/dist/storage/JsonPluginStorage.d.ts.map +1 -0
- package/dist/storage/JsonPluginStorage.js +63 -0
- package/dist/storage/JsonPluginStorage.js.map +1 -0
- package/dist/{src/types.d.ts → types.d.ts} +19 -60
- package/dist/types.d.ts.map +1 -0
- package/dist/{src/types.js → types.js} +0 -7
- package/dist/types.js.map +1 -0
- package/dist/utils/errorParser.d.ts.map +1 -0
- package/dist/{src/utils → utils}/errorParser.js +5 -2
- package/dist/utils/errorParser.js.map +1 -0
- package/dist/utils/pluginValidator.d.ts +93 -0
- package/dist/utils/pluginValidator.d.ts.map +1 -0
- package/dist/{src/utils → utils}/pluginValidator.js +14 -12
- package/dist/utils/pluginValidator.js.map +1 -0
- package/dist/utils/security.d.ts.map +1 -0
- package/dist/utils/security.js.map +1 -0
- package/dist/worker/WorkerRunner.d.ts.map +1 -0
- package/dist/{src/worker → worker}/WorkerRunner.js +13 -33
- package/dist/worker/WorkerRunner.js.map +1 -0
- package/package.json +7 -18
- package/LICENSE +0 -9
- package/dist/plugins/ActionRegistryPlugin.d.ts +0 -23
- package/dist/plugins/ActionRegistryPlugin.d.ts.map +0 -1
- package/dist/plugins/ActionRegistryPlugin.js +0 -56
- package/dist/plugins/ActionRegistryPlugin.js.map +0 -1
- package/dist/plugins/DynamicJSActionsPlugin.d.ts +0 -21
- package/dist/plugins/DynamicJSActionsPlugin.d.ts.map +0 -1
- package/dist/plugins/DynamicJSActionsPlugin.js +0 -57
- package/dist/plugins/DynamicJSActionsPlugin.js.map +0 -1
- package/dist/plugins/DynamicMathActionsPlugin.d.ts +0 -22
- package/dist/plugins/DynamicMathActionsPlugin.d.ts.map +0 -1
- package/dist/plugins/DynamicMathActionsPlugin.js +0 -64
- package/dist/plugins/DynamicMathActionsPlugin.js.map +0 -1
- package/dist/plugins/DynamicTextActionsPlugin.d.ts +0 -22
- package/dist/plugins/DynamicTextActionsPlugin.d.ts.map +0 -1
- package/dist/plugins/DynamicTextActionsPlugin.js +0 -58
- package/dist/plugins/DynamicTextActionsPlugin.js.map +0 -1
- package/dist/plugins/DynamicUtilityActionsPlugin.d.ts +0 -22
- package/dist/plugins/DynamicUtilityActionsPlugin.d.ts.map +0 -1
- package/dist/plugins/DynamicUtilityActionsPlugin.js +0 -75
- package/dist/plugins/DynamicUtilityActionsPlugin.js.map +0 -1
- package/dist/plugins/LifecycleDemoPlugin.d.ts +0 -20
- package/dist/plugins/LifecycleDemoPlugin.d.ts.map +0 -1
- package/dist/plugins/LifecycleDemoPlugin.js +0 -34
- package/dist/plugins/LifecycleDemoPlugin.js.map +0 -1
- package/dist/plugins/MathPlugin.d.ts +0 -16
- package/dist/plugins/MathPlugin.d.ts.map +0 -1
- package/dist/plugins/MathPlugin.js +0 -25
- package/dist/plugins/MathPlugin.js.map +0 -1
- package/dist/plugins/MyJSPlugin.d.ts +0 -7
- package/dist/plugins/MyJSPlugin.d.ts.map +0 -1
- package/dist/plugins/MyJSPlugin.js +0 -12
- package/dist/plugins/MyJSPlugin.js.map +0 -1
- package/dist/plugins/arktype/index.d.ts +0 -8
- package/dist/plugins/arktype/index.d.ts.map +0 -1
- package/dist/plugins/arktype/index.js +0 -25
- package/dist/plugins/arktype/index.js.map +0 -1
- package/dist/src/Plugin.d.ts +0 -28
- package/dist/src/Plugin.d.ts.map +0 -1
- package/dist/src/Plugin.js +0 -36
- package/dist/src/Plugin.js.map +0 -1
- package/dist/src/PluginManager.d.ts.map +0 -1
- package/dist/src/PluginManager.js.map +0 -1
- package/dist/src/index.d.ts.map +0 -1
- package/dist/src/index.js.map +0 -1
- package/dist/src/logger/LoggerAdapter.d.ts +0 -77
- package/dist/src/logger/LoggerAdapter.d.ts.map +0 -1
- package/dist/src/logger/LoggerAdapter.js +0 -242
- package/dist/src/logger/LoggerAdapter.js.map +0 -1
- package/dist/src/logger/LoggerFactory.d.ts +0 -73
- package/dist/src/logger/LoggerFactory.d.ts.map +0 -1
- package/dist/src/logger/LoggerFactory.js +0 -99
- package/dist/src/logger/LoggerFactory.js.map +0 -1
- package/dist/src/logger/index.d.ts +0 -3
- package/dist/src/logger/index.d.ts.map +0 -1
- package/dist/src/logger/index.js +0 -3
- package/dist/src/logger/index.js.map +0 -1
- package/dist/src/managers/ContextFactory.d.ts.map +0 -1
- package/dist/src/managers/ContextFactory.js.map +0 -1
- package/dist/src/managers/DependencyManager.d.ts.map +0 -1
- package/dist/src/managers/DependencyManager.js.map +0 -1
- package/dist/src/managers/HooksManager.d.ts.map +0 -1
- package/dist/src/managers/HooksManager.js.map +0 -1
- package/dist/src/managers/ResourceManager.d.ts.map +0 -1
- package/dist/src/managers/ResourceManager.js.map +0 -1
- package/dist/src/storage/JsonPluginStorage.d.ts.map +0 -1
- package/dist/src/storage/JsonPluginStorage.js +0 -75
- package/dist/src/storage/JsonPluginStorage.js.map +0 -1
- package/dist/src/types/arktype_converter.d.ts +0 -19
- package/dist/src/types/arktype_converter.d.ts.map +0 -1
- package/dist/src/types/arktype_converter.js +0 -73
- package/dist/src/types/arktype_converter.js.map +0 -1
- package/dist/src/types/generator.d.ts +0 -9
- package/dist/src/types/generator.d.ts.map +0 -1
- package/dist/src/types/generator.js +0 -9
- package/dist/src/types/generator.js.map +0 -1
- package/dist/src/types/interfaces.d.ts +0 -55
- package/dist/src/types/interfaces.d.ts.map +0 -1
- package/dist/src/types/interfaces.js +0 -5
- package/dist/src/types/interfaces.js.map +0 -1
- package/dist/src/types/plugin-registry-base.d.ts +0 -78
- package/dist/src/types/plugin-registry-base.d.ts.map +0 -1
- package/dist/src/types/plugin-registry-base.js +0 -6
- package/dist/src/types/plugin-registry-base.js.map +0 -1
- package/dist/src/types/plugin-registry.d.ts +0 -2
- package/dist/src/types/plugin-registry.d.ts.map +0 -1
- package/dist/src/types/plugin-registry.js +0 -4
- package/dist/src/types/plugin-registry.js.map +0 -1
- package/dist/src/types/plugin_generator.d.ts +0 -62
- package/dist/src/types/plugin_generator.d.ts.map +0 -1
- package/dist/src/types/plugin_generator.js +0 -700
- package/dist/src/types/plugin_generator.js.map +0 -1
- package/dist/src/types.d.ts.map +0 -1
- package/dist/src/types.js.map +0 -1
- package/dist/src/utils/errorParser.d.ts.map +0 -1
- package/dist/src/utils/errorParser.js.map +0 -1
- package/dist/src/utils/pluginValidator.d.ts +0 -17
- package/dist/src/utils/pluginValidator.d.ts.map +0 -1
- package/dist/src/utils/pluginValidator.js.map +0 -1
- package/dist/src/utils/security.d.ts.map +0 -1
- package/dist/src/utils/security.js.map +0 -1
- package/dist/src/worker/WorkerRunner.d.ts.map +0 -1
- package/dist/src/worker/WorkerRunner.js.map +0 -1
- package/plugin-types/index.d.ts +0 -20
- package/plugin-types/plugin-registry.d.ts +0 -148
- /package/dist/{src/managers → managers}/DependencyManager.d.ts +0 -0
- /package/dist/{src/managers → managers}/ResourceManager.d.ts +0 -0
- /package/dist/{src/utils → utils}/errorParser.d.ts +0 -0
- /package/dist/{src/utils → utils}/security.d.ts +0 -0
- /package/dist/{src/utils → utils}/security.js +0 -0
- /package/dist/{src/worker → worker}/WorkerRunner.d.ts +0 -0
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
import { EventEmitter } from "node:events";
|
|
2
2
|
import { readdir, mkdir } from "node:fs/promises";
|
|
3
|
-
import { join } from "node:path";
|
|
3
|
+
import { join, dirname } from "node:path";
|
|
4
4
|
import { watch, existsSync } from "node:fs";
|
|
5
5
|
import { WorkerMessageType, PluginPermission, RPCMethod, HookType } from "./types";
|
|
6
6
|
import { validatePlugin } from "./utils/pluginValidator";
|
|
7
7
|
import { JsonPluginStorage } from "./storage/JsonPluginStorage";
|
|
8
|
-
import
|
|
8
|
+
import semver from "semver";
|
|
9
9
|
import { ResourceManager } from "./managers/ResourceManager";
|
|
10
10
|
import { DependencyManager } from "./managers/DependencyManager";
|
|
11
11
|
import { HooksManager } from "./managers/HooksManager";
|
|
12
12
|
import { createPluginContext } from "./managers/ContextFactory";
|
|
13
13
|
import { errorParser } from "./utils/errorParser";
|
|
14
14
|
import { checkNetworkPermission, checkPermission as checkGeneralPermission } from "./utils/security";
|
|
15
|
-
import { logger } from "./logger";
|
|
16
15
|
export class PluginManager extends EventEmitter {
|
|
17
16
|
plugins = new Map();
|
|
18
17
|
availablePlugins = new Map();
|
|
19
|
-
|
|
18
|
+
pluginFiles = new Map(); // filePath -> pluginName
|
|
20
19
|
// Modules
|
|
21
20
|
resources;
|
|
22
21
|
dependencyManager;
|
|
@@ -26,6 +25,8 @@ export class PluginManager extends EventEmitter {
|
|
|
26
25
|
pluginLoadTimeout;
|
|
27
26
|
workerFactory;
|
|
28
27
|
workerRunnerPath;
|
|
28
|
+
hotReloadWatcher = null;
|
|
29
|
+
hotReloadTimer = null;
|
|
29
30
|
constructor(storageRoot = join(process.cwd(), "storage"), options) {
|
|
30
31
|
super();
|
|
31
32
|
this.storageRoot = storageRoot;
|
|
@@ -61,7 +62,7 @@ export class PluginManager extends EventEmitter {
|
|
|
61
62
|
throw new Error(`Plugin ${plugin.name} requires host version ${plugin.engines.host}, but found ${this.hostVersion}`);
|
|
62
63
|
}
|
|
63
64
|
}
|
|
64
|
-
|
|
65
|
+
console.log(`Loading plugin: ${plugin.name} v${plugin.version}`);
|
|
65
66
|
// 1. Prepare Storage
|
|
66
67
|
const storage = new JsonPluginStorage(this.storageRoot, plugin.name);
|
|
67
68
|
// 2. Load & Validate Configuration
|
|
@@ -72,7 +73,8 @@ export class PluginManager extends EventEmitter {
|
|
|
72
73
|
}
|
|
73
74
|
catch (e) {
|
|
74
75
|
const error = errorParser(e, `Error in plugin ${plugin.name}`);
|
|
75
|
-
|
|
76
|
+
console.warn(`[SafeMode] Using default config, validation failed. Error: ${error.message}`);
|
|
77
|
+
config = {}; // Reset to empty config on validation failure to prevent downstream errors
|
|
76
78
|
}
|
|
77
79
|
}
|
|
78
80
|
// 3. Initialize Resources
|
|
@@ -82,57 +84,37 @@ export class PluginManager extends EventEmitter {
|
|
|
82
84
|
throw new Error(`Invalid plugin name: ${plugin.name}. Name cannot contain path traversal characters.`);
|
|
83
85
|
}
|
|
84
86
|
// 4. Context Creation
|
|
85
|
-
this
|
|
86
|
-
const context = createPluginContext(this, plugin, this.resources, storage);
|
|
87
|
+
const context = createPluginContext(this, plugin, this.resources, storage, config);
|
|
87
88
|
// 5. Setup Hooks with Performance Monitoring (Delegated to HooksManager via Builder)
|
|
88
89
|
if (plugin.setup) {
|
|
89
90
|
try {
|
|
90
|
-
await plugin.setup(this.hooksManager.getBuilder(plugin.name
|
|
91
|
+
await plugin.setup(this.hooksManager.getBuilder(plugin.name));
|
|
91
92
|
}
|
|
92
93
|
catch (e) {
|
|
93
|
-
|
|
94
|
+
console.error(`Error during setup for ${plugin.name}:`, e);
|
|
94
95
|
throw errorParser(e, `Error during setup for ${plugin.name}`);
|
|
95
96
|
}
|
|
96
97
|
}
|
|
97
98
|
// 6. Lifecycle onLoad
|
|
98
99
|
try {
|
|
99
|
-
|
|
100
|
-
let timeoutError = null;
|
|
101
|
-
const timeoutId = setTimeout(() => {
|
|
102
|
-
if (!completed) {
|
|
103
|
-
completed = true;
|
|
104
|
-
timeoutError = new Error(`Plugin ${plugin.name} timed out (${this.pluginLoadTimeout}ms)`);
|
|
105
|
-
}
|
|
106
|
-
}, this.pluginLoadTimeout);
|
|
100
|
+
const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error(`Plugin ${plugin.name} timed out (${this.pluginLoadTimeout}ms)`)), this.pluginLoadTimeout));
|
|
107
101
|
const start = performance.now();
|
|
108
|
-
|
|
109
|
-
await plugin.onLoad(context);
|
|
110
|
-
}
|
|
111
|
-
finally {
|
|
112
|
-
completed = true;
|
|
113
|
-
clearTimeout(timeoutId);
|
|
114
|
-
}
|
|
115
|
-
// Check if timeout triggered
|
|
116
|
-
if (timeoutError) {
|
|
117
|
-
throw timeoutError;
|
|
118
|
-
}
|
|
102
|
+
await Promise.race([plugin.onLoad(context), timeoutPromise]);
|
|
119
103
|
const duration = performance.now() - start;
|
|
120
104
|
this.plugins.set(plugin.name, plugin);
|
|
121
|
-
|
|
105
|
+
console.log(`Plugin ${plugin.name} loaded successfully in ${duration.toFixed(2)}ms.`);
|
|
122
106
|
// Trigger onStarted after registration if provided
|
|
123
107
|
if (plugin.onStarted) {
|
|
124
108
|
try {
|
|
125
109
|
await plugin.onStarted();
|
|
126
110
|
}
|
|
127
111
|
catch (e) {
|
|
128
|
-
|
|
112
|
+
console.error(`Error in onStarted for ${plugin.name}:`, e);
|
|
129
113
|
}
|
|
130
114
|
}
|
|
131
|
-
// 7. Trigger onStart hooks registered via setup()
|
|
132
|
-
await this.hooksManager.runOnStart();
|
|
133
115
|
}
|
|
134
116
|
catch (error) {
|
|
135
|
-
|
|
117
|
+
console.error(`Failed to load plugin ${plugin.name}:`, error);
|
|
136
118
|
// Cleanup resources directly since plugin is not in this.plugins yet
|
|
137
119
|
this.resources.cleanup(plugin.name, this);
|
|
138
120
|
this.hooksManager.cleanup(plugin.name);
|
|
@@ -142,7 +124,7 @@ export class PluginManager extends EventEmitter {
|
|
|
142
124
|
async registerIsolated(pluginPath, pluginName) {
|
|
143
125
|
return new Promise((resolve, reject) => {
|
|
144
126
|
const workerScript = this.workerRunnerPath;
|
|
145
|
-
|
|
127
|
+
console.log("Worker Path:", workerScript);
|
|
146
128
|
const timeoutId = setTimeout(() => {
|
|
147
129
|
worker.terminate();
|
|
148
130
|
reject(new Error(`Isolated plugin ${pluginName} timed out during loading (${this.pluginLoadTimeout}ms)`));
|
|
@@ -223,22 +205,16 @@ export class PluginManager extends EventEmitter {
|
|
|
223
205
|
else if (type === HookType.ON_LOAD) {
|
|
224
206
|
this.hooksManager.registerOnLoad(filterRegExp, proxyCallback, pluginName, options?.order);
|
|
225
207
|
}
|
|
226
|
-
else if (type === HookType.ON_START) {
|
|
227
|
-
this.hooksManager.registerOnStart(() => proxyCallback({}), pluginName);
|
|
228
|
-
}
|
|
229
208
|
result = true;
|
|
230
209
|
}
|
|
231
210
|
else if (method === RPCMethod.ManagerGetPlugin) {
|
|
232
211
|
const targetName = args[0];
|
|
233
212
|
const p = this.getPlugin(targetName);
|
|
234
|
-
result = p
|
|
235
|
-
? p.getApi()
|
|
236
|
-
: undefined;
|
|
213
|
+
result = p?.getSharedApi ? p.getSharedApi() : undefined;
|
|
237
214
|
}
|
|
238
215
|
else if (method === RPCMethod.Log) {
|
|
239
216
|
const level = args[0];
|
|
240
|
-
|
|
241
|
-
logger.getLogger(pluginName)[level](msg, ...restArgs);
|
|
217
|
+
console[level](`[${pluginName}]`, ...args.slice(1));
|
|
242
218
|
result = true;
|
|
243
219
|
}
|
|
244
220
|
else if (method === RPCMethod.PermissionCheck) {
|
|
@@ -254,22 +230,13 @@ export class PluginManager extends EventEmitter {
|
|
|
254
230
|
const urlStr = typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url;
|
|
255
231
|
checkPermission(PluginPermission.Network, urlStr);
|
|
256
232
|
const response = await fetch(input, init);
|
|
257
|
-
const responseHeaders = {};
|
|
258
|
-
response.headers.forEach((v, k) => { responseHeaders[k] = v; });
|
|
259
233
|
result = {
|
|
260
234
|
status: response.status,
|
|
261
235
|
statusText: response.statusText,
|
|
262
|
-
headers:
|
|
236
|
+
headers: Object.fromEntries(response.headers.entries()),
|
|
263
237
|
body: await response.text()
|
|
264
238
|
};
|
|
265
239
|
}
|
|
266
|
-
else if (method === RPCMethod.ConfigGet) {
|
|
267
|
-
result = this.getPluginConfig(pluginName);
|
|
268
|
-
}
|
|
269
|
-
else if (method === RPCMethod.StorageReload) {
|
|
270
|
-
await storage.reload();
|
|
271
|
-
result = true;
|
|
272
|
-
}
|
|
273
240
|
worker.postMessage({ id, result });
|
|
274
241
|
}
|
|
275
242
|
catch (e) {
|
|
@@ -296,7 +263,7 @@ export class PluginManager extends EventEmitter {
|
|
|
296
263
|
else if (msg.type === WorkerMessageType.MANIFEST) {
|
|
297
264
|
const { metadata } = msg;
|
|
298
265
|
pluginMetadata = metadata;
|
|
299
|
-
|
|
266
|
+
console.log(`Metadata received for isolated plugin: ${metadata.name}`);
|
|
300
267
|
}
|
|
301
268
|
else if (msg.type === WorkerMessageType.LOAD_SUCCESS) {
|
|
302
269
|
clearTimeout(timeoutId); // Success, clear global timeout
|
|
@@ -319,20 +286,20 @@ export class PluginManager extends EventEmitter {
|
|
|
319
286
|
await new Promise(r => setTimeout(r, 200));
|
|
320
287
|
worker.terminate();
|
|
321
288
|
// Clear pending hooks on unload
|
|
322
|
-
for (const pending of
|
|
289
|
+
for (const pending of pendingHooks.values()) {
|
|
323
290
|
pending.reject(new Error("Plugin unloaded"));
|
|
324
291
|
}
|
|
325
292
|
pendingHooks.clear();
|
|
326
293
|
},
|
|
327
294
|
};
|
|
328
295
|
this.plugins.set(metadata.name, proxyPlugin);
|
|
329
|
-
|
|
296
|
+
console.log(`Isolated Plugin ${metadata.name} loaded in worker.`);
|
|
330
297
|
resolve();
|
|
331
298
|
}
|
|
332
299
|
else if (msg.type === WorkerMessageType.LOAD_ERROR) {
|
|
333
300
|
clearTimeout(timeoutId);
|
|
334
301
|
// Cleanup pending hooks on load error
|
|
335
|
-
for (const pending of
|
|
302
|
+
for (const pending of pendingHooks.values()) {
|
|
336
303
|
pending.reject(new Error(`Load error: ${msg.error}`));
|
|
337
304
|
}
|
|
338
305
|
pendingHooks.clear();
|
|
@@ -343,9 +310,9 @@ export class PluginManager extends EventEmitter {
|
|
|
343
310
|
worker.addEventListener("message", (event) => rpcHandler(event.data));
|
|
344
311
|
worker.addEventListener("error", (err) => {
|
|
345
312
|
clearTimeout(timeoutId);
|
|
346
|
-
|
|
313
|
+
console.error(`[Isolated:${pluginName}] Worker Error:`, err);
|
|
347
314
|
// Reject all pending hooks on crash
|
|
348
|
-
for (const pending of
|
|
315
|
+
for (const pending of pendingHooks.values()) {
|
|
349
316
|
pending.reject(new Error("Worker terminated unexpectedly"));
|
|
350
317
|
}
|
|
351
318
|
pendingHooks.clear();
|
|
@@ -357,39 +324,27 @@ export class PluginManager extends EventEmitter {
|
|
|
357
324
|
const plugin = this.plugins.get(pluginName);
|
|
358
325
|
if (!plugin)
|
|
359
326
|
return;
|
|
360
|
-
for (const [name, p] of
|
|
327
|
+
for (const [name, p] of this.plugins.entries()) {
|
|
361
328
|
if (p.dependencies && p.dependencies[pluginName]) {
|
|
362
|
-
|
|
329
|
+
console.warn(`Warning: Plugin ${name} depends on ${pluginName} which is being unloaded.`);
|
|
363
330
|
}
|
|
364
331
|
}
|
|
365
332
|
try {
|
|
366
333
|
await plugin.onUnload();
|
|
367
334
|
}
|
|
368
335
|
catch (error) {
|
|
369
|
-
|
|
336
|
+
console.error(`Error unloading plugin ${pluginName}:`, error);
|
|
370
337
|
}
|
|
371
338
|
finally {
|
|
372
339
|
this.plugins.delete(pluginName);
|
|
373
340
|
this.resources.cleanup(pluginName, this);
|
|
374
341
|
this.hooksManager.cleanup(pluginName);
|
|
375
|
-
|
|
342
|
+
console.log(`Plugin ${pluginName} unloaded.`);
|
|
376
343
|
}
|
|
377
344
|
}
|
|
378
345
|
getPlugin(name) {
|
|
379
346
|
return this.plugins.get(name);
|
|
380
347
|
}
|
|
381
|
-
getApi(name) {
|
|
382
|
-
const plugin = this.plugins.get(name);
|
|
383
|
-
if (!plugin)
|
|
384
|
-
return undefined;
|
|
385
|
-
if (plugin.getApi) {
|
|
386
|
-
return plugin.getApi();
|
|
387
|
-
}
|
|
388
|
-
return undefined;
|
|
389
|
-
}
|
|
390
|
-
getPluginConfig(name) {
|
|
391
|
-
return this.configs.get(name) || {};
|
|
392
|
-
}
|
|
393
348
|
listPlugins() {
|
|
394
349
|
return Array.from(this.plugins.keys());
|
|
395
350
|
}
|
|
@@ -399,12 +354,6 @@ export class PluginManager extends EventEmitter {
|
|
|
399
354
|
on(eventName, listener) {
|
|
400
355
|
return super.on(eventName, listener);
|
|
401
356
|
}
|
|
402
|
-
once(eventName, listener) {
|
|
403
|
-
return super.once(eventName, listener);
|
|
404
|
-
}
|
|
405
|
-
off(eventName, listener) {
|
|
406
|
-
return super.off(eventName, listener);
|
|
407
|
-
}
|
|
408
357
|
async loadPluginsFromDirectory(directoryPath = join(process.cwd(), "plugins")) {
|
|
409
358
|
try {
|
|
410
359
|
const globalConfigPath = join(this.storageRoot, "plugins.json");
|
|
@@ -417,35 +366,15 @@ export class PluginManager extends EventEmitter {
|
|
|
417
366
|
}
|
|
418
367
|
}
|
|
419
368
|
catch (e) {
|
|
420
|
-
|
|
369
|
+
console.warn("Failed to load global plugin config", e);
|
|
421
370
|
}
|
|
422
|
-
const
|
|
371
|
+
const files = await readdir(directoryPath);
|
|
423
372
|
this.availablePlugins.clear();
|
|
424
|
-
for (const
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
const pluginDir = join(directoryPath, entry.name);
|
|
428
|
-
const pkgPath = join(pluginDir, "package.json");
|
|
429
|
-
if (existsSync(pkgPath)) {
|
|
430
|
-
// Auto-install dependencies if node_modules missing
|
|
431
|
-
await this.ensureDependenciesInstalled(pluginDir);
|
|
432
|
-
fullPath = pluginDir;
|
|
433
|
-
}
|
|
434
|
-
else {
|
|
435
|
-
const distPath = join(pluginDir, "dist");
|
|
436
|
-
if (existsSync(distPath)) {
|
|
437
|
-
fullPath = distPath;
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
else if ((entry.name.endsWith(".ts") || entry.name.endsWith(".js")) && !entry.name.endsWith(".d.ts")) {
|
|
442
|
-
fullPath = join(directoryPath, entry.name);
|
|
443
|
-
}
|
|
444
|
-
if (fullPath) {
|
|
373
|
+
for (const file of files) {
|
|
374
|
+
if ((file.endsWith(".ts") || file.endsWith(".js")) && !file.endsWith(".d.ts")) {
|
|
375
|
+
const fullPath = join(directoryPath, file);
|
|
445
376
|
try {
|
|
446
|
-
|
|
447
|
-
const query = `?update=${Date.now()}`;
|
|
448
|
-
const module = await import(fullPath + query);
|
|
377
|
+
const module = await import(fullPath);
|
|
449
378
|
for (const key in module) {
|
|
450
379
|
const ExportedItem = module[key];
|
|
451
380
|
const validation = validatePlugin(ExportedItem);
|
|
@@ -453,23 +382,22 @@ export class PluginManager extends EventEmitter {
|
|
|
453
382
|
this.availablePlugins.set(validation.plugin.name, validation.plugin);
|
|
454
383
|
}
|
|
455
384
|
else {
|
|
456
|
-
|
|
457
|
-
logger.getLogger("PluginManager").warn(`Skipping invalid plugin item in ${entry.name}: ${errorMsg}`);
|
|
385
|
+
console.warn(`Skipping invalid plugin in ${file}: ${validation.error}`);
|
|
458
386
|
}
|
|
459
387
|
}
|
|
460
388
|
}
|
|
461
389
|
catch (err) {
|
|
462
|
-
|
|
390
|
+
console.error(`Error importing ${fullPath}:`, err);
|
|
463
391
|
}
|
|
464
392
|
}
|
|
465
393
|
}
|
|
466
394
|
const pluginsToLoad = [];
|
|
467
|
-
for (const plugin of
|
|
395
|
+
for (const plugin of this.availablePlugins.values()) {
|
|
468
396
|
if (!disabledPlugins.includes(plugin.name)) {
|
|
469
397
|
pluginsToLoad.push(plugin);
|
|
470
398
|
}
|
|
471
399
|
else {
|
|
472
|
-
|
|
400
|
+
console.log(`Plugin ${plugin.name} is disabled.`);
|
|
473
401
|
}
|
|
474
402
|
}
|
|
475
403
|
try {
|
|
@@ -480,7 +408,7 @@ export class PluginManager extends EventEmitter {
|
|
|
480
408
|
if (plugin.dependencies) {
|
|
481
409
|
for (const dep of Object.keys(plugin.dependencies)) {
|
|
482
410
|
if (!this.plugins.has(dep)) {
|
|
483
|
-
|
|
411
|
+
console.warn(`Skipping ${plugin.name}: Dependency ${dep} failed to load or is missing.`);
|
|
484
412
|
dependenciesOk = false;
|
|
485
413
|
break;
|
|
486
414
|
}
|
|
@@ -489,18 +417,13 @@ export class PluginManager extends EventEmitter {
|
|
|
489
417
|
if (!dependenciesOk)
|
|
490
418
|
continue;
|
|
491
419
|
try {
|
|
492
|
-
if (this.plugins.has(plugin.name)) {
|
|
493
|
-
logger.getLogger("PluginManager").info(`Updating plugin: ${plugin.name}`);
|
|
494
|
-
await this.reloadPlugin(plugin.name);
|
|
495
|
-
loadedPlugins.push(plugin);
|
|
496
|
-
}
|
|
497
|
-
else {
|
|
420
|
+
if (!this.plugins.has(plugin.name)) {
|
|
498
421
|
await this.register(plugin);
|
|
499
422
|
loadedPlugins.push(plugin);
|
|
500
423
|
}
|
|
501
424
|
}
|
|
502
425
|
catch (e) {
|
|
503
|
-
|
|
426
|
+
console.error(`Failed to load ${plugin.name}:`, e);
|
|
504
427
|
}
|
|
505
428
|
}
|
|
506
429
|
for (const plugin of loadedPlugins) {
|
|
@@ -509,23 +432,23 @@ export class PluginManager extends EventEmitter {
|
|
|
509
432
|
await plugin.onStarted();
|
|
510
433
|
}
|
|
511
434
|
catch (e) {
|
|
512
|
-
|
|
435
|
+
console.error(`Error in onStarted for ${plugin.name}:`, e);
|
|
513
436
|
}
|
|
514
437
|
}
|
|
515
438
|
}
|
|
516
439
|
}
|
|
517
440
|
catch (e) {
|
|
518
|
-
|
|
441
|
+
console.error("Failed to resolve plugin dependencies or load plugins:", e);
|
|
519
442
|
}
|
|
520
443
|
}
|
|
521
444
|
catch (error) {
|
|
522
|
-
|
|
445
|
+
console.error(`Failed to load plugins from ${directoryPath}`, error);
|
|
523
446
|
}
|
|
524
447
|
}
|
|
525
448
|
async disablePlugin(name) {
|
|
526
449
|
await this.unregister(name);
|
|
527
450
|
await this.updatePluginState(name, true);
|
|
528
|
-
|
|
451
|
+
console.log(`Plugin ${name} disabled.`);
|
|
529
452
|
}
|
|
530
453
|
async enablePlugin(name) {
|
|
531
454
|
await this.updatePluginState(name, false);
|
|
@@ -535,7 +458,7 @@ export class PluginManager extends EventEmitter {
|
|
|
535
458
|
if (plugin.dependencies) {
|
|
536
459
|
for (const dep of Object.keys(plugin.dependencies)) {
|
|
537
460
|
if (!this.plugins.has(dep)) {
|
|
538
|
-
|
|
461
|
+
console.warn(`Cannot enable ${name}: Dependency ${dep} is not loaded.`);
|
|
539
462
|
return;
|
|
540
463
|
}
|
|
541
464
|
}
|
|
@@ -543,33 +466,19 @@ export class PluginManager extends EventEmitter {
|
|
|
543
466
|
await this.register(plugin);
|
|
544
467
|
}
|
|
545
468
|
else {
|
|
546
|
-
|
|
469
|
+
console.warn(`Plugin ${name} enabled but not found in available plugins.`);
|
|
547
470
|
}
|
|
548
471
|
}
|
|
549
472
|
}
|
|
550
473
|
async reloadPlugin(name) {
|
|
551
|
-
const
|
|
552
|
-
if (
|
|
553
|
-
|
|
474
|
+
const plugin = this.plugins.get(name);
|
|
475
|
+
if (plugin) {
|
|
476
|
+
console.log(`Reloading plugin ${name}...`);
|
|
554
477
|
await this.unregister(name);
|
|
555
478
|
}
|
|
556
479
|
const definition = this.availablePlugins.get(name);
|
|
557
480
|
if (definition) {
|
|
558
481
|
await this.register(definition);
|
|
559
|
-
const newPlugin = this.plugins.get(name);
|
|
560
|
-
// If the new version has onReload, call it
|
|
561
|
-
if (newPlugin && newPlugin.onReload) {
|
|
562
|
-
// We need a context for onReload as well
|
|
563
|
-
const storage = new JsonPluginStorage(this.storageRoot, name);
|
|
564
|
-
const context = createPluginContext(this, newPlugin, this.resources, storage);
|
|
565
|
-
try {
|
|
566
|
-
await newPlugin.onReload(context);
|
|
567
|
-
}
|
|
568
|
-
catch (e) {
|
|
569
|
-
logger.getLogger("PluginManager").error(`Error in onReload for ${name}:`, e);
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
this.emit("plugin:updated", { name: definition.name, version: definition.version });
|
|
573
482
|
}
|
|
574
483
|
else {
|
|
575
484
|
throw new Error(`Plugin ${name} not found in available plugins.`);
|
|
@@ -595,7 +504,7 @@ export class PluginManager extends EventEmitter {
|
|
|
595
504
|
await Bun.write(globalConfigPath, JSON.stringify(config, null, 2));
|
|
596
505
|
}
|
|
597
506
|
catch (e) {
|
|
598
|
-
|
|
507
|
+
console.error("Failed to update plugin state", e);
|
|
599
508
|
}
|
|
600
509
|
}
|
|
601
510
|
async runOnResolve(args) {
|
|
@@ -604,34 +513,158 @@ export class PluginManager extends EventEmitter {
|
|
|
604
513
|
async runOnLoad(args) {
|
|
605
514
|
return this.hooksManager.runOnLoad(args);
|
|
606
515
|
}
|
|
607
|
-
// Bun-like Aliases
|
|
608
|
-
async use(plugin) { return this.register(plugin); }
|
|
609
|
-
async plugin(plugin) { return this.register(plugin); }
|
|
610
|
-
async remove(pluginName) { return this.unregister(pluginName); }
|
|
611
516
|
toBunPlugin() {
|
|
612
517
|
return this.hooksManager.toBunPlugin();
|
|
613
518
|
}
|
|
614
|
-
hotReloadTimer = null;
|
|
615
519
|
enableHotReload(pluginDir) {
|
|
616
|
-
|
|
617
|
-
|
|
520
|
+
// Close existing watcher if any
|
|
521
|
+
if (this.hotReloadWatcher) {
|
|
522
|
+
this.hotReloadWatcher.close();
|
|
523
|
+
}
|
|
524
|
+
// Initial scan to build file map
|
|
525
|
+
this.scanAndTrackPlugins(pluginDir);
|
|
526
|
+
console.log(`[HotReload] Watching ${pluginDir} for changes...`);
|
|
527
|
+
this.hotReloadWatcher = watch(pluginDir, { recursive: true }, async (event, filename) => {
|
|
618
528
|
if (!filename || (!filename.endsWith(".ts") && !filename.endsWith(".js")))
|
|
619
529
|
return;
|
|
530
|
+
if (filename.endsWith(".d.ts"))
|
|
531
|
+
return; // Skip type definitions
|
|
532
|
+
const fullPath = join(pluginDir, filename);
|
|
620
533
|
if (this.hotReloadTimer)
|
|
621
534
|
clearTimeout(this.hotReloadTimer);
|
|
622
535
|
this.hotReloadTimer = setTimeout(async () => {
|
|
623
|
-
|
|
624
|
-
await this.loadPluginsFromDirectory(pluginDir);
|
|
536
|
+
await this.handleFileChange(event, fullPath, filename, pluginDir);
|
|
625
537
|
}, 300);
|
|
626
538
|
});
|
|
627
539
|
}
|
|
540
|
+
async scanAndTrackPlugins(pluginDir) {
|
|
541
|
+
this.pluginFiles.clear();
|
|
542
|
+
try {
|
|
543
|
+
const files = await readdir(pluginDir);
|
|
544
|
+
for (const file of files) {
|
|
545
|
+
if ((file.endsWith(".ts") || file.endsWith(".js")) && !file.endsWith(".d.ts")) {
|
|
546
|
+
const fullPath = join(pluginDir, file);
|
|
547
|
+
try {
|
|
548
|
+
const module = await import(fullPath);
|
|
549
|
+
for (const key in module) {
|
|
550
|
+
const ExportedItem = module[key];
|
|
551
|
+
const validation = validatePlugin(ExportedItem);
|
|
552
|
+
if (validation.valid) {
|
|
553
|
+
this.pluginFiles.set(fullPath, validation.plugin.name);
|
|
554
|
+
this.availablePlugins.set(validation.plugin.name, validation.plugin);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
catch (err) {
|
|
559
|
+
console.warn(`[HotReload] Failed to scan ${file}:`, err);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
catch (err) {
|
|
565
|
+
console.error(`[HotReload] Failed to scan plugin directory:`, err);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
async handleFileChange(event, fullPath, filename, pluginDir) {
|
|
569
|
+
const exists = existsSync(fullPath);
|
|
570
|
+
const previousName = this.pluginFiles.get(fullPath);
|
|
571
|
+
if (!exists && previousName) {
|
|
572
|
+
// File was removed - uninstall plugin
|
|
573
|
+
console.log(`[HotReload] Plugin file removed: ${filename}. Unloading plugin ${previousName}...`);
|
|
574
|
+
this.pluginFiles.delete(fullPath);
|
|
575
|
+
if (this.plugins.has(previousName)) {
|
|
576
|
+
await this.unregister(previousName);
|
|
577
|
+
console.log(`[HotReload] Plugin ${previousName} unloaded.`);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
else if (exists && !previousName) {
|
|
581
|
+
// New file added - install plugin
|
|
582
|
+
console.log(event, `[HotReload] New plugin file detected: ${filename}. Loading plugin...`);
|
|
583
|
+
try {
|
|
584
|
+
const module = await import(fullPath);
|
|
585
|
+
for (const key in module) {
|
|
586
|
+
const ExportedItem = module[key];
|
|
587
|
+
const validation = validatePlugin(ExportedItem);
|
|
588
|
+
if (validation.valid) {
|
|
589
|
+
const plugin = validation.plugin;
|
|
590
|
+
this.pluginFiles.set(fullPath, plugin.name);
|
|
591
|
+
this.availablePlugins.set(plugin.name, plugin);
|
|
592
|
+
// Check if not disabled and load it
|
|
593
|
+
const globalConfigPath = join(this.storageRoot, "plugins.json");
|
|
594
|
+
let disabledPlugins = [];
|
|
595
|
+
try {
|
|
596
|
+
const file = Bun.file(globalConfigPath);
|
|
597
|
+
if (await file.exists()) {
|
|
598
|
+
const data = await file.json();
|
|
599
|
+
disabledPlugins = data.disabled || [];
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
catch (e) { /* ignore */ }
|
|
603
|
+
if (!disabledPlugins.includes(plugin.name) && !this.plugins.has(plugin.name)) {
|
|
604
|
+
await this.register(plugin);
|
|
605
|
+
console.log(`[HotReload] Plugin ${plugin.name} loaded.`);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
catch (err) {
|
|
611
|
+
console.error(`[HotReload] Failed to load new plugin ${filename}:`, pluginDir, err);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
else if (exists && previousName) {
|
|
615
|
+
// File was modified - reload plugin
|
|
616
|
+
console.log(`[HotReload] Plugin file modified: ${filename}. Reloading plugin ${previousName}...`);
|
|
617
|
+
try {
|
|
618
|
+
// Remove from module cache to force reimport
|
|
619
|
+
delete require.cache[fullPath];
|
|
620
|
+
// Unload existing plugin
|
|
621
|
+
if (this.plugins.has(previousName)) {
|
|
622
|
+
await this.unregister(previousName);
|
|
623
|
+
}
|
|
624
|
+
// Reload the plugin
|
|
625
|
+
const module = await import(fullPath);
|
|
626
|
+
for (const key in module) {
|
|
627
|
+
const ExportedItem = module[key];
|
|
628
|
+
const validation = validatePlugin(ExportedItem);
|
|
629
|
+
if (validation.valid) {
|
|
630
|
+
const plugin = validation.plugin;
|
|
631
|
+
this.availablePlugins.set(plugin.name, plugin);
|
|
632
|
+
// Check if not disabled and load it
|
|
633
|
+
const globalConfigPath = join(this.storageRoot, "plugins.json");
|
|
634
|
+
let disabledPlugins = [];
|
|
635
|
+
try {
|
|
636
|
+
const file = Bun.file(globalConfigPath);
|
|
637
|
+
if (await file.exists()) {
|
|
638
|
+
const data = await file.json();
|
|
639
|
+
disabledPlugins = data.disabled || [];
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
catch (e) { /* ignore */ }
|
|
643
|
+
if (!disabledPlugins.includes(plugin.name)) {
|
|
644
|
+
await this.register(plugin);
|
|
645
|
+
console.log(`[HotReload] Plugin ${plugin.name} reloaded.`);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
catch (err) {
|
|
651
|
+
console.error(`[HotReload] Failed to reload plugin ${filename}:`, err);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
disableHotReload() {
|
|
656
|
+
if (this.hotReloadWatcher) {
|
|
657
|
+
this.hotReloadWatcher.close();
|
|
658
|
+
this.hotReloadWatcher = null;
|
|
659
|
+
console.log(`[HotReload] File watcher stopped.`);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
628
662
|
getMetrics() {
|
|
629
663
|
return {
|
|
630
664
|
totalPlugins: this.plugins.size,
|
|
631
665
|
activePlugins: Array.from(this.plugins.keys()),
|
|
632
666
|
resources: this.resources.getUsageSummary(),
|
|
633
667
|
hooks: {
|
|
634
|
-
onStart: this.hooksManager.getHookCount(HookType.ON_START),
|
|
635
668
|
onResolve: this.hooksManager.getHookCount(HookType.ON_RESOLVE),
|
|
636
669
|
onLoad: this.hooksManager.getHookCount(HookType.ON_LOAD)
|
|
637
670
|
}
|
|
@@ -639,7 +672,7 @@ export class PluginManager extends EventEmitter {
|
|
|
639
672
|
}
|
|
640
673
|
getPluginStatus() {
|
|
641
674
|
const status = {};
|
|
642
|
-
for (const [name, plugin] of
|
|
675
|
+
for (const [name, plugin] of this.plugins) {
|
|
643
676
|
const resources = this.resources.get(name);
|
|
644
677
|
status[name] = {
|
|
645
678
|
version: plugin.version,
|
|
@@ -653,37 +686,5 @@ export class PluginManager extends EventEmitter {
|
|
|
653
686
|
}
|
|
654
687
|
return status;
|
|
655
688
|
}
|
|
656
|
-
async ensureDependenciesInstalled(pluginPath) {
|
|
657
|
-
const nodeModulesPath = join(pluginPath, "node_modules");
|
|
658
|
-
if (!existsSync(nodeModulesPath)) {
|
|
659
|
-
// 1. Check if we are in production
|
|
660
|
-
const isProduction = process.env.NODE_ENV === "production";
|
|
661
|
-
if (isProduction) {
|
|
662
|
-
logger.getLogger("PluginManager").warn(`[PluginManager] Warning: Plugin at ${pluginPath} is missing node_modules. Auto-install is disabled in production.`);
|
|
663
|
-
return;
|
|
664
|
-
}
|
|
665
|
-
// 2. Check if 'bun' CLI is even available in the system
|
|
666
|
-
const bunPath = Bun.which("bun");
|
|
667
|
-
if (!bunPath) {
|
|
668
|
-
logger.getLogger("PluginManager").warn(`[PluginManager] Warning: node_modules missing in ${pluginPath}, but 'bun' CLI was not found. Cannot auto-install.`);
|
|
669
|
-
return;
|
|
670
|
-
}
|
|
671
|
-
logger.getLogger("PluginManager").info(`[PluginManager] Development mode detected. node_modules missing in ${pluginPath}. Installing deps...`);
|
|
672
|
-
try {
|
|
673
|
-
const proc = Bun.spawn([bunPath, "install"], {
|
|
674
|
-
cwd: pluginPath,
|
|
675
|
-
stdout: "inherit",
|
|
676
|
-
stderr: "inherit",
|
|
677
|
-
});
|
|
678
|
-
const exitCode = await proc.exited;
|
|
679
|
-
if (exitCode !== 0) {
|
|
680
|
-
logger.getLogger("PluginManager").error(`[PluginManager] Failed to install dependencies for ${pluginPath}. Exit code: ${exitCode}`);
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
catch (e) {
|
|
684
|
-
logger.getLogger("PluginManager").error(`[PluginManager] Error running bun install for ${pluginPath}:`, e);
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
689
|
}
|
|
689
690
|
//# sourceMappingURL=PluginManager.js.map
|