@bluelibs/runner 1.0.0 → 1.2.0

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.
Files changed (76) hide show
  1. package/README.md +231 -12
  2. package/dist/DependencyProcessor.d.ts +2 -2
  3. package/dist/DependencyProcessor.js +3 -3
  4. package/dist/DependencyProcessor.js.map +1 -1
  5. package/dist/EventManager.d.ts +4 -0
  6. package/dist/EventManager.js +15 -0
  7. package/dist/EventManager.js.map +1 -1
  8. package/dist/Store.d.ts +30 -2
  9. package/dist/Store.js +134 -29
  10. package/dist/Store.js.map +1 -1
  11. package/dist/TaskRunner.d.ts +3 -0
  12. package/dist/TaskRunner.js +3 -0
  13. package/dist/TaskRunner.js.map +1 -1
  14. package/dist/define.js +2 -7
  15. package/dist/define.js.map +1 -1
  16. package/dist/defs.d.ts +15 -4
  17. package/dist/errors.d.ts +2 -0
  18. package/dist/errors.js +2 -0
  19. package/dist/errors.js.map +1 -1
  20. package/dist/globalEvents.d.ts +2 -0
  21. package/dist/globalEvents.js +3 -0
  22. package/dist/globalEvents.js.map +1 -1
  23. package/dist/globalResources.d.ts +8 -6
  24. package/dist/globalResources.js +9 -4
  25. package/dist/globalResources.js.map +1 -1
  26. package/dist/index.d.ts +8 -6
  27. package/dist/index.js +3 -3
  28. package/dist/index.js.map +1 -1
  29. package/dist/models/DependencyProcessor.d.ts +49 -0
  30. package/dist/models/DependencyProcessor.js +178 -0
  31. package/dist/models/DependencyProcessor.js.map +1 -0
  32. package/dist/models/EventManager.d.ts +17 -0
  33. package/dist/models/EventManager.js +73 -0
  34. package/dist/models/EventManager.js.map +1 -0
  35. package/dist/models/Logger.d.ts +33 -0
  36. package/dist/models/Logger.js +76 -0
  37. package/dist/models/Logger.js.map +1 -0
  38. package/dist/models/ResourceInitializer.d.ts +13 -0
  39. package/dist/models/ResourceInitializer.js +54 -0
  40. package/dist/models/ResourceInitializer.js.map +1 -0
  41. package/dist/models/Store.d.ts +90 -0
  42. package/dist/models/Store.js +302 -0
  43. package/dist/models/Store.js.map +1 -0
  44. package/dist/models/TaskRunner.d.ts +25 -0
  45. package/dist/models/TaskRunner.js +96 -0
  46. package/dist/models/TaskRunner.js.map +1 -0
  47. package/dist/models/index.d.ts +5 -0
  48. package/dist/models/index.js +22 -0
  49. package/dist/models/index.js.map +1 -0
  50. package/dist/run.d.ts +4 -4
  51. package/dist/run.js +14 -7
  52. package/dist/run.js.map +1 -1
  53. package/package.json +1 -2
  54. package/src/__tests__/index.ts +8 -4
  55. package/src/__tests__/{EventManager.test.ts → models/EventManager.test.ts} +20 -2
  56. package/src/__tests__/models/Logger.test.ts +140 -0
  57. package/src/__tests__/{ResourceInitializer.test.ts → models/ResourceInitializer.test.ts} +4 -4
  58. package/src/__tests__/models/Store.test.ts +128 -0
  59. package/src/__tests__/{TaskRunner.test.ts → models/TaskRunner.test.ts} +5 -5
  60. package/src/__tests__/run.overrides.test.ts +392 -0
  61. package/src/__tests__/run.test.ts +28 -1
  62. package/src/define.ts +4 -8
  63. package/src/defs.ts +20 -4
  64. package/src/errors.ts +6 -0
  65. package/src/globalEvents.ts +4 -0
  66. package/src/globalResources.ts +18 -11
  67. package/src/index.ts +3 -3
  68. package/src/{DependencyProcessor.ts → models/DependencyProcessor.ts} +6 -6
  69. package/src/{EventManager.ts → models/EventManager.ts} +21 -1
  70. package/src/models/Logger.ts +100 -0
  71. package/src/{ResourceInitializer.ts → models/ResourceInitializer.ts} +2 -2
  72. package/src/{Store.ts → models/Store.ts} +181 -41
  73. package/src/{TaskRunner.ts → models/TaskRunner.ts} +6 -3
  74. package/src/models/index.ts +5 -0
  75. package/src/run.ts +17 -9
  76. package/src/__tests__/Store.test.ts +0 -143
@@ -9,12 +9,12 @@ import {
9
9
  RegisterableItems,
10
10
  symbols,
11
11
  IMiddleware,
12
- } from "./defs";
13
- import * as utils from "./define";
14
- import { IDependentNode } from "./tools/findCircularDependencies";
15
- import { globalEventsArray } from "./globalEvents";
16
- import { Errors } from "./errors";
17
- import { globalResources } from "./globalResources";
12
+ } from "../defs";
13
+ import * as utils from "../define";
14
+ import { IDependentNode } from "../tools/findCircularDependencies";
15
+ import { globalEventsArray } from "../globalEvents";
16
+ import { Errors } from "../errors";
17
+ import { globalResources } from "../globalResources";
18
18
  import { EventManager } from "./EventManager";
19
19
  import { TaskRunner } from "./TaskRunner";
20
20
 
@@ -51,7 +51,7 @@ export type EventStoreElementType = {
51
51
  };
52
52
 
53
53
  /**
54
- * @internal This should be used for testing purposes only.
54
+ * Store class which is used to store all the resources, tasks, middleware and events.
55
55
  */
56
56
  export class Store {
57
57
  root!: ResourceStoreElementType;
@@ -59,15 +59,41 @@ export class Store {
59
59
  public resources: Map<string, ResourceStoreElementType> = new Map();
60
60
  public events: Map<string, EventStoreElementType> = new Map();
61
61
  public middlewares: Map<string, MiddlewareStoreElementType> = new Map();
62
+ public overrides: Map<
63
+ string,
64
+ IResource | IMiddleware | ITask | IResourceWithConfig
65
+ > = new Map();
66
+
67
+ #isLocked = false;
68
+ #isInitialized = false;
62
69
 
63
70
  constructor(protected readonly eventManager: EventManager) {}
64
71
 
72
+ get isLocked() {
73
+ return this.#isLocked;
74
+ }
75
+
76
+ lock() {
77
+ this.#isLocked = true;
78
+ this.eventManager.lock();
79
+ }
80
+
81
+ checkLock() {
82
+ if (this.#isLocked) {
83
+ throw new Error("Cannot modify the Store when it is locked.");
84
+ }
85
+ }
86
+
65
87
  /**
66
88
  * Store the root before beginning registration
67
89
  * @param root
68
90
  * @param config
69
91
  */
70
92
  initializeStore(root: IResource<any>, config: any) {
93
+ if (this.#isInitialized) {
94
+ throw Errors.storeAlreadyInitialized();
95
+ }
96
+
71
97
  this.storeGenericItem(globalResources.eventManager.with(this.eventManager));
72
98
  this.storeGenericItem(globalResources.store.with(this));
73
99
 
@@ -89,7 +115,14 @@ export class Store {
89
115
  this.storeEvent(event);
90
116
  });
91
117
 
118
+ this.computeRegistrationDeeply(root, config);
92
119
  this.resources.set(root.id, this.root);
120
+
121
+ for (const resource of this.resources.values()) {
122
+ this.storeOverridesDeeply(resource.resource);
123
+ }
124
+
125
+ this.#isInitialized = true;
93
126
  }
94
127
 
95
128
  /**
@@ -97,17 +130,37 @@ export class Store {
97
130
  * @param element
98
131
  * @param config
99
132
  */
100
- computeRegisterOfResource<C>(element: IResource<C>, config?: C) {
133
+ private computeRegistrationDeeply<C>(element: IResource<C>, config?: C) {
101
134
  const items =
102
135
  typeof element.register === "function"
103
136
  ? element.register(config as C)
104
137
  : element.register;
105
138
 
106
139
  for (const item of items) {
140
+ // will call registration if it detects another resource.
107
141
  this.storeGenericItem<C>(item);
108
142
  }
109
143
  }
110
144
 
145
+ /**
146
+ * @param element
147
+ */
148
+ private storeOverridesDeeply<C>(element: IResource<C, any, any>) {
149
+ element.overrides.forEach((override) => {
150
+ // the one on top has priority of setting the last override.
151
+ if (utils.isResource(override)) {
152
+ this.storeOverridesDeeply(override);
153
+ }
154
+
155
+ if (utils.isResourceWithConfig(override)) {
156
+ this.storeOverridesDeeply(override.resource);
157
+ this.overrides.set(override.resource.id, override);
158
+ } else {
159
+ this.overrides.set(override.id, override);
160
+ }
161
+ });
162
+ }
163
+
111
164
  /**
112
165
  * middlewares are already stored in their final form and the check for them would be redundant
113
166
  * @param id
@@ -122,6 +175,63 @@ export class Store {
122
175
  if (this.events.has(id)) {
123
176
  throw Errors.duplicateRegistration("Event", id);
124
177
  }
178
+ if (this.middlewares.has(id)) {
179
+ throw Errors.duplicateRegistration("Middleware", id);
180
+ }
181
+ }
182
+
183
+ /**
184
+ * Cleanup
185
+ */
186
+ public async dispose() {
187
+ for (const resource of this.resources.values()) {
188
+ if (resource.resource.dispose) {
189
+ await resource.resource.dispose(
190
+ resource.value,
191
+ resource.config,
192
+ resource.computedDependencies as DependencyMapType
193
+ );
194
+ }
195
+ }
196
+ }
197
+
198
+ /**
199
+ * When this is called, all overrides should have been stored in the "overrides" store.
200
+ */
201
+ public processOverrides() {
202
+ // If we are trying to use override on something that wasn't previously registered, we throw an error.
203
+ for (const override of this.overrides.values()) {
204
+ let hasAnyItem = false;
205
+ if (utils.isTask(override)) {
206
+ hasAnyItem = this.tasks.has(override.id);
207
+ } else if (utils.isResource(override)) {
208
+ hasAnyItem = this.resources.has(override.id);
209
+ } else if (utils.isMiddleware(override)) {
210
+ hasAnyItem = this.middlewares.has(override.id);
211
+ } else if (utils.isResourceWithConfig(override)) {
212
+ hasAnyItem = this.resources.has(override.resource.id);
213
+ }
214
+
215
+ if (!hasAnyItem) {
216
+ const id = utils.isResourceWithConfig(override)
217
+ ? override.resource.id
218
+ : override.id;
219
+
220
+ throw Errors.dependencyNotFound(id);
221
+ }
222
+ }
223
+
224
+ for (const override of this.overrides.values()) {
225
+ if (utils.isTask(override)) {
226
+ this.storeTask(override, false);
227
+ } else if (utils.isResource(override)) {
228
+ this.storeResource(override, false);
229
+ } else if (utils.isMiddleware(override)) {
230
+ this.storeMiddleware(override, false);
231
+ } else if (utils.isResourceWithConfig(override)) {
232
+ this.storeResourceWithConfig(override, false);
233
+ }
234
+ }
125
235
  }
126
236
 
127
237
  public getGlobalMiddlewares(excludingIds: string[]): IMiddleware[] {
@@ -144,19 +254,7 @@ export class Store {
144
254
  } else if (utils.isEvent(item)) {
145
255
  this.storeEvent<C>(item);
146
256
  } else if (utils.isMiddleware(item)) {
147
- if (this.middlewares.has(item.id)) {
148
- throw Errors.duplicateRegistration("Middleware", item.id);
149
- }
150
-
151
- item.dependencies =
152
- typeof item.dependencies === "function"
153
- ? item.dependencies()
154
- : item.dependencies;
155
-
156
- this.middlewares.set(item.id, {
157
- middleware: item,
158
- computedDependencies: {},
159
- });
257
+ this.storeMiddleware<C>(item);
160
258
  } else if (utils.isResourceWithConfig(item)) {
161
259
  this.storeResourceWithConfig<C>(item);
162
260
  } else {
@@ -164,14 +262,33 @@ export class Store {
164
262
  }
165
263
  }
166
264
 
265
+ private storeMiddleware<C>(item: IMiddleware<any>, check = true) {
266
+ check && this.checkIfIDExists(item.id);
267
+
268
+ item.dependencies =
269
+ typeof item.dependencies === "function"
270
+ ? item.dependencies()
271
+ : item.dependencies;
272
+
273
+ this.middlewares.set(item.id, {
274
+ middleware: item,
275
+ computedDependencies: {},
276
+ });
277
+ }
278
+
167
279
  public storeEvent<C>(item: IEventDefinition<void>) {
168
280
  this.checkIfIDExists(item.id);
169
281
 
170
282
  this.events.set(item.id, { event: item });
171
283
  }
172
284
 
173
- private storeResourceWithConfig<C>(item: IResourceWithConfig<any, any, any>) {
174
- this.checkIfIDExists(item.resource.id);
285
+ private storeResourceWithConfig<C>(
286
+ item: IResourceWithConfig<any, any, any>,
287
+ check = true
288
+ ) {
289
+ check && this.checkIfIDExists(item.resource.id);
290
+
291
+ this.prepareResource(item.resource, item.config);
175
292
 
176
293
  this.resources.set(item.resource.id, {
177
294
  resource: item.resource,
@@ -180,20 +297,17 @@ export class Store {
180
297
  isInitialized: false,
181
298
  });
182
299
 
183
- this.computeRegisterOfResource(item.resource, item.config);
300
+ this.computeRegistrationDeeply(item.resource, item.config);
184
301
  }
185
302
 
186
- private storeResource<C>(item: IResource<any, any, any>) {
187
- this.checkIfIDExists(item.id);
188
-
189
- this.storeEvent(item.events.beforeInit);
190
- this.storeEvent(item.events.afterInit);
191
- this.storeEvent(item.events.onError);
303
+ /**
304
+ * This is for storing a resource without a config.
305
+ * @param item
306
+ */
307
+ private storeResource<C>(item: IResource<any, any, any>, check = true) {
308
+ check && this.checkIfIDExists(item.id);
192
309
 
193
- item.dependencies =
194
- typeof item.dependencies === "function"
195
- ? item.dependencies()
196
- : item.dependencies;
310
+ this.prepareResource(item, {});
197
311
 
198
312
  this.resources.set(item.id, {
199
313
  resource: item,
@@ -202,20 +316,46 @@ export class Store {
202
316
  isInitialized: false,
203
317
  });
204
318
 
205
- this.computeRegisterOfResource(item, {});
319
+ this.computeRegistrationDeeply(item, {});
206
320
  }
207
321
 
208
- private storeTask<C>(item: ITask<any, any, {}>) {
209
- this.checkIfIDExists(item.id);
322
+ public storeEventsForAllTasks() {
323
+ for (const task of this.tasks.values()) {
324
+ this.storeEvent(task.task.events.beforeRun);
325
+ this.storeEvent(task.task.events.afterRun);
326
+ this.storeEvent(task.task.events.onError);
327
+ }
210
328
 
329
+ for (const resource of this.resources.values()) {
330
+ this.storeEvent(resource.resource.events.beforeInit);
331
+ this.storeEvent(resource.resource.events.afterInit);
332
+ this.storeEvent(resource.resource.events.onError);
333
+ }
334
+ }
335
+
336
+ /**
337
+ * This is for storing a resource without a config.
338
+ * @param item
339
+ */
340
+ private prepareResource<C>(
341
+ item: IResource<any, any, any>,
342
+ config: any
343
+ ): IResource<any, any, any> {
211
344
  item.dependencies =
212
345
  typeof item.dependencies === "function"
213
- ? item.dependencies()
346
+ ? item.dependencies(config)
214
347
  : item.dependencies;
215
348
 
216
- this.storeEvent(item.events.beforeRun);
217
- this.storeEvent(item.events.afterRun);
218
- this.storeEvent(item.events.onError);
349
+ return item;
350
+ }
351
+
352
+ private storeTask<C>(item: ITask<any, any, {}>, check = true) {
353
+ check && this.checkIfIDExists(item.id);
354
+
355
+ item.dependencies =
356
+ typeof item.dependencies === "function"
357
+ ? item.dependencies()
358
+ : item.dependencies;
219
359
 
220
360
  this.tasks.set(item.id, {
221
361
  task: item,
@@ -1,7 +1,7 @@
1
- import { DependencyMapType, DependencyValuesType, ITask } from "./defs";
2
- import { Errors } from "./errors";
1
+ import { DependencyMapType, DependencyValuesType, ITask } from "../defs";
2
+ import { Errors } from "../errors";
3
3
  import { EventManager } from "./EventManager";
4
- import { globalEvents } from "./globalEvents";
4
+ import { globalEvents } from "../globalEvents";
5
5
  import {
6
6
  MiddlewareStoreElementType,
7
7
  Store,
@@ -22,6 +22,9 @@ export class TaskRunner {
22
22
  /**
23
23
  * Begins the execution of an task. These are registered tasks and all sanity checks have been performed at this stage to ensure consistency of the object.
24
24
  * This function can throw only if any of the event listeners or run function throws
25
+ * @param task the task to be run
26
+ * @param input the input to be passed to the task
27
+ * @param taskDependencies optional dependencies to be passed to the task, if not provided, the dependencies will be the ones already computed from the store.
25
28
  */
26
29
  public async run<
27
30
  TInput,
@@ -0,0 +1,5 @@
1
+ export * from "./DependencyProcessor";
2
+ export * from "./EventManager";
3
+ export * from "./Logger";
4
+ export * from "./Store";
5
+ export * from "./TaskRunner";
package/src/run.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { TaskRunner } from "./TaskRunner";
1
+ import { TaskRunner } from "./models/TaskRunner";
2
2
  import {
3
3
  DependencyMapType,
4
4
  ITaskDefinition,
@@ -8,13 +8,14 @@ import {
8
8
  DependencyValuesType,
9
9
  IResource,
10
10
  } from "./defs";
11
- import { DependencyProcessor } from "./DependencyProcessor";
12
- import { EventManager } from "./EventManager";
11
+ import { DependencyProcessor } from "./models/DependencyProcessor";
12
+ import { EventManager } from "./models/EventManager";
13
13
  import { globalEvents } from "./globalEvents";
14
- import { Store } from "./Store";
14
+ import { Store } from "./models/Store";
15
15
  import { findCircularDependencies } from "./tools/findCircularDependencies";
16
16
  import { Errors } from "./errors";
17
17
  import { globalResources } from "./globalResources";
18
+ import { Logger } from "./models/Logger";
18
19
 
19
20
  export type ResourcesStoreElementType<
20
21
  C = any,
@@ -58,18 +59,19 @@ export type RunnerType = {
58
59
  };
59
60
 
60
61
  export async function run<C, V>(
61
- resource: IResource<C>,
62
+ resource: IResource<C, V>,
62
63
  config?: C
63
64
  ): Promise<V> {
64
65
  const eventManager = new EventManager();
65
66
  const store = new Store(eventManager);
66
67
  const taskRunner = new TaskRunner(store, eventManager);
67
68
  const processor = new DependencyProcessor(store, eventManager, taskRunner);
69
+ const logger = new Logger(eventManager);
68
70
 
69
71
  // In the registration phase we register deeply all the resources, tasks, middleware and events
70
72
  store.initializeStore(resource, config);
73
+ store.storeGenericItem(globalResources.logger.with(logger));
71
74
  store.storeGenericItem(globalResources.taskRunner.with(taskRunner));
72
- store.computeRegisterOfResource(resource, config);
73
75
 
74
76
  // We verify that there isn't any circular dependencies before we begin computing the dependencies
75
77
  const dependentNodes = store.getDependentNodes();
@@ -78,14 +80,17 @@ export async function run<C, V>(
78
80
  throw Errors.circularDependencies(circularDependencies.cycles);
79
81
  }
80
82
 
81
- await processor.processHooks();
83
+ await store.processOverrides();
84
+
85
+ // a form of hooking, we store the events for all tasks
86
+ await store.storeEventsForAllTasks();
87
+ await processor.attachHooks();
88
+ await processor.computeAllDependencies();
82
89
 
83
90
  // Now we can safely compute dependencies without being afraid of an infinite loop.
84
91
  // The hooking part is done here.
85
92
  await eventManager.emit(globalEvents.beforeInit);
86
93
 
87
- await processor.computeAllDependencies();
88
-
89
94
  // leftovers that were registered but not depended upon, except root
90
95
  await processor.initializeUninitializedResources();
91
96
 
@@ -94,5 +99,8 @@ export async function run<C, V>(
94
99
 
95
100
  await eventManager.emit(globalEvents.afterInit);
96
101
 
102
+ // disallow manipulation or attaching more
103
+ store.lock();
104
+
97
105
  return store.root.value;
98
106
  }
@@ -1,143 +0,0 @@
1
- import { EventManager } from "../EventManager";
2
- import { Store } from "../Store";
3
- import {
4
- defineTask,
5
- defineResource,
6
- defineEvent,
7
- defineMiddleware,
8
- } from "../define";
9
-
10
- describe("Store", () => {
11
- let store: Store;
12
- let eventManager: EventManager;
13
-
14
- beforeEach(() => {
15
- eventManager = new EventManager();
16
- store = new Store(eventManager);
17
- });
18
-
19
- it("should register an task", () => {
20
- const task = defineTask({
21
- id: "testTask",
22
- run: async () => {},
23
- });
24
-
25
- store.computeRegisterOfResource(
26
- defineResource({
27
- id: "root",
28
- register: [task],
29
- })
30
- );
31
-
32
- expect(store.tasks.get("testTask")).toBeDefined();
33
- expect(store.tasks.get("testTask")?.task).toBe(task);
34
- });
35
-
36
- it("should register a resource", () => {
37
- const resource = defineResource({
38
- id: "testResource",
39
- });
40
-
41
- store.computeRegisterOfResource(
42
- defineResource({
43
- id: "root",
44
- register: [resource],
45
- })
46
- );
47
-
48
- expect(store.resources.get("testResource")).toBeDefined();
49
- expect(store.resources.get("testResource")?.resource).toBe(resource);
50
- });
51
-
52
- it("should register an event", () => {
53
- const event = defineEvent({
54
- id: "testEvent",
55
- });
56
-
57
- store.computeRegisterOfResource(
58
- defineResource({
59
- id: "root",
60
- register: [event],
61
- })
62
- );
63
-
64
- expect(store.events.get("testEvent")).toBeDefined();
65
- expect(store.events.get("testEvent")?.event).toBe(event);
66
- });
67
-
68
- it("should register a middleware", () => {
69
- const middleware = defineMiddleware({
70
- id: "testMiddleware",
71
- run: async () => {},
72
- });
73
-
74
- store.computeRegisterOfResource(
75
- defineResource({
76
- id: "root",
77
- register: [middleware],
78
- })
79
- );
80
-
81
- expect(store.middlewares.get("testMiddleware")).toBeDefined();
82
- expect(store.middlewares.get("testMiddleware")?.middleware).toBe(
83
- middleware
84
- );
85
- });
86
-
87
- it("should throw an error when registering duplicate items", () => {
88
- const task = defineTask({
89
- id: "duplicateItem",
90
- run: async () => {},
91
- });
92
-
93
- store.computeRegisterOfResource(
94
- defineResource({
95
- id: "root",
96
- register: [task],
97
- })
98
- );
99
-
100
- expect(() => {
101
- store.computeRegisterOfResource(
102
- defineResource({
103
- id: "anotherRoot",
104
- register: [task],
105
- })
106
- );
107
- }).toThrow('Task "duplicateItem" already registered');
108
- });
109
-
110
- it("should return dependent nodes", () => {
111
- const task1 = defineTask({
112
- id: "task1",
113
- dependencies: { dep1: {} as any },
114
- run: async () => {},
115
- });
116
-
117
- const task2 = defineTask({
118
- id: "task2",
119
- dependencies: { dep2: {} as any },
120
- run: async () => {},
121
- });
122
-
123
- store.computeRegisterOfResource(
124
- defineResource({
125
- id: "root",
126
- register: [task1, task2],
127
- })
128
- );
129
-
130
- const dependentNodes = store.getDependentNodes();
131
-
132
- // global store, global event manager
133
- expect(dependentNodes).toHaveLength(2);
134
- expect(dependentNodes).toContainEqual({
135
- id: "task1",
136
- dependencies: { dep1: {} },
137
- });
138
- expect(dependentNodes).toContainEqual({
139
- id: "task2",
140
- dependencies: { dep2: {} },
141
- });
142
- });
143
- });