@kanian77/choux 0.1.5 → 0.1.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.
Files changed (135) hide show
  1. package/dist/core/ChouxModule.d.ts +3 -0
  2. package/dist/core/ChouxModule.d.ts.map +1 -0
  3. package/{src/core/ChouxModule.ts → dist/core/ChouxModule.js} +11 -11
  4. package/dist/core/ChouxModule.js.map +1 -0
  5. package/dist/core/HookRegistry.d.ts +47 -0
  6. package/dist/core/HookRegistry.d.ts.map +1 -0
  7. package/dist/core/HookRegistry.js +79 -0
  8. package/dist/core/HookRegistry.js.map +1 -0
  9. package/dist/core/HookRegistry.spec.d.ts +2 -0
  10. package/dist/core/HookRegistry.spec.d.ts.map +1 -0
  11. package/dist/core/HookRegistry.spec.js +67 -0
  12. package/dist/core/HookRegistry.spec.js.map +1 -0
  13. package/dist/core/Plugin.d.ts +22 -0
  14. package/dist/core/Plugin.d.ts.map +1 -0
  15. package/dist/core/Plugin.js +4 -0
  16. package/dist/core/Plugin.js.map +1 -0
  17. package/dist/core/PluginLoader.d.ts +13 -0
  18. package/dist/core/PluginLoader.d.ts.map +1 -0
  19. package/dist/core/PluginLoader.js +147 -0
  20. package/dist/core/PluginLoader.js.map +1 -0
  21. package/dist/core/PluginLoader.spec.d.ts +2 -0
  22. package/dist/core/PluginLoader.spec.d.ts.map +1 -0
  23. package/dist/core/PluginLoader.spec.js +114 -0
  24. package/dist/core/PluginLoader.spec.js.map +1 -0
  25. package/dist/core/PluginManager.d.ts +12 -0
  26. package/dist/core/PluginManager.d.ts.map +1 -0
  27. package/dist/core/PluginManager.js +56 -0
  28. package/dist/core/PluginManager.js.map +1 -0
  29. package/dist/core/PluginManager.spec.d.ts +2 -0
  30. package/dist/core/PluginManager.spec.d.ts.map +1 -0
  31. package/dist/core/PluginManager.spec.js +50 -0
  32. package/dist/core/PluginManager.spec.js.map +1 -0
  33. package/dist/core/index.d.ts +5 -0
  34. package/dist/core/index.d.ts.map +1 -0
  35. package/{src/core/index.ts → dist/core/index.js} +1 -0
  36. package/dist/core/index.js.map +1 -0
  37. package/dist/decorators/hookDecorator.d.ts +8 -0
  38. package/dist/decorators/hookDecorator.d.ts.map +1 -0
  39. package/dist/decorators/hookDecorator.js +22 -0
  40. package/dist/decorators/hookDecorator.js.map +1 -0
  41. package/dist/decorators/index.d.ts +2 -0
  42. package/dist/decorators/index.d.ts.map +1 -0
  43. package/{src/decorators/index.ts → dist/decorators/index.js} +1 -0
  44. package/dist/decorators/index.js.map +1 -0
  45. package/dist/example-plugin/module.d.ts.map +1 -0
  46. package/{src → dist}/example-plugin/module.js +9 -9
  47. package/dist/example-plugin/module.js.map +1 -0
  48. package/dist/example-plugin/services.d.ts.map +1 -0
  49. package/{src → dist}/example-plugin/services.js +2 -1
  50. package/dist/example-plugin/services.js.map +1 -0
  51. package/dist/example-plugin/tokens.d.ts.map +1 -0
  52. package/dist/example-plugin/tokens.js.map +1 -0
  53. package/dist/index.d.ts +5 -0
  54. package/dist/index.d.ts.map +1 -0
  55. package/{src/index.ts → dist/index.js} +1 -0
  56. package/dist/index.js.map +1 -0
  57. package/dist/lib/functions/index.d.ts +3 -0
  58. package/dist/lib/functions/index.d.ts.map +1 -0
  59. package/{src/lib/functions/index.ts → dist/lib/functions/index.js} +1 -0
  60. package/dist/lib/functions/index.js.map +1 -0
  61. package/dist/lib/functions/registerHookHandler.d.ts +2 -0
  62. package/dist/lib/functions/registerHookHandler.d.ts.map +1 -0
  63. package/dist/lib/functions/registerHookHandler.js +7 -0
  64. package/dist/lib/functions/registerHookHandler.js.map +1 -0
  65. package/dist/lib/functions/registerHooksForInstance.d.ts +2 -0
  66. package/dist/lib/functions/registerHooksForInstance.d.ts.map +1 -0
  67. package/dist/lib/functions/registerHooksForInstance.js +22 -0
  68. package/dist/lib/functions/registerHooksForInstance.js.map +1 -0
  69. package/dist/lib/test-related/plugins/plugin-a/module.d.ts +21 -0
  70. package/dist/lib/test-related/plugins/plugin-a/module.d.ts.map +1 -0
  71. package/dist/lib/test-related/plugins/plugin-a/module.js +84 -0
  72. package/dist/lib/test-related/plugins/plugin-a/module.js.map +1 -0
  73. package/dist/lib/test-related/plugins/plugin-b/module.d.ts +21 -0
  74. package/dist/lib/test-related/plugins/plugin-b/module.d.ts.map +1 -0
  75. package/dist/lib/test-related/plugins/plugin-b/module.js +84 -0
  76. package/dist/lib/test-related/plugins/plugin-b/module.js.map +1 -0
  77. package/dist/lib/types/HookMetadata.d.ts +5 -0
  78. package/dist/lib/types/HookMetadata.d.ts.map +1 -0
  79. package/dist/lib/types/HookMetadata.js +1 -0
  80. package/dist/lib/types/HookMetadata.js.map +1 -0
  81. package/dist/lib/types/IHookRegistry.d.ts +10 -0
  82. package/dist/lib/types/IHookRegistry.d.ts.map +1 -0
  83. package/dist/lib/types/IHookRegistry.js +1 -0
  84. package/dist/lib/types/IHookRegistry.js.map +1 -0
  85. package/dist/lib/types/IPluginManager.d.ts +8 -0
  86. package/dist/lib/types/IPluginManager.d.ts.map +1 -0
  87. package/dist/lib/types/IPluginManager.js +1 -0
  88. package/dist/lib/types/IPluginManager.js.map +1 -0
  89. package/{src/lib/types/LoadedPlugin.ts → dist/lib/types/LoadedPlugin.d.ts} +4 -4
  90. package/dist/lib/types/LoadedPlugin.d.ts.map +1 -0
  91. package/dist/lib/types/LoadedPlugin.js +1 -0
  92. package/dist/lib/types/LoadedPlugin.js.map +1 -0
  93. package/dist/lib/types/PluginMetadata.d.ts +7 -0
  94. package/dist/lib/types/PluginMetadata.d.ts.map +1 -0
  95. package/dist/lib/types/PluginMetadata.js +1 -0
  96. package/dist/lib/types/PluginMetadata.js.map +1 -0
  97. package/dist/lib/types/index.d.ts +7 -0
  98. package/dist/lib/types/index.d.ts.map +1 -0
  99. package/{src/lib/types/index.ts → dist/lib/types/index.js} +1 -0
  100. package/dist/lib/types/index.js.map +1 -0
  101. package/dist/lib/types/tokens.d.ts +5 -0
  102. package/dist/lib/types/tokens.d.ts.map +1 -0
  103. package/{src/lib/types/tokens.ts → dist/lib/types/tokens.js} +1 -0
  104. package/dist/lib/types/tokens.js.map +1 -0
  105. package/package.json +1 -1
  106. package/src/core/HookRegistry.spec.ts +0 -75
  107. package/src/core/HookRegistry.ts +0 -78
  108. package/src/core/Plugin.ts +0 -26
  109. package/src/core/PluginLoader.spec.ts +0 -138
  110. package/src/core/PluginLoader.ts +0 -192
  111. package/src/core/PluginManager.spec.ts +0 -57
  112. package/src/core/PluginManager.ts +0 -47
  113. package/src/decorators/hookDecorator.ts +0 -30
  114. package/src/example-plugin/module.d.ts.map +0 -1
  115. package/src/example-plugin/module.js.map +0 -1
  116. package/src/example-plugin/module.ts +0 -80
  117. package/src/example-plugin/services.d.ts.map +0 -1
  118. package/src/example-plugin/services.js.map +0 -1
  119. package/src/example-plugin/services.ts +0 -22
  120. package/src/example-plugin/tokens.d.ts.map +0 -1
  121. package/src/example-plugin/tokens.js.map +0 -1
  122. package/src/example-plugin/tokens.ts +0 -1
  123. package/src/lib/functions/registerHookHandler.ts +0 -11
  124. package/src/lib/functions/registerHooksForInstance.ts +0 -26
  125. package/src/lib/test-related/plugins/plugin-a/module.ts +0 -74
  126. package/src/lib/test-related/plugins/plugin-b/module.ts +0 -74
  127. package/src/lib/types/HookMetadata.ts +0 -4
  128. package/src/lib/types/IHookRegistry.ts +0 -10
  129. package/src/lib/types/IPluginManager.ts +0 -8
  130. package/src/lib/types/PluginMetadata.ts +0 -9
  131. package/src/lib/types/README.md +0 -3
  132. /package/{src → dist}/example-plugin/module.d.ts +0 -0
  133. /package/{src → dist}/example-plugin/services.d.ts +0 -0
  134. /package/{src → dist}/example-plugin/tokens.d.ts +0 -0
  135. /package/{src → dist}/example-plugin/tokens.js +0 -0
@@ -0,0 +1,3 @@
1
+ import { Module } from '@kanian77/simple-di';
2
+ export declare const ChouxModule: Module;
3
+ //# sourceMappingURL=ChouxModule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChouxModule.d.ts","sourceRoot":"","sources":["../../src/core/ChouxModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAK7C,eAAO,MAAM,WAAW,QAWtB,CAAC"}
@@ -2,16 +2,16 @@ import { Module } from '@kanian77/simple-di';
2
2
  import { HookRegistry } from './HookRegistry';
3
3
  import { PluginManager } from './PluginManager';
4
4
  import { HOOK_REGISTRY_TOKEN, PLUGIN_MANAGER_TOKEN } from '../lib/types/tokens';
5
-
6
5
  export const ChouxModule = new Module({
7
- providers: [
8
- {
9
- provide: HOOK_REGISTRY_TOKEN,
10
- useClass: HookRegistry,
11
- },
12
- {
13
- provide: PLUGIN_MANAGER_TOKEN,
14
- useClass: PluginManager,
15
- },
16
- ],
6
+ providers: [
7
+ {
8
+ provide: HOOK_REGISTRY_TOKEN,
9
+ useClass: HookRegistry,
10
+ },
11
+ {
12
+ provide: PLUGIN_MANAGER_TOKEN,
13
+ useClass: PluginManager,
14
+ },
15
+ ],
17
16
  });
17
+ //# sourceMappingURL=ChouxModule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChouxModule.js","sourceRoot":"","sources":["../../src/core/ChouxModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAEhF,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,MAAM,CAAC;IACpC,SAAS,EAAE;QACT;YACE,OAAO,EAAE,mBAAmB;YAC5B,QAAQ,EAAE,YAAY;SACvB;QACD;YACE,OAAO,EAAE,oBAAoB;YAC7B,QAAQ,EAAE,aAAa;SACxB;KACF;CACF,CAAC,CAAC"}
@@ -0,0 +1,47 @@
1
+ import type { HookFn, IHookRegistry } from '../lib/types';
2
+ /**
3
+ * Registry for application hooks.
4
+ */
5
+ export declare class HookRegistry implements IHookRegistry {
6
+ private hooks;
7
+ /**
8
+ * Register a hook handler.
9
+ * @param name The name of the hook to register the function for.
10
+ * @param fn The hook handler function.
11
+ */
12
+ register(name: string, fn: HookFn): void;
13
+ /**
14
+ * Trigger a hook, calling all registered handlers.
15
+ * @param name The name of the hook to trigger.
16
+ * @param args Arguments to pass to the hook handlers.
17
+ */
18
+ trigger(name: string, ...args: any[]): Promise<void>;
19
+ /**
20
+ * Unregister a hook handler.
21
+ * If no function is provided, all handlers for the hook will be removed.
22
+ * @param name The name of the hook to unregister from.
23
+ * @param fn Optional specific handler to remove.
24
+ */
25
+ unregister(name: string, fn?: HookFn): void;
26
+ /**
27
+ * Get all registered handlers for a specific hook.
28
+ * @param name The name of the hook to retrieve handlers for.
29
+ * @returns An array of hook handler functions.
30
+ */
31
+ getHooks(name: string): HookFn[];
32
+ /**
33
+ * Clear all registered hooks.
34
+ */
35
+ clear(): void;
36
+ }
37
+ export declare const LIFECYCLE_HOOKS: {
38
+ readonly BEFORE_PLUGIN_LOAD: "core:before-plugin-load";
39
+ readonly AFTER_PLUGIN_LOAD: "core:after-plugin-load";
40
+ readonly BEFORE_PLUGIN_INIT: "core:before-plugin-init";
41
+ readonly AFTER_PLUGIN_INIT: "core:after-plugin-init";
42
+ readonly BEFORE_PLUGIN_DESTROY: "core:before-plugin-destroy";
43
+ readonly AFTER_PLUGIN_DESTROY: "core:after-plugin-destroy";
44
+ readonly APPLICATION_START: "core:application-start";
45
+ readonly APPLICATION_SHUTDOWN: "core:application-shutdown";
46
+ };
47
+ //# sourceMappingURL=HookRegistry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HookRegistry.d.ts","sourceRoot":"","sources":["../../src/core/HookRegistry.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAW,aAAa,EAAE,MAAM,cAAc,CAAC;AAEnE;;GAEG;AACH,qBACa,YAAa,YAAW,aAAa;IAChD,OAAO,CAAC,KAAK,CAAsB;IAEnC;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI;IAKxC;;;;OAIG;IACG,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAO1D;;;;;OAKG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI;IAW3C;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE;IAIhC;;OAEG;IACH,KAAK,IAAI,IAAI;CAGd;AAGD,eAAO,MAAM,eAAe;;;;;;;;;CASlB,CAAC"}
@@ -0,0 +1,79 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { Service } from '@kanian77/simple-di';
8
+ import { HOOK_REGISTRY_TOKEN } from '../lib/types/tokens';
9
+ /**
10
+ * Registry for application hooks.
11
+ */
12
+ let HookRegistry = class HookRegistry {
13
+ hooks = new Map();
14
+ /**
15
+ * Register a hook handler.
16
+ * @param name The name of the hook to register the function for.
17
+ * @param fn The hook handler function.
18
+ */
19
+ register(name, fn) {
20
+ const list = this.hooks.get(name) || [];
21
+ this.hooks.set(name, [...list, fn]);
22
+ }
23
+ /**
24
+ * Trigger a hook, calling all registered handlers.
25
+ * @param name The name of the hook to trigger.
26
+ * @param args Arguments to pass to the hook handlers.
27
+ */
28
+ async trigger(name, ...args) {
29
+ const fns = this.hooks.get(name) || [];
30
+ for (const fn of fns) {
31
+ await fn(...args);
32
+ }
33
+ }
34
+ /**
35
+ * Unregister a hook handler.
36
+ * If no function is provided, all handlers for the hook will be removed.
37
+ * @param name The name of the hook to unregister from.
38
+ * @param fn Optional specific handler to remove.
39
+ */
40
+ unregister(name, fn) {
41
+ if (!fn) {
42
+ this.hooks.delete(name);
43
+ return;
44
+ }
45
+ const list = this.hooks.get(name) || [];
46
+ const filtered = list.filter((registeredFn) => registeredFn !== fn);
47
+ this.hooks.set(name, filtered);
48
+ }
49
+ /**
50
+ * Get all registered handlers for a specific hook.
51
+ * @param name The name of the hook to retrieve handlers for.
52
+ * @returns An array of hook handler functions.
53
+ */
54
+ getHooks(name) {
55
+ return this.hooks.get(name) || [];
56
+ }
57
+ /**
58
+ * Clear all registered hooks.
59
+ */
60
+ clear() {
61
+ this.hooks.clear();
62
+ }
63
+ };
64
+ HookRegistry = __decorate([
65
+ Service({ token: HOOK_REGISTRY_TOKEN })
66
+ ], HookRegistry);
67
+ export { HookRegistry };
68
+ // Standard lifecycle hooks
69
+ export const LIFECYCLE_HOOKS = {
70
+ BEFORE_PLUGIN_LOAD: 'core:before-plugin-load',
71
+ AFTER_PLUGIN_LOAD: 'core:after-plugin-load',
72
+ BEFORE_PLUGIN_INIT: 'core:before-plugin-init',
73
+ AFTER_PLUGIN_INIT: 'core:after-plugin-init',
74
+ BEFORE_PLUGIN_DESTROY: 'core:before-plugin-destroy',
75
+ AFTER_PLUGIN_DESTROY: 'core:after-plugin-destroy',
76
+ APPLICATION_START: 'core:application-start',
77
+ APPLICATION_SHUTDOWN: 'core:application-shutdown',
78
+ };
79
+ //# sourceMappingURL=HookRegistry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HookRegistry.js","sourceRoot":"","sources":["../../src/core/HookRegistry.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAG1D;;GAEG;AAEI,IAAM,YAAY,GAAlB,MAAM,YAAY;IACf,KAAK,GAAY,IAAI,GAAG,EAAE,CAAC;IAEnC;;;;OAIG;IACH,QAAQ,CAAC,IAAY,EAAE,EAAU;QAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QACxC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;IACtC,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,OAAO,CAAC,IAAY,EAAE,GAAG,IAAW;QACxC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QACvC,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;YACrB,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACH,UAAU,CAAC,IAAY,EAAE,EAAW;QAClC,IAAI,CAAC,EAAE,EAAE,CAAC;YACR,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACxB,OAAO;QACT,CAAC;QAED,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QACxC,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,YAAY,KAAK,EAAE,CAAC,CAAC;QACpE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACjC,CAAC;IAED;;;;OAIG;IACH,QAAQ,CAAC,IAAY;QACnB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IACpC,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;CACF,CAAA;AAzDY,YAAY;IADxB,OAAO,CAAC,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC;GAC3B,YAAY,CAyDxB;;AAED,2BAA2B;AAC3B,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,kBAAkB,EAAE,yBAAyB;IAC7C,iBAAiB,EAAE,wBAAwB;IAC3C,kBAAkB,EAAE,yBAAyB;IAC7C,iBAAiB,EAAE,wBAAwB;IAC3C,qBAAqB,EAAE,4BAA4B;IACnD,oBAAoB,EAAE,2BAA2B;IACjD,iBAAiB,EAAE,wBAAwB;IAC3C,oBAAoB,EAAE,2BAA2B;CACzC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=HookRegistry.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HookRegistry.spec.d.ts","sourceRoot":"","sources":["../../src/core/HookRegistry.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,67 @@
1
+ import { describe, it, expect, beforeEach } from 'bun:test';
2
+ import { HookRegistry } from './HookRegistry';
3
+ describe('HookRegistry', () => {
4
+ let registry;
5
+ beforeEach(() => {
6
+ registry = new HookRegistry();
7
+ });
8
+ it('registers and triggers a hook', async () => {
9
+ let called = false;
10
+ const fn = () => {
11
+ called = true;
12
+ };
13
+ registry.register('test:hook', fn);
14
+ await registry.trigger('test:hook');
15
+ expect(called).toBe(true);
16
+ });
17
+ it('unregisters a specific hook handler', async () => {
18
+ let called = false;
19
+ const fn = () => {
20
+ called = true;
21
+ };
22
+ registry.register('test:hook', fn);
23
+ registry.unregister('test:hook', fn);
24
+ await registry.trigger('test:hook');
25
+ expect(called).toBe(false);
26
+ });
27
+ it('unregisters all handlers for a hook', async () => {
28
+ let called = false;
29
+ const fn = () => {
30
+ called = true;
31
+ };
32
+ registry.register('test:hook', fn);
33
+ registry.unregister('test:hook');
34
+ await registry.trigger('test:hook');
35
+ expect(called).toBe(false);
36
+ });
37
+ it('getHooks returns all registered handlers', () => {
38
+ const fn1 = () => { };
39
+ const fn2 = () => { };
40
+ registry.register('test:hook', fn1);
41
+ registry.register('test:hook', fn2);
42
+ const hooks = registry.getHooks('test:hook');
43
+ expect(hooks.length).toBe(2);
44
+ expect(hooks).toContain(fn1);
45
+ expect(hooks).toContain(fn2);
46
+ });
47
+ it('clear removes all hooks', () => {
48
+ const fn = () => { };
49
+ registry.register('test:hook', fn);
50
+ registry.clear();
51
+ expect(registry.getHooks('test:hook').length).toBe(0);
52
+ });
53
+ it('trigger calls all registered async handlers', async () => {
54
+ let result = '';
55
+ const fn1 = async () => {
56
+ result += 'a';
57
+ };
58
+ const fn2 = async () => {
59
+ result += 'b';
60
+ };
61
+ registry.register('test:hook', fn1);
62
+ registry.register('test:hook', fn2);
63
+ await registry.trigger('test:hook');
64
+ expect(result).toBe('ab');
65
+ });
66
+ });
67
+ //# sourceMappingURL=HookRegistry.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HookRegistry.spec.js","sourceRoot":"","sources":["../../src/core/HookRegistry.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { Module } from '@kanian77/simple-di';
2
+ import type { PluginMetadata } from '../lib/types';
3
+ export declare abstract class Plugin extends Module {
4
+ abstract readonly metadata: PluginMetadata;
5
+ /**
6
+ * Called when plugin is loaded but before dependencies are resolved
7
+ */
8
+ onLoad?(): Promise<void>;
9
+ /**
10
+ * Called after all dependencies are resolved and injected
11
+ */
12
+ onInit?(): Promise<void>;
13
+ /**
14
+ * Called when plugin is being unloaded
15
+ */
16
+ onDestroy?(): Promise<void>;
17
+ /**
18
+ * Register custom hooks specific to this plugin
19
+ */
20
+ protected registerCustomHooks?(): void;
21
+ }
22
+ //# sourceMappingURL=Plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Plugin.d.ts","sourceRoot":"","sources":["../../src/core/Plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,8BAAsB,MAAO,SAAQ,MAAM;IACzC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAE3C;;OAEG;IACG,MAAM,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC;IAE9B;;OAEG;IACG,MAAM,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC;IAE9B;;OAEG;IACG,SAAS,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC;IAEjC;;OAEG;IACH,SAAS,CAAC,mBAAmB,CAAC,IAAI,IAAI;CACvC"}
@@ -0,0 +1,4 @@
1
+ import { Module } from '@kanian77/simple-di';
2
+ export class Plugin extends Module {
3
+ }
4
+ //# sourceMappingURL=Plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Plugin.js","sourceRoot":"","sources":["../../src/core/Plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAG7C,MAAM,OAAgB,MAAO,SAAQ,MAAM;CAsB1C"}
@@ -0,0 +1,13 @@
1
+ import type { LoadedPlugin } from '../lib/types';
2
+ export declare class PluginLoader {
3
+ private loadedPlugins;
4
+ findPlugins(pluginsDir: string): Promise<string[]>;
5
+ loadPlugin(pluginPath: string): Promise<LoadedPlugin>;
6
+ loadPlugins(pluginsDir: string): Promise<LoadedPlugin[]>;
7
+ private resolveDependencyOrder;
8
+ private initializePlugin;
9
+ unloadPlugin(pluginName: string): Promise<void>;
10
+ getPlugin(name: string): LoadedPlugin | undefined;
11
+ getAllPlugins(): LoadedPlugin[];
12
+ }
13
+ //# sourceMappingURL=PluginLoader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PluginLoader.d.ts","sourceRoot":"","sources":["../../src/core/PluginLoader.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAkB,YAAY,EAAiB,MAAM,cAAc,CAAC;AAQhF,qBAAa,YAAY;IACvB,OAAO,CAAC,aAAa,CAAwC;IAEvD,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IA0BlD,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IA8CrD,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAwB9D,OAAO,CAAC,sBAAsB;YA4ChB,gBAAgB;IAYxB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBrD,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAIjD,aAAa,IAAI,YAAY,EAAE;CAGhC"}
@@ -0,0 +1,147 @@
1
+ import { inject } from '@kanian77/simple-di';
2
+ import { LIFECYCLE_HOOKS } from './HookRegistry';
3
+ import { HOOK_REGISTRY_TOKEN } from '../lib/types/tokens';
4
+ import * as fs from 'fs/promises';
5
+ import * as path from 'path';
6
+ // LoadedPlugin now imported from types
7
+ export class PluginLoader {
8
+ loadedPlugins = new Map();
9
+ async findPlugins(pluginsDir) {
10
+ try {
11
+ const entries = await fs.readdir(pluginsDir, { withFileTypes: true });
12
+ const pluginDirs = [];
13
+ for (const entry of entries) {
14
+ if (entry.isDirectory()) {
15
+ const pluginPath = path.join(pluginsDir, entry.name);
16
+ const modulePath = path.join(pluginPath, 'module.ts');
17
+ try {
18
+ await fs.access(modulePath);
19
+ pluginDirs.push(pluginPath);
20
+ }
21
+ catch {
22
+ // Skip directories without module.ts
23
+ }
24
+ }
25
+ }
26
+ return pluginDirs;
27
+ }
28
+ catch (error) {
29
+ console.warn(`Could not read plugins directory: ${pluginsDir}`, error);
30
+ return [];
31
+ }
32
+ }
33
+ async loadPlugin(pluginPath) {
34
+ const modulePath = path.join(pluginPath, 'module.ts');
35
+ console.log(`Loading plugin from ${modulePath}`);
36
+ const hookRegistry = inject(HOOK_REGISTRY_TOKEN);
37
+ await hookRegistry.trigger(LIFECYCLE_HOOKS.BEFORE_PLUGIN_LOAD, pluginPath);
38
+ // Dynamic import of the plugin module
39
+ const module = await import(modulePath);
40
+ const PluginClass = module.default;
41
+ if (!PluginClass || !PluginClass.prototype) {
42
+ throw new Error(`Invalid plugin at ${pluginPath}: must export a Plugin class as default`);
43
+ }
44
+ // Create plugin instance (which is also a Module)
45
+ const plugin = new PluginClass();
46
+ if (!plugin.metadata) {
47
+ throw new Error(`Plugin at ${pluginPath} must have metadata property`);
48
+ }
49
+ // Call plugin's onLoad lifecycle method
50
+ if (plugin.onLoad) {
51
+ await plugin.onLoad();
52
+ }
53
+ // Register custom hooks if defined
54
+ if (plugin.registerCustomHooks) {
55
+ plugin.registerCustomHooks();
56
+ }
57
+ const loadedPlugin = {
58
+ plugin, // plugin is already a Module
59
+ metadata: plugin.metadata,
60
+ pluginClass: PluginClass,
61
+ };
62
+ this.loadedPlugins.set(plugin.metadata.name, loadedPlugin);
63
+ await hookRegistry.trigger(LIFECYCLE_HOOKS.AFTER_PLUGIN_LOAD, loadedPlugin);
64
+ return loadedPlugin;
65
+ }
66
+ async loadPlugins(pluginsDir) {
67
+ const pluginDirs = await this.findPlugins(pluginsDir);
68
+ const loadedPlugins = [];
69
+ // Load all plugins first
70
+ for (const dir of pluginDirs) {
71
+ try {
72
+ const loaded = await this.loadPlugin(dir);
73
+ loadedPlugins.push(loaded);
74
+ }
75
+ catch (error) {
76
+ console.error(`Failed to load plugin from ${dir}:`, error);
77
+ }
78
+ }
79
+ // Resolve dependencies and initialize
80
+ const sortedPlugins = this.resolveDependencyOrder(loadedPlugins);
81
+ for (const loaded of sortedPlugins) {
82
+ await this.initializePlugin(loaded);
83
+ }
84
+ return sortedPlugins;
85
+ }
86
+ resolveDependencyOrder(plugins) {
87
+ const sorted = [];
88
+ const visited = new Set();
89
+ const visiting = new Set();
90
+ const visit = (plugin) => {
91
+ const name = plugin.metadata.name;
92
+ if (visiting.has(name)) {
93
+ throw new Error(`Circular dependency detected involving plugin: ${name}`);
94
+ }
95
+ if (visited.has(name)) {
96
+ return;
97
+ }
98
+ visiting.add(name);
99
+ // Visit dependencies first
100
+ const dependencies = plugin.metadata.dependencies || [];
101
+ for (const depName of dependencies) {
102
+ const dep = plugins.find((p) => p.metadata.name === depName);
103
+ if (!dep) {
104
+ throw new Error(`Plugin ${name} depends on ${depName}, but it's not loaded`);
105
+ }
106
+ visit(dep);
107
+ }
108
+ visiting.delete(name);
109
+ visited.add(name);
110
+ sorted.push(plugin);
111
+ };
112
+ for (const plugin of plugins) {
113
+ visit(plugin);
114
+ }
115
+ return sorted;
116
+ }
117
+ async initializePlugin(loaded) {
118
+ const hookRegistry = inject(HOOK_REGISTRY_TOKEN);
119
+ await hookRegistry.trigger(LIFECYCLE_HOOKS.BEFORE_PLUGIN_INIT, loaded);
120
+ // Call plugin's onInit lifecycle method
121
+ if (loaded.plugin.onInit) {
122
+ await loaded.plugin.onInit();
123
+ }
124
+ await hookRegistry.trigger(LIFECYCLE_HOOKS.AFTER_PLUGIN_INIT, loaded);
125
+ }
126
+ async unloadPlugin(pluginName) {
127
+ const loaded = this.loadedPlugins.get(pluginName);
128
+ if (!loaded) {
129
+ throw new Error(`Plugin ${pluginName} is not loaded`);
130
+ }
131
+ const hookRegistry = inject(HOOK_REGISTRY_TOKEN);
132
+ await hookRegistry.trigger(LIFECYCLE_HOOKS.BEFORE_PLUGIN_DESTROY, loaded);
133
+ // Call plugin's onDestroy lifecycle method
134
+ if (loaded.plugin.onDestroy) {
135
+ await loaded.plugin.onDestroy();
136
+ }
137
+ this.loadedPlugins.delete(pluginName);
138
+ await hookRegistry.trigger(LIFECYCLE_HOOKS.AFTER_PLUGIN_DESTROY, loaded);
139
+ }
140
+ getPlugin(name) {
141
+ return this.loadedPlugins.get(name);
142
+ }
143
+ getAllPlugins() {
144
+ return Array.from(this.loadedPlugins.values());
145
+ }
146
+ }
147
+ //# sourceMappingURL=PluginLoader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PluginLoader.js","sourceRoot":"","sources":["../../src/core/PluginLoader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=PluginLoader.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PluginLoader.spec.d.ts","sourceRoot":"","sources":["../../src/core/PluginLoader.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,114 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'bun:test';
2
+ import { PluginLoader } from './PluginLoader';
3
+ import { Plugin } from './Plugin';
4
+ import { tmpdir } from 'os';
5
+ import { mkdtempSync } from 'fs';
6
+ // Minimal IHookRegistry mock
7
+ const mockHookRegistry = {
8
+ trigger: async () => { },
9
+ register: () => { },
10
+ unregister: () => { },
11
+ getHooks: () => [],
12
+ clear: () => { },
13
+ };
14
+ // // Patch inject to return our mock
15
+ // import * as simpleDi from "@kanian77/simple-di";
16
+ // (simpleDi as any).inject = () => mockHookRegistry;
17
+ // Patch fs.promises for plugin discovery
18
+ import { mkdir, writeFile, rm } from 'fs/promises';
19
+ import { join, resolve } from 'path';
20
+ // Helper for plugin mocks
21
+ class PluginMock extends Plugin {
22
+ metadata = { name: 'pluginA', version: '1.0.0' };
23
+ imports = [];
24
+ providers = [];
25
+ async onLoad() { }
26
+ async onInit() { }
27
+ async onDestroy() { }
28
+ registerCustomHooks() { }
29
+ }
30
+ class PluginMockA extends Plugin {
31
+ metadata = { name: 'A', version: '1.0.0', dependencies: ['B'] };
32
+ imports = [];
33
+ providers = [];
34
+ }
35
+ class PluginMockB extends Plugin {
36
+ metadata = { name: 'B', version: '1.0.0', dependencies: ['A'] };
37
+ imports = [];
38
+ providers = [];
39
+ }
40
+ describe('PluginLoader', () => {
41
+ let loader;
42
+ let tempRoot;
43
+ beforeEach(async () => {
44
+ loader = new PluginLoader();
45
+ tempRoot = mkdtempSync(join(tmpdir(), 'plugin-test-'));
46
+ // Create pluginA with module.ts
47
+ const pluginADir = join(tempRoot, 'pluginA');
48
+ await mkdir(pluginADir);
49
+ await writeFile(join(pluginADir, 'module.ts'), '// pluginA module');
50
+ // Create pluginB without module.ts
51
+ const pluginBDir = join(tempRoot, 'pluginB');
52
+ await mkdir(pluginBDir);
53
+ // Create a non-directory file
54
+ await writeFile(join(tempRoot, 'file.txt'), 'not a directory');
55
+ });
56
+ afterEach(async () => {
57
+ await rm(tempRoot, { recursive: true, force: true });
58
+ });
59
+ it('findPlugins returns only directories with module.ts', async () => {
60
+ const plugins = await loader.findPlugins('./src/lib/test-related/plugins');
61
+ expect(plugins).toEqual([
62
+ 'src/lib/test-related/plugins/plugin-a',
63
+ 'src/lib/test-related/plugins/plugin-b',
64
+ ]);
65
+ });
66
+ it('loadPlugin throws if no default export', async () => {
67
+ const origJoin = require('path').join;
68
+ require('path').join = (...args) => args.join('/');
69
+ const origImport = global.import;
70
+ global.import = async (_) => ({});
71
+ await expect(loader.loadPlugin('./src/lib/test-related/plugins/plugin-a')).rejects.toThrow();
72
+ global.import = origImport;
73
+ require('path').join = origJoin;
74
+ });
75
+ it('loadPlugin loads valid plugin and triggers hooks', async () => {
76
+ const modulePath = resolve('src/lib/test-related/plugins/plugin-a');
77
+ const loaded = await loader.loadPlugin(modulePath);
78
+ expect(loaded.plugin).toBeInstanceOf(await import(join(modulePath, 'module.ts')).then((m) => m.default));
79
+ expect(loaded.metadata.name).toBe('pluginA');
80
+ });
81
+ it('unloadPlugin triggers destroy hooks and removes plugin', async () => {
82
+ const loaded = {
83
+ plugin: new PluginMock({}),
84
+ metadata: { name: 'pluginA', version: '1.0.0' },
85
+ pluginClass: PluginMock,
86
+ };
87
+ loader['loadedPlugins'].set('pluginA', loaded);
88
+ await loader.unloadPlugin('pluginA');
89
+ expect(loader.getPlugin('pluginA')).toBeUndefined();
90
+ });
91
+ it('getAllPlugins returns all loaded plugins', () => {
92
+ const loaded = {
93
+ plugin: new PluginMock({}),
94
+ metadata: { name: 'pluginA', version: '1.0.0' },
95
+ pluginClass: PluginMock,
96
+ };
97
+ loader['loadedPlugins'].set('pluginA', loaded);
98
+ expect(loader.getAllPlugins()).toEqual([loaded]);
99
+ });
100
+ it('resolveDependencyOrder throws on circular dependency', () => {
101
+ const pluginA = {
102
+ plugin: new PluginMockA({}),
103
+ metadata: { name: 'A', version: '1.0.0', dependencies: ['B'] },
104
+ pluginClass: PluginMockA,
105
+ };
106
+ const pluginB = {
107
+ plugin: new PluginMockB({}),
108
+ metadata: { name: 'B', version: '1.0.0', dependencies: ['A'] },
109
+ pluginClass: PluginMockB,
110
+ };
111
+ expect(() => loader['resolveDependencyOrder']([pluginA, pluginB])).toThrow();
112
+ });
113
+ });
114
+ //# sourceMappingURL=PluginLoader.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PluginLoader.spec.js","sourceRoot":"","sources":["../../src/core/PluginLoader.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { IHookRegistry, IPluginManager, LoadedPlugin } from '../lib/types';
2
+ import { PluginLoader } from './PluginLoader';
3
+ export declare class PluginManager implements IPluginManager {
4
+ private hookRegistry;
5
+ private loader;
6
+ constructor(hookRegistry: IHookRegistry, loader: PluginLoader);
7
+ initialize(pluginsDir: string): Promise<void>;
8
+ shutdown(): Promise<void>;
9
+ getPlugin(name: string): LoadedPlugin | undefined;
10
+ getAllPlugins(): LoadedPlugin[];
11
+ }
12
+ //# sourceMappingURL=PluginManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PluginManager.d.ts","sourceRoot":"","sources":["../../src/core/PluginManager.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAChF,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAI9C,qBACa,aAAc,YAAW,cAAc;IAEnB,OAAO,CAAC,YAAY;IAC3B,OAAO,CAAC,MAAM;gBADC,YAAY,EAAE,aAAa,EAClC,MAAM,EAAE,YAAY;IAG9C,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAW7C,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAa/B,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAIjD,aAAa,IAAI,YAAY,EAAE;CAGhC"}