@polylith/core 0.1.9 → 0.1.11
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/Defer.js +11 -0
- package/EventBus.js +1 -1
- package/Registry.js +161 -4
- package/ServiceObject.js +62 -0
- package/package.json +1 -1
package/Defer.js
ADDED
package/EventBus.js
CHANGED
package/Registry.js
CHANGED
|
@@ -1,31 +1,76 @@
|
|
|
1
1
|
import { ServiceOject } from "./ServiceObject.js";
|
|
2
2
|
import { makeEventable} from "./Eventable.js";
|
|
3
|
+
import Defer from "./Defer.js";
|
|
3
4
|
|
|
5
|
+
/**
|
|
6
|
+
* This calls is the implementation of the serices registry. There will be a
|
|
7
|
+
* single global instance of this class.
|
|
8
|
+
*/
|
|
4
9
|
export class Registry {
|
|
10
|
+
/** Constructor for the registry */
|
|
5
11
|
constructor () {
|
|
6
12
|
this.services = {};
|
|
13
|
+
this.deferreds = {};
|
|
7
14
|
|
|
8
15
|
makeEventable(this);
|
|
9
16
|
}
|
|
10
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Call this method to construct a new service object.
|
|
20
|
+
* @param {String} [name] if pass this is the name of the sercice object
|
|
21
|
+
* service objects can be anonymous
|
|
22
|
+
* @returns the newly created sercice object
|
|
23
|
+
*/
|
|
11
24
|
createServiceObject(name) {
|
|
12
25
|
var result = new ServiceOject(name);
|
|
13
26
|
|
|
14
27
|
return result;
|
|
15
28
|
}
|
|
16
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Call this method to register a new service object. The serice object can '
|
|
32
|
+
* be subscribed to fromn the registry by the given name.
|
|
33
|
+
*
|
|
34
|
+
* @param {String} name the namke of the object being registered.
|
|
35
|
+
* @param {ServiceOject} serviceObject the service object being registered.
|
|
36
|
+
*/
|
|
17
37
|
register(name, serviceObject) {
|
|
18
38
|
this.services[name] = serviceObject;
|
|
19
39
|
}
|
|
20
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Call this method to remove the service object from the registry. Any
|
|
43
|
+
* references thos this service will be retained.
|
|
44
|
+
*
|
|
45
|
+
* @param {String} name the name of the service to remove
|
|
46
|
+
*/
|
|
21
47
|
unregister(name) {
|
|
22
48
|
delete this.services[name];
|
|
23
49
|
}
|
|
24
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Call this method to get a reference to the service object.
|
|
53
|
+
*
|
|
54
|
+
* @param {String} name the registered name of the service object
|
|
55
|
+
* @returns {ServiceOject} the registered service object, ot false if it was
|
|
56
|
+
* not found.
|
|
57
|
+
*/
|
|
25
58
|
subscribe(name) {
|
|
26
59
|
return this.services[name];
|
|
27
60
|
}
|
|
28
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Call this method to create and register a service object and then
|
|
64
|
+
* implement methods on it. The created service object will be added as a
|
|
65
|
+
* member of the implementation object named serviceObject and the methods
|
|
66
|
+
* fire, listen and unlisten will be added as well. When called these
|
|
67
|
+
* methods will operate directly on the service object.
|
|
68
|
+
*
|
|
69
|
+
* @param {String} serviceName the name of the service being implemented.
|
|
70
|
+
* @param {Object} obj the implementation object for the given methods
|
|
71
|
+
* @param {Array.<String>} methodList the list of methods to add. These will
|
|
72
|
+
* be bound to the passed implementation object.
|
|
73
|
+
*/
|
|
29
74
|
makeService(serviceName, obj, methodList) {
|
|
30
75
|
obj.serviceObject = new ServiceOject(serviceName);
|
|
31
76
|
|
|
@@ -52,6 +97,17 @@ export class Registry {
|
|
|
52
97
|
}
|
|
53
98
|
}
|
|
54
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Call this method to extend the service object with a new list of methods.
|
|
102
|
+
* If not already impementated the methods fire, listen and unlisten will be
|
|
103
|
+
* added to the implementation object. If the service doesn't exist, it will
|
|
104
|
+
* be created. It will not be registered.
|
|
105
|
+
*
|
|
106
|
+
* @param {String} serviceName the name of the service being extended
|
|
107
|
+
* @param {*} obj the object implementing the methods
|
|
108
|
+
* @param {Array.<String>} methodList the list of methods to add. These will
|
|
109
|
+
* be bound to the passed implementation object.
|
|
110
|
+
*/
|
|
55
111
|
extendService(serviceName, obj, methodList) {
|
|
56
112
|
var serviceObject = this.subscribe(serviceName);
|
|
57
113
|
|
|
@@ -76,14 +132,40 @@ export class Registry {
|
|
|
76
132
|
}
|
|
77
133
|
}
|
|
78
134
|
|
|
79
|
-
|
|
135
|
+
/**
|
|
136
|
+
* Call this method to invoke an event on every service in the list
|
|
137
|
+
*
|
|
138
|
+
* @param {*} serviceNames the list of services to act on
|
|
139
|
+
* @param {*} event the event to fire
|
|
140
|
+
* @param {*} [preprocess] if provided, this method will be called on all
|
|
141
|
+
* services before firing the events
|
|
142
|
+
* @param {*} [processResult] if provided, this method will be called,
|
|
143
|
+
* passing the result for every event call
|
|
144
|
+
* @returns {Promise.<any>} an array of any promises that were returned from
|
|
145
|
+
* the called methods.
|
|
146
|
+
*/
|
|
147
|
+
callAll(serviceNames, event, preprocess, processResult, ...args ) {
|
|
80
148
|
var promises = [];
|
|
81
149
|
|
|
150
|
+
// run the preprocess callback on each service
|
|
151
|
+
if (preprocess) {
|
|
152
|
+
serviceNames.forEach(function (name) {
|
|
153
|
+
var serviceObject = this.services[name];
|
|
154
|
+
preprocess(serviceObject);
|
|
155
|
+
}, this);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// invoke the event on all services
|
|
82
159
|
serviceNames.forEach(function (name) {
|
|
83
160
|
var serviceObject = this.services[name];
|
|
84
161
|
if (!serviceObject) return;
|
|
85
162
|
|
|
86
|
-
var result = serviceObject.invoke.apply(serviceObject, [
|
|
163
|
+
var result = serviceObject.invoke.apply(serviceObject, [event, ...args]);
|
|
164
|
+
|
|
165
|
+
// now thta we have a result, let the post process have it
|
|
166
|
+
if (processResult) processResult(serviceObject, result)
|
|
167
|
+
|
|
168
|
+
// if the result is a promise, then we add ot the list to
|
|
87
169
|
if (result && result.then) {
|
|
88
170
|
promises.push(result);
|
|
89
171
|
}
|
|
@@ -92,17 +174,92 @@ export class Registry {
|
|
|
92
174
|
return promises;
|
|
93
175
|
}
|
|
94
176
|
|
|
177
|
+
/**
|
|
178
|
+
* Call this method handle the service start deferreds
|
|
179
|
+
*
|
|
180
|
+
* @param {ServiceOject} serviceObject the service object to add the method
|
|
181
|
+
* to
|
|
182
|
+
* @param {*} result the result of the start method
|
|
183
|
+
*/
|
|
184
|
+
resolveDeferred(serviceObject, result) {
|
|
185
|
+
var promise = result?.then ? result : Promise.resolve(true);
|
|
186
|
+
|
|
187
|
+
// wait for the promise to finish to resolve the deferred
|
|
188
|
+
promise.then(function(result) {
|
|
189
|
+
this.deferreds[serviceObject.name].resolve(result)
|
|
190
|
+
}.bind(this)).catch(function(error) {
|
|
191
|
+
this.deferreds[serviceObject.name].reject(result)
|
|
192
|
+
}.bind(this))
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
addWaitMethod(serviceObject) {
|
|
196
|
+
this.deferreds[serviceObject.name] = new Defer()
|
|
197
|
+
|
|
198
|
+
serviceObject.waitStarted = function() {
|
|
199
|
+
// wait on
|
|
200
|
+
return this.deferreds[serviceObject.name].promise;
|
|
201
|
+
}.bind(this)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Call this method to verify that all services have their required services
|
|
206
|
+
* registered. Any services that does not have all requirements will be
|
|
207
|
+
* removed from the registry
|
|
208
|
+
*/
|
|
209
|
+
checkRequirements() {
|
|
210
|
+
var more = true;
|
|
211
|
+
|
|
212
|
+
while (!more) {
|
|
213
|
+
let names = Object.keys(this.services);
|
|
214
|
+
let toRemove = [];
|
|
215
|
+
|
|
216
|
+
names.forEach(function(name) {
|
|
217
|
+
var service = this.services[name];
|
|
218
|
+
var required = service.required;
|
|
219
|
+
|
|
220
|
+
var missing = required.some(function(requirement) {
|
|
221
|
+
if (!requirement in this.services) {
|
|
222
|
+
console.error(`reqiuirement ${requirement} for service ${name} missing. Service removed`)
|
|
223
|
+
}
|
|
224
|
+
return !(requirement in this.services);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
if (missing) toRemove.push(name);
|
|
228
|
+
}, this);
|
|
229
|
+
|
|
230
|
+
more = toRemove.length != 0;
|
|
231
|
+
toRemove.forEach(function(name) {
|
|
232
|
+
delete this.services[name];
|
|
233
|
+
})
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Call this method to initiate the startup sequence. The startup sequence
|
|
239
|
+
* is to first invoke the start method on all the specified registered
|
|
240
|
+
* services. Then, after all returtned promises have settled, the ready
|
|
241
|
+
* method will be invoked. The ready method is assumed to be synchronous
|
|
242
|
+
*
|
|
243
|
+
* @param {String} [prefix] if passed, only sevice that start with the
|
|
244
|
+
* given prefix will have the start process run.
|
|
245
|
+
*
|
|
246
|
+
* @returns a promise that will be resolved when the startup sequence is
|
|
247
|
+
* completed.
|
|
248
|
+
*/
|
|
95
249
|
async start(prefix = '') {
|
|
96
250
|
var names = Object.keys(this.services);
|
|
97
251
|
|
|
252
|
+
this.checkRequirements();
|
|
253
|
+
|
|
254
|
+
|
|
98
255
|
var services = names.filter(function(name) {
|
|
99
256
|
return name.indexOf(prefix) === 0;
|
|
100
257
|
}, this);
|
|
101
258
|
|
|
102
|
-
var promises = this.callAll(services, 'start');
|
|
259
|
+
var promises = this.callAll(services, 'start', this.addWaitMethod.bind(this), this.resolveDeferred.bind(this));
|
|
103
260
|
return Promise.allSettled(promises)
|
|
104
261
|
.then(function () {
|
|
105
|
-
this.callAll(services, 'ready');
|
|
262
|
+
this.callAll(services, 'ready', null, null);
|
|
106
263
|
this.fire('ready', prefix);
|
|
107
264
|
}.bind(this));
|
|
108
265
|
}
|
package/ServiceObject.js
CHANGED
|
@@ -1,14 +1,36 @@
|
|
|
1
1
|
import { EventBus } from './EventBus.js';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* The registry creates an instance of this class for every created service.
|
|
5
|
+
* Service implementations will extend this object by adding methods and events.
|
|
6
|
+
* Service objects are eventable objects, and inherit from the EventBus object
|
|
7
|
+
*/
|
|
3
8
|
export class ServiceOject extends EventBus {
|
|
9
|
+
/**
|
|
10
|
+
* Constructor for the service object
|
|
11
|
+
* @param {String} name the name of the service being registered.
|
|
12
|
+
*/
|
|
4
13
|
constructor(name) {
|
|
5
14
|
super('service:');
|
|
6
15
|
|
|
7
16
|
this.name = name;
|
|
8
17
|
this.bound = true;
|
|
9
18
|
this.methods = [];
|
|
19
|
+
this.required = [];
|
|
10
20
|
}
|
|
11
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Call this method to add a method to the serviceobject that will directly
|
|
24
|
+
* call the event listener for an event. This is a much faster
|
|
25
|
+
* implementation of event handling in the case where there is a single
|
|
26
|
+
* listener. If there is more than a single listener this method will
|
|
27
|
+
* fallback to calling fire.
|
|
28
|
+
*
|
|
29
|
+
* @private
|
|
30
|
+
* @param {String} name the name of the event being bound to a listener.
|
|
31
|
+
* This will be the name of the creted method
|
|
32
|
+
* @param {Function} method the function to call when the method is called
|
|
33
|
+
*/
|
|
12
34
|
assignMethod(name, method) {
|
|
13
35
|
// if there is already a listener, unbind and force invoking
|
|
14
36
|
var bind = this.bound || !this.listeners[name];
|
|
@@ -23,12 +45,21 @@ export class ServiceOject extends EventBus {
|
|
|
23
45
|
this.methods.push(name);
|
|
24
46
|
}
|
|
25
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Call this method to unbind the method and make it instead fire an event.
|
|
50
|
+
* when called
|
|
51
|
+
*
|
|
52
|
+
* @param {String} name the name of the method to unbind
|
|
53
|
+
*/
|
|
26
54
|
unbindMethod(name) {
|
|
27
55
|
if (this[name]) {
|
|
28
56
|
this[name] = this.invoke.bind(this, name)
|
|
29
57
|
}
|
|
30
58
|
}
|
|
31
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Call this method to unbind all methods and prevent future binding.
|
|
62
|
+
*/
|
|
32
63
|
unbind() {
|
|
33
64
|
this.bound = false;
|
|
34
65
|
this.methods.forEach(function(name) {
|
|
@@ -36,6 +67,13 @@ export class ServiceOject extends EventBus {
|
|
|
36
67
|
}, this)
|
|
37
68
|
}
|
|
38
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Call this method to bind a number of methods to the service object.
|
|
72
|
+
*
|
|
73
|
+
* @param {Object} methods a map of methods to implement. The key is the
|
|
74
|
+
* method name, the value is the method to call.
|
|
75
|
+
*
|
|
76
|
+
*/
|
|
39
77
|
implement(methods) {
|
|
40
78
|
var names = Object.keys(methods);
|
|
41
79
|
|
|
@@ -49,7 +87,31 @@ export class ServiceOject extends EventBus {
|
|
|
49
87
|
}, this);
|
|
50
88
|
}
|
|
51
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Call this method to safely invoke a method. This is a semantic function
|
|
92
|
+
* to conceptually separate methods from events, although they are
|
|
93
|
+
* implemented the same.
|
|
94
|
+
*
|
|
95
|
+
* @param {String} name the name of the method
|
|
96
|
+
* @param {...any} args the paramaters to pass
|
|
97
|
+
* @returns
|
|
98
|
+
*/
|
|
52
99
|
invoke(name, ...args) {
|
|
53
100
|
return this.fire(name, ...args);
|
|
54
101
|
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Call this method to add a list of service names that this service
|
|
105
|
+
* depends on
|
|
106
|
+
*
|
|
107
|
+
* @param {*} services array of services
|
|
108
|
+
*/
|
|
109
|
+
require(services) {
|
|
110
|
+
this.required = [...this.required, ...services];
|
|
111
|
+
|
|
112
|
+
this.required = [...new Set(this.required)];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
|
|
55
117
|
}
|