UNPKG

10.7 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.PluginManager = void 0;
4const tslib_1 = require("tslib");
5const plugin_api_1 = require("@remixproject/plugin-api");
6const abstract_1 = require("./abstract");
7/**
8 * Wait for all promises to settle
9 * catch if one of them fail
10 */
11function catchAllPromises(promises) {
12 return new Promise((res, rej) => {
13 const resolved = [];
14 const rejected = [];
15 let ended = 0;
16 const settle = (value, err) => {
17 if (err)
18 rejected.push(err);
19 if (value)
20 resolved.push(value);
21 if (++ended === promises.length) {
22 rejected.length ? rej(resolved) : res(rejected);
23 }
24 };
25 for (const promise of promises) {
26 promise
27 .then(value => settle(value, null))
28 .catch(err => settle(null, err));
29 }
30 });
31}
32class PluginManager extends abstract_1.Plugin {
33 constructor(profile = plugin_api_1.pluginManagerProfile) {
34 super(profile);
35 this.profiles = {};
36 this.actives = [];
37 }
38 /** Return the name of the caller. If no request provided, this mean that the method has been called from the IDE so we use "manager" */
39 get requestFrom() {
40 var _a;
41 return ((_a = this.currentRequest) === null || _a === void 0 ? void 0 : _a.from) || 'manager';
42 }
43 /** Run engine activation. Implemented by Engine */
44 engineActivatePlugin(name) {
45 const error = `You cannot activate plugin "${name}", manager plugin is not register yet. `;
46 const solution = 'Run "engine.register(manager)" first';
47 throw new Error(error + solution);
48 }
49 /** Run engine deactivation. Implemented by Engine */
50 engineDeactivatePlugin(name) {
51 const error = `You cannot deactivate plugin "${name}", manager plugin is not register yet. `;
52 const solution = 'Run "engine.register(manager)" first';
53 throw new Error(error + solution);
54 }
55 /**
56 * Get the profile if it's registered.
57 * @param name The name of the plugin
58 * @note This method can be overrided
59 */
60 getProfile(name) {
61 return tslib_1.__awaiter(this, void 0, void 0, function* () {
62 return this.profiles[name];
63 });
64 }
65 /** Get all the profiles of the manager */
66 getProfiles() {
67 return Object.values(this.profiles);
68 }
69 /** Get all active profiles of the manager */
70 getActiveProfiles() {
71 return this.actives.map(name => this.profiles[name]);
72 }
73 /**
74 * Update the profile of the plugin
75 * @param profile The Updated version of the plugin
76 * @note Only the caller plugin should be able to update its profile
77 */
78 updateProfile(to) {
79 return tslib_1.__awaiter(this, void 0, void 0, function* () {
80 if (!to)
81 return;
82 if (!this.profiles[to.name]) {
83 throw new Error(`Plugin ${to.name} is not register, you cannot update it's profile.`);
84 }
85 const from = yield this.getProfile(this.requestFrom);
86 yield this.canUpdateProfile(from, to);
87 this.profiles[to.name] = Object.assign(Object.assign({}, this.profiles[to.name]), to);
88 this.emit('profileUpdated', this.profiles[to.name]);
89 });
90 }
91 /**
92 * Add a profile to the list of profile
93 * @param profile The profile to add
94 * @note This method should only be used by the engine
95 */
96 addProfile(profiles) {
97 const add = (profile) => {
98 if (this.profiles[profile.name]) {
99 throw new Error(`Plugin ${profile.name} already exist`);
100 }
101 this.profiles[profile.name] = profile;
102 // emit only if manager is already activated
103 if (this.actives.includes('manager')) {
104 this.emit('profileAdded', profile);
105 }
106 if (this.onProfileAdded) {
107 this.onProfileAdded(profile);
108 }
109 };
110 return Array.isArray(profiles) ? profiles.map(add) : add(profiles);
111 }
112 /**
113 * Verify if a plugin is currently active
114 * @param name Name of the plugin
115 */
116 isActive(name) {
117 return tslib_1.__awaiter(this, void 0, void 0, function* () {
118 return this.actives.includes(name);
119 });
120 }
121 /**
122 * Check if caller can activate plugin and activate it if authorized
123 * @param name The name of the plugin to activate
124 */
125 activatePlugin(names) {
126 return tslib_1.__awaiter(this, void 0, void 0, function* () {
127 if (!this.actives.includes('manager')) {
128 yield this.toggleActive('manager');
129 }
130 const activate = (name) => tslib_1.__awaiter(this, void 0, void 0, function* () {
131 const isActive = yield this.isActive(name);
132 if (isActive)
133 return;
134 const [to, from] = yield Promise.all([
135 this.getProfile(name),
136 this.getProfile(this.requestFrom)
137 ]);
138 const canActivate = yield this.canActivatePlugin(from, to);
139 if (canActivate) {
140 yield this.toggleActive(name);
141 }
142 else {
143 throw new Error(`Plugin ${this.requestFrom} has no right to activate plugin ${name}`);
144 }
145 });
146 return Array.isArray(names) ? catchAllPromises(names.map(activate)) : activate(names);
147 });
148 }
149 /**
150 * Check if caller can deactivate plugin and deactivate it if authorized
151 * @param name The name of the plugin to activate
152 */
153 deactivatePlugin(names) {
154 return tslib_1.__awaiter(this, void 0, void 0, function* () {
155 const deactivate = (name) => tslib_1.__awaiter(this, void 0, void 0, function* () {
156 const isActive = yield this.isActive(name);
157 if (!isActive)
158 return;
159 const [to, from] = yield Promise.all([
160 this.getProfile(name),
161 this.getProfile(this.requestFrom)
162 ]);
163 // Manager should have all right else plugin could totally block deactivation
164 if (from.name === 'manager') {
165 return this.toggleActive(name);
166 }
167 // Check manager rules
168 const managerCanDeactivate = yield this.canDeactivatePlugin(from, to);
169 if (!managerCanDeactivate) {
170 throw new Error(`Plugin ${this.requestFrom} has no right to deactivate plugin ${name}`);
171 }
172 // Ask plugin, if it wasn't the one which called on the first place
173 const pluginCanDeactivate = from.name !== to.name ? yield this.call(to.name, 'canDeactivate', from) : true;
174 if (!pluginCanDeactivate) {
175 throw new Error(`Plugin ${this.requestFrom} has no right to deactivate plugin ${name}`);
176 }
177 return this.toggleActive(name);
178 });
179 return Array.isArray(names) ? catchAllPromises(names.map(deactivate)) : deactivate(names);
180 });
181 }
182 /**
183 * Activate or deactivate by bypassing permission
184 * @param name The name of the plugin to activate
185 * @note This method should ONLY be used by the IDE
186 */
187 toggleActive(names) {
188 return tslib_1.__awaiter(this, void 0, void 0, function* () {
189 const toggle = (name) => tslib_1.__awaiter(this, void 0, void 0, function* () {
190 const [isActive, profile] = yield Promise.all([
191 this.isActive(name),
192 this.getProfile(name)
193 ]);
194 if (isActive) {
195 yield this.engineDeactivatePlugin(name);
196 this.actives = this.actives.filter(pluginName => pluginName !== name);
197 this.emit('pluginDeactivated', profile);
198 if (this.onPluginDeactivated) {
199 this.onPluginDeactivated(profile);
200 }
201 }
202 else {
203 yield this.engineActivatePlugin(name);
204 this.actives.push(name);
205 this.emit('pluginActivated', profile);
206 if (this.onPluginActivated) {
207 this.onPluginActivated(profile);
208 }
209 }
210 });
211 return Array.isArray(names) ? Promise.all(names.map(toggle)) : toggle(names);
212 });
213 }
214 /**
215 * Check if a plugin can activate another
216 * @param from Profile of the caller plugin
217 * @param to Profile of the target plugin
218 * @note This method should be overrided
219 */
220 canActivatePlugin(from, to) {
221 return tslib_1.__awaiter(this, void 0, void 0, function* () {
222 return true;
223 });
224 }
225 /**
226 * Check if a plugin can deactivate another
227 * @param from Profile of the caller plugin
228 * @param to Profile of the target plugin
229 * @note This method should be overrided
230 */
231 canDeactivatePlugin(from, to) {
232 return tslib_1.__awaiter(this, void 0, void 0, function* () {
233 if (from.name === 'manager' || from.name === to.name) {
234 return true;
235 }
236 return false;
237 });
238 }
239 /**
240 * Check if a plugin can call a method of another
241 * @param from Profile of the caller plugin
242 * @param to Profile of the target plugin
243 * @param method Method targetted by the caller
244 * @param message Method provided by the targetted method plugin
245 */
246 canCall(from, to, method, message) {
247 return tslib_1.__awaiter(this, void 0, void 0, function* () {
248 return true;
249 });
250 }
251 /**
252 * Check if a plugin can update profile of another one
253 * @param from Profile of the caller plugin
254 * @param to Updates on the profile of the target plugin
255 * @note This method can be overrided
256 */
257 canUpdateProfile(from, to) {
258 return tslib_1.__awaiter(this, void 0, void 0, function* () {
259 if (to.name && from.name !== to.name) {
260 throw new Error('A plugin cannot change its name.');
261 }
262 if (to['url'] && to['url'] !== this.profiles[to.name]['url']) {
263 throw new Error('Url from Profile cannot be updated.');
264 }
265 return true;
266 });
267 }
268}
269exports.PluginManager = PluginManager;
270//# sourceMappingURL=manager.js.map
\No newline at end of file