1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.Engine = void 0;
|
4 | const tslib_1 = require("tslib");
|
5 | const plugin_utils_1 = require("@remixproject/plugin-utils");
|
6 | class Engine {
|
7 | constructor() {
|
8 | this.plugins = {};
|
9 | this.events = {};
|
10 | this.listeners = {};
|
11 | this.eventMemory = {};
|
12 | }
|
13 | |
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 | broadcast(emitter, event, ...payload) {
|
20 | const eventName = plugin_utils_1.listenEvent(emitter, event);
|
21 | if (!this.listeners[eventName])
|
22 | return;
|
23 | const listeners = this.listeners[eventName] || [];
|
24 | listeners.forEach((listener) => {
|
25 | if (!this.events[listener][eventName]) {
|
26 | throw new Error(`Plugin ${listener} should be listening on event ${event} from ${emitter}. But no callback have been found`);
|
27 | }
|
28 | this.events[listener][eventName](...payload);
|
29 | });
|
30 |
|
31 | this.eventMemory[emitter]
|
32 | ? this.eventMemory[emitter][event] = payload
|
33 | : this.eventMemory[emitter] = { [event]: payload };
|
34 | }
|
35 | |
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 | addListener(listener, emitter, event, cb) {
|
43 | const eventName = plugin_utils_1.listenEvent(emitter, event);
|
44 |
|
45 | if (!this.events[listener][eventName]) {
|
46 | this.events[listener][eventName] = cb;
|
47 | }
|
48 |
|
49 | if (!this.listeners[eventName])
|
50 | this.listeners[eventName] = [];
|
51 |
|
52 | if (!this.listeners[eventName].includes(listener)) {
|
53 | this.listeners[eventName].push(listener);
|
54 | }
|
55 |
|
56 | if (emitter in this.eventMemory && event in this.eventMemory[emitter]) {
|
57 | cb(...this.eventMemory[emitter][event]);
|
58 | }
|
59 | }
|
60 | |
61 |
|
62 |
|
63 |
|
64 |
|
65 |
|
66 | removeListener(listener, emitter, event) {
|
67 | const eventName = plugin_utils_1.listenEvent(emitter, event);
|
68 |
|
69 | this.listeners[eventName] = this.listeners[eventName].filter(name => name !== listener);
|
70 |
|
71 | delete this.events[listener][eventName];
|
72 | }
|
73 | |
74 |
|
75 |
|
76 |
|
77 |
|
78 |
|
79 |
|
80 | listenOnce(listener, emitter, event, cb) {
|
81 | this.addListener(listener, emitter, event, (...args) => {
|
82 | cb(...args);
|
83 | this.removeListener(listener, emitter, event);
|
84 | });
|
85 | }
|
86 | |
87 |
|
88 |
|
89 |
|
90 |
|
91 |
|
92 |
|
93 | callMethod(caller, path, method, ...payload) {
|
94 | var _a;
|
95 | return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
96 | const target = path.split('.').shift();
|
97 | if (!this.plugins[target]) {
|
98 | throw new Error(`Cannot call ${target} from ${caller}, because ${target} is not registered`);
|
99 | }
|
100 |
|
101 | const [to, from] = yield Promise.all([
|
102 | this.manager.getProfile(target),
|
103 | this.manager.getProfile(caller),
|
104 | ]);
|
105 |
|
106 | const isActive = yield this.manager.isActive(target);
|
107 | if (!isActive) {
|
108 | const managerCanActivate = yield this.manager.canActivatePlugin(from, to, method);
|
109 | const pluginCanActivate = yield ((_a = this.plugins[to.name]) === null || _a === void 0 ? void 0 : _a.canActivate(to, method));
|
110 | if (managerCanActivate && pluginCanActivate) {
|
111 | yield this.manager.toggleActive(target);
|
112 | }
|
113 | else {
|
114 | throw new Error(`${from.name} cannot call ${method} of ${target}, because ${target} is not activated yet`);
|
115 | }
|
116 | }
|
117 |
|
118 |
|
119 | const methods = [...(to.methods || []), 'canDeactivate'];
|
120 | if (!methods.includes(method)) {
|
121 | const notExposedMsg = `Cannot call method "${method}" of "${target}" from "${caller}", because "${method}" is not exposed.`;
|
122 | const exposedMethodsMsg = `Here is the list of exposed methods: ${methods.map(m => `"${m}"`).join(',')}`;
|
123 | throw new Error(`${notExposedMsg} ${exposedMethodsMsg}`);
|
124 | }
|
125 | const request = { from: caller, path };
|
126 | return this.plugins[target]['addRequest'](request, method, payload);
|
127 | });
|
128 | }
|
129 | |
130 |
|
131 |
|
132 |
|
133 |
|
134 | createApp(name) {
|
135 | return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
136 | const getProfiles = Object.keys(this.plugins).map(key => this.manager.getProfile(key));
|
137 | const profiles = yield Promise.all(getProfiles);
|
138 | return profiles.reduce((app, target) => {
|
139 | app[target.name] = (target.methods || []).reduce((methods, method) => {
|
140 | methods[method] = (...payload) => this.callMethod(name, target.name, method, ...payload);
|
141 | return methods;
|
142 | }, {
|
143 | on: (event, cb) => this.addListener(name, target.name, event, cb),
|
144 | once: (event, cb) => this.listenOnce(name, target.name, event, cb),
|
145 | off: (event) => this.removeListener(name, target.name, event),
|
146 | profile: target
|
147 | });
|
148 | return app;
|
149 | }, {});
|
150 | });
|
151 | }
|
152 | |
153 |
|
154 |
|
155 |
|
156 |
|
157 | activatePlugin(name) {
|
158 | return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
159 | if (!this.plugins[name]) {
|
160 | throw new Error(`Cannot active plugin ${name} because it's not registered yet`);
|
161 | }
|
162 | const isActive = yield this.manager.isActive(name);
|
163 | if (isActive)
|
164 | return;
|
165 | const plugin = this.plugins[name];
|
166 | this.events[name] = {};
|
167 | plugin['on'] = (emitter, event, cb) => {
|
168 | this.addListener(name, emitter, event, cb);
|
169 | };
|
170 | plugin['once'] = (emitter, event, cb) => {
|
171 | this.listenOnce(name, emitter, event, cb);
|
172 | };
|
173 | plugin['off'] = (emitter, event) => {
|
174 | this.removeListener(name, emitter, event);
|
175 | };
|
176 | plugin['emit'] = (event, ...payload) => {
|
177 | this.broadcast(name, event, ...payload);
|
178 | };
|
179 | plugin['call'] = (target, method, ...payload) => {
|
180 | return this.callMethod(name, target, method, ...payload);
|
181 | };
|
182 |
|
183 | plugin['app'] = yield this.createApp(name);
|
184 | plugin['createApp'] = () => this.createApp(name);
|
185 |
|
186 | yield plugin.activate();
|
187 | });
|
188 | }
|
189 | |
190 |
|
191 |
|
192 |
|
193 |
|
194 | deactivatePlugin(name) {
|
195 | return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
196 | if (!this.plugins[name]) {
|
197 | throw new Error(`Cannot deactive plugin ${name} because it's not registered yet`);
|
198 | }
|
199 | const isActive = yield this.manager.isActive(name);
|
200 | if (!isActive)
|
201 | return;
|
202 | const plugin = this.plugins[name];
|
203 |
|
204 | yield plugin.deactivate();
|
205 | this.updateErrorHandler(plugin);
|
206 |
|
207 | delete plugin['app'];
|
208 | delete plugin['createApp'];
|
209 |
|
210 |
|
211 |
|
212 | delete this.events[name];
|
213 |
|
214 | delete this.eventMemory[name];
|
215 |
|
216 | Object.keys(this.listeners).forEach(eventName => {
|
217 | this.listeners[eventName].forEach((listener, i) => {
|
218 | if (listener === name)
|
219 | this.listeners[eventName].splice(i, 1);
|
220 | });
|
221 | });
|
222 | });
|
223 | }
|
224 | |
225 |
|
226 |
|
227 |
|
228 | updateErrorHandler(plugin) {
|
229 | const name = plugin.name;
|
230 |
|
231 | const deactivatedWarning = (message) => {
|
232 | return `Plugin "${name}" is currently deactivated. ${message}. Activate "${name}" first.`;
|
233 | };
|
234 | plugin['call'] = (target, key, ...payload) => {
|
235 | throw new Error(deactivatedWarning(`It cannot call method ${key} of plugin ${target}.`));
|
236 | };
|
237 | plugin['on'] = (target, event) => {
|
238 | throw new Error(deactivatedWarning(`It cannot listen on event ${event} of plugin ${target}.`));
|
239 | };
|
240 | plugin['once'] = (target, event) => {
|
241 | throw new Error(deactivatedWarning(`It cannot listen on event ${event} of plugin ${target}.`));
|
242 | };
|
243 | plugin['off'] = (target, event) => {
|
244 | throw new Error(deactivatedWarning('All event listeners are already removed.'));
|
245 | };
|
246 | plugin['emit'] = (event, ...payload) => {
|
247 | throw new Error(deactivatedWarning(`It cannot emit the event ${event}`));
|
248 | };
|
249 | }
|
250 | |
251 |
|
252 |
|
253 |
|
254 | register(plugins) {
|
255 | const register = (plugin) => {
|
256 | var _a;
|
257 | if (this.plugins[plugin.name]) {
|
258 | throw new Error(`Plugin ${plugin.name} is already register.`);
|
259 | }
|
260 | if (plugin.name === 'manager') {
|
261 | this.registerManager(plugin);
|
262 | }
|
263 | this.plugins[plugin.name] = plugin;
|
264 | (_a = this.manager) === null || _a === void 0 ? void 0 : _a.addProfile(plugin.profile);
|
265 |
|
266 | this.updateErrorHandler(plugin);
|
267 |
|
268 | if (this.setPluginOption) {
|
269 | const options = this.setPluginOption(plugin.profile);
|
270 | plugin.setOptions(options);
|
271 | }
|
272 | if (plugin.onRegistration)
|
273 | plugin.onRegistration();
|
274 | if (this.onRegistration)
|
275 | this.onRegistration(plugin);
|
276 | return plugin.name;
|
277 | };
|
278 | return Array.isArray(plugins) ? plugins.map(register) : register(plugins);
|
279 | }
|
280 |
|
281 | registerManager(manager) {
|
282 | this.manager = manager;
|
283 |
|
284 | this.manager['engineActivatePlugin'] = (name) => this.activatePlugin(name);
|
285 | this.manager['engineDeactivatePlugin'] = (name) => this.deactivatePlugin(name);
|
286 |
|
287 | const profiles = Object.values(this.plugins).map(p => p.profile);
|
288 | this.manager.addProfile(profiles);
|
289 | }
|
290 |
|
291 | remove(names) {
|
292 | const remove = (name) => tslib_1.__awaiter(this, void 0, void 0, function* () {
|
293 | yield this.manager.deactivatePlugin(name);
|
294 | delete this.listeners[name];
|
295 | delete this.plugins[name];
|
296 | });
|
297 | return Array.isArray(names)
|
298 | ? Promise.all(names.map(remove))
|
299 | : remove(names);
|
300 | }
|
301 | |
302 |
|
303 |
|
304 |
|
305 | isRegistered(name) {
|
306 | return !!this.plugins[name];
|
307 | }
|
308 | }
|
309 | exports.Engine = Engine;
|
310 |
|
\ | No newline at end of file |