bun_plugins 1.2.6 → 1.2.7

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.
Files changed (178) hide show
  1. package/LICENSE +9 -0
  2. package/README.md +127 -3
  3. package/dist/examples/action-registry-declarative.d.ts +14 -0
  4. package/dist/examples/action-registry-declarative.d.ts.map +1 -0
  5. package/dist/examples/action-registry-declarative.js +130 -0
  6. package/dist/examples/action-registry-declarative.js.map +1 -0
  7. package/dist/examples/action-registry-example.d.ts +31 -0
  8. package/dist/examples/action-registry-example.d.ts.map +1 -0
  9. package/dist/examples/action-registry-example.js +84 -0
  10. package/dist/examples/action-registry-example.js.map +1 -0
  11. package/dist/examples/index-declarative.d.ts +32 -0
  12. package/dist/examples/index-declarative.d.ts.map +1 -0
  13. package/dist/examples/index-declarative.js +206 -0
  14. package/dist/examples/index-declarative.js.map +1 -0
  15. package/dist/examples/logger-declarative.d.ts +109 -0
  16. package/dist/examples/logger-declarative.d.ts.map +1 -0
  17. package/dist/examples/logger-declarative.js +330 -0
  18. package/dist/examples/logger-declarative.js.map +1 -0
  19. package/dist/examples/logger-simple.d.ts +8 -0
  20. package/dist/examples/logger-simple.d.ts.map +1 -0
  21. package/dist/examples/logger-simple.js +165 -0
  22. package/dist/examples/logger-simple.js.map +1 -0
  23. package/dist/examples/server-declarative.d.ts +46 -0
  24. package/dist/examples/server-declarative.d.ts.map +1 -0
  25. package/dist/examples/server-declarative.js +205 -0
  26. package/dist/examples/server-declarative.js.map +1 -0
  27. package/dist/examples/shared/action-registry.d.ts +45 -0
  28. package/dist/examples/shared/action-registry.d.ts.map +1 -0
  29. package/dist/examples/shared/action-registry.js +65 -0
  30. package/dist/examples/shared/action-registry.js.map +1 -0
  31. package/dist/examples/shared/plugin-builder.d.ts +41 -0
  32. package/dist/examples/shared/plugin-builder.d.ts.map +1 -0
  33. package/dist/examples/shared/plugin-builder.js +85 -0
  34. package/dist/examples/shared/plugin-builder.js.map +1 -0
  35. package/dist/examples/storage-declarative.d.ts +13 -0
  36. package/dist/examples/storage-declarative.d.ts.map +1 -0
  37. package/dist/examples/storage-declarative.js +203 -0
  38. package/dist/examples/storage-declarative.js.map +1 -0
  39. package/dist/plugins/ActionRegistryPlugin.d.ts +14 -0
  40. package/dist/plugins/ActionRegistryPlugin.d.ts.map +1 -0
  41. package/dist/plugins/ActionRegistryPlugin.js +52 -0
  42. package/dist/plugins/ActionRegistryPlugin.js.map +1 -0
  43. package/dist/plugins/DynamicJSActionsPlugin.d.ts +21 -0
  44. package/dist/plugins/DynamicJSActionsPlugin.d.ts.map +1 -0
  45. package/dist/plugins/DynamicJSActionsPlugin.js +57 -0
  46. package/dist/plugins/DynamicJSActionsPlugin.js.map +1 -0
  47. package/dist/plugins/DynamicMathActionsPlugin.d.ts +22 -0
  48. package/dist/plugins/DynamicMathActionsPlugin.d.ts.map +1 -0
  49. package/dist/plugins/DynamicMathActionsPlugin.js +64 -0
  50. package/dist/plugins/DynamicMathActionsPlugin.js.map +1 -0
  51. package/dist/plugins/DynamicTextActionsPlugin.d.ts +22 -0
  52. package/dist/plugins/DynamicTextActionsPlugin.d.ts.map +1 -0
  53. package/dist/plugins/DynamicTextActionsPlugin.js +58 -0
  54. package/dist/plugins/DynamicTextActionsPlugin.js.map +1 -0
  55. package/dist/plugins/DynamicUtilityActionsPlugin.d.ts +22 -0
  56. package/dist/plugins/DynamicUtilityActionsPlugin.d.ts.map +1 -0
  57. package/dist/plugins/DynamicUtilityActionsPlugin.js +75 -0
  58. package/dist/plugins/DynamicUtilityActionsPlugin.js.map +1 -0
  59. package/dist/plugins/ExamplePlugin.d.ts +3 -0
  60. package/dist/plugins/ExamplePlugin.d.ts.map +1 -0
  61. package/dist/plugins/ExamplePlugin.js +24 -0
  62. package/dist/plugins/ExamplePlugin.js.map +1 -0
  63. package/dist/plugins/LifecycleDemoPlugin.d.ts +20 -0
  64. package/dist/plugins/LifecycleDemoPlugin.d.ts.map +1 -0
  65. package/dist/plugins/LifecycleDemoPlugin.js +34 -0
  66. package/dist/plugins/LifecycleDemoPlugin.js.map +1 -0
  67. package/dist/plugins/MathPlugin.d.ts +16 -0
  68. package/dist/plugins/MathPlugin.d.ts.map +1 -0
  69. package/dist/plugins/MathPlugin.js +30 -0
  70. package/dist/plugins/MathPlugin.js.map +1 -0
  71. package/dist/plugins/MyJSPlugin.d.ts +7 -0
  72. package/dist/plugins/MyJSPlugin.d.ts.map +1 -0
  73. package/dist/plugins/MyJSPlugin.js +12 -0
  74. package/dist/plugins/MyJSPlugin.js.map +1 -0
  75. package/dist/plugins/TypedExamplePlugin.d.ts +10 -0
  76. package/dist/plugins/TypedExamplePlugin.d.ts.map +1 -0
  77. package/dist/plugins/TypedExamplePlugin.js +61 -0
  78. package/dist/plugins/TypedExamplePlugin.js.map +1 -0
  79. package/dist/plugins/arktype/index.d.ts +8 -0
  80. package/dist/plugins/arktype/index.d.ts.map +1 -0
  81. package/dist/plugins/arktype/index.js +25 -0
  82. package/dist/plugins/arktype/index.js.map +1 -0
  83. package/dist/src/Plugin.d.ts +28 -0
  84. package/dist/src/Plugin.d.ts.map +1 -0
  85. package/dist/src/Plugin.js +36 -0
  86. package/dist/src/Plugin.js.map +1 -0
  87. package/dist/{PluginManager.d.ts → src/PluginManager.d.ts} +33 -10
  88. package/dist/src/PluginManager.d.ts.map +1 -0
  89. package/dist/{PluginManager.js → src/PluginManager.js} +245 -193
  90. package/dist/src/PluginManager.js.map +1 -0
  91. package/dist/{index.d.ts → src/index.d.ts} +2 -1
  92. package/dist/src/index.d.ts.map +1 -0
  93. package/dist/{index.js → src/index.js} +2 -0
  94. package/dist/src/index.js.map +1 -0
  95. package/dist/src/logger/LoggerAdapter.d.ts +77 -0
  96. package/dist/src/logger/LoggerAdapter.d.ts.map +1 -0
  97. package/dist/src/logger/LoggerAdapter.js +242 -0
  98. package/dist/src/logger/LoggerAdapter.js.map +1 -0
  99. package/dist/src/logger/LoggerFactory.d.ts +73 -0
  100. package/dist/src/logger/LoggerFactory.d.ts.map +1 -0
  101. package/dist/src/logger/LoggerFactory.js +99 -0
  102. package/dist/src/logger/LoggerFactory.js.map +1 -0
  103. package/dist/src/logger/index.d.ts +3 -0
  104. package/dist/src/logger/index.d.ts.map +1 -0
  105. package/dist/src/logger/index.js +3 -0
  106. package/dist/src/logger/index.js.map +1 -0
  107. package/dist/{managers → src/managers}/ContextFactory.d.ts +1 -1
  108. package/dist/src/managers/ContextFactory.d.ts.map +1 -0
  109. package/dist/{managers → src/managers}/ContextFactory.js +51 -26
  110. package/dist/src/managers/ContextFactory.js.map +1 -0
  111. package/dist/src/managers/DependencyManager.d.ts.map +1 -0
  112. package/dist/{managers → src/managers}/DependencyManager.js +7 -5
  113. package/dist/src/managers/DependencyManager.js.map +1 -0
  114. package/dist/{managers → src/managers}/HooksManager.d.ts +4 -1
  115. package/dist/src/managers/HooksManager.d.ts.map +1 -0
  116. package/dist/{managers → src/managers}/HooksManager.js +36 -8
  117. package/dist/src/managers/HooksManager.js.map +1 -0
  118. package/dist/src/managers/ResourceManager.d.ts.map +1 -0
  119. package/dist/{managers → src/managers}/ResourceManager.js +3 -3
  120. package/dist/src/managers/ResourceManager.js.map +1 -0
  121. package/dist/src/storage/JsonPluginStorage.d.ts +75 -0
  122. package/dist/src/storage/JsonPluginStorage.d.ts.map +1 -0
  123. package/dist/src/storage/JsonPluginStorage.js +240 -0
  124. package/dist/src/storage/JsonPluginStorage.js.map +1 -0
  125. package/dist/src/types/plugin-registry-base.d.ts +80 -0
  126. package/dist/src/types/plugin-registry-base.d.ts.map +1 -0
  127. package/dist/src/types/plugin-registry-base.js +6 -0
  128. package/dist/src/types/plugin-registry-base.js.map +1 -0
  129. package/dist/{types.d.ts → src/types.d.ts} +60 -19
  130. package/dist/src/types.d.ts.map +1 -0
  131. package/dist/{types.js → src/types.js} +6 -0
  132. package/dist/src/types.js.map +1 -0
  133. package/dist/src/utils/errorParser.d.ts.map +1 -0
  134. package/dist/{utils → src/utils}/errorParser.js +2 -5
  135. package/dist/src/utils/errorParser.js.map +1 -0
  136. package/dist/src/utils/pluginValidator.d.ts +17 -0
  137. package/dist/src/utils/pluginValidator.d.ts.map +1 -0
  138. package/dist/{utils → src/utils}/pluginValidator.js +11 -14
  139. package/dist/src/utils/pluginValidator.js.map +1 -0
  140. package/dist/src/utils/security.d.ts.map +1 -0
  141. package/dist/src/utils/security.js.map +1 -0
  142. package/dist/src/worker/WorkerRunner.d.ts.map +1 -0
  143. package/dist/{worker → src/worker}/WorkerRunner.js +38 -13
  144. package/dist/src/worker/WorkerRunner.js.map +1 -0
  145. package/package.json +9 -6
  146. package/dist/PluginManager.d.ts.map +0 -1
  147. package/dist/PluginManager.js.map +0 -1
  148. package/dist/index.d.ts.map +0 -1
  149. package/dist/index.js.map +0 -1
  150. package/dist/managers/ContextFactory.d.ts.map +0 -1
  151. package/dist/managers/ContextFactory.js.map +0 -1
  152. package/dist/managers/DependencyManager.d.ts.map +0 -1
  153. package/dist/managers/DependencyManager.js.map +0 -1
  154. package/dist/managers/HooksManager.d.ts.map +0 -1
  155. package/dist/managers/HooksManager.js.map +0 -1
  156. package/dist/managers/ResourceManager.d.ts.map +0 -1
  157. package/dist/managers/ResourceManager.js.map +0 -1
  158. package/dist/storage/JsonPluginStorage.d.ts +0 -14
  159. package/dist/storage/JsonPluginStorage.d.ts.map +0 -1
  160. package/dist/storage/JsonPluginStorage.js +0 -63
  161. package/dist/storage/JsonPluginStorage.js.map +0 -1
  162. package/dist/types.d.ts.map +0 -1
  163. package/dist/types.js.map +0 -1
  164. package/dist/utils/errorParser.d.ts.map +0 -1
  165. package/dist/utils/errorParser.js.map +0 -1
  166. package/dist/utils/pluginValidator.d.ts +0 -93
  167. package/dist/utils/pluginValidator.d.ts.map +0 -1
  168. package/dist/utils/pluginValidator.js.map +0 -1
  169. package/dist/utils/security.d.ts.map +0 -1
  170. package/dist/utils/security.js.map +0 -1
  171. package/dist/worker/WorkerRunner.d.ts.map +0 -1
  172. package/dist/worker/WorkerRunner.js.map +0 -1
  173. /package/dist/{managers → src/managers}/DependencyManager.d.ts +0 -0
  174. /package/dist/{managers → src/managers}/ResourceManager.d.ts +0 -0
  175. /package/dist/{utils → src/utils}/errorParser.d.ts +0 -0
  176. /package/dist/{utils → src/utils}/security.d.ts +0 -0
  177. /package/dist/{utils → src/utils}/security.js +0 -0
  178. /package/dist/{worker → src/worker}/WorkerRunner.d.ts +0 -0
@@ -1,21 +1,24 @@
1
1
  import { EventEmitter } from "node:events";
2
2
  import { readdir, mkdir } from "node:fs/promises";
3
- import { join, dirname } from "node:path";
3
+ import { join } 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 semver from "semver";
8
+ import * as 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";
15
16
  export class PluginManager extends EventEmitter {
16
17
  plugins = new Map();
17
18
  availablePlugins = new Map();
18
- pluginFiles = new Map(); // filePath -> pluginName
19
+ configs = new Map();
20
+ pluginApis = new Map();
21
+ pluginFilePaths = new Map();
19
22
  // Modules
20
23
  resources;
21
24
  dependencyManager;
@@ -25,8 +28,6 @@ export class PluginManager extends EventEmitter {
25
28
  pluginLoadTimeout;
26
29
  workerFactory;
27
30
  workerRunnerPath;
28
- hotReloadWatcher = null;
29
- hotReloadTimer = null;
30
31
  constructor(storageRoot = join(process.cwd(), "storage"), options) {
31
32
  super();
32
33
  this.storageRoot = storageRoot;
@@ -62,9 +63,9 @@ export class PluginManager extends EventEmitter {
62
63
  throw new Error(`Plugin ${plugin.name} requires host version ${plugin.engines.host}, but found ${this.hostVersion}`);
63
64
  }
64
65
  }
65
- console.log(`Loading plugin: ${plugin.name} v${plugin.version}`);
66
- // 1. Prepare Storage
67
- const storage = new JsonPluginStorage(this.storageRoot, plugin.name);
66
+ logger.getLogger("PluginManager").info(`Loading plugin: ${plugin.name} v${plugin.version}`);
67
+ // 1. Prepare Storage (singleton)
68
+ const storage = JsonPluginStorage.getInstance(this.storageRoot, plugin.name);
68
69
  // 2. Load & Validate Configuration
69
70
  let config = plugin.defaultConfig || {};
70
71
  if (plugin.configSchema) {
@@ -73,8 +74,7 @@ export class PluginManager extends EventEmitter {
73
74
  }
74
75
  catch (e) {
75
76
  const error = errorParser(e, `Error in plugin ${plugin.name}`);
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
77
+ logger.getLogger("PluginManager").warn(`[SafeMode] Using default config, validation failed. Error: ${error.message}`);
78
78
  }
79
79
  }
80
80
  // 3. Initialize Resources
@@ -84,37 +84,57 @@ export class PluginManager extends EventEmitter {
84
84
  throw new Error(`Invalid plugin name: ${plugin.name}. Name cannot contain path traversal characters.`);
85
85
  }
86
86
  // 4. Context Creation
87
- const context = createPluginContext(this, plugin, this.resources, storage, config);
87
+ this.configs.set(plugin.name, config);
88
+ const context = createPluginContext(this, plugin, this.resources, storage);
88
89
  // 5. Setup Hooks with Performance Monitoring (Delegated to HooksManager via Builder)
89
90
  if (plugin.setup) {
90
91
  try {
91
- await plugin.setup(this.hooksManager.getBuilder(plugin.name));
92
+ await plugin.setup(this.hooksManager.getBuilder(plugin.name, config));
92
93
  }
93
94
  catch (e) {
94
- console.error(`Error during setup for ${plugin.name}:`, e);
95
+ logger.getLogger("PluginManager").error(`Error during setup for ${plugin.name}:`, e);
95
96
  throw errorParser(e, `Error during setup for ${plugin.name}`);
96
97
  }
97
98
  }
98
99
  // 6. Lifecycle onLoad
99
100
  try {
100
- const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error(`Plugin ${plugin.name} timed out (${this.pluginLoadTimeout}ms)`)), this.pluginLoadTimeout));
101
+ let completed = false;
102
+ let timeoutError = null;
103
+ const timeoutId = setTimeout(() => {
104
+ if (!completed) {
105
+ completed = true;
106
+ timeoutError = new Error(`Plugin ${plugin.name} timed out (${this.pluginLoadTimeout}ms)`);
107
+ }
108
+ }, this.pluginLoadTimeout);
101
109
  const start = performance.now();
102
- await Promise.race([plugin.onLoad(context), timeoutPromise]);
110
+ try {
111
+ await plugin.onLoad(context);
112
+ }
113
+ finally {
114
+ completed = true;
115
+ clearTimeout(timeoutId);
116
+ }
117
+ // Check if timeout triggered
118
+ if (timeoutError) {
119
+ throw timeoutError;
120
+ }
103
121
  const duration = performance.now() - start;
104
122
  this.plugins.set(plugin.name, plugin);
105
- console.log(`Plugin ${plugin.name} loaded successfully in ${duration.toFixed(2)}ms.`);
123
+ logger.getLogger("PluginManager").info(`Plugin ${plugin.name} loaded successfully in ${duration.toFixed(2)}ms.`);
106
124
  // Trigger onStarted after registration if provided
107
125
  if (plugin.onStarted) {
108
126
  try {
109
127
  await plugin.onStarted();
110
128
  }
111
129
  catch (e) {
112
- console.error(`Error in onStarted for ${plugin.name}:`, e);
130
+ logger.getLogger("PluginManager").error(`Error in onStarted for ${plugin.name}:`, e);
113
131
  }
114
132
  }
133
+ // 7. Trigger onStart hooks registered via setup()
134
+ await this.hooksManager.runOnStart();
115
135
  }
116
136
  catch (error) {
117
- console.error(`Failed to load plugin ${plugin.name}:`, error);
137
+ logger.getLogger("PluginManager").error(`Failed to load plugin ${plugin.name}:`, error);
118
138
  // Cleanup resources directly since plugin is not in this.plugins yet
119
139
  this.resources.cleanup(plugin.name, this);
120
140
  this.hooksManager.cleanup(plugin.name);
@@ -124,7 +144,7 @@ export class PluginManager extends EventEmitter {
124
144
  async registerIsolated(pluginPath, pluginName) {
125
145
  return new Promise((resolve, reject) => {
126
146
  const workerScript = this.workerRunnerPath;
127
- console.log("Worker Path:", workerScript);
147
+ logger.getLogger("PluginManager").info("Worker Path:", workerScript);
128
148
  const timeoutId = setTimeout(() => {
129
149
  worker.terminate();
130
150
  reject(new Error(`Isolated plugin ${pluginName} timed out during loading (${this.pluginLoadTimeout}ms)`));
@@ -138,7 +158,7 @@ export class PluginManager extends EventEmitter {
138
158
  worker.terminate();
139
159
  return reject(new Error(`Invalid plugin name: ${pluginName}. Name cannot contain path traversal characters.`));
140
160
  }
141
- const storage = new JsonPluginStorage(this.storageRoot, pluginName);
161
+ const storage = JsonPluginStorage.getInstance(this.storageRoot, pluginName);
142
162
  const pendingHooks = new Map();
143
163
  // Initialize resources
144
164
  const res = this.resources.init(pluginName);
@@ -205,16 +225,28 @@ export class PluginManager extends EventEmitter {
205
225
  else if (type === HookType.ON_LOAD) {
206
226
  this.hooksManager.registerOnLoad(filterRegExp, proxyCallback, pluginName, options?.order);
207
227
  }
228
+ else if (type === HookType.ON_START) {
229
+ this.hooksManager.registerOnStart(() => proxyCallback({}), pluginName);
230
+ }
208
231
  result = true;
209
232
  }
210
233
  else if (method === RPCMethod.ManagerGetPlugin) {
211
234
  const targetName = args[0];
212
- const p = this.getPlugin(targetName);
213
- result = p?.getSharedApi ? p.getSharedApi() : undefined;
235
+ // Get the registered API
236
+ const api = this.getPluginApi(targetName);
237
+ if (api) {
238
+ result = api;
239
+ }
240
+ else {
241
+ // Fallback to the plugin itself
242
+ const p = this.getPlugin(targetName);
243
+ result = api !== undefined ? api : p || undefined;
244
+ }
214
245
  }
215
246
  else if (method === RPCMethod.Log) {
216
247
  const level = args[0];
217
- console[level](`[${pluginName}]`, ...args.slice(1));
248
+ const [msg, ...restArgs] = args.slice(1);
249
+ logger.getLogger(pluginName)[level](msg, ...restArgs);
218
250
  result = true;
219
251
  }
220
252
  else if (method === RPCMethod.PermissionCheck) {
@@ -230,13 +262,27 @@ export class PluginManager extends EventEmitter {
230
262
  const urlStr = typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url;
231
263
  checkPermission(PluginPermission.Network, urlStr);
232
264
  const response = await fetch(input, init);
265
+ const responseHeaders = {};
266
+ response.headers.forEach((v, k) => { responseHeaders[k] = v; });
233
267
  result = {
234
268
  status: response.status,
235
269
  statusText: response.statusText,
236
- headers: Object.fromEntries(response.headers.entries()),
270
+ headers: responseHeaders,
237
271
  body: await response.text()
238
272
  };
239
273
  }
274
+ else if (method === RPCMethod.ConfigGet) {
275
+ result = this.getPluginConfig(pluginName);
276
+ }
277
+ else if (method === RPCMethod.StorageReload) {
278
+ await storage.reload();
279
+ result = true;
280
+ }
281
+ else if (method === RPCMethod.ManagerRegisterApi) {
282
+ const [api] = args;
283
+ this.registerApi(pluginName, api);
284
+ result = true;
285
+ }
240
286
  worker.postMessage({ id, result });
241
287
  }
242
288
  catch (e) {
@@ -263,7 +309,7 @@ export class PluginManager extends EventEmitter {
263
309
  else if (msg.type === WorkerMessageType.MANIFEST) {
264
310
  const { metadata } = msg;
265
311
  pluginMetadata = metadata;
266
- console.log(`Metadata received for isolated plugin: ${metadata.name}`);
312
+ logger.getLogger("PluginManager").info(`Metadata received for isolated plugin: ${metadata.name}`);
267
313
  }
268
314
  else if (msg.type === WorkerMessageType.LOAD_SUCCESS) {
269
315
  clearTimeout(timeoutId); // Success, clear global timeout
@@ -286,20 +332,26 @@ export class PluginManager extends EventEmitter {
286
332
  await new Promise(r => setTimeout(r, 200));
287
333
  worker.terminate();
288
334
  // Clear pending hooks on unload
289
- for (const pending of pendingHooks.values()) {
335
+ for (const pending of Array.from(pendingHooks.values())) {
290
336
  pending.reject(new Error("Plugin unloaded"));
291
337
  }
292
338
  pendingHooks.clear();
293
339
  },
294
340
  };
295
341
  this.plugins.set(metadata.name, proxyPlugin);
296
- console.log(`Isolated Plugin ${metadata.name} loaded in worker.`);
342
+ // Registrar el path del archivo del plugin aislado
343
+ this.pluginFilePaths.set(metadata.name, pluginPath);
344
+ // Register API if provided in metadata
345
+ if (metadata.api) {
346
+ this.registerApi(metadata.name, metadata.api);
347
+ }
348
+ logger.getLogger("PluginManager").info(`Isolated Plugin ${metadata.name} loaded in worker.`);
297
349
  resolve();
298
350
  }
299
351
  else if (msg.type === WorkerMessageType.LOAD_ERROR) {
300
352
  clearTimeout(timeoutId);
301
353
  // Cleanup pending hooks on load error
302
- for (const pending of pendingHooks.values()) {
354
+ for (const pending of Array.from(pendingHooks.values())) {
303
355
  pending.reject(new Error(`Load error: ${msg.error}`));
304
356
  }
305
357
  pendingHooks.clear();
@@ -310,9 +362,9 @@ export class PluginManager extends EventEmitter {
310
362
  worker.addEventListener("message", (event) => rpcHandler(event.data));
311
363
  worker.addEventListener("error", (err) => {
312
364
  clearTimeout(timeoutId);
313
- console.error(`[Isolated:${pluginName}] Worker Error:`, err);
365
+ logger.getLogger("PluginManager").error(`[Isolated:${pluginName}] Worker Error:`, err);
314
366
  // Reject all pending hooks on crash
315
- for (const pending of pendingHooks.values()) {
367
+ for (const pending of Array.from(pendingHooks.values())) {
316
368
  pending.reject(new Error("Worker terminated unexpectedly"));
317
369
  }
318
370
  pendingHooks.clear();
@@ -320,40 +372,87 @@ export class PluginManager extends EventEmitter {
320
372
  });
321
373
  });
322
374
  }
323
- async unregister(pluginName) {
375
+ async unregister(pluginName, clean = false) {
324
376
  const plugin = this.plugins.get(pluginName);
325
377
  if (!plugin)
326
378
  return;
327
- for (const [name, p] of this.plugins.entries()) {
379
+ for (const [name, p] of Array.from(this.plugins.entries())) {
328
380
  if (p.dependencies && p.dependencies[pluginName]) {
329
- console.warn(`Warning: Plugin ${name} depends on ${pluginName} which is being unloaded.`);
381
+ logger.getLogger("PluginManager").warn(`Warning: Plugin ${name} depends on ${pluginName} which is being unloaded.`);
330
382
  }
331
383
  }
332
384
  try {
333
385
  await plugin.onUnload();
334
386
  }
335
387
  catch (error) {
336
- console.error(`Error unloading plugin ${pluginName}:`, error);
388
+ logger.getLogger("PluginManager").error(`Error unloading plugin ${pluginName}:`, error);
337
389
  }
338
390
  finally {
339
391
  this.plugins.delete(pluginName);
340
392
  this.resources.cleanup(pluginName, this);
341
393
  this.hooksManager.cleanup(pluginName);
342
- console.log(`Plugin ${pluginName} unloaded.`);
394
+ this.unregisterApi(pluginName);
395
+ if (clean) {
396
+ this.pluginFilePaths.delete(pluginName);
397
+ }
398
+ logger.getLogger("PluginManager").info(`Plugin ${pluginName} unloaded.`);
343
399
  }
344
400
  }
345
401
  getPlugin(name) {
346
402
  return this.plugins.get(name);
347
403
  }
404
+ /**
405
+ * @param pluginName - Plugin name
406
+ * @returns The absolute path of the plugin file, or undefined if not found
407
+ */
408
+ getPluginPath(pluginName) {
409
+ return this.pluginFilePaths.get(pluginName);
410
+ }
411
+ /**
412
+ * Get all plugin paths
413
+ * @returns Map with plugin names as keys and their paths as values
414
+ */
415
+ getAllPluginPaths() {
416
+ return new Map(this.pluginFilePaths);
417
+ }
418
+ getApi(name) {
419
+ // Get the registered API
420
+ return this.getPluginApi(name);
421
+ }
422
+ getPluginConfig(name) {
423
+ return this.configs.get(name) || {};
424
+ }
425
+ setPluginConfig(name, config) {
426
+ this.configs.set(name, config);
427
+ return this.configs.get(name) || {};
428
+ }
348
429
  listPlugins() {
349
430
  return Array.from(this.plugins.keys());
350
431
  }
432
+ // register plugin API
433
+ registerApi(pluginName, api) {
434
+ this.pluginApis.set(pluginName, api);
435
+ }
436
+ // get plugin API
437
+ getPluginApi(pluginName) {
438
+ return this.pluginApis.get(pluginName);
439
+ }
440
+ // unregister plugin API
441
+ unregisterApi(pluginName) {
442
+ this.pluginApis.delete(pluginName);
443
+ }
351
444
  emit(eventName, ...args) {
352
445
  return super.emit(eventName, ...args);
353
446
  }
354
447
  on(eventName, listener) {
355
448
  return super.on(eventName, listener);
356
449
  }
450
+ once(eventName, listener) {
451
+ return super.once(eventName, listener);
452
+ }
453
+ off(eventName, listener) {
454
+ return super.off(eventName, listener);
455
+ }
357
456
  async loadPluginsFromDirectory(directoryPath = join(process.cwd(), "plugins")) {
358
457
  try {
359
458
  const globalConfigPath = join(this.storageRoot, "plugins.json");
@@ -366,38 +465,64 @@ export class PluginManager extends EventEmitter {
366
465
  }
367
466
  }
368
467
  catch (e) {
369
- console.warn("Failed to load global plugin config", e);
468
+ logger.getLogger("PluginManager").warn("Failed to load global plugin config", e);
370
469
  }
371
- const files = await readdir(directoryPath);
470
+ const entries = await readdir(directoryPath, { withFileTypes: true });
372
471
  this.availablePlugins.clear();
373
- for (const file of files) {
374
- if ((file.endsWith(".ts") || file.endsWith(".js")) && !file.endsWith(".d.ts")) {
375
- const fullPath = join(directoryPath, file);
472
+ // Clear the file paths when reloading the directory
473
+ this.pluginFilePaths.clear();
474
+ for (const entry of entries) {
475
+ let fullPath = null;
476
+ if (entry.isDirectory()) {
477
+ const pluginDir = join(directoryPath, entry.name);
478
+ const pkgPath = join(pluginDir, "package.json");
479
+ if (existsSync(pkgPath)) {
480
+ // Auto-install dependencies if node_modules missing
481
+ await this.ensureDependenciesInstalled(pluginDir);
482
+ fullPath = pluginDir;
483
+ }
484
+ else {
485
+ const distPath = join(pluginDir, "dist");
486
+ if (existsSync(distPath)) {
487
+ fullPath = distPath;
488
+ }
489
+ }
490
+ }
491
+ else if ((entry.name.endsWith(".ts") || entry.name.endsWith(".js")) && !entry.name.endsWith(".d.ts")) {
492
+ fullPath = join(directoryPath, entry.name);
493
+ }
494
+ if (fullPath) {
376
495
  try {
377
- const module = await import(fullPath);
496
+ // Use cache-busting for hot reload
497
+ const query = `?update=${Date.now()}`;
498
+ const module = await import(fullPath + query);
378
499
  for (const key in module) {
379
500
  const ExportedItem = module[key];
380
501
  const validation = validatePlugin(ExportedItem);
381
502
  if (validation.valid) {
382
503
  this.availablePlugins.set(validation.plugin.name, validation.plugin);
504
+ // Registrar la ubicación del archivo del plugin
505
+ this.pluginFilePaths.set(validation.plugin.name, fullPath);
506
+ logger.getLogger("PluginManager").info(`[PluginManager] Plugin file path registered: ${validation.plugin.name} -> ${fullPath}`);
383
507
  }
384
508
  else {
385
- console.warn(`Skipping invalid plugin in ${file}: ${validation.error}`);
509
+ const errorMsg = validation.error;
510
+ logger.getLogger("PluginManager").warn(`Skipping invalid plugin item in ${entry.name}: ${errorMsg}`);
386
511
  }
387
512
  }
388
513
  }
389
514
  catch (err) {
390
- console.error(`Error importing ${fullPath}:`, err);
515
+ logger.getLogger("PluginManager").error(`Error importing ${fullPath}:`, err);
391
516
  }
392
517
  }
393
518
  }
394
519
  const pluginsToLoad = [];
395
- for (const plugin of this.availablePlugins.values()) {
520
+ for (const plugin of Array.from(this.availablePlugins.values())) {
396
521
  if (!disabledPlugins.includes(plugin.name)) {
397
522
  pluginsToLoad.push(plugin);
398
523
  }
399
524
  else {
400
- console.log(`Plugin ${plugin.name} is disabled.`);
525
+ logger.getLogger("PluginManager").info(`Plugin ${plugin.name} is disabled.`);
401
526
  }
402
527
  }
403
528
  try {
@@ -408,7 +533,7 @@ export class PluginManager extends EventEmitter {
408
533
  if (plugin.dependencies) {
409
534
  for (const dep of Object.keys(plugin.dependencies)) {
410
535
  if (!this.plugins.has(dep)) {
411
- console.warn(`Skipping ${plugin.name}: Dependency ${dep} failed to load or is missing.`);
536
+ logger.getLogger("PluginManager").warn(`Skipping ${plugin.name}: Dependency ${dep} failed to load or is missing.`);
412
537
  dependenciesOk = false;
413
538
  break;
414
539
  }
@@ -417,13 +542,18 @@ export class PluginManager extends EventEmitter {
417
542
  if (!dependenciesOk)
418
543
  continue;
419
544
  try {
420
- if (!this.plugins.has(plugin.name)) {
545
+ if (this.plugins.has(plugin.name)) {
546
+ logger.getLogger("PluginManager").info(`Updating plugin: ${plugin.name}`);
547
+ await this.reloadPlugin(plugin.name);
548
+ loadedPlugins.push(plugin);
549
+ }
550
+ else {
421
551
  await this.register(plugin);
422
552
  loadedPlugins.push(plugin);
423
553
  }
424
554
  }
425
555
  catch (e) {
426
- console.error(`Failed to load ${plugin.name}:`, e);
556
+ logger.getLogger("PluginManager").error(`Failed to load/update ${plugin.name}:`, e);
427
557
  }
428
558
  }
429
559
  for (const plugin of loadedPlugins) {
@@ -432,23 +562,23 @@ export class PluginManager extends EventEmitter {
432
562
  await plugin.onStarted();
433
563
  }
434
564
  catch (e) {
435
- console.error(`Error in onStarted for ${plugin.name}:`, e);
565
+ logger.getLogger("PluginManager").error(`Error in onStarted for ${plugin.name}:`, e);
436
566
  }
437
567
  }
438
568
  }
439
569
  }
440
570
  catch (e) {
441
- console.error("Failed to resolve plugin dependencies or load plugins:", e);
571
+ logger.getLogger("PluginManager").error("Failed to resolve plugin dependencies or load plugins:", e);
442
572
  }
443
573
  }
444
574
  catch (error) {
445
- console.error(`Failed to load plugins from ${directoryPath}`, error);
575
+ logger.getLogger("PluginManager").error(`Failed to load plugins from ${directoryPath}`, error);
446
576
  }
447
577
  }
448
578
  async disablePlugin(name) {
449
579
  await this.unregister(name);
450
580
  await this.updatePluginState(name, true);
451
- console.log(`Plugin ${name} disabled.`);
581
+ logger.getLogger("PluginManager").info(`Plugin ${name} disabled.`);
452
582
  }
453
583
  async enablePlugin(name) {
454
584
  await this.updatePluginState(name, false);
@@ -458,7 +588,7 @@ export class PluginManager extends EventEmitter {
458
588
  if (plugin.dependencies) {
459
589
  for (const dep of Object.keys(plugin.dependencies)) {
460
590
  if (!this.plugins.has(dep)) {
461
- console.warn(`Cannot enable ${name}: Dependency ${dep} is not loaded.`);
591
+ logger.getLogger("PluginManager").warn(`Cannot enable ${name}: Dependency ${dep} is not loaded.`);
462
592
  return;
463
593
  }
464
594
  }
@@ -466,19 +596,33 @@ export class PluginManager extends EventEmitter {
466
596
  await this.register(plugin);
467
597
  }
468
598
  else {
469
- console.warn(`Plugin ${name} enabled but not found in available plugins.`);
599
+ logger.getLogger("PluginManager").warn(`Plugin ${name} enabled but not found in available plugins.`);
470
600
  }
471
601
  }
472
602
  }
473
603
  async reloadPlugin(name) {
474
- const plugin = this.plugins.get(name);
475
- if (plugin) {
476
- console.log(`Reloading plugin ${name}...`);
604
+ const oldPlugin = this.plugins.get(name);
605
+ if (oldPlugin) {
606
+ logger.getLogger("PluginManager").info(`Reloading plugin ${name}...`);
477
607
  await this.unregister(name);
478
608
  }
479
609
  const definition = this.availablePlugins.get(name);
480
610
  if (definition) {
481
611
  await this.register(definition);
612
+ const newPlugin = this.plugins.get(name);
613
+ // If the new version has onReload, call it
614
+ if (newPlugin && newPlugin.onReload) {
615
+ // We need a context for onReload as well
616
+ const storage = JsonPluginStorage.getInstance(this.storageRoot, name);
617
+ const context = createPluginContext(this, newPlugin, this.resources, storage);
618
+ try {
619
+ await newPlugin.onReload(context);
620
+ }
621
+ catch (e) {
622
+ logger.getLogger("PluginManager").error(`Error in onReload for ${name}:`, e);
623
+ }
624
+ }
625
+ this.emit("plugin:updated", { name: definition.name, version: definition.version });
482
626
  }
483
627
  else {
484
628
  throw new Error(`Plugin ${name} not found in available plugins.`);
@@ -504,7 +648,7 @@ export class PluginManager extends EventEmitter {
504
648
  await Bun.write(globalConfigPath, JSON.stringify(config, null, 2));
505
649
  }
506
650
  catch (e) {
507
- console.error("Failed to update plugin state", e);
651
+ logger.getLogger("PluginManager").error("Failed to update plugin state", e);
508
652
  }
509
653
  }
510
654
  async runOnResolve(args) {
@@ -513,158 +657,34 @@ export class PluginManager extends EventEmitter {
513
657
  async runOnLoad(args) {
514
658
  return this.hooksManager.runOnLoad(args);
515
659
  }
660
+ // Bun-like Aliases
661
+ async use(plugin) { return this.register(plugin); }
662
+ async plugin(plugin) { return this.register(plugin); }
663
+ async remove(pluginName) { return this.unregister(pluginName); }
516
664
  toBunPlugin() {
517
665
  return this.hooksManager.toBunPlugin();
518
666
  }
667
+ hotReloadTimer = null;
519
668
  enableHotReload(pluginDir) {
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) => {
669
+ logger.getLogger("PluginManager").info(`[HotReload] Watching ${pluginDir} for changes...`);
670
+ watch(pluginDir, { recursive: true }, async (event, filename) => {
528
671
  if (!filename || (!filename.endsWith(".ts") && !filename.endsWith(".js")))
529
672
  return;
530
- if (filename.endsWith(".d.ts"))
531
- return; // Skip type definitions
532
- const fullPath = join(pluginDir, filename);
533
673
  if (this.hotReloadTimer)
534
674
  clearTimeout(this.hotReloadTimer);
535
675
  this.hotReloadTimer = setTimeout(async () => {
536
- await this.handleFileChange(event, fullPath, filename, pluginDir);
676
+ logger.getLogger("PluginManager").info(`[HotReload] Change detected in ${filename}. Re-scanning plugins...`);
677
+ await this.loadPluginsFromDirectory(pluginDir);
537
678
  }, 300);
538
679
  });
539
680
  }
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
- }
662
681
  getMetrics() {
663
682
  return {
664
683
  totalPlugins: this.plugins.size,
665
684
  activePlugins: Array.from(this.plugins.keys()),
666
685
  resources: this.resources.getUsageSummary(),
667
686
  hooks: {
687
+ onStart: this.hooksManager.getHookCount(HookType.ON_START),
668
688
  onResolve: this.hooksManager.getHookCount(HookType.ON_RESOLVE),
669
689
  onLoad: this.hooksManager.getHookCount(HookType.ON_LOAD)
670
690
  }
@@ -672,7 +692,7 @@ export class PluginManager extends EventEmitter {
672
692
  }
673
693
  getPluginStatus() {
674
694
  const status = {};
675
- for (const [name, plugin] of this.plugins) {
695
+ for (const [name, plugin] of Array.from(this.plugins)) {
676
696
  const resources = this.resources.get(name);
677
697
  status[name] = {
678
698
  version: plugin.version,
@@ -686,5 +706,37 @@ export class PluginManager extends EventEmitter {
686
706
  }
687
707
  return status;
688
708
  }
709
+ async ensureDependenciesInstalled(pluginPath) {
710
+ const nodeModulesPath = join(pluginPath, "node_modules");
711
+ if (!existsSync(nodeModulesPath)) {
712
+ // 1. Check if we are in production
713
+ const isProduction = process.env.NODE_ENV === "production";
714
+ if (isProduction) {
715
+ logger.getLogger("PluginManager").warn(`[PluginManager] Warning: Plugin at ${pluginPath} is missing node_modules. Auto-install is disabled in production.`);
716
+ return;
717
+ }
718
+ // 2. Check if 'bun' CLI is even available in the system
719
+ const bunPath = Bun.which("bun");
720
+ if (!bunPath) {
721
+ logger.getLogger("PluginManager").warn(`[PluginManager] Warning: node_modules missing in ${pluginPath}, but 'bun' CLI was not found. Cannot auto-install.`);
722
+ return;
723
+ }
724
+ logger.getLogger("PluginManager").info(`[PluginManager] Development mode detected. node_modules missing in ${pluginPath}. Installing deps...`);
725
+ try {
726
+ const proc = Bun.spawn([bunPath, "install"], {
727
+ cwd: pluginPath,
728
+ stdout: "inherit",
729
+ stderr: "inherit",
730
+ });
731
+ const exitCode = await proc.exited;
732
+ if (exitCode !== 0) {
733
+ logger.getLogger("PluginManager").error(`[PluginManager] Failed to install dependencies for ${pluginPath}. Exit code: ${exitCode}`);
734
+ }
735
+ }
736
+ catch (e) {
737
+ logger.getLogger("PluginManager").error(`[PluginManager] Error running bun install for ${pluginPath}:`, e);
738
+ }
739
+ }
740
+ }
689
741
  }
690
742
  //# sourceMappingURL=PluginManager.js.map