@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.
- package/README.md +289 -22
- package/dist/Console/Commands/EventCacheCommand.d.ts +15 -0
- package/dist/Console/Commands/EventCacheCommand.d.ts.map +1 -0
- package/dist/Console/Commands/EventCacheCommand.js +99 -0
- package/dist/Console/Commands/EventCacheCommand.js.map +1 -0
- package/dist/Console/Commands/EventClearCommand.d.ts +15 -0
- package/dist/Console/Commands/EventClearCommand.d.ts.map +1 -0
- package/dist/Console/Commands/EventClearCommand.js +71 -0
- package/dist/Console/Commands/EventClearCommand.js.map +1 -0
- package/dist/Console/Commands/EventListCommand.d.ts +19 -0
- package/dist/Console/Commands/EventListCommand.d.ts.map +1 -0
- package/dist/Console/Commands/EventListCommand.js +106 -0
- package/dist/Console/Commands/EventListCommand.js.map +1 -0
- package/dist/Console/Commands/MakeEventCommand.d.ts +27 -0
- package/dist/Console/Commands/MakeEventCommand.d.ts.map +1 -0
- package/dist/Console/Commands/MakeEventCommand.js +117 -0
- package/dist/Console/Commands/MakeEventCommand.js.map +1 -0
- package/dist/Console/Commands/MakeListenerCommand.d.ts +27 -0
- package/dist/Console/Commands/MakeListenerCommand.d.ts.map +1 -0
- package/dist/Console/Commands/MakeListenerCommand.js +157 -0
- package/dist/Console/Commands/MakeListenerCommand.js.map +1 -0
- package/dist/Console/orchestr.js +10 -0
- package/dist/Console/orchestr.js.map +1 -1
- package/dist/Contracts/Events/Dispatcher.d.ts +94 -0
- package/dist/Contracts/Events/Dispatcher.d.ts.map +1 -0
- package/dist/Contracts/Events/Dispatcher.js +9 -0
- package/dist/Contracts/Events/Dispatcher.js.map +1 -0
- package/dist/Contracts/Events/index.d.ts +7 -0
- package/dist/Contracts/Events/index.d.ts.map +1 -0
- package/dist/Contracts/Events/index.js +8 -0
- package/dist/Contracts/Events/index.js.map +1 -0
- package/dist/Database/Ensemble/Ensemble.d.ts +7 -0
- package/dist/Database/Ensemble/Ensemble.d.ts.map +1 -1
- package/dist/Database/Ensemble/Ensemble.js +119 -0
- package/dist/Database/Ensemble/Ensemble.js.map +1 -1
- package/dist/Database/Ensemble/EnsembleBuilder.d.ts.map +1 -1
- package/dist/Database/Ensemble/EnsembleBuilder.js +7 -0
- package/dist/Database/Ensemble/EnsembleBuilder.js.map +1 -1
- package/dist/Database/Ensemble/Events/ModelCreated.d.ts +11 -0
- package/dist/Database/Ensemble/Events/ModelCreated.d.ts.map +1 -0
- package/dist/Database/Ensemble/Events/ModelCreated.js +16 -0
- package/dist/Database/Ensemble/Events/ModelCreated.js.map +1 -0
- package/dist/Database/Ensemble/Events/ModelCreating.d.ts +12 -0
- package/dist/Database/Ensemble/Events/ModelCreating.d.ts.map +1 -0
- package/dist/Database/Ensemble/Events/ModelCreating.js +17 -0
- package/dist/Database/Ensemble/Events/ModelCreating.js.map +1 -0
- package/dist/Database/Ensemble/Events/ModelDeleted.d.ts +11 -0
- package/dist/Database/Ensemble/Events/ModelDeleted.d.ts.map +1 -0
- package/dist/Database/Ensemble/Events/ModelDeleted.js +16 -0
- package/dist/Database/Ensemble/Events/ModelDeleted.js.map +1 -0
- package/dist/Database/Ensemble/Events/ModelDeleting.d.ts +12 -0
- package/dist/Database/Ensemble/Events/ModelDeleting.d.ts.map +1 -0
- package/dist/Database/Ensemble/Events/ModelDeleting.js +17 -0
- package/dist/Database/Ensemble/Events/ModelDeleting.js.map +1 -0
- package/dist/Database/Ensemble/Events/ModelEvent.d.ts +17 -0
- package/dist/Database/Ensemble/Events/ModelEvent.d.ts.map +1 -0
- package/dist/Database/Ensemble/Events/ModelEvent.js +22 -0
- package/dist/Database/Ensemble/Events/ModelEvent.js.map +1 -0
- package/dist/Database/Ensemble/Events/ModelRetrieved.d.ts +11 -0
- package/dist/Database/Ensemble/Events/ModelRetrieved.d.ts.map +1 -0
- package/dist/Database/Ensemble/Events/ModelRetrieved.js +16 -0
- package/dist/Database/Ensemble/Events/ModelRetrieved.js.map +1 -0
- package/dist/Database/Ensemble/Events/ModelSaved.d.ts +11 -0
- package/dist/Database/Ensemble/Events/ModelSaved.d.ts.map +1 -0
- package/dist/Database/Ensemble/Events/ModelSaved.js +16 -0
- package/dist/Database/Ensemble/Events/ModelSaved.js.map +1 -0
- package/dist/Database/Ensemble/Events/ModelSaving.d.ts +12 -0
- package/dist/Database/Ensemble/Events/ModelSaving.d.ts.map +1 -0
- package/dist/Database/Ensemble/Events/ModelSaving.js +17 -0
- package/dist/Database/Ensemble/Events/ModelSaving.js.map +1 -0
- package/dist/Database/Ensemble/Events/ModelUpdated.d.ts +11 -0
- package/dist/Database/Ensemble/Events/ModelUpdated.d.ts.map +1 -0
- package/dist/Database/Ensemble/Events/ModelUpdated.js +16 -0
- package/dist/Database/Ensemble/Events/ModelUpdated.js.map +1 -0
- package/dist/Database/Ensemble/Events/ModelUpdating.d.ts +12 -0
- package/dist/Database/Ensemble/Events/ModelUpdating.d.ts.map +1 -0
- package/dist/Database/Ensemble/Events/ModelUpdating.js +17 -0
- package/dist/Database/Ensemble/Events/ModelUpdating.js.map +1 -0
- package/dist/Database/Ensemble/Events/index.d.ts +16 -0
- package/dist/Database/Ensemble/Events/index.d.ts.map +1 -0
- package/dist/Database/Ensemble/Events/index.js +29 -0
- package/dist/Database/Ensemble/Events/index.js.map +1 -0
- package/dist/Events/Concerns/Dispatchable.d.ts +121 -0
- package/dist/Events/Concerns/Dispatchable.d.ts.map +1 -0
- package/dist/Events/Concerns/Dispatchable.js +165 -0
- package/dist/Events/Concerns/Dispatchable.js.map +1 -0
- package/dist/Events/Contracts/Dispatcher.d.ts +144 -0
- package/dist/Events/Contracts/Dispatcher.d.ts.map +1 -0
- package/dist/Events/Contracts/Dispatcher.js +3 -0
- package/dist/Events/Contracts/Dispatcher.js.map +1 -0
- package/dist/Events/Contracts/ShouldDispatchAfterCommit.d.ts +21 -0
- package/dist/Events/Contracts/ShouldDispatchAfterCommit.d.ts.map +1 -0
- package/dist/Events/Contracts/ShouldDispatchAfterCommit.js +3 -0
- package/dist/Events/Contracts/ShouldDispatchAfterCommit.js.map +1 -0
- package/dist/Events/Decorators/HandlesEvents.d.ts +83 -0
- package/dist/Events/Decorators/HandlesEvents.d.ts.map +1 -0
- package/dist/Events/Decorators/HandlesEvents.js +105 -0
- package/dist/Events/Decorators/HandlesEvents.js.map +1 -0
- package/dist/Events/Dispatcher.d.ts +201 -0
- package/dist/Events/Dispatcher.d.ts.map +1 -0
- package/dist/Events/Dispatcher.js +359 -0
- package/dist/Events/Dispatcher.js.map +1 -0
- package/dist/Events/Event.d.ts +164 -0
- package/dist/Events/Event.d.ts.map +1 -0
- package/dist/Events/Event.js +266 -0
- package/dist/Events/Event.js.map +1 -0
- package/dist/Events/EventDiscoveryCache.d.ts +205 -0
- package/dist/Events/EventDiscoveryCache.d.ts.map +1 -0
- package/dist/Events/EventDiscoveryCache.js +310 -0
- package/dist/Events/EventDiscoveryCache.js.map +1 -0
- package/dist/Events/EventServiceProvider.d.ts +148 -0
- package/dist/Events/EventServiceProvider.d.ts.map +1 -0
- package/dist/Events/EventServiceProvider.js +258 -0
- package/dist/Events/EventServiceProvider.js.map +1 -0
- package/dist/Events/NullDispatcher.d.ts +97 -0
- package/dist/Events/NullDispatcher.d.ts.map +1 -0
- package/dist/Events/NullDispatcher.js +118 -0
- package/dist/Events/NullDispatcher.js.map +1 -0
- package/dist/Events/PendingDispatch.d.ts +119 -0
- package/dist/Events/PendingDispatch.d.ts.map +1 -0
- package/dist/Events/PendingDispatch.js +162 -0
- package/dist/Events/PendingDispatch.js.map +1 -0
- package/dist/Events/index.d.ts +13 -0
- package/dist/Events/index.d.ts.map +1 -0
- package/dist/Events/index.js +20 -0
- package/dist/Events/index.js.map +1 -0
- package/dist/Events/types.d.ts +77 -0
- package/dist/Events/types.d.ts.map +1 -0
- package/dist/Events/types.js +3 -0
- package/dist/Events/types.js.map +1 -0
- package/dist/Facades/Event.d.ts +200 -0
- package/dist/Facades/Event.d.ts.map +1 -0
- package/dist/Facades/Event.js +331 -0
- package/dist/Facades/Event.js.map +1 -0
- package/dist/Foundation/Application.d.ts +33 -0
- package/dist/Foundation/Application.d.ts.map +1 -1
- package/dist/Foundation/Application.js +40 -0
- package/dist/Foundation/Application.js.map +1 -1
- package/dist/Listeners/Contracts/ShouldQueue.d.ts +142 -0
- package/dist/Listeners/Contracts/ShouldQueue.d.ts.map +1 -0
- package/dist/Listeners/Contracts/ShouldQueue.js +20 -0
- package/dist/Listeners/Contracts/ShouldQueue.js.map +1 -0
- package/dist/Listeners/Contracts/ShouldQueueAfterCommit.d.ts +24 -0
- package/dist/Listeners/Contracts/ShouldQueueAfterCommit.d.ts.map +1 -0
- package/dist/Listeners/Contracts/ShouldQueueAfterCommit.js +3 -0
- package/dist/Listeners/Contracts/ShouldQueueAfterCommit.js.map +1 -0
- package/dist/Support/EventDiscovery.d.ts +142 -0
- package/dist/Support/EventDiscovery.d.ts.map +1 -0
- package/dist/Support/EventDiscovery.js +302 -0
- package/dist/Support/EventDiscovery.js.map +1 -0
- package/dist/Support/Testing/Fakes/EventFake.d.ts +291 -0
- package/dist/Support/Testing/Fakes/EventFake.d.ts.map +1 -0
- package/dist/Support/Testing/Fakes/EventFake.js +444 -0
- package/dist/Support/Testing/Fakes/EventFake.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -1
- package/dist/index.js.map +1 -1
- 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":"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"}
|