@orchestr-sh/orchestr 1.6.1 → 1.7.1

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 (159) hide show
  1. package/README.md +289 -22
  2. package/dist/Console/Commands/EventCacheCommand.d.ts +15 -0
  3. package/dist/Console/Commands/EventCacheCommand.d.ts.map +1 -0
  4. package/dist/Console/Commands/EventCacheCommand.js +99 -0
  5. package/dist/Console/Commands/EventCacheCommand.js.map +1 -0
  6. package/dist/Console/Commands/EventClearCommand.d.ts +15 -0
  7. package/dist/Console/Commands/EventClearCommand.d.ts.map +1 -0
  8. package/dist/Console/Commands/EventClearCommand.js +71 -0
  9. package/dist/Console/Commands/EventClearCommand.js.map +1 -0
  10. package/dist/Console/Commands/EventListCommand.d.ts +19 -0
  11. package/dist/Console/Commands/EventListCommand.d.ts.map +1 -0
  12. package/dist/Console/Commands/EventListCommand.js +106 -0
  13. package/dist/Console/Commands/EventListCommand.js.map +1 -0
  14. package/dist/Console/Commands/MakeEventCommand.d.ts +27 -0
  15. package/dist/Console/Commands/MakeEventCommand.d.ts.map +1 -0
  16. package/dist/Console/Commands/MakeEventCommand.js +117 -0
  17. package/dist/Console/Commands/MakeEventCommand.js.map +1 -0
  18. package/dist/Console/Commands/MakeListenerCommand.d.ts +27 -0
  19. package/dist/Console/Commands/MakeListenerCommand.d.ts.map +1 -0
  20. package/dist/Console/Commands/MakeListenerCommand.js +157 -0
  21. package/dist/Console/Commands/MakeListenerCommand.js.map +1 -0
  22. package/dist/Console/orchestr.js +10 -0
  23. package/dist/Console/orchestr.js.map +1 -1
  24. package/dist/Contracts/Events/Dispatcher.d.ts +94 -0
  25. package/dist/Contracts/Events/Dispatcher.d.ts.map +1 -0
  26. package/dist/Contracts/Events/Dispatcher.js +9 -0
  27. package/dist/Contracts/Events/Dispatcher.js.map +1 -0
  28. package/dist/Contracts/Events/index.d.ts +7 -0
  29. package/dist/Contracts/Events/index.d.ts.map +1 -0
  30. package/dist/Contracts/Events/index.js +8 -0
  31. package/dist/Contracts/Events/index.js.map +1 -0
  32. package/dist/Database/Ensemble/Ensemble.d.ts +7 -0
  33. package/dist/Database/Ensemble/Ensemble.d.ts.map +1 -1
  34. package/dist/Database/Ensemble/Ensemble.js +119 -0
  35. package/dist/Database/Ensemble/Ensemble.js.map +1 -1
  36. package/dist/Database/Ensemble/EnsembleBuilder.d.ts.map +1 -1
  37. package/dist/Database/Ensemble/EnsembleBuilder.js +7 -0
  38. package/dist/Database/Ensemble/EnsembleBuilder.js.map +1 -1
  39. package/dist/Database/Ensemble/Events/ModelCreated.d.ts +11 -0
  40. package/dist/Database/Ensemble/Events/ModelCreated.d.ts.map +1 -0
  41. package/dist/Database/Ensemble/Events/ModelCreated.js +16 -0
  42. package/dist/Database/Ensemble/Events/ModelCreated.js.map +1 -0
  43. package/dist/Database/Ensemble/Events/ModelCreating.d.ts +12 -0
  44. package/dist/Database/Ensemble/Events/ModelCreating.d.ts.map +1 -0
  45. package/dist/Database/Ensemble/Events/ModelCreating.js +17 -0
  46. package/dist/Database/Ensemble/Events/ModelCreating.js.map +1 -0
  47. package/dist/Database/Ensemble/Events/ModelDeleted.d.ts +11 -0
  48. package/dist/Database/Ensemble/Events/ModelDeleted.d.ts.map +1 -0
  49. package/dist/Database/Ensemble/Events/ModelDeleted.js +16 -0
  50. package/dist/Database/Ensemble/Events/ModelDeleted.js.map +1 -0
  51. package/dist/Database/Ensemble/Events/ModelDeleting.d.ts +12 -0
  52. package/dist/Database/Ensemble/Events/ModelDeleting.d.ts.map +1 -0
  53. package/dist/Database/Ensemble/Events/ModelDeleting.js +17 -0
  54. package/dist/Database/Ensemble/Events/ModelDeleting.js.map +1 -0
  55. package/dist/Database/Ensemble/Events/ModelEvent.d.ts +17 -0
  56. package/dist/Database/Ensemble/Events/ModelEvent.d.ts.map +1 -0
  57. package/dist/Database/Ensemble/Events/ModelEvent.js +22 -0
  58. package/dist/Database/Ensemble/Events/ModelEvent.js.map +1 -0
  59. package/dist/Database/Ensemble/Events/ModelRetrieved.d.ts +11 -0
  60. package/dist/Database/Ensemble/Events/ModelRetrieved.d.ts.map +1 -0
  61. package/dist/Database/Ensemble/Events/ModelRetrieved.js +16 -0
  62. package/dist/Database/Ensemble/Events/ModelRetrieved.js.map +1 -0
  63. package/dist/Database/Ensemble/Events/ModelSaved.d.ts +11 -0
  64. package/dist/Database/Ensemble/Events/ModelSaved.d.ts.map +1 -0
  65. package/dist/Database/Ensemble/Events/ModelSaved.js +16 -0
  66. package/dist/Database/Ensemble/Events/ModelSaved.js.map +1 -0
  67. package/dist/Database/Ensemble/Events/ModelSaving.d.ts +12 -0
  68. package/dist/Database/Ensemble/Events/ModelSaving.d.ts.map +1 -0
  69. package/dist/Database/Ensemble/Events/ModelSaving.js +17 -0
  70. package/dist/Database/Ensemble/Events/ModelSaving.js.map +1 -0
  71. package/dist/Database/Ensemble/Events/ModelUpdated.d.ts +11 -0
  72. package/dist/Database/Ensemble/Events/ModelUpdated.d.ts.map +1 -0
  73. package/dist/Database/Ensemble/Events/ModelUpdated.js +16 -0
  74. package/dist/Database/Ensemble/Events/ModelUpdated.js.map +1 -0
  75. package/dist/Database/Ensemble/Events/ModelUpdating.d.ts +12 -0
  76. package/dist/Database/Ensemble/Events/ModelUpdating.d.ts.map +1 -0
  77. package/dist/Database/Ensemble/Events/ModelUpdating.js +17 -0
  78. package/dist/Database/Ensemble/Events/ModelUpdating.js.map +1 -0
  79. package/dist/Database/Ensemble/Events/index.d.ts +16 -0
  80. package/dist/Database/Ensemble/Events/index.d.ts.map +1 -0
  81. package/dist/Database/Ensemble/Events/index.js +29 -0
  82. package/dist/Database/Ensemble/Events/index.js.map +1 -0
  83. package/dist/Events/Concerns/Dispatchable.d.ts +121 -0
  84. package/dist/Events/Concerns/Dispatchable.d.ts.map +1 -0
  85. package/dist/Events/Concerns/Dispatchable.js +165 -0
  86. package/dist/Events/Concerns/Dispatchable.js.map +1 -0
  87. package/dist/Events/Contracts/Dispatcher.d.ts +144 -0
  88. package/dist/Events/Contracts/Dispatcher.d.ts.map +1 -0
  89. package/dist/Events/Contracts/Dispatcher.js +3 -0
  90. package/dist/Events/Contracts/Dispatcher.js.map +1 -0
  91. package/dist/Events/Contracts/ShouldDispatchAfterCommit.d.ts +21 -0
  92. package/dist/Events/Contracts/ShouldDispatchAfterCommit.d.ts.map +1 -0
  93. package/dist/Events/Contracts/ShouldDispatchAfterCommit.js +3 -0
  94. package/dist/Events/Contracts/ShouldDispatchAfterCommit.js.map +1 -0
  95. package/dist/Events/Decorators/HandlesEvents.d.ts +83 -0
  96. package/dist/Events/Decorators/HandlesEvents.d.ts.map +1 -0
  97. package/dist/Events/Decorators/HandlesEvents.js +105 -0
  98. package/dist/Events/Decorators/HandlesEvents.js.map +1 -0
  99. package/dist/Events/Dispatcher.d.ts +201 -0
  100. package/dist/Events/Dispatcher.d.ts.map +1 -0
  101. package/dist/Events/Dispatcher.js +359 -0
  102. package/dist/Events/Dispatcher.js.map +1 -0
  103. package/dist/Events/Event.d.ts +164 -0
  104. package/dist/Events/Event.d.ts.map +1 -0
  105. package/dist/Events/Event.js +266 -0
  106. package/dist/Events/Event.js.map +1 -0
  107. package/dist/Events/EventDiscoveryCache.d.ts +205 -0
  108. package/dist/Events/EventDiscoveryCache.d.ts.map +1 -0
  109. package/dist/Events/EventDiscoveryCache.js +310 -0
  110. package/dist/Events/EventDiscoveryCache.js.map +1 -0
  111. package/dist/Events/EventServiceProvider.d.ts +148 -0
  112. package/dist/Events/EventServiceProvider.d.ts.map +1 -0
  113. package/dist/Events/EventServiceProvider.js +258 -0
  114. package/dist/Events/EventServiceProvider.js.map +1 -0
  115. package/dist/Events/NullDispatcher.d.ts +97 -0
  116. package/dist/Events/NullDispatcher.d.ts.map +1 -0
  117. package/dist/Events/NullDispatcher.js +118 -0
  118. package/dist/Events/NullDispatcher.js.map +1 -0
  119. package/dist/Events/PendingDispatch.d.ts +119 -0
  120. package/dist/Events/PendingDispatch.d.ts.map +1 -0
  121. package/dist/Events/PendingDispatch.js +162 -0
  122. package/dist/Events/PendingDispatch.js.map +1 -0
  123. package/dist/Events/index.d.ts +13 -0
  124. package/dist/Events/index.d.ts.map +1 -0
  125. package/dist/Events/index.js +20 -0
  126. package/dist/Events/index.js.map +1 -0
  127. package/dist/Events/types.d.ts +77 -0
  128. package/dist/Events/types.d.ts.map +1 -0
  129. package/dist/Events/types.js +3 -0
  130. package/dist/Events/types.js.map +1 -0
  131. package/dist/Facades/Event.d.ts +200 -0
  132. package/dist/Facades/Event.d.ts.map +1 -0
  133. package/dist/Facades/Event.js +331 -0
  134. package/dist/Facades/Event.js.map +1 -0
  135. package/dist/Foundation/Application.d.ts +33 -0
  136. package/dist/Foundation/Application.d.ts.map +1 -1
  137. package/dist/Foundation/Application.js +40 -0
  138. package/dist/Foundation/Application.js.map +1 -1
  139. package/dist/Listeners/Contracts/ShouldQueue.d.ts +142 -0
  140. package/dist/Listeners/Contracts/ShouldQueue.d.ts.map +1 -0
  141. package/dist/Listeners/Contracts/ShouldQueue.js +20 -0
  142. package/dist/Listeners/Contracts/ShouldQueue.js.map +1 -0
  143. package/dist/Listeners/Contracts/ShouldQueueAfterCommit.d.ts +24 -0
  144. package/dist/Listeners/Contracts/ShouldQueueAfterCommit.d.ts.map +1 -0
  145. package/dist/Listeners/Contracts/ShouldQueueAfterCommit.js +3 -0
  146. package/dist/Listeners/Contracts/ShouldQueueAfterCommit.js.map +1 -0
  147. package/dist/Support/EventDiscovery.d.ts +142 -0
  148. package/dist/Support/EventDiscovery.d.ts.map +1 -0
  149. package/dist/Support/EventDiscovery.js +302 -0
  150. package/dist/Support/EventDiscovery.js.map +1 -0
  151. package/dist/Support/Testing/Fakes/EventFake.d.ts +291 -0
  152. package/dist/Support/Testing/Fakes/EventFake.d.ts.map +1 -0
  153. package/dist/Support/Testing/Fakes/EventFake.js +444 -0
  154. package/dist/Support/Testing/Fakes/EventFake.js.map +1 -0
  155. package/dist/index.d.ts +6 -0
  156. package/dist/index.d.ts.map +1 -1
  157. package/dist/index.js +11 -1
  158. package/dist/index.js.map +1 -1
  159. package/package.json +3 -3
@@ -0,0 +1,142 @@
1
+ import { Application } from '../Foundation/Application';
2
+ import 'reflect-metadata';
3
+ /**
4
+ * EventDiscovery
5
+ *
6
+ * Automatically discovers event listeners by scanning directories for listener files
7
+ * and extracting event type information using TypeScript's reflect-metadata.
8
+ *
9
+ * This enables Laravel 11's automatic listener discovery feature where listeners
10
+ * are automatically registered based on their type hints without manual registration.
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * const discovery = new EventDiscovery(app)
15
+ * const listeners = await discovery.discover([app.path('Listeners')])
16
+ * // Returns: Map<string, string[]> - event names to listener class paths
17
+ * ```
18
+ */
19
+ export declare class EventDiscovery {
20
+ protected app: Application;
21
+ constructor(app: Application);
22
+ /**
23
+ * Discover all event listeners within the given directories
24
+ *
25
+ * Scans each directory recursively, loads listener classes, extracts their
26
+ * event type hints, and builds a map of events to their listeners.
27
+ *
28
+ * @param directories Array of absolute paths to directories to scan
29
+ * @returns Map of event names to listener class paths
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * const listeners = await discovery.discover([
34
+ * '/app/Listeners',
35
+ * '/app/Modules/User/Listeners'
36
+ * ])
37
+ * ```
38
+ */
39
+ discover(directories: string[]): Promise<Map<string, string[]>>;
40
+ /**
41
+ * Discover listeners in a single directory
42
+ *
43
+ * Recursively scans the directory for TypeScript/JavaScript files,
44
+ * loads each file, and extracts event-listener mappings.
45
+ *
46
+ * @param directory Absolute path to directory to scan
47
+ * @returns Map of event names to listener class paths for this directory
48
+ */
49
+ protected discoverInDirectory(directory: string): Promise<Map<string, string[]>>;
50
+ /**
51
+ * Get all TypeScript/JavaScript files in directory recursively
52
+ *
53
+ * Walks the directory tree and collects all valid listener files,
54
+ * excluding test files and index files.
55
+ *
56
+ * @param directory Directory to scan
57
+ * @param files Accumulator for recursive scanning
58
+ * @returns Array of absolute file paths
59
+ */
60
+ protected getListenerFiles(directory: string, files?: string[]): string[];
61
+ /**
62
+ * Check if file is a valid listener file
63
+ *
64
+ * Valid listener files:
65
+ * - Must be .ts or .js
66
+ * - Cannot be test files (.test.ts, .spec.ts)
67
+ * - Cannot be index files (index.ts, index.js)
68
+ *
69
+ * @param file Absolute path to file
70
+ * @returns True if file should be processed as a listener
71
+ */
72
+ protected isListenerFile(file: string): boolean;
73
+ /**
74
+ * Load listener class from file
75
+ *
76
+ * Dynamically imports the file and attempts to find a valid listener class
77
+ * from either the default export or named exports.
78
+ *
79
+ * @param file Absolute path to listener file
80
+ * @returns Listener class constructor or undefined if not found
81
+ */
82
+ protected loadListenerClass(file: string): Promise<any>;
83
+ /**
84
+ * Check if a class is a listener
85
+ *
86
+ * A valid listener must have either a `handle` method or `__invoke` method
87
+ * on its prototype.
88
+ *
89
+ * @param cls Class constructor to check
90
+ * @returns True if class is a valid listener
91
+ */
92
+ protected isListenerClass(cls: any): boolean;
93
+ /**
94
+ * Extract event types from listener class
95
+ *
96
+ * Uses reflect-metadata to inspect the listener's handle method and extract
97
+ * the event type from the first parameter. Also checks for the @HandlesEvents
98
+ * decorator for union types.
99
+ *
100
+ * TypeScript's emitDecoratorMetadata must be enabled in tsconfig.json.
101
+ *
102
+ * @param listenerClass Listener class constructor
103
+ * @returns Array of event class names this listener handles
104
+ *
105
+ * @example
106
+ * ```typescript
107
+ * // For: handle(event: UserRegistered)
108
+ * // Returns: ['UserRegistered']
109
+ *
110
+ * // For: @HandlesEvents([UserRegistered, UserUpdated])
111
+ * // Returns: ['UserRegistered', 'UserUpdated']
112
+ * ```
113
+ */
114
+ protected extractEventTypes(listenerClass: any): string[];
115
+ /**
116
+ * Get listener name from file path
117
+ *
118
+ * Converts an absolute file path to a listener class reference string
119
+ * that can be used for container resolution.
120
+ *
121
+ * @param file Absolute path to listener file
122
+ * @returns Listener class path relative to app directory
123
+ *
124
+ * @example
125
+ * ```typescript
126
+ * // Input: /app/Listeners/SendWelcomeEmail.ts
127
+ * // Output: Listeners/SendWelcomeEmail
128
+ *
129
+ * // Input: /app/Modules/User/Listeners/NotifyAdmin.ts
130
+ * // Output: Modules/User/Listeners/NotifyAdmin
131
+ * ```
132
+ */
133
+ protected getListenerName(file: string): string;
134
+ /**
135
+ * Check if directory exists
136
+ *
137
+ * @param directory Absolute path to directory
138
+ * @returns True if directory exists and is accessible
139
+ */
140
+ protected directoryExists(directory: string): boolean;
141
+ }
142
+ //# sourceMappingURL=EventDiscovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EventDiscovery.d.ts","sourceRoot":"","sources":["../../src/Support/EventDiscovery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAGxD,OAAO,kBAAkB,CAAC;AAE1B;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,cAAc;IACb,SAAS,CAAC,GAAG,EAAE,WAAW;gBAAhB,GAAG,EAAE,WAAW;IAEtC;;;;;;;;;;;;;;;;OAgBG;IACG,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAkBrE;;;;;;;;OAQG;cACa,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAkCtF;;;;;;;;;OASG;IACH,SAAS,CAAC,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,GAAE,MAAM,EAAO,GAAG,MAAM,EAAE;IAiB7E;;;;;;;;;;OAUG;IACH,SAAS,CAAC,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAqB/C;;;;;;;;OAQG;cACa,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAS7D;;;;;;;;OAQG;IACH,SAAS,CAAC,eAAe,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO;IAS5C;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,SAAS,CAAC,iBAAiB,CAAC,aAAa,EAAE,GAAG,GAAG,MAAM,EAAE;IA+BzD;;;;;;;;;;;;;;;;;OAiBG;IACH,SAAS,CAAC,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAe/C;;;;;OAKG;IACH,SAAS,CAAC,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;CAOtD"}
@@ -0,0 +1,302 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.EventDiscovery = void 0;
37
+ const fs_1 = require("fs");
38
+ const path_1 = require("path");
39
+ require("reflect-metadata");
40
+ /**
41
+ * EventDiscovery
42
+ *
43
+ * Automatically discovers event listeners by scanning directories for listener files
44
+ * and extracting event type information using TypeScript's reflect-metadata.
45
+ *
46
+ * This enables Laravel 11's automatic listener discovery feature where listeners
47
+ * are automatically registered based on their type hints without manual registration.
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * const discovery = new EventDiscovery(app)
52
+ * const listeners = await discovery.discover([app.path('Listeners')])
53
+ * // Returns: Map<string, string[]> - event names to listener class paths
54
+ * ```
55
+ */
56
+ class EventDiscovery {
57
+ app;
58
+ constructor(app) {
59
+ this.app = app;
60
+ }
61
+ /**
62
+ * Discover all event listeners within the given directories
63
+ *
64
+ * Scans each directory recursively, loads listener classes, extracts their
65
+ * event type hints, and builds a map of events to their listeners.
66
+ *
67
+ * @param directories Array of absolute paths to directories to scan
68
+ * @returns Map of event names to listener class paths
69
+ *
70
+ * @example
71
+ * ```typescript
72
+ * const listeners = await discovery.discover([
73
+ * '/app/Listeners',
74
+ * '/app/Modules/User/Listeners'
75
+ * ])
76
+ * ```
77
+ */
78
+ async discover(directories) {
79
+ const listeners = new Map();
80
+ for (const directory of directories) {
81
+ const discovered = await this.discoverInDirectory(directory);
82
+ // Merge discovered listeners
83
+ for (const [event, listenerList] of discovered) {
84
+ if (!listeners.has(event)) {
85
+ listeners.set(event, []);
86
+ }
87
+ listeners.get(event).push(...listenerList);
88
+ }
89
+ }
90
+ return listeners;
91
+ }
92
+ /**
93
+ * Discover listeners in a single directory
94
+ *
95
+ * Recursively scans the directory for TypeScript/JavaScript files,
96
+ * loads each file, and extracts event-listener mappings.
97
+ *
98
+ * @param directory Absolute path to directory to scan
99
+ * @returns Map of event names to listener class paths for this directory
100
+ */
101
+ async discoverInDirectory(directory) {
102
+ const listeners = new Map();
103
+ if (!this.directoryExists(directory)) {
104
+ return listeners;
105
+ }
106
+ const files = this.getListenerFiles(directory);
107
+ for (const file of files) {
108
+ try {
109
+ const listenerClass = await this.loadListenerClass(file);
110
+ if (!listenerClass)
111
+ continue;
112
+ const events = this.extractEventTypes(listenerClass);
113
+ for (const event of events) {
114
+ if (!listeners.has(event)) {
115
+ listeners.set(event, []);
116
+ }
117
+ listeners.get(event).push(this.getListenerName(file));
118
+ }
119
+ }
120
+ catch (error) {
121
+ // Skip files that can't be loaded
122
+ if (this.app.isDebug()) {
123
+ console.warn(`Could not load listener from ${file}:`, error);
124
+ }
125
+ }
126
+ }
127
+ return listeners;
128
+ }
129
+ /**
130
+ * Get all TypeScript/JavaScript files in directory recursively
131
+ *
132
+ * Walks the directory tree and collects all valid listener files,
133
+ * excluding test files and index files.
134
+ *
135
+ * @param directory Directory to scan
136
+ * @param files Accumulator for recursive scanning
137
+ * @returns Array of absolute file paths
138
+ */
139
+ getListenerFiles(directory, files = []) {
140
+ const entries = (0, fs_1.readdirSync)(directory);
141
+ for (const entry of entries) {
142
+ const fullPath = (0, path_1.join)(directory, entry);
143
+ const stat = (0, fs_1.statSync)(fullPath);
144
+ if (stat.isDirectory()) {
145
+ this.getListenerFiles(fullPath, files);
146
+ }
147
+ else if (this.isListenerFile(fullPath)) {
148
+ files.push(fullPath);
149
+ }
150
+ }
151
+ return files;
152
+ }
153
+ /**
154
+ * Check if file is a valid listener file
155
+ *
156
+ * Valid listener files:
157
+ * - Must be .ts or .js
158
+ * - Cannot be test files (.test.ts, .spec.ts)
159
+ * - Cannot be index files (index.ts, index.js)
160
+ *
161
+ * @param file Absolute path to file
162
+ * @returns True if file should be processed as a listener
163
+ */
164
+ isListenerFile(file) {
165
+ const ext = (0, path_1.extname)(file);
166
+ // Must be .ts or .js file
167
+ if (ext !== '.ts' && ext !== '.js') {
168
+ return false;
169
+ }
170
+ // Exclude test files
171
+ if (file.includes('.test.') || file.includes('.spec.')) {
172
+ return false;
173
+ }
174
+ // Exclude index files
175
+ if (file.endsWith('index.ts') || file.endsWith('index.js')) {
176
+ return false;
177
+ }
178
+ return true;
179
+ }
180
+ /**
181
+ * Load listener class from file
182
+ *
183
+ * Dynamically imports the file and attempts to find a valid listener class
184
+ * from either the default export or named exports.
185
+ *
186
+ * @param file Absolute path to listener file
187
+ * @returns Listener class constructor or undefined if not found
188
+ */
189
+ async loadListenerClass(file) {
190
+ const module = await Promise.resolve(`${file}`).then(s => __importStar(require(s)));
191
+ // Try to find the default export or named export
192
+ return module.default || Object.values(module).find(exp => typeof exp === 'function' && this.isListenerClass(exp));
193
+ }
194
+ /**
195
+ * Check if a class is a listener
196
+ *
197
+ * A valid listener must have either a `handle` method or `__invoke` method
198
+ * on its prototype.
199
+ *
200
+ * @param cls Class constructor to check
201
+ * @returns True if class is a valid listener
202
+ */
203
+ isListenerClass(cls) {
204
+ if (typeof cls !== 'function')
205
+ return false;
206
+ // Must have a handle method or implement __invoke
207
+ const prototype = cls.prototype;
208
+ return typeof prototype?.handle === 'function' ||
209
+ typeof prototype?.__invoke === 'function';
210
+ }
211
+ /**
212
+ * Extract event types from listener class
213
+ *
214
+ * Uses reflect-metadata to inspect the listener's handle method and extract
215
+ * the event type from the first parameter. Also checks for the @HandlesEvents
216
+ * decorator for union types.
217
+ *
218
+ * TypeScript's emitDecoratorMetadata must be enabled in tsconfig.json.
219
+ *
220
+ * @param listenerClass Listener class constructor
221
+ * @returns Array of event class names this listener handles
222
+ *
223
+ * @example
224
+ * ```typescript
225
+ * // For: handle(event: UserRegistered)
226
+ * // Returns: ['UserRegistered']
227
+ *
228
+ * // For: @HandlesEvents([UserRegistered, UserUpdated])
229
+ * // Returns: ['UserRegistered', 'UserUpdated']
230
+ * ```
231
+ */
232
+ extractEventTypes(listenerClass) {
233
+ const events = [];
234
+ // Get the handle method
235
+ const handleMethod = listenerClass.prototype.handle || listenerClass.prototype.__invoke;
236
+ if (!handleMethod)
237
+ return events;
238
+ // Use reflect-metadata to get parameter types
239
+ const paramTypes = Reflect.getMetadata('design:paramtypes', listenerClass.prototype, 'handle') ||
240
+ Reflect.getMetadata('design:paramtypes', listenerClass.prototype, '__invoke');
241
+ if (!paramTypes || paramTypes.length === 0)
242
+ return events;
243
+ // First parameter should be the event
244
+ const eventType = paramTypes[0];
245
+ if (eventType && eventType.name && eventType.name !== 'Object') {
246
+ events.push(eventType.name);
247
+ }
248
+ // Check for union types (TypeScript limitation - requires custom decorator)
249
+ // The @HandlesEvents decorator can be used to specify multiple event types
250
+ const unionTypes = Reflect.getMetadata('event:types', listenerClass);
251
+ if (unionTypes && Array.isArray(unionTypes)) {
252
+ events.push(...unionTypes.map((t) => t.name));
253
+ }
254
+ return events;
255
+ }
256
+ /**
257
+ * Get listener name from file path
258
+ *
259
+ * Converts an absolute file path to a listener class reference string
260
+ * that can be used for container resolution.
261
+ *
262
+ * @param file Absolute path to listener file
263
+ * @returns Listener class path relative to app directory
264
+ *
265
+ * @example
266
+ * ```typescript
267
+ * // Input: /app/Listeners/SendWelcomeEmail.ts
268
+ * // Output: Listeners/SendWelcomeEmail
269
+ *
270
+ * // Input: /app/Modules/User/Listeners/NotifyAdmin.ts
271
+ * // Output: Modules/User/Listeners/NotifyAdmin
272
+ * ```
273
+ */
274
+ getListenerName(file) {
275
+ // Convert file path to class name
276
+ // Remove extension
277
+ let name = file.replace(/\.[jt]s$/, '');
278
+ // Get relative to app path
279
+ const appPath = this.app.path();
280
+ if (name.startsWith(appPath)) {
281
+ name = name.substring(appPath.length + 1);
282
+ }
283
+ // Convert to class path (e.g., Listeners/SendWelcomeEmail)
284
+ return name;
285
+ }
286
+ /**
287
+ * Check if directory exists
288
+ *
289
+ * @param directory Absolute path to directory
290
+ * @returns True if directory exists and is accessible
291
+ */
292
+ directoryExists(directory) {
293
+ try {
294
+ return (0, fs_1.statSync)(directory).isDirectory();
295
+ }
296
+ catch {
297
+ return false;
298
+ }
299
+ }
300
+ }
301
+ exports.EventDiscovery = EventDiscovery;
302
+ //# sourceMappingURL=EventDiscovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EventDiscovery.js","sourceRoot":"","sources":["../../src/Support/EventDiscovery.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,2BAA2C;AAC3C,+BAAqC;AACrC,4BAA0B;AAE1B;;;;;;;;;;;;;;;GAeG;AACH,MAAa,cAAc;IACH;IAAtB,YAAsB,GAAgB;QAAhB,QAAG,GAAH,GAAG,CAAa;IAAG,CAAC;IAE1C;;;;;;;;;;;;;;;;OAgBG;IACH,KAAK,CAAC,QAAQ,CAAC,WAAqB;QAClC,MAAM,SAAS,GAAG,IAAI,GAAG,EAAoB,CAAC;QAE9C,KAAK,MAAM,SAAS,IAAI,WAAW,EAAE,CAAC;YACpC,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,mBAAmB,CAAC,SAAS,CAAC,CAAC;YAE7D,6BAA6B;YAC7B,KAAK,MAAM,CAAC,KAAK,EAAE,YAAY,CAAC,IAAI,UAAU,EAAE,CAAC;gBAC/C,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;oBAC1B,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;gBAC3B,CAAC;gBACD,SAAS,CAAC,GAAG,CAAC,KAAK,CAAE,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,CAAC;YAC9C,CAAC;QACH,CAAC;QAED,OAAO,SAAS,CAAC;IACnB,CAAC;IAED;;;;;;;;OAQG;IACO,KAAK,CAAC,mBAAmB,CAAC,SAAiB;QACnD,MAAM,SAAS,GAAG,IAAI,GAAG,EAAoB,CAAC;QAE9C,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,EAAE,CAAC;YACrC,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,MAAM,KAAK,GAAG,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC;QAE/C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC;gBACH,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;gBAEzD,IAAI,CAAC,aAAa;oBAAE,SAAS;gBAE7B,MAAM,MAAM,GAAG,IAAI,CAAC,iBAAiB,CAAC,aAAa,CAAC,CAAC;gBAErD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;oBAC3B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;wBAC1B,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;oBAC3B,CAAC;oBACD,SAAS,CAAC,GAAG,CAAC,KAAK,CAAE,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC;gBACzD,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,kCAAkC;gBAClC,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC;oBACvB,OAAO,CAAC,IAAI,CAAC,gCAAgC,IAAI,GAAG,EAAE,KAAK,CAAC,CAAC;gBAC/D,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,SAAS,CAAC;IACnB,CAAC;IAED;;;;;;;;;OASG;IACO,gBAAgB,CAAC,SAAiB,EAAE,QAAkB,EAAE;QAChE,MAAM,OAAO,GAAG,IAAA,gBAAW,EAAC,SAAS,CAAC,CAAC;QAEvC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,MAAM,QAAQ,GAAG,IAAA,WAAI,EAAC,SAAS,EAAE,KAAK,CAAC,CAAC;YACxC,MAAM,IAAI,GAAG,IAAA,aAAQ,EAAC,QAAQ,CAAC,CAAC;YAEhC,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;gBACvB,IAAI,CAAC,gBAAgB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;YACzC,CAAC;iBAAM,IAAI,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACzC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACvB,CAAC;QACH,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;;;;;;;;OAUG;IACO,cAAc,CAAC,IAAY;QACnC,MAAM,GAAG,GAAG,IAAA,cAAO,EAAC,IAAI,CAAC,CAAC;QAE1B,0BAA0B;QAC1B,IAAI,GAAG,KAAK,KAAK,IAAI,GAAG,KAAK,KAAK,EAAE,CAAC;YACnC,OAAO,KAAK,CAAC;QACf,CAAC;QAED,qBAAqB;QACrB,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YACvD,OAAO,KAAK,CAAC;QACf,CAAC;QAED,sBAAsB;QACtB,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;YAC3D,OAAO,KAAK,CAAC;QACf,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;;;OAQG;IACO,KAAK,CAAC,iBAAiB,CAAC,IAAY;QAC5C,MAAM,MAAM,GAAG,yBAAa,IAAI,uCAAC,CAAC;QAElC,iDAAiD;QACjD,OAAO,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CACxD,OAAO,GAAG,KAAK,UAAU,IAAI,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,CACvD,CAAC;IACJ,CAAC;IAED;;;;;;;;OAQG;IACO,eAAe,CAAC,GAAQ;QAChC,IAAI,OAAO,GAAG,KAAK,UAAU;YAAE,OAAO,KAAK,CAAC;QAE5C,kDAAkD;QAClD,MAAM,SAAS,GAAG,GAAG,CAAC,SAAS,CAAC;QAChC,OAAO,OAAO,SAAS,EAAE,MAAM,KAAK,UAAU;YACvC,OAAO,SAAS,EAAE,QAAQ,KAAK,UAAU,CAAC;IACnD,CAAC;IAED;;;;;;;;;;;;;;;;;;;;OAoBG;IACO,iBAAiB,CAAC,aAAkB;QAC5C,MAAM,MAAM,GAAa,EAAE,CAAC;QAE5B,wBAAwB;QACxB,MAAM,YAAY,GAAG,aAAa,CAAC,SAAS,CAAC,MAAM,IAAI,aAAa,CAAC,SAAS,CAAC,QAAQ,CAAC;QAExF,IAAI,CAAC,YAAY;YAAE,OAAO,MAAM,CAAC;QAEjC,8CAA8C;QAC9C,MAAM,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,mBAAmB,EAAE,aAAa,CAAC,SAAS,EAAE,QAAQ,CAAC;YAC3E,OAAO,CAAC,WAAW,CAAC,mBAAmB,EAAE,aAAa,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;QAEjG,IAAI,CAAC,UAAU,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,MAAM,CAAC;QAE1D,sCAAsC;QACtC,MAAM,SAAS,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;QAEhC,IAAI,SAAS,IAAI,SAAS,CAAC,IAAI,IAAI,SAAS,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC/D,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QAC9B,CAAC;QAED,4EAA4E;QAC5E,2EAA2E;QAC3E,MAAM,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;QACrE,IAAI,UAAU,IAAI,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;YAC5C,MAAM,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACrD,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;;;;;;;;;;;;;;;;OAiBG;IACO,eAAe,CAAC,IAAY;QACpC,kCAAkC;QAClC,mBAAmB;QACnB,IAAI,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;QAExC,2BAA2B;QAC3B,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QAChC,IAAI,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YAC7B,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAC5C,CAAC;QAED,2DAA2D;QAC3D,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;OAKG;IACO,eAAe,CAAC,SAAiB;QACzC,IAAI,CAAC;YACH,OAAO,IAAA,aAAQ,EAAC,SAAS,CAAC,CAAC,WAAW,EAAE,CAAC;QAC3C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;CACF;AAlRD,wCAkRC"}