readiness-manager 1.2.0-rc.xf50ab1c7f → 1.2.0-rc.xf8d667095

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "readiness-manager",
3
- "version": "1.2.0-rc.xf50ab1c7f",
3
+ "version": "1.2.0-rc.xf8d667095",
4
4
  "description": "👨‍💼 Define when your app is ready",
5
5
  "main": "./dist/src/index.js",
6
6
  "dependencies": {
package/src/error.ts CHANGED
@@ -9,7 +9,15 @@ export const ERRORS = {
9
9
  BEACON_EXECUTION_FAILED: 'Beacon execution failed',
10
10
  } as const;
11
11
 
12
+ /**
13
+ * The retry function to be triggered upon beacon errors.
14
+ * @returns {Promise<unknown>}
15
+ */
12
16
  export type ActionRetry = () => Promise<unknown>;
17
+
18
+ /**
19
+ * The error handler to be triggered upon beacon errors.
20
+ */
13
21
  export type ActionErrorHandler = (error: ActionExecutionError, retry: ActionRetry) => void;
14
22
 
15
23
  /**
package/src/index.ts CHANGED
@@ -1,35 +1,6 @@
1
1
  import { logger } from '@fiverr-private/obs';
2
2
  import { ERRORS, ActionExecutionError, defaultErrorHandler, ActionErrorHandler } from './error';
3
- import { BeaconStatus, ActionStatus } from './constants';
4
-
5
- export type ReadyCallback = () => void;
6
- export type ReadyAction = () => unknown | Promise<unknown>;
7
-
8
- export interface ActionsStatus {
9
- [status: string]: string[];
10
- }
11
-
12
- interface Beacon {
13
- name: string;
14
- action: ReadyAction;
15
- status: ActionStatus;
16
- callbacks: ReadyCallback[];
17
- debug?: boolean;
18
- }
19
-
20
- interface BeaconsMap {
21
- [name: string]: Beacon;
22
- }
23
-
24
- /**
25
- * Sets of private attributes which should not be exposed / accessed by consumer.
26
- */
27
- const beaconsSymbol = Symbol('beacons');
28
- const callbacksSymbol = Symbol('callbacks');
29
- const executeSymbol = Symbol('execute');
30
- const trackBeaconUpdateSymbol = Symbol('trackBeaconUpdate');
31
- const updateBeaconSymbol = Symbol('updateBeacon');
32
- const errorHandlerSymbol = Symbol('errorHandler');
3
+ import type { ActionStatus, ReadyAction, ReadyCallback, ActionsStatus, BeaconsMap, Beacon } from './types';
33
4
 
34
5
  /**
35
6
  * The internal "ready" state to determine process readiness.
@@ -46,21 +17,29 @@ export interface ReadinessManager {
46
17
  ready: boolean;
47
18
  }
48
19
 
20
+ /**
21
+ * The internal implementation of the ReadinessManager.
22
+ * @example
23
+ * const readinessManager = new ReadinessManager();
24
+ * readinessManager.register('database', () => connectToDB());
25
+ * readinessManager.register('redis', () => connectToRedis());
26
+ * readinessManager.run();
27
+ * readinessManager.onReady(() => console.log('App is ready! o/'));
28
+ * @returns {ReadinessManager}
29
+ */
49
30
  class ReadinessManagerImpl implements ReadinessManager {
50
- private [beaconsSymbol]: BeaconsMap;
51
- private [callbacksSymbol]: ReadyCallback[];
52
- private [errorHandlerSymbol]: ActionErrorHandler;
31
+ #beacons: BeaconsMap;
32
+ #callbacks: ReadyCallback[];
33
+ #errorHandler: ActionErrorHandler;
53
34
 
54
35
  constructor() {
55
- this[beaconsSymbol] = {};
56
- this[callbacksSymbol] = [];
57
- this[errorHandlerSymbol] = defaultErrorHandler;
58
-
59
- this[executeSymbol] = this[executeSymbol].bind(this);
36
+ this.#beacons = {};
37
+ this.#callbacks = [];
38
+ this.#errorHandler = defaultErrorHandler;
60
39
  }
61
40
 
62
41
  /**
63
- * Returns true if current process state is considered as "ready".
42
+ * Returns `true` if all the registered actions has been ran without exceptions/rejections.
64
43
  */
65
44
  get ready(): boolean {
66
45
  return readyState;
@@ -70,7 +49,7 @@ class ReadinessManagerImpl implements ReadinessManager {
70
49
  * Returns current registered actions status
71
50
  */
72
51
  status(): ActionsStatus {
73
- return Object.values(this[beaconsSymbol]).reduce((acc, { name, status }) => {
52
+ return Object.values(this.#beacons).reduce((acc, { name, status }) => {
74
53
  const current = acc[status] || [];
75
54
  return Object.assign(acc, { [status]: [...current, name] });
76
55
  }, {} as ActionsStatus);
@@ -78,16 +57,18 @@ class ReadinessManagerImpl implements ReadinessManager {
78
57
 
79
58
  /**
80
59
  * Registers a given name and action as beacon under manager.
60
+ * registered actions will be observed once ran in order to determine the process readiness state.
81
61
  * @param name - The operation name to register with.
82
62
  * @param action - The ready emitter to condition the process readiness with.
83
- * @param debug - Optional flag to enable debug timing logs.
63
+ * @param debug - (Optional) flag to enable timing logs (default: `false`).
64
+ * @throws {Error} If the action name is already registered.
84
65
  */
85
66
  register(name: string, action: ReadyAction, debug = false): void {
86
67
  if (this.ready) {
87
68
  return;
88
69
  }
89
70
 
90
- if (this[beaconsSymbol][name]) {
71
+ if (this.#beacons[name]) {
91
72
  throw new Error(ERRORS.BEACON_ALREADY_EXISTS);
92
73
  }
93
74
 
@@ -95,27 +76,30 @@ class ReadinessManagerImpl implements ReadinessManager {
95
76
  name,
96
77
  action,
97
78
  callbacks: [],
98
- status: BeaconStatus.NOT_STARTED,
79
+ status: 'not_started',
99
80
  debug,
100
81
  };
101
82
 
102
- Object.assign(this[beaconsSymbol], { [name]: beacon });
83
+ Object.assign(this.#beacons, { [name]: beacon });
103
84
  }
104
85
 
105
86
  /**
106
- * Runs current registered beacons execution to determine process "ready" state.
87
+ *
88
+ * Runs all registered actions. Once all actions are resolved successfully, your app state will be determined as `ready`.
89
+ * @returns {ReadinessManager}
107
90
  */
108
91
  run(): ReadinessManager {
109
92
  readyState = false;
110
93
 
111
- Object.values(this[beaconsSymbol]).forEach((beacon) => this[executeSymbol](beacon));
94
+ Object.values(this.#beacons).forEach((beacon) => this.#execute(beacon));
112
95
 
113
96
  return this;
114
97
  }
115
98
 
116
99
  /**
117
- * Registers a given callback to be triggered once process is marked as "ready".
118
- * @param callback
100
+ * Registers a given callback on the global manager ready event.
101
+ * @param callback - The callback to run upon manager ready event.
102
+ * @returns {ReadinessManager}
119
103
  */
120
104
  onReady(callback: ReadyCallback): ReadinessManager {
121
105
  if (readyState) {
@@ -124,7 +108,7 @@ class ReadinessManagerImpl implements ReadinessManager {
124
108
  return this;
125
109
  }
126
110
 
127
- this[callbacksSymbol].push(callback);
111
+ this.#callbacks.push(callback);
128
112
  return this;
129
113
  }
130
114
 
@@ -132,20 +116,22 @@ class ReadinessManagerImpl implements ReadinessManager {
132
116
  * Registers a given callback on a specific action resolved.
133
117
  * @param name - The name of the action the callback should be attached to.
134
118
  * @param callback - The callback to run upon beacon resolved.
119
+ * @returns {ReadinessManager}
120
+ * @throws {Error} If the action name does not exists.
135
121
  */
136
122
  onActionReady(name: string, callback: ReadyCallback): ReadinessManager {
137
- const beacon = this[beaconsSymbol][name];
123
+ const beacon = this.#beacons[name];
138
124
  if (!beacon) {
139
125
  throw new Error(ERRORS.BEACON_DOES_NOT_EXISTS);
140
126
  }
141
127
 
142
- if (beacon.status === BeaconStatus.RESOLVED) {
128
+ if (beacon.status === 'resolved') {
143
129
  // Invokes immediately given callback if beacon status is already resolved.
144
130
  callback();
145
131
  return this;
146
132
  }
147
133
 
148
- this[beaconsSymbol][name].callbacks.push(callback);
134
+ this.#beacons[name].callbacks.push(callback);
149
135
  return this;
150
136
  }
151
137
 
@@ -154,7 +140,7 @@ class ReadinessManagerImpl implements ReadinessManager {
154
140
  * @param errorHandler - The handler to trigger upon action errors.
155
141
  */
156
142
  onError(errorHandler: ActionErrorHandler): ReadinessManager {
157
- this[errorHandlerSymbol] = errorHandler;
143
+ this.#errorHandler = errorHandler;
158
144
  return this;
159
145
  }
160
146
 
@@ -165,12 +151,12 @@ class ReadinessManagerImpl implements ReadinessManager {
165
151
  * @throws {ActionExecutionError}
166
152
  * @private
167
153
  */
168
- private async [executeSymbol](beacon: Beacon, attempt = 1): Promise<void> {
154
+ async #execute(beacon: Beacon, attempt = 1): Promise<void> {
169
155
  const { name, action, debug } = beacon;
170
156
 
171
- const update = (status: ActionStatus) => this[updateBeaconSymbol](name, status);
157
+ const update = (status: ActionStatus) => this.#updateBeacon(name, status);
172
158
 
173
- update(BeaconStatus.PENDING);
159
+ update('pending');
174
160
 
175
161
  const startTime = debug ? Date.now() : null;
176
162
 
@@ -187,18 +173,18 @@ class ReadinessManagerImpl implements ReadinessManager {
187
173
  logger.debug(`[ReadinessManager] Action "${name}" completed in ${duration}ms`);
188
174
  }
189
175
 
190
- update(BeaconStatus.RESOLVED);
176
+ update('resolved');
191
177
  } catch (error) {
192
178
  if (debug && startTime !== null) {
193
179
  const duration = Date.now() - startTime;
194
180
  logger.debug(`[ReadinessManager] Action "${name}" failed after ${duration}ms`);
195
181
  }
196
182
 
197
- update(BeaconStatus.REJECTED);
183
+ update('rejected');
198
184
 
199
185
  // Invokes consumer error hook with a retry method and the beacon error.
200
- this[errorHandlerSymbol](new ActionExecutionError(name, attempt, error as Error), () =>
201
- this[executeSymbol](beacon, attempt + 1)
186
+ this.#errorHandler(new ActionExecutionError(name, attempt, error as Error), () =>
187
+ this.#execute(beacon, attempt + 1)
202
188
  );
203
189
  }
204
190
  }
@@ -209,15 +195,15 @@ class ReadinessManagerImpl implements ReadinessManager {
209
195
  * @param status - The beacon status to set.
210
196
  * @private
211
197
  */
212
- private [updateBeaconSymbol](name: string, status: ActionStatus): void {
213
- Object.assign(this[beaconsSymbol][name], { status });
198
+ #updateBeacon(name: string, status: ActionStatus): void {
199
+ Object.assign(this.#beacons[name], { status });
214
200
 
215
201
  // Resolves specific beacon ready listeners.
216
- if (status === BeaconStatus.RESOLVED) {
217
- this[beaconsSymbol][name].callbacks.forEach((callback) => callback());
202
+ if (status === 'resolved') {
203
+ this.#beacons[name].callbacks.forEach((callback) => callback());
218
204
  }
219
205
 
220
- this[trackBeaconUpdateSymbol]();
206
+ this.#trackBeaconUpdate();
221
207
  }
222
208
 
223
209
  /**
@@ -225,15 +211,15 @@ class ReadinessManagerImpl implements ReadinessManager {
225
211
  * If so, will trigger registered `onReady` callbacks.
226
212
  * @private
227
213
  */
228
- private [trackBeaconUpdateSymbol](): void {
214
+ #trackBeaconUpdate(): void {
229
215
  if (readyState) {
230
216
  return;
231
217
  }
232
218
 
233
- readyState = Object.values(this[beaconsSymbol]).every((beacon) => beacon.status === BeaconStatus.RESOLVED);
219
+ readyState = Object.values(this.#beacons).every((beacon) => beacon.status === 'resolved');
234
220
 
235
221
  if (readyState) {
236
- this[callbacksSymbol].forEach((callback) => callback());
222
+ this.#callbacks.forEach((callback) => callback());
237
223
  }
238
224
  }
239
225
  }
package/src/spec.ts CHANGED
@@ -1,4 +1,3 @@
1
- import { BeaconStatus } from './constants';
2
1
  import { ERRORS, ActionExecutionError } from './error';
3
2
  import { ReadinessManager } from './index';
4
3
 
@@ -8,12 +7,6 @@ import { ReadinessManager } from './index';
8
7
  */
9
8
  const wait = (time = 0): Promise<unknown> => new Promise((resolve) => setTimeout(resolve, time));
10
9
 
11
- /**
12
- * Delays a promise resolve with a given time.
13
- * @param time - The time to wait.
14
- */
15
- const delayResolve = (time = 100): Promise<unknown> => new Promise((resolve) => setTimeout(resolve, time));
16
-
17
10
  describe('lib/readinessManager', () => {
18
11
  let readyManager: ReadinessManager;
19
12
 
@@ -59,8 +52,8 @@ describe('lib/readinessManager', () => {
59
52
 
60
53
  const status = readyManager.status();
61
54
 
62
- expect(status[BeaconStatus.RESOLVED]).toEqual(['success', 'other']);
63
- expect(status[BeaconStatus.REJECTED]).toEqual(['fail']);
55
+ expect(status.resolved).toEqual(['success', 'other']);
56
+ expect(status.rejected).toEqual(['fail']);
64
57
  });
65
58
  });
66
59
  });
@@ -70,7 +63,7 @@ describe('lib/readinessManager', () => {
70
63
  it('Registers action and name as beacon with not_started status', () => {
71
64
  readyManager.register('action', () => true);
72
65
 
73
- expect(readyManager.status()[BeaconStatus.NOT_STARTED]).toHaveLength(1);
66
+ expect(readyManager.status().not_started).toHaveLength(1);
74
67
  });
75
68
  });
76
69
 
@@ -85,18 +78,18 @@ describe('lib/readinessManager', () => {
85
78
 
86
79
  describe('run', () => {
87
80
  beforeEach(() => {
88
- readyManager.register('action1', delayResolve);
89
- readyManager.register('action2', delayResolve);
81
+ readyManager.register('action1', wait);
82
+ readyManager.register('action2', wait);
90
83
  });
91
84
 
92
85
  it('Executes all given actions', async () => {
93
- expect(readyManager.status()[BeaconStatus.NOT_STARTED]).toHaveLength(2);
86
+ expect(readyManager.status().not_started).toHaveLength(2);
94
87
 
95
88
  readyManager.run();
96
89
 
97
- expect(readyManager.status()[BeaconStatus.NOT_STARTED]).toBeUndefined();
90
+ expect(readyManager.status().not_started).toBeUndefined();
98
91
 
99
- expect(readyManager.status()[BeaconStatus.PENDING]).toHaveLength(2);
92
+ expect(readyManager.status().pending).toHaveLength(2);
100
93
  });
101
94
  });
102
95
 
@@ -176,8 +169,8 @@ describe('lib/readinessManager', () => {
176
169
  describe('When action is not ready', () => {
177
170
  it('Triggers callback once desired aciton is ready', async () => {
178
171
  readyManager.register('some', () => true);
179
- readyManager.register('thing', () => delayResolve(500));
180
- readyManager.register('other', () => delayResolve(1000));
172
+ readyManager.register('thing', () => wait(500));
173
+ readyManager.register('other', () => wait(1000));
181
174
 
182
175
  readyManager.onActionReady('thing', callback);
183
176
 
package/src/types.ts ADDED
@@ -0,0 +1,38 @@
1
+ /**
2
+ * A status of an action.
3
+ */
4
+ export type ActionStatus = 'not_started' | 'pending' | 'resolved' | 'rejected';
5
+
6
+ /**
7
+ * A callback to be executed when an action is ready.
8
+ */
9
+ export type ReadyCallback = () => void;
10
+
11
+ /**
12
+ * An action is a single function that needs to be executed to determine if the app is ready.
13
+ */
14
+ export type ReadyAction = () => unknown | Promise<unknown>;
15
+
16
+ /**
17
+ * A map of action statuses by their name.
18
+ */
19
+ export type ActionsStatus = {
20
+ [key in ActionStatus]?: string[];
21
+ };
22
+
23
+ /**
24
+ * A beacon is a single action that needs to be executed to determine if the app is ready.
25
+ */
26
+ export interface Beacon {
27
+ name: string;
28
+ action: ReadyAction;
29
+ status: ActionStatus;
30
+ callbacks: ReadyCallback[];
31
+ debug?: boolean;
32
+ }
33
+ /**
34
+ * A map of beacons by their name.
35
+ */
36
+ export interface BeaconsMap {
37
+ [name: string]: Beacon;
38
+ }
package/tests/mock.ts CHANGED
@@ -1,5 +1,6 @@
1
- import { ReadinessManager, ReadyCallback, ReadyAction, ActionsStatus } from '../src/index';
2
- import { ActionErrorHandler } from '../src/error';
1
+ import { ReadinessManager } from '../src/index';
2
+ import type { ReadyCallback, ReadyAction, ActionsStatus } from '../src/types';
3
+ import type { ActionErrorHandler } from '../src/error';
3
4
 
4
5
  export interface ReadinessManagerMock extends ReadinessManager {
5
6
  setReady: (isReady: boolean) => void;
@@ -1,3 +0,0 @@
1
- export type ActionStatus = 'not_started' | 'pending' | 'resolved' | 'rejected';
2
- export declare const BeaconStatus: Record<string, ActionStatus>;
3
- //# sourceMappingURL=constants.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG,aAAa,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,CAAC;AAK/E,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAK5C,CAAC"}
@@ -1,10 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.BeaconStatus = void 0;
4
- exports.BeaconStatus = {
5
- NOT_STARTED: 'not_started',
6
- PENDING: 'pending',
7
- RESOLVED: 'resolved',
8
- REJECTED: 'rejected',
9
- };
10
- //# sourceMappingURL=constants.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":";;;AAKa,QAAA,YAAY,GAAiC;IACtD,WAAW,EAAE,aAAa;IAC1B,OAAO,EAAE,SAAS;IAClB,QAAQ,EAAE,UAAU;IACpB,QAAQ,EAAE,UAAU;CACd,CAAC"}
package/src/constants.ts DELETED
@@ -1,11 +0,0 @@
1
- export type ActionStatus = 'not_started' | 'pending' | 'resolved' | 'rejected';
2
-
3
- /**
4
- * The possible beacon status.
5
- */
6
- export const BeaconStatus: Record<string, ActionStatus> = {
7
- NOT_STARTED: 'not_started',
8
- PENDING: 'pending',
9
- RESOLVED: 'resolved',
10
- REJECTED: 'rejected',
11
- } as const;