comes 0.0.1 → 0.0.3

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/build/index.d.ts CHANGED
@@ -1,9 +1,5 @@
1
- /**
2
- * Function to exclude an item from the array.
3
- * @param item Item that will be removed
4
- * @param list Array where to find the item
5
- */
6
- export declare function deleteFromArray(item: any, list: any[]): void;
1
+ export type EventListenerType<T = any> = (event: T) => void;
2
+ export type EventInterceptorType<T = any> = (id: string, event: T, es: EventSystem) => T;
7
3
  /**
8
4
  * Type that holds the value for an event address.
9
5
  */
@@ -15,7 +11,7 @@ export type ES_ValueType = {
15
11
  /**
16
12
  * Listeners of this event.
17
13
  */
18
- listeners: ((event: any) => void)[];
14
+ listeners: EventListenerType[];
19
15
  /**
20
16
  * Last time an event was emitted.
21
17
  *
@@ -30,11 +26,35 @@ export type ES_ValueType = {
30
26
  * If some event/value was emitted before the first listener registered, so
31
27
  * this function will not be called.
32
28
  *
33
- * @param id The address name
29
+ * @param id The address name of the event
34
30
  * @param args Extra parameters to the function
35
- * @returns
31
+ * @returns void
36
32
  */
37
33
  loader?: (id: string, ...args: any[]) => void;
34
+ /**
35
+ * Error handler of the loader. If the execution of the loader throw
36
+ * an Error/Exception then this function will be called with the
37
+ * exception.
38
+ *
39
+ * This function can be async, and will be awaited.
40
+ *
41
+ * If this functions returns a value (not undefined), then the loader will
42
+ * resolve successfully with that value.
43
+ * If this function does not return a value, then loader will reject with the
44
+ * original exception.
45
+ * If this function throws an exception, then the loader will reject with that exception,
46
+ * and the original exception of the loader will be in the "loaderEx" field of that exception.
47
+ *
48
+ * @param id The address name of the event
49
+ * @param ex Exception thrown by the loader
50
+ * @returns void
51
+ */
52
+ loaderCatch?: (id: string, ex: Error | any) => any;
53
+ /**
54
+ * Promise resolved when loader ends execution.
55
+ * If some error occurss then the "loaderCatch" function
56
+ * will be called.
57
+ */
38
58
  loaderProm?: Promise<any>;
39
59
  };
40
60
  /**
@@ -48,18 +68,30 @@ export declare class EventSystem {
48
68
  data: {
49
69
  [id: string]: ES_ValueType;
50
70
  };
71
+ /**
72
+ * Interceptors to transform data.
73
+ * Interceptors are called in the installation order.
74
+ * The special empty string event address will be called for all events sent.
75
+ *
76
+ * The interceptors exists to transform, monitor, prepare or validate (or many other uses)
77
+ * the event sent to an address. The sequence of interceptors will be called like a
78
+ * "chain of responsibility" pattern, if any exception is thrown the following interceptors
79
+ * will not be executed, and the listeners not called.
80
+ */
81
+ inters: {
82
+ [id: string]: EventInterceptorType[];
83
+ };
51
84
  /**
52
85
  * Gets a reference to {@link ES_ValueType} of the address informed.
53
- * @param id The address name
86
+ * @param id The address name of the event
54
87
  */
55
88
  get(id: string): ES_ValueType;
56
89
  /**
57
90
  * Sends the value to the listeners of the event address.
58
- * @param id The address name
91
+ * @param id The address name of the event
59
92
  * @param event The value to send to listeners
60
- * @returns The value informed
61
93
  */
62
- emit<T>(id: string, event: T): T;
94
+ send<T>(id: string, event: T): Promise<T>;
63
95
  /**
64
96
  * Register a listener for the address.
65
97
  * The listener will receive the last value emitted.
@@ -67,25 +99,84 @@ export declare class EventSystem {
67
99
  *
68
100
  * The listener will be called with the last value available in the cache.
69
101
  *
70
- * @param id The address name
102
+ * @param id The address name of the event
71
103
  * @param listener The listener, will be called every time a new value is emitted to this address
72
104
  * @returns An unregister function. Use this function to remove the listener
73
105
  */
74
- listen(id: string, listener: (event: any) => void): () => void;
75
- unlisten(id: string, listener: (event: any) => void): void;
106
+ listen(id: string, listener: EventListenerType): () => void;
76
107
  /**
77
- * Configure a loader to execute when the first listener is registered and no value exists yet.
108
+ * Remove the listener from the list of this event address.
109
+ *
110
+ * If you need to remove a listener from the event address inside the exection of
111
+ * the listener, then use this function like so:
112
+ * ```ts
113
+ * const listener = () => {
114
+ * es.unlisten(ES_EVENT_NAME, listener);
115
+ * // then do what you need...
116
+ * // ...
117
+ * };
118
+ * es.listen(ES_EVENT_NAME, listener);
119
+ * ```
120
+ * This is because in some cases the return value of the {@link EventSystem.listen listen}
121
+ * will not be ready to unregister the listener (the variable will not be set yet).
78
122
  *
79
- * @param id The address name
123
+ * @param id The address name of the event
124
+ * @param listener Listener to remove
125
+ */
126
+ unlisten(id: string, listener: EventListenerType): void;
127
+ /**
128
+ * Add an interceptor to transform data.
129
+ * Interceptors are called in the installation order.
130
+ * The special empty string event address will be called for all events sent.
131
+ *
132
+ * The interceptors exists to transform, monitor, prepare or validate (or many other uses)
133
+ * the event sent to an address. The sequence of interceptors will be called like a
134
+ * "chain of responsibility" pattern, if any exception is thrown the following interceptors
135
+ * will not be executed, and the listeners not called.
136
+ *
137
+ * Interceptors will be executed synchronous in the call of the {@link send} method.
138
+ * So any exception thrown will be thrown also in the {@link send} call.
139
+ *
140
+ * @param id The address name of the event
141
+ * @param listener Interceptor to add
142
+ * @returns An unregister function. Use this function to remove the interceptor
143
+ */
144
+ addInter(id: string, listener: EventInterceptorType): () => void;
145
+ /**
146
+ * Remove an interceptor.
147
+ *
148
+ * @param id The address name of the event
149
+ * @param listener Interceptor to add
150
+ * @returns An unregister function. Use this function to remove the interceptor
151
+ */
152
+ removeInter(id: string, listener: EventInterceptorType): void;
153
+ /**
154
+ * Configure a {@link ES_ValueType.loader loader} to execute when the first listener is registered and no value exists yet.
155
+ *
156
+ * Remember to configure a Error Handler ({@link ES_ValueType.loaderCatch loaderCatch}) before configuring
157
+ * the {@link ES_ValueType.loader loader}, or use the 3o argumento to
158
+ * configure the {@link ES_ValueType.loaderCatch loaderCatch}.
159
+ *
160
+ * If any listener is registered, and no value was emitted yet, then the loader will
161
+ * be called by this method to fetch the first value of the event address.
162
+ *
163
+ * @param id The address name of the event
80
164
  * @param loader The function to execute when the first liteners is registered
81
165
  * and no value exists yet.
166
+ * @param loaderCatch The error handler for exceptions thrown by the {@link ES_ValueType.loader loader}
82
167
  */
83
- setLoader(id: string, loader: ES_ValueType['loader']): void;
168
+ setLoader(id: string, loader: ES_ValueType['loader'], loaderCatch?: ES_ValueType['loaderCatch']): void;
169
+ setLoaderCatch(id: string, loaderCatch: ES_ValueType['loaderCatch']): void;
84
170
  /**
85
- * Executes the loader for an address.
86
- * @param id The address name
87
- * @param args Arguments to pass to the loader
88
- * @returns Promise that will resolve when the loader resolves
171
+ * Executes the {@link ES_ValueType.loader loader} for an address.
172
+ *
173
+ * If some error hapens in the {@link ES_ValueType.loader loader} function, then the {@link ES_ValueType.loaderCatch loaderCatch} will be
174
+ * called with the error (if configured).
175
+ * Check the {@link ES_ValueType.loaderCatch loaderCatch} docs to understand the behavior.
176
+ *
177
+ * @param id The address name of the event
178
+ * @param args Arguments to pass to the {@link ES_ValueType.loader loader}
179
+ * @returns Promise that will resolve when the {@link ES_ValueType.loader loader} resolves
89
180
  */
90
181
  load<T>(id: string, ...args: any[]): Promise<() => T>;
91
182
  }
package/build/index.js CHANGED
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.es = exports.EventSystem = void 0;
4
- exports.deleteFromArray = deleteFromArray;
5
4
  // -------
6
5
  /**
7
6
  * Function to exclude an item from the array.
@@ -27,10 +26,21 @@ class EventSystem {
27
26
  * holds the last value for the address.
28
27
  */
29
28
  this.data = {};
29
+ /**
30
+ * Interceptors to transform data.
31
+ * Interceptors are called in the installation order.
32
+ * The special empty string event address will be called for all events sent.
33
+ *
34
+ * The interceptors exists to transform, monitor, prepare or validate (or many other uses)
35
+ * the event sent to an address. The sequence of interceptors will be called like a
36
+ * "chain of responsibility" pattern, if any exception is thrown the following interceptors
37
+ * will not be executed, and the listeners not called.
38
+ */
39
+ this.inters = {};
30
40
  }
31
41
  /**
32
42
  * Gets a reference to {@link ES_ValueType} of the address informed.
33
- * @param id The address name
43
+ * @param id The address name of the event
34
44
  */
35
45
  get(id) {
36
46
  if (!this.data[id])
@@ -39,17 +49,34 @@ class EventSystem {
39
49
  }
40
50
  /**
41
51
  * Sends the value to the listeners of the event address.
42
- * @param id The address name
52
+ * @param id The address name of the event
43
53
  * @param event The value to send to listeners
44
- * @returns The value informed
45
54
  */
46
- emit(id, event) {
55
+ async send(id, event) {
56
+ let inters = [...(this.inters[''] || []), ...(this.inters[id] || [])];
57
+ let newValue = event;
58
+ try {
59
+ for (let inte of inters) {
60
+ newValue = await inte(id, newValue, this);
61
+ }
62
+ }
63
+ catch (err) {
64
+ console.error(`error on interceptor of ${id}`, err);
65
+ throw err;
66
+ }
47
67
  let esData = this.get(id);
48
- esData.last = event;
68
+ esData.last = newValue;
49
69
  esData.date = new Date();
50
- for (let func of esData.listeners)
51
- func(event);
52
- return event;
70
+ for (let func of esData.listeners) {
71
+ try {
72
+ await func(newValue);
73
+ }
74
+ catch (err) {
75
+ console.error(`Error on listener of ${id}, ${func}`, err);
76
+ throw err;
77
+ }
78
+ }
79
+ return newValue;
53
80
  }
54
81
  /**
55
82
  * Register a listener for the address.
@@ -58,7 +85,7 @@ class EventSystem {
58
85
  *
59
86
  * The listener will be called with the last value available in the cache.
60
87
  *
61
- * @param id The address name
88
+ * @param id The address name of the event
62
89
  * @param listener The listener, will be called every time a new value is emitted to this address
63
90
  * @returns An unregister function. Use this function to remove the listener
64
91
  */
@@ -68,40 +95,135 @@ class EventSystem {
68
95
  if (esData.date)
69
96
  listener(esData.last);
70
97
  else if (esData.loader)
71
- exports.es.load(id);
98
+ esData.loaderProm = this.load(id);
72
99
  return () => this.unlisten(id, listener);
73
100
  }
101
+ /**
102
+ * Remove the listener from the list of this event address.
103
+ *
104
+ * If you need to remove a listener from the event address inside the exection of
105
+ * the listener, then use this function like so:
106
+ * ```ts
107
+ * const listener = () => {
108
+ * es.unlisten(ES_EVENT_NAME, listener);
109
+ * // then do what you need...
110
+ * // ...
111
+ * };
112
+ * es.listen(ES_EVENT_NAME, listener);
113
+ * ```
114
+ * This is because in some cases the return value of the {@link EventSystem.listen listen}
115
+ * will not be ready to unregister the listener (the variable will not be set yet).
116
+ *
117
+ * @param id The address name of the event
118
+ * @param listener Listener to remove
119
+ */
74
120
  unlisten(id, listener) {
75
121
  let esData = this.get(id);
76
122
  deleteFromArray(listener, esData.listeners);
77
123
  }
78
124
  /**
79
- * Configure a loader to execute when the first listener is registered and no value exists yet.
125
+ * Add an interceptor to transform data.
126
+ * Interceptors are called in the installation order.
127
+ * The special empty string event address will be called for all events sent.
128
+ *
129
+ * The interceptors exists to transform, monitor, prepare or validate (or many other uses)
130
+ * the event sent to an address. The sequence of interceptors will be called like a
131
+ * "chain of responsibility" pattern, if any exception is thrown the following interceptors
132
+ * will not be executed, and the listeners not called.
133
+ *
134
+ * Interceptors will be executed synchronous in the call of the {@link send} method.
135
+ * So any exception thrown will be thrown also in the {@link send} call.
136
+ *
137
+ * @param id The address name of the event
138
+ * @param listener Interceptor to add
139
+ * @returns An unregister function. Use this function to remove the interceptor
140
+ */
141
+ addInter(id, listener) {
142
+ if (!this.inters[id])
143
+ this.inters[id] = [];
144
+ this.inters[id].push(listener);
145
+ return () => this.removeInter(id, listener);
146
+ }
147
+ /**
148
+ * Remove an interceptor.
149
+ *
150
+ * @param id The address name of the event
151
+ * @param listener Interceptor to add
152
+ * @returns An unregister function. Use this function to remove the interceptor
153
+ */
154
+ removeInter(id, listener) {
155
+ deleteFromArray(listener, this.inters[id]);
156
+ }
157
+ /**
158
+ * Configure a {@link ES_ValueType.loader loader} to execute when the first listener is registered and no value exists yet.
159
+ *
160
+ * Remember to configure a Error Handler ({@link ES_ValueType.loaderCatch loaderCatch}) before configuring
161
+ * the {@link ES_ValueType.loader loader}, or use the 3o argumento to
162
+ * configure the {@link ES_ValueType.loaderCatch loaderCatch}.
163
+ *
164
+ * If any listener is registered, and no value was emitted yet, then the loader will
165
+ * be called by this method to fetch the first value of the event address.
80
166
  *
81
- * @param id The address name
167
+ * @param id The address name of the event
82
168
  * @param loader The function to execute when the first liteners is registered
83
169
  * and no value exists yet.
170
+ * @param loaderCatch The error handler for exceptions thrown by the {@link ES_ValueType.loader loader}
84
171
  */
85
- setLoader(id, loader) {
172
+ setLoader(id, loader, loaderCatch) {
86
173
  let esData = this.get(id);
87
174
  esData.loader = loader;
175
+ if (loaderCatch)
176
+ esData.loaderCatch = loaderCatch;
88
177
  if (!esData.date && esData.listeners.length)
89
- this.load(id);
178
+ esData.loaderProm = this.load(id);
179
+ }
180
+ setLoaderCatch(id, loaderCatch) {
181
+ let esData = this.get(id);
182
+ esData.loaderCatch = loaderCatch;
90
183
  }
91
184
  /**
92
- * Executes the loader for an address.
93
- * @param id The address name
94
- * @param args Arguments to pass to the loader
95
- * @returns Promise that will resolve when the loader resolves
185
+ * Executes the {@link ES_ValueType.loader loader} for an address.
186
+ *
187
+ * If some error hapens in the {@link ES_ValueType.loader loader} function, then the {@link ES_ValueType.loaderCatch loaderCatch} will be
188
+ * called with the error (if configured).
189
+ * Check the {@link ES_ValueType.loaderCatch loaderCatch} docs to understand the behavior.
190
+ *
191
+ * @param id The address name of the event
192
+ * @param args Arguments to pass to the {@link ES_ValueType.loader loader}
193
+ * @returns Promise that will resolve when the {@link ES_ValueType.loader loader} resolves
96
194
  */
97
195
  async load(id, ...args) {
98
196
  let esData = this.get(id);
99
- if (!esData.loader)
100
- return esData.last;
101
- esData.loaderProm = new Promise(async (res) => {
102
- await esData.loader(id, ...args);
197
+ if (!esData.loader) {
103
198
  esData.loaderProm = undefined;
104
- res(esData.last);
199
+ return esData.last;
200
+ }
201
+ esData.loaderProm = new Promise(async (res, rej) => {
202
+ try {
203
+ await esData.loader(id, ...args);
204
+ esData.loaderProm = undefined;
205
+ res(esData.last);
206
+ }
207
+ catch (ex) {
208
+ if (esData.loaderCatch) {
209
+ try {
210
+ const catchRes = await esData.loaderCatch(id, ex);
211
+ if (catchRes !== undefined) {
212
+ this.send(id, catchRes);
213
+ res(catchRes);
214
+ }
215
+ }
216
+ catch (ex2) {
217
+ try {
218
+ ex2.loaderEx = ex;
219
+ }
220
+ catch (ex3) { /* do nothing */ }
221
+ rej(ex2);
222
+ }
223
+ }
224
+ else
225
+ rej(ex);
226
+ }
105
227
  });
106
228
  return esData.loaderProm;
107
229
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "comes",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "Simple Event System Communication",
5
5
  "keywords": [
6
6
  "event",