@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 ADDED
@@ -0,0 +1,11 @@
1
+
2
+ export default class Defer {
3
+ constructor()
4
+ {
5
+ this.promise = new Promise(function(resolve, reject)
6
+ {
7
+ this.resolve = resolve;
8
+ this.reject = reject;
9
+ }.bind(this));
10
+ }
11
+ }
package/EventBus.js CHANGED
@@ -7,7 +7,7 @@ export class EventBus {
7
7
  }
8
8
 
9
9
  listen (eventName, cb) {
10
- var listenerId = uuid.v1();
10
+ var listenerId = uuid.v4();
11
11
  var name = this.prefix + eventName;
12
12
 
13
13
  this.listeners[name] = this.listeners[name] || [];
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
- callAll(serviceNames, which, ...args) {
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, [which, ...args]);
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
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@polylith/core",
3
3
  "access": "public",
4
- "version": "0.1.9",
4
+ "version": "0.1.11",
5
5
  "description": "Core of the client-side polylith framework",
6
6
  "main": "index.js",
7
7
  "repository": {