@bluelibs/runner 2.2.4 → 3.1.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 (211) hide show
  1. package/README.md +1409 -935
  2. package/dist/common.types.d.ts +20 -0
  3. package/dist/common.types.js +4 -0
  4. package/dist/common.types.js.map +1 -0
  5. package/dist/context.d.ts +34 -0
  6. package/dist/context.js +58 -0
  7. package/dist/context.js.map +1 -0
  8. package/dist/define.d.ts +24 -5
  9. package/dist/define.js +89 -20
  10. package/dist/define.js.map +1 -1
  11. package/dist/defs.d.ts +109 -73
  12. package/dist/defs.js +12 -2
  13. package/dist/defs.js.map +1 -1
  14. package/dist/errors.d.ts +5 -5
  15. package/dist/errors.js +6 -5
  16. package/dist/errors.js.map +1 -1
  17. package/dist/event.types.d.ts +18 -0
  18. package/dist/event.types.js +4 -0
  19. package/dist/event.types.js.map +1 -0
  20. package/dist/examples/registrator-example.d.ts +122 -0
  21. package/dist/examples/registrator-example.js +147 -0
  22. package/dist/examples/registrator-example.js.map +1 -0
  23. package/dist/globals/globalEvents.d.ts +41 -0
  24. package/dist/globals/globalEvents.js +94 -0
  25. package/dist/globals/globalEvents.js.map +1 -0
  26. package/dist/globals/globalMiddleware.d.ts +23 -0
  27. package/dist/globals/globalMiddleware.js +15 -0
  28. package/dist/globals/globalMiddleware.js.map +1 -0
  29. package/dist/globals/globalResources.d.ts +27 -0
  30. package/dist/globals/globalResources.js +47 -0
  31. package/dist/globals/globalResources.js.map +1 -0
  32. package/dist/globals/middleware/cache.middleware.d.ts +34 -0
  33. package/dist/globals/middleware/cache.middleware.js +85 -0
  34. package/dist/globals/middleware/cache.middleware.js.map +1 -0
  35. package/dist/globals/middleware/requireContext.middleware.d.ts +6 -0
  36. package/dist/globals/middleware/requireContext.middleware.js +25 -0
  37. package/dist/globals/middleware/requireContext.middleware.js.map +1 -0
  38. package/dist/globals/middleware/retry.middleware.d.ts +20 -0
  39. package/dist/globals/middleware/retry.middleware.js +34 -0
  40. package/dist/globals/middleware/retry.middleware.js.map +1 -0
  41. package/dist/globals/resources/queue.resource.d.ts +7 -0
  42. package/dist/globals/resources/queue.resource.js +31 -0
  43. package/dist/globals/resources/queue.resource.js.map +1 -0
  44. package/dist/index.d.ts +54 -18
  45. package/dist/index.js +14 -9
  46. package/dist/index.js.map +1 -1
  47. package/dist/middleware.types.d.ts +40 -0
  48. package/dist/middleware.types.js +4 -0
  49. package/dist/middleware.types.js.map +1 -0
  50. package/dist/models/DependencyProcessor.d.ts +6 -5
  51. package/dist/models/DependencyProcessor.js +13 -15
  52. package/dist/models/DependencyProcessor.js.map +1 -1
  53. package/dist/models/EventManager.d.ts +9 -4
  54. package/dist/models/EventManager.js +44 -2
  55. package/dist/models/EventManager.js.map +1 -1
  56. package/dist/models/Logger.d.ts +30 -13
  57. package/dist/models/Logger.js +132 -54
  58. package/dist/models/Logger.js.map +1 -1
  59. package/dist/models/OverrideManager.d.ts +13 -0
  60. package/dist/models/OverrideManager.js +70 -0
  61. package/dist/models/OverrideManager.js.map +1 -0
  62. package/dist/models/Queue.d.ts +25 -0
  63. package/dist/models/Queue.js +54 -0
  64. package/dist/models/Queue.js.map +1 -0
  65. package/dist/models/ResourceInitializer.d.ts +5 -2
  66. package/dist/models/ResourceInitializer.js +22 -14
  67. package/dist/models/ResourceInitializer.js.map +1 -1
  68. package/dist/models/Semaphore.d.ts +61 -0
  69. package/dist/models/Semaphore.js +166 -0
  70. package/dist/models/Semaphore.js.map +1 -0
  71. package/dist/models/Store.d.ts +18 -73
  72. package/dist/models/Store.js +71 -269
  73. package/dist/models/Store.js.map +1 -1
  74. package/dist/models/StoreConstants.d.ts +11 -0
  75. package/dist/models/StoreConstants.js +18 -0
  76. package/dist/models/StoreConstants.js.map +1 -0
  77. package/dist/models/StoreRegistry.d.ts +25 -0
  78. package/dist/models/StoreRegistry.js +171 -0
  79. package/dist/models/StoreRegistry.js.map +1 -0
  80. package/dist/models/StoreTypes.d.ts +21 -0
  81. package/dist/models/StoreTypes.js +3 -0
  82. package/dist/models/StoreTypes.js.map +1 -0
  83. package/dist/models/StoreValidator.d.ts +10 -0
  84. package/dist/models/StoreValidator.js +41 -0
  85. package/dist/models/StoreValidator.js.map +1 -0
  86. package/dist/models/TaskRunner.d.ts +1 -1
  87. package/dist/models/TaskRunner.js +39 -24
  88. package/dist/models/TaskRunner.js.map +1 -1
  89. package/dist/models/VarStore.d.ts +17 -0
  90. package/dist/models/VarStore.js +60 -0
  91. package/dist/models/VarStore.js.map +1 -0
  92. package/dist/models/index.d.ts +3 -0
  93. package/dist/models/index.js +3 -0
  94. package/dist/models/index.js.map +1 -1
  95. package/dist/resource.types.d.ts +31 -0
  96. package/dist/resource.types.js +3 -0
  97. package/dist/resource.types.js.map +1 -0
  98. package/dist/run.d.ts +4 -1
  99. package/dist/run.js +6 -3
  100. package/dist/run.js.map +1 -1
  101. package/dist/symbols.d.ts +24 -0
  102. package/dist/symbols.js +29 -0
  103. package/dist/symbols.js.map +1 -0
  104. package/dist/task.types.d.ts +55 -0
  105. package/dist/task.types.js +23 -0
  106. package/dist/task.types.js.map +1 -0
  107. package/dist/tools/getCallerFile.d.ts +9 -1
  108. package/dist/tools/getCallerFile.js +41 -0
  109. package/dist/tools/getCallerFile.js.map +1 -1
  110. package/dist/tools/registratorId.d.ts +4 -0
  111. package/dist/tools/registratorId.js +40 -0
  112. package/dist/tools/registratorId.js.map +1 -0
  113. package/dist/tools/simpleHash.d.ts +9 -0
  114. package/dist/tools/simpleHash.js +34 -0
  115. package/dist/tools/simpleHash.js.map +1 -0
  116. package/dist/types/base-interfaces.d.ts +18 -0
  117. package/dist/types/base-interfaces.js +6 -0
  118. package/dist/types/base-interfaces.js.map +1 -0
  119. package/dist/types/base.d.ts +13 -0
  120. package/dist/types/base.js +3 -0
  121. package/dist/types/base.js.map +1 -0
  122. package/dist/types/dependencies.d.ts +22 -0
  123. package/dist/types/dependencies.js +3 -0
  124. package/dist/types/dependencies.js.map +1 -0
  125. package/dist/types/dependency-core.d.ts +14 -0
  126. package/dist/types/dependency-core.js +5 -0
  127. package/dist/types/dependency-core.js.map +1 -0
  128. package/dist/types/events.d.ts +52 -0
  129. package/dist/types/events.js +6 -0
  130. package/dist/types/events.js.map +1 -0
  131. package/dist/types/hooks.d.ts +16 -0
  132. package/dist/types/hooks.js +5 -0
  133. package/dist/types/hooks.js.map +1 -0
  134. package/dist/types/index.d.ts +14 -0
  135. package/dist/types/index.js +27 -0
  136. package/dist/types/index.js.map +1 -0
  137. package/dist/types/meta.d.ts +13 -0
  138. package/dist/types/meta.js +5 -0
  139. package/dist/types/meta.js.map +1 -0
  140. package/dist/types/middleware.d.ts +38 -0
  141. package/dist/types/middleware.js +6 -0
  142. package/dist/types/middleware.js.map +1 -0
  143. package/dist/types/registerable.d.ts +10 -0
  144. package/dist/types/registerable.js +5 -0
  145. package/dist/types/registerable.js.map +1 -0
  146. package/dist/types/resources.d.ts +44 -0
  147. package/dist/types/resources.js +5 -0
  148. package/dist/types/resources.js.map +1 -0
  149. package/dist/types/symbols.d.ts +24 -0
  150. package/dist/types/symbols.js +30 -0
  151. package/dist/types/symbols.js.map +1 -0
  152. package/dist/types/tasks.d.ts +41 -0
  153. package/dist/types/tasks.js +5 -0
  154. package/dist/types/tasks.js.map +1 -0
  155. package/dist/types/utilities.d.ts +7 -0
  156. package/dist/types/utilities.js +5 -0
  157. package/dist/types/utilities.js.map +1 -0
  158. package/package.json +10 -6
  159. package/src/__tests__/benchmark/benchmark.test.ts +1 -1
  160. package/src/__tests__/context.test.ts +91 -0
  161. package/src/__tests__/errors.test.ts +8 -5
  162. package/src/__tests__/globalEvents.test.ts +1 -1
  163. package/src/__tests__/globals/cache.middleware.test.ts +772 -0
  164. package/src/__tests__/globals/queue.resource.test.ts +141 -0
  165. package/src/__tests__/globals/requireContext.middleware.test.ts +98 -0
  166. package/src/__tests__/globals/retry.middleware.test.ts +157 -0
  167. package/src/__tests__/index.helper.test.ts +55 -0
  168. package/src/__tests__/models/EventManager.test.ts +157 -11
  169. package/src/__tests__/models/Logger.test.ts +291 -34
  170. package/src/__tests__/models/Queue.test.ts +189 -0
  171. package/src/__tests__/models/ResourceInitializer.test.ts +8 -6
  172. package/src/__tests__/models/Semaphore.test.ts +713 -0
  173. package/src/__tests__/models/Store.test.ts +40 -0
  174. package/src/__tests__/models/TaskRunner.test.ts +86 -5
  175. package/src/__tests__/run.anonymous.test.ts +679 -0
  176. package/src/__tests__/run.middleware.test.ts +312 -12
  177. package/src/__tests__/run.overrides.test.ts +13 -10
  178. package/src/__tests__/run.test.ts +364 -13
  179. package/src/__tests__/setOutput.test.ts +244 -0
  180. package/src/__tests__/tools/getCallerFile.test.ts +124 -9
  181. package/src/__tests__/typesafety.test.ts +71 -41
  182. package/src/context.ts +86 -0
  183. package/src/define.ts +129 -34
  184. package/src/defs.ts +156 -119
  185. package/src/errors.ts +15 -10
  186. package/src/{globalEvents.ts → globals/globalEvents.ts} +13 -12
  187. package/src/globals/globalMiddleware.ts +14 -0
  188. package/src/{globalResources.ts → globals/globalResources.ts} +14 -10
  189. package/src/globals/middleware/cache.middleware.ts +115 -0
  190. package/src/globals/middleware/requireContext.middleware.ts +36 -0
  191. package/src/globals/middleware/retry.middleware.ts +56 -0
  192. package/src/globals/resources/queue.resource.ts +34 -0
  193. package/src/index.ts +9 -5
  194. package/src/models/DependencyProcessor.ts +42 -49
  195. package/src/models/EventManager.ts +64 -13
  196. package/src/models/Logger.ts +181 -64
  197. package/src/models/OverrideManager.ts +84 -0
  198. package/src/models/Queue.ts +66 -0
  199. package/src/models/ResourceInitializer.ts +40 -20
  200. package/src/models/Semaphore.ts +208 -0
  201. package/src/models/Store.ts +94 -342
  202. package/src/models/StoreConstants.ts +17 -0
  203. package/src/models/StoreRegistry.ts +228 -0
  204. package/src/models/StoreTypes.ts +46 -0
  205. package/src/models/StoreValidator.ts +43 -0
  206. package/src/models/TaskRunner.ts +54 -41
  207. package/src/models/index.ts +3 -0
  208. package/src/run.ts +7 -4
  209. package/src/tools/getCallerFile.ts +54 -2
  210. package/src/__tests__/index.ts +0 -15
  211. package/src/examples/express-mongo/index.ts +0 -1
@@ -0,0 +1,115 @@
1
+ import { defineMiddleware, defineResource, defineTask } from "../../define";
2
+ import { LRUCache } from "lru-cache";
3
+ import { IResource, ITask } from "../../defs";
4
+
5
+ export interface ICacheInstance {
6
+ set(key: string, value: any): void;
7
+ get(key: string): any;
8
+ clear(): void;
9
+ }
10
+
11
+ // Default cache factory task that can be overridden
12
+ export const cacheFactoryTask = defineTask({
13
+ id: "globals.tasks.cacheFactory",
14
+ run: async (options: any) => {
15
+ return new LRUCache(options) as ICacheInstance;
16
+ },
17
+ });
18
+
19
+ type CacheResourceConfig = {
20
+ defaultOptions?: any;
21
+ /**
22
+ * This specifies whether the cache handler is async or not (get, set, clear)
23
+ * This is for speed purposes.
24
+ */
25
+ async?: boolean;
26
+ };
27
+
28
+ type CacheMiddlewareConfig = {
29
+ keyBuilder?: (taskId: string, input: any) => string;
30
+ } & any;
31
+
32
+ export const cacheResource = defineResource({
33
+ id: "globals.resources.cache",
34
+ register: [cacheFactoryTask],
35
+ dependencies: {
36
+ cacheFactoryTask,
37
+ },
38
+ init: async (config: CacheResourceConfig, { cacheFactoryTask }) => {
39
+ return {
40
+ map: new Map<string | symbol, ICacheInstance>(),
41
+ cacheFactoryTask,
42
+ async: config.async,
43
+ defaultOptions: {
44
+ ttl: 10 * 1000,
45
+ max: 100, // Maximum number of items in cache
46
+ ttlAutopurge: true, // Automatically purge expired items
47
+ ...config.defaultOptions,
48
+ },
49
+ };
50
+ },
51
+ dispose: async (cache) => {
52
+ for (const cacheInstance of cache.map.values()) {
53
+ await cacheInstance.clear();
54
+ }
55
+ },
56
+ });
57
+
58
+ const defaultKeyBuilder = (taskId: string, input: any) =>
59
+ `${taskId}-${JSON.stringify(input)}`;
60
+
61
+ export const cacheMiddleware = defineMiddleware({
62
+ id: "globals.middleware.cache",
63
+ dependencies: { cache: cacheResource },
64
+ async run({ task, resource, next }, deps, config: CacheMiddlewareConfig) {
65
+ const { cache } = deps;
66
+ config = {
67
+ keyBuilder: defaultKeyBuilder,
68
+ ttl: 10 * 1000,
69
+ max: 100, // Maximum number of items in cache
70
+ ttlAutopurge: true, // Automatically purge expired items
71
+ ...config,
72
+ };
73
+
74
+ if (!task) {
75
+ throw new Error("Cache middleware can only be used in tasks");
76
+ }
77
+
78
+ const taskId = task.definition.id;
79
+ const isAsync = cache.async;
80
+ let cacheHolderForTask = cache.map.get(taskId);
81
+ if (!cacheHolderForTask) {
82
+ // Extract only LRUCache options, excluding keyBuilder
83
+ const { keyBuilder, ...lruOptions } = config;
84
+ const cacheOptions = {
85
+ ...cache.defaultOptions,
86
+ ...lruOptions,
87
+ };
88
+
89
+ // Use the factory task to create the cache instance
90
+ cacheHolderForTask = await cache.cacheFactoryTask(cacheOptions);
91
+
92
+ cache.map.set(taskId, cacheHolderForTask);
93
+ }
94
+
95
+ const key = config.keyBuilder!(taskId, task.input);
96
+
97
+ const cachedValue = isAsync
98
+ ? await cacheHolderForTask.get(key)
99
+ : cacheHolderForTask.get(key);
100
+
101
+ if (cachedValue) {
102
+ return cachedValue;
103
+ }
104
+
105
+ const result = await next(task.input);
106
+
107
+ if (isAsync) {
108
+ await cacheHolderForTask.set(key, result);
109
+ } else {
110
+ cacheHolderForTask.set(key, result);
111
+ }
112
+
113
+ return result;
114
+ },
115
+ });
@@ -0,0 +1,36 @@
1
+ import { Context, ContextError } from "../../context";
2
+ import { defineMiddleware } from "../../define";
3
+
4
+ type RequireContextMiddlewareConfig = {
5
+ context: Context<any>;
6
+ };
7
+
8
+ export const requireContextMiddleware = defineMiddleware({
9
+ id: "globals.middleware.requireContext",
10
+ async run(
11
+ { task, resource, next },
12
+ deps,
13
+ config: RequireContextMiddlewareConfig
14
+ ) {
15
+ if (!config.context) {
16
+ throw new Error(
17
+ "Context not available. Did you forget to pass 'context' to the middleware?"
18
+ );
19
+ }
20
+
21
+ const ctx = config.context.use();
22
+ if (!ctx) {
23
+ throw new ContextError(
24
+ "Context not available. Did you forget to provide the context via ContextName.provide()?"
25
+ );
26
+ }
27
+ if (task) {
28
+ return next(task.input);
29
+ }
30
+ if (resource) {
31
+ return next(resource.config);
32
+ }
33
+
34
+ return next();
35
+ },
36
+ });
@@ -0,0 +1,56 @@
1
+ import { defineMiddleware } from "../../define";
2
+
3
+ /**
4
+ * Configuration options for the retry middleware
5
+ */
6
+ export interface RetryMiddlewareConfig {
7
+ /**
8
+ * Maximum number of retry attempts (default: 3)
9
+ */
10
+ retries?: number;
11
+ /**
12
+ * Callback to determine if retry should stop based on error
13
+ * @default () => false (retry all errors)
14
+ */
15
+ stopRetryIf?: (error: Error) => boolean;
16
+ /**
17
+ * Custom delay strategy function
18
+ * @default Exponential backoff starting at 100ms
19
+ */
20
+ delayStrategy?: (attempt: number, error: Error) => number;
21
+ }
22
+
23
+ export const retryMiddleware = defineMiddleware({
24
+ id: "globals.middleware.retry",
25
+ async run({ task, resource, next }, deps, config: RetryMiddlewareConfig) {
26
+ const input = task ? task.input : resource?.config;
27
+ let attempts = 0;
28
+
29
+ // Set defaults for required parameters
30
+ const maxRetries = config.retries ?? 3;
31
+ const shouldStop = config.stopRetryIf ?? (() => false);
32
+
33
+ while (true) {
34
+ try {
35
+ return await next(input);
36
+ } catch (error) {
37
+ const err = error as Error;
38
+
39
+ if (shouldStop(err) || attempts >= maxRetries) {
40
+ throw error;
41
+ }
42
+
43
+ // Calculate delay using custom strategy or default exponential backoff
44
+ const delay = config.delayStrategy
45
+ ? config.delayStrategy(attempts, err)
46
+ : 100 * Math.pow(2, attempts);
47
+
48
+ if (delay > 0) {
49
+ await new Promise((resolve) => setTimeout(resolve, delay));
50
+ }
51
+
52
+ attempts++;
53
+ }
54
+ }
55
+ },
56
+ });
@@ -0,0 +1,34 @@
1
+ import { defineResource } from "../../define";
2
+ import { Queue } from "../../models/Queue";
3
+
4
+ export const queueResource = defineResource({
5
+ id: "globals.resources.queue",
6
+ context: () => ({
7
+ map: new Map<string | symbol, Queue>(),
8
+ }),
9
+ init: async (_, deps, context) => {
10
+ const map = context.map;
11
+
12
+ return {
13
+ map,
14
+ run: <T>(
15
+ id: string,
16
+ task: (signal: AbortSignal) => Promise<T>
17
+ ): Promise<T> => {
18
+ if (!map.has(id)) {
19
+ map.set(id, new Queue());
20
+ }
21
+
22
+ return map.get(id)!.run(task);
23
+ },
24
+ };
25
+ },
26
+ dispose: async (value, _, deps, context) => {
27
+ context.map.forEach((queue) => queue.dispose());
28
+ },
29
+ meta: {
30
+ title: "Queue Map",
31
+ description:
32
+ "A global map that can be used to store and retrieve queues. You can run exclusive tasks based on using an id. queue.run(id, task) will run the task in the queue with the given id. If the queue does not exist, it will be created.",
33
+ },
34
+ });
package/src/index.ts CHANGED
@@ -3,14 +3,18 @@ import {
3
3
  defineResource,
4
4
  defineEvent,
5
5
  defineMiddleware,
6
+ defineIndex,
6
7
  } from "./define";
7
- import { globalEvents } from "./globalEvents";
8
- import { globalResources } from "./globalResources";
8
+ import { createContext } from "./context";
9
+ import { globalEvents } from "./globals/globalEvents";
10
+ import { globalResources } from "./globals/globalResources";
11
+ import { globalMiddlewares } from "./globals/globalMiddleware";
9
12
  import { run } from "./run";
10
13
 
11
14
  const globals = {
12
15
  events: globalEvents,
13
16
  resources: globalResources,
17
+ middlewares: globalMiddlewares,
14
18
  };
15
19
 
16
20
  export { globals };
@@ -19,10 +23,10 @@ export {
19
23
  defineResource as resource,
20
24
  defineEvent as event,
21
25
  defineMiddleware as middleware,
26
+ defineIndex as index,
22
27
  run,
28
+ createContext,
23
29
  };
24
30
 
25
31
  export * as definitions from "./defs";
26
- export { Store } from "./models/Store";
27
- export { EventManager } from "./models/EventManager";
28
- export { TaskRunner } from "./models/TaskRunner";
32
+ export { Semaphore, Store, EventManager, TaskRunner, Queue } from "./models";
@@ -3,11 +3,12 @@ import {
3
3
  DependencyValuesType,
4
4
  ITask,
5
5
  IResource,
6
- IHookDefinition,
7
6
  IEventDefinition,
8
7
  IEvent,
8
+ IEventEmission,
9
9
  } from "../defs";
10
- import { ResourceStoreElementType, Store, TaskStoreElementType } from "./Store";
10
+ import { Store } from "./Store";
11
+ import { ResourceStoreElementType, TaskStoreElementType } from "./StoreTypes";
11
12
  import * as utils from "../define";
12
13
  import { EventManager } from "./EventManager";
13
14
  import { ResourceInitializer } from "./ResourceInitializer";
@@ -81,11 +82,14 @@ export class DependencyProcessor {
81
82
  resource.resource.id !== this.store.root.resource.id
82
83
  ) {
83
84
  await this.processResourceDependencies(resource);
84
- resource.value = await this.resourceInitializer.initializeResource(
85
- resource.resource,
86
- resource.config,
87
- resource.computedDependencies as DependencyValuesType<{}>
88
- );
85
+ const { value, context } =
86
+ await this.resourceInitializer.initializeResource(
87
+ resource.resource,
88
+ resource.config,
89
+ resource.computedDependencies as DependencyValuesType<{}>
90
+ );
91
+ resource.context = context;
92
+ resource.value = value;
89
93
  }
90
94
  }
91
95
  }
@@ -107,13 +111,16 @@ export class DependencyProcessor {
107
111
  public async initializeRoot() {
108
112
  const storeResource = this.store.root;
109
113
 
110
- storeResource.value = await this.resourceInitializer.initializeResource(
111
- storeResource.resource,
112
- storeResource.config,
113
- // They are already computed
114
- storeResource.computedDependencies as DependencyValuesType<{}>
115
- );
114
+ const { value, context } =
115
+ await this.resourceInitializer.initializeResource(
116
+ storeResource.resource,
117
+ storeResource.config,
118
+ // They are already computed
119
+ storeResource.computedDependencies as DependencyValuesType<{}>
120
+ );
116
121
 
122
+ storeResource.context = context;
123
+ storeResource.value = value;
117
124
  storeResource.isInitialized = true;
118
125
  }
119
126
 
@@ -125,25 +132,13 @@ export class DependencyProcessor {
125
132
  if (task.task.on) {
126
133
  let eventDefinition = task.task.on;
127
134
 
128
- const handler = async (receivedEvent: IEvent<any>) => {
135
+ const handler = async (receivedEvent: IEventEmission<any>) => {
129
136
  if (receivedEvent.source === task.task.id) {
130
137
  // we don't want to trigger the same task that emitted the event
131
138
  // process.exit(0);
132
139
  return;
133
140
  }
134
141
 
135
- this.logger.debug(
136
- {
137
- message:
138
- eventDefinition === "*"
139
- ? `Task ${task.task.id} being triggered by all events`
140
- : `Task ${task.task.id} being triggered by event: ${eventDefinition.id}`,
141
-
142
- event: receivedEvent,
143
- },
144
- task.task.id
145
- );
146
-
147
142
  return this.taskRunner.run(
148
143
  task.task,
149
144
  receivedEvent,
@@ -169,7 +164,7 @@ export class DependencyProcessor {
169
164
 
170
165
  async extractDependencies<T extends DependencyMapType>(
171
166
  map: T,
172
- source: string
167
+ source: string | symbol
173
168
  ): Promise<DependencyValuesType<T>> {
174
169
  const object = {} as DependencyValuesType<T>;
175
170
 
@@ -180,7 +175,7 @@ export class DependencyProcessor {
180
175
  return object;
181
176
  }
182
177
 
183
- async extractDependency(object, source: string) {
178
+ async extractDependency(object: any, source: string | symbol) {
184
179
  if (utils.isResource(object)) {
185
180
  return this.extractResourceDependency(object);
186
181
  } else if (utils.isTask(object)) {
@@ -197,19 +192,8 @@ export class DependencyProcessor {
197
192
  * @param object
198
193
  * @returns
199
194
  */
200
- extractEventDependency(
201
- object: IEventDefinition<Record<string, any>>,
202
- source: string
203
- ) {
204
- return async (input) => {
205
- // runs it in background.
206
- this.logger.debug(
207
- {
208
- message: `Event ${object.id} was emitted from ${source}`,
209
- },
210
- source
211
- );
212
-
195
+ extractEventDependency(object: IEvent<any>, source: string | symbol) {
196
+ return async (input: any) => {
213
197
  return this.eventManager.emit(object, input, source);
214
198
  };
215
199
  }
@@ -217,7 +201,7 @@ export class DependencyProcessor {
217
201
  async extractTaskDependency(object: ITask<any, any, {}>) {
218
202
  const storeTask = this.store.tasks.get(object.id);
219
203
  if (storeTask === undefined) {
220
- throw Errors.dependencyNotFound(`Task ${object.id}`);
204
+ throw Errors.dependencyNotFound(`Task ${object.id.toString()}`);
221
205
  }
222
206
 
223
207
  if (!storeTask.isInitialized) {
@@ -232,7 +216,7 @@ export class DependencyProcessor {
232
216
  );
233
217
  }
234
218
 
235
- return (input) => {
219
+ return (input: any) => {
236
220
  return this.taskRunner.run(
237
221
  storeTask.task,
238
222
  input,
@@ -245,7 +229,7 @@ export class DependencyProcessor {
245
229
  // check if it exists in the store with the value
246
230
  const storeResource = this.store.resources.get(object.id);
247
231
  if (storeResource === undefined) {
248
- throw Errors.dependencyNotFound(`Resource ${object.id}`);
232
+ throw Errors.dependencyNotFound(`Resource ${object.id.toString()}`);
249
233
  }
250
234
 
251
235
  const { resource, config } = storeResource;
@@ -256,11 +240,20 @@ export class DependencyProcessor {
256
240
  storeResource.isInitialized = true;
257
241
 
258
242
  // check if it has an initialisation function that provides the value
259
- storeResource.value = await this.resourceInitializer.initializeResource(
260
- resource,
261
- config,
262
- await this.extractDependencies(resource.dependencies || {}, resource.id)
263
- );
243
+ if (resource.init) {
244
+ const { value, context } =
245
+ await this.resourceInitializer.initializeResource(
246
+ resource,
247
+ config,
248
+ await this.extractDependencies(
249
+ resource.dependencies || {},
250
+ resource.id
251
+ )
252
+ );
253
+
254
+ storeResource.context = context;
255
+ storeResource.value = value;
256
+ }
264
257
  }
265
258
 
266
259
  return storeResource.value;
@@ -1,4 +1,9 @@
1
- import { EventHandlerType, IEvent, IEventDefinition } from "../defs";
1
+ import {
2
+ EventHandlerType,
3
+ IEvent,
4
+ IEventDefinition,
5
+ IEventEmission,
6
+ } from "../defs";
2
7
  import { Errors } from "../errors";
3
8
  import { Logger } from "./Logger";
4
9
 
@@ -6,13 +11,13 @@ const HandlerOptionsDefaults = { order: 0 };
6
11
 
7
12
  interface IListenerStorage {
8
13
  order: number;
9
- filter?: (event: IEvent<any>) => boolean;
14
+ filter?: (event: IEventEmission<any>) => boolean;
10
15
  handler: EventHandlerType;
11
16
  }
12
17
 
13
18
  export interface IEventHandlerOptions<T = any> {
14
19
  order?: number;
15
- filter?: (event: IEvent<T>) => boolean;
20
+ filter?: (event: IEventEmission<T>) => boolean;
16
21
  /**
17
22
  * Represents the listener ID. Use this to avoid a listener calling himself.
18
23
  */
@@ -20,8 +25,11 @@ export interface IEventHandlerOptions<T = any> {
20
25
  }
21
26
 
22
27
  export class EventManager {
23
- private listeners: Map<string, IListenerStorage[]> = new Map();
28
+ private listeners: Map<string | symbol, IListenerStorage[]> = new Map();
24
29
  private globalListeners: IListenerStorage[] = [];
30
+ private cachedMergedListeners: Map<string | symbol, IListenerStorage[]> =
31
+ new Map();
32
+ private globalListenersCacheValid = true;
25
33
  #isLocked = false;
26
34
 
27
35
  get isLocked() {
@@ -57,18 +65,54 @@ export class EventManager {
57
65
  return result;
58
66
  }
59
67
 
68
+ private getCachedMergedListeners(
69
+ eventId: string | symbol
70
+ ): IListenerStorage[] {
71
+ if (!this.globalListenersCacheValid) {
72
+ this.cachedMergedListeners.clear();
73
+ this.globalListenersCacheValid = true;
74
+ }
75
+
76
+ let cached = this.cachedMergedListeners.get(eventId);
77
+ if (!cached) {
78
+ const eventListeners = this.listeners.get(eventId) || [];
79
+ if (eventListeners.length === 0 && this.globalListeners.length === 0) {
80
+ cached = [];
81
+ } else if (eventListeners.length === 0) {
82
+ cached = this.globalListeners;
83
+ } else if (this.globalListeners.length === 0) {
84
+ cached = eventListeners;
85
+ } else {
86
+ cached = this.mergeSortedListeners(
87
+ eventListeners,
88
+ this.globalListeners
89
+ );
90
+ }
91
+ this.cachedMergedListeners.set(eventId, cached);
92
+ }
93
+ return cached;
94
+ }
95
+
96
+ private invalidateCache(eventId?: string | symbol): void {
97
+ if (eventId) {
98
+ this.cachedMergedListeners.delete(eventId);
99
+ } else {
100
+ this.globalListenersCacheValid = false;
101
+ }
102
+ }
103
+
60
104
  async emit<TInput>(
61
- eventDefinition: IEventDefinition<TInput>,
105
+ eventDefinition: IEvent<TInput>,
62
106
  data: TInput,
63
- source: string
107
+ source: string | symbol
64
108
  ): Promise<void> {
65
- const eventListeners = this.listeners.get(eventDefinition.id) || [];
66
- const allListeners = this.mergeSortedListeners(
67
- eventListeners,
68
- this.globalListeners
69
- );
109
+ const allListeners = this.getCachedMergedListeners(eventDefinition.id);
70
110
 
71
- const event: IEvent = {
111
+ if (allListeners.length === 0) {
112
+ return;
113
+ }
114
+
115
+ const event: IEventEmission = {
72
116
  id: eventDefinition.id,
73
117
  data,
74
118
  timestamp: new Date(),
@@ -100,7 +144,7 @@ export class EventManager {
100
144
  }
101
145
 
102
146
  addListener<T>(
103
- event: IEventDefinition | Array<IEventDefinition>,
147
+ event: IEvent<T> | Array<IEvent<T>>,
104
148
  handler: EventHandlerType<T>,
105
149
  options: IEventHandlerOptions<T> = HandlerOptionsDefaults
106
150
  ): void {
@@ -121,6 +165,7 @@ export class EventManager {
121
165
  } else {
122
166
  this.listeners.set(eventId, [newListener]);
123
167
  }
168
+ this.invalidateCache(eventId);
124
169
  }
125
170
  }
126
171
 
@@ -135,5 +180,11 @@ export class EventManager {
135
180
  filter: options.filter,
136
181
  };
137
182
  this.insertListener(this.globalListeners, newListener);
183
+ this.invalidateCache();
184
+ }
185
+
186
+ hasListeners<T>(eventDefinition: IEvent<T>): boolean {
187
+ const eventListeners = this.listeners.get(eventDefinition.id) || [];
188
+ return eventListeners.length > 0 || this.globalListeners.length > 0;
138
189
  }
139
190
  }