@bluelibs/runner 2.2.3 → 3.0.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 (204) hide show
  1. package/README.md +1315 -942
  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 +22 -3
  9. package/dist/define.js +52 -8
  10. package/dist/define.js.map +1 -1
  11. package/dist/defs.d.ts +52 -31
  12. package/dist/defs.js +10 -2
  13. package/dist/defs.js.map +1 -1
  14. package/dist/errors.js +1 -1
  15. package/dist/errors.js.map +1 -1
  16. package/dist/event.types.d.ts +18 -0
  17. package/dist/event.types.js +4 -0
  18. package/dist/event.types.js.map +1 -0
  19. package/dist/examples/registrator-example.d.ts +122 -0
  20. package/dist/examples/registrator-example.js +147 -0
  21. package/dist/examples/registrator-example.js.map +1 -0
  22. package/dist/globals/globalEvents.d.ts +41 -0
  23. package/dist/globals/globalEvents.js +94 -0
  24. package/dist/globals/globalEvents.js.map +1 -0
  25. package/dist/globals/globalMiddleware.d.ts +23 -0
  26. package/dist/globals/globalMiddleware.js +15 -0
  27. package/dist/globals/globalMiddleware.js.map +1 -0
  28. package/dist/globals/globalResources.d.ts +27 -0
  29. package/dist/globals/globalResources.js +47 -0
  30. package/dist/globals/globalResources.js.map +1 -0
  31. package/dist/globals/middleware/cache.middleware.d.ts +34 -0
  32. package/dist/globals/middleware/cache.middleware.js +85 -0
  33. package/dist/globals/middleware/cache.middleware.js.map +1 -0
  34. package/dist/globals/middleware/requireContext.middleware.d.ts +6 -0
  35. package/dist/globals/middleware/requireContext.middleware.js +25 -0
  36. package/dist/globals/middleware/requireContext.middleware.js.map +1 -0
  37. package/dist/globals/middleware/retry.middleware.d.ts +20 -0
  38. package/dist/globals/middleware/retry.middleware.js +34 -0
  39. package/dist/globals/middleware/retry.middleware.js.map +1 -0
  40. package/dist/globals/resources/queue.resource.d.ts +7 -0
  41. package/dist/globals/resources/queue.resource.js +31 -0
  42. package/dist/globals/resources/queue.resource.js.map +1 -0
  43. package/dist/index.d.ts +45 -9
  44. package/dist/index.js +14 -9
  45. package/dist/index.js.map +1 -1
  46. package/dist/middleware.types.d.ts +40 -0
  47. package/dist/middleware.types.js +4 -0
  48. package/dist/middleware.types.js.map +1 -0
  49. package/dist/models/DependencyProcessor.d.ts +2 -1
  50. package/dist/models/DependencyProcessor.js +11 -13
  51. package/dist/models/DependencyProcessor.js.map +1 -1
  52. package/dist/models/EventManager.d.ts +5 -0
  53. package/dist/models/EventManager.js +44 -2
  54. package/dist/models/EventManager.js.map +1 -1
  55. package/dist/models/Logger.d.ts +30 -12
  56. package/dist/models/Logger.js +130 -42
  57. package/dist/models/Logger.js.map +1 -1
  58. package/dist/models/OverrideManager.d.ts +13 -0
  59. package/dist/models/OverrideManager.js +70 -0
  60. package/dist/models/OverrideManager.js.map +1 -0
  61. package/dist/models/Queue.d.ts +25 -0
  62. package/dist/models/Queue.js +54 -0
  63. package/dist/models/Queue.js.map +1 -0
  64. package/dist/models/ResourceInitializer.d.ts +5 -2
  65. package/dist/models/ResourceInitializer.js +20 -14
  66. package/dist/models/ResourceInitializer.js.map +1 -1
  67. package/dist/models/Semaphore.d.ts +61 -0
  68. package/dist/models/Semaphore.js +166 -0
  69. package/dist/models/Semaphore.js.map +1 -0
  70. package/dist/models/Store.d.ts +17 -72
  71. package/dist/models/Store.js +71 -269
  72. package/dist/models/Store.js.map +1 -1
  73. package/dist/models/StoreConstants.d.ts +11 -0
  74. package/dist/models/StoreConstants.js +18 -0
  75. package/dist/models/StoreConstants.js.map +1 -0
  76. package/dist/models/StoreRegistry.d.ts +25 -0
  77. package/dist/models/StoreRegistry.js +171 -0
  78. package/dist/models/StoreRegistry.js.map +1 -0
  79. package/dist/models/StoreTypes.d.ts +21 -0
  80. package/dist/models/StoreTypes.js +3 -0
  81. package/dist/models/StoreTypes.js.map +1 -0
  82. package/dist/models/StoreValidator.d.ts +10 -0
  83. package/dist/models/StoreValidator.js +41 -0
  84. package/dist/models/StoreValidator.js.map +1 -0
  85. package/dist/models/TaskRunner.js +39 -24
  86. package/dist/models/TaskRunner.js.map +1 -1
  87. package/dist/models/VarStore.d.ts +17 -0
  88. package/dist/models/VarStore.js +60 -0
  89. package/dist/models/VarStore.js.map +1 -0
  90. package/dist/models/index.d.ts +3 -0
  91. package/dist/models/index.js +3 -0
  92. package/dist/models/index.js.map +1 -1
  93. package/dist/resource.types.d.ts +31 -0
  94. package/dist/resource.types.js +3 -0
  95. package/dist/resource.types.js.map +1 -0
  96. package/dist/run.d.ts +4 -1
  97. package/dist/run.js +6 -3
  98. package/dist/run.js.map +1 -1
  99. package/dist/symbols.d.ts +24 -0
  100. package/dist/symbols.js +29 -0
  101. package/dist/symbols.js.map +1 -0
  102. package/dist/task.types.d.ts +55 -0
  103. package/dist/task.types.js +23 -0
  104. package/dist/task.types.js.map +1 -0
  105. package/dist/tools/registratorId.d.ts +4 -0
  106. package/dist/tools/registratorId.js +40 -0
  107. package/dist/tools/registratorId.js.map +1 -0
  108. package/dist/tools/simpleHash.d.ts +9 -0
  109. package/dist/tools/simpleHash.js +34 -0
  110. package/dist/tools/simpleHash.js.map +1 -0
  111. package/dist/types/base-interfaces.d.ts +18 -0
  112. package/dist/types/base-interfaces.js +6 -0
  113. package/dist/types/base-interfaces.js.map +1 -0
  114. package/dist/types/base.d.ts +13 -0
  115. package/dist/types/base.js +3 -0
  116. package/dist/types/base.js.map +1 -0
  117. package/dist/types/dependencies.d.ts +22 -0
  118. package/dist/types/dependencies.js +3 -0
  119. package/dist/types/dependencies.js.map +1 -0
  120. package/dist/types/dependency-core.d.ts +14 -0
  121. package/dist/types/dependency-core.js +5 -0
  122. package/dist/types/dependency-core.js.map +1 -0
  123. package/dist/types/events.d.ts +52 -0
  124. package/dist/types/events.js +6 -0
  125. package/dist/types/events.js.map +1 -0
  126. package/dist/types/hooks.d.ts +16 -0
  127. package/dist/types/hooks.js +5 -0
  128. package/dist/types/hooks.js.map +1 -0
  129. package/dist/types/index.d.ts +14 -0
  130. package/dist/types/index.js +27 -0
  131. package/dist/types/index.js.map +1 -0
  132. package/dist/types/meta.d.ts +13 -0
  133. package/dist/types/meta.js +5 -0
  134. package/dist/types/meta.js.map +1 -0
  135. package/dist/types/middleware.d.ts +38 -0
  136. package/dist/types/middleware.js +6 -0
  137. package/dist/types/middleware.js.map +1 -0
  138. package/dist/types/registerable.d.ts +10 -0
  139. package/dist/types/registerable.js +5 -0
  140. package/dist/types/registerable.js.map +1 -0
  141. package/dist/types/resources.d.ts +44 -0
  142. package/dist/types/resources.js +5 -0
  143. package/dist/types/resources.js.map +1 -0
  144. package/dist/types/symbols.d.ts +24 -0
  145. package/dist/types/symbols.js +30 -0
  146. package/dist/types/symbols.js.map +1 -0
  147. package/dist/types/tasks.d.ts +41 -0
  148. package/dist/types/tasks.js +5 -0
  149. package/dist/types/tasks.js.map +1 -0
  150. package/dist/types/utilities.d.ts +7 -0
  151. package/dist/types/utilities.js +5 -0
  152. package/dist/types/utilities.js.map +1 -0
  153. package/package.json +10 -6
  154. package/src/__tests__/benchmark/benchmark.test.ts +1 -1
  155. package/src/__tests__/context.test.ts +91 -0
  156. package/src/__tests__/errors.test.ts +8 -5
  157. package/src/__tests__/globalEvents.test.ts +1 -1
  158. package/src/__tests__/globals/cache.middleware.test.ts +772 -0
  159. package/src/__tests__/globals/queue.resource.test.ts +141 -0
  160. package/src/__tests__/globals/requireContext.middleware.test.ts +98 -0
  161. package/src/__tests__/globals/retry.middleware.test.ts +157 -0
  162. package/src/__tests__/index.helper.test.ts +55 -0
  163. package/src/__tests__/models/EventManager.test.ts +144 -0
  164. package/src/__tests__/models/Logger.test.ts +291 -34
  165. package/src/__tests__/models/Queue.test.ts +189 -0
  166. package/src/__tests__/models/ResourceInitializer.test.ts +8 -6
  167. package/src/__tests__/models/Semaphore.test.ts +713 -0
  168. package/src/__tests__/models/Store.test.ts +40 -0
  169. package/src/__tests__/models/TaskRunner.test.ts +86 -5
  170. package/src/__tests__/run.middleware.test.ts +166 -12
  171. package/src/__tests__/run.overrides.test.ts +13 -10
  172. package/src/__tests__/run.test.ts +363 -12
  173. package/src/__tests__/setOutput.test.ts +244 -0
  174. package/src/__tests__/tools/getCallerFile.test.ts +9 -9
  175. package/src/__tests__/typesafety.test.ts +54 -39
  176. package/src/context.ts +86 -0
  177. package/src/define.ts +84 -14
  178. package/src/defs.ts +91 -41
  179. package/src/errors.ts +3 -1
  180. package/src/{globalEvents.ts → globals/globalEvents.ts} +13 -12
  181. package/src/globals/globalMiddleware.ts +14 -0
  182. package/src/{globalResources.ts → globals/globalResources.ts} +14 -10
  183. package/src/globals/middleware/cache.middleware.ts +115 -0
  184. package/src/globals/middleware/requireContext.middleware.ts +36 -0
  185. package/src/globals/middleware/retry.middleware.ts +56 -0
  186. package/src/globals/resources/queue.resource.ts +34 -0
  187. package/src/index.ts +9 -5
  188. package/src/models/DependencyProcessor.ts +36 -40
  189. package/src/models/EventManager.ts +45 -5
  190. package/src/models/Logger.ts +170 -48
  191. package/src/models/OverrideManager.ts +84 -0
  192. package/src/models/Queue.ts +66 -0
  193. package/src/models/ResourceInitializer.ts +38 -20
  194. package/src/models/Semaphore.ts +208 -0
  195. package/src/models/Store.ts +94 -342
  196. package/src/models/StoreConstants.ts +17 -0
  197. package/src/models/StoreRegistry.ts +217 -0
  198. package/src/models/StoreTypes.ts +46 -0
  199. package/src/models/StoreValidator.ts +38 -0
  200. package/src/models/TaskRunner.ts +53 -40
  201. package/src/models/index.ts +3 -0
  202. package/src/run.ts +7 -4
  203. package/src/__tests__/index.ts +0 -15
  204. package/src/examples/express-mongo/index.ts +0 -1
@@ -1,10 +1,10 @@
1
- import { defineEvent } from "./define";
2
- import { ITask, IResource, IEvent } from "./defs";
3
- import { ILog } from "./models/Logger";
1
+ import { defineEvent } from "../define";
2
+ import { ITask, IResource, IEvent } from "../defs";
3
+ import { ILog } from "../models/Logger";
4
4
 
5
5
  export const globalEvents = {
6
6
  beforeInit: defineEvent({
7
- id: "global.events.beforeInit",
7
+ id: "globals.events.beforeInit",
8
8
  meta: {
9
9
  title: "Before Initialization",
10
10
  description:
@@ -13,7 +13,7 @@ export const globalEvents = {
13
13
  },
14
14
  }),
15
15
  afterInit: defineEvent({
16
- id: "global.events.afterInit",
16
+ id: "globals.events.afterInit",
17
17
  meta: {
18
18
  title: "After Initialization",
19
19
  description:
@@ -22,7 +22,7 @@ export const globalEvents = {
22
22
  },
23
23
  }),
24
24
  log: defineEvent<ILog>({
25
- id: "global.events.log",
25
+ id: "globals.events.log",
26
26
  meta: {
27
27
  title: "Log Event",
28
28
  description: "Used to log events and messages across the system.",
@@ -34,7 +34,7 @@ export const globalEvents = {
34
34
  task: ITask<any, any, any>;
35
35
  input: any;
36
36
  }>({
37
- id: "global.events.tasks.beforeRun",
37
+ id: "globals.events.tasks.beforeRun",
38
38
  meta: {
39
39
  title: "Before Task Execution",
40
40
  description:
@@ -46,8 +46,9 @@ export const globalEvents = {
46
46
  task: ITask<any, any, any>;
47
47
  input: any;
48
48
  output: any;
49
+ setOutput: (newOutput: any) => void;
49
50
  }>({
50
- id: "global.events.tasks.afterRun",
51
+ id: "globals.events.tasks.afterRun",
51
52
  meta: {
52
53
  title: "After Task Execution",
53
54
  description:
@@ -60,7 +61,7 @@ export const globalEvents = {
60
61
  suppress: () => void;
61
62
  task: ITask<any, any, any>;
62
63
  }>({
63
- id: "global.events.tasks.onError",
64
+ id: "globals.events.tasks.onError",
64
65
  meta: {
65
66
  title: "Task Error",
66
67
  description:
@@ -74,7 +75,7 @@ export const globalEvents = {
74
75
  resource: IResource<any, any, any>;
75
76
  config: any;
76
77
  }>({
77
- id: "global.events.resources.beforeInit",
78
+ id: "globals.events.resources.beforeInit",
78
79
  meta: {
79
80
  title: "Before Resource Initialization",
80
81
  description:
@@ -87,7 +88,7 @@ export const globalEvents = {
87
88
  config: any;
88
89
  value: any;
89
90
  }>({
90
- id: "global.events.resources.afterInit",
91
+ id: "globals.events.resources.afterInit",
91
92
  meta: {
92
93
  title: "After Resource Initialization",
93
94
  description:
@@ -100,7 +101,7 @@ export const globalEvents = {
100
101
  suppress: () => void;
101
102
  resource: IResource<any, any, any>;
102
103
  }>({
103
- id: "global.events.resources.onError",
104
+ id: "globals.events.resources.onError",
104
105
  meta: {
105
106
  title: "Resource Error",
106
107
  description:
@@ -0,0 +1,14 @@
1
+ import { Context, ContextError } from "../context";
2
+ import { defineMiddleware } from "../define";
3
+ import { cacheMiddleware } from "./middleware/cache.middleware";
4
+ import { requireContextMiddleware } from "./middleware/requireContext.middleware";
5
+ import { retryMiddleware } from "./middleware/retry.middleware";
6
+
7
+ /**
8
+ * Global middlewares
9
+ */
10
+ export const globalMiddlewares = {
11
+ requireContext: requireContextMiddleware,
12
+ retry: retryMiddleware,
13
+ cache: cacheMiddleware,
14
+ };
@@ -1,11 +1,13 @@
1
- import { defineResource } from "./define";
2
- import { EventManager } from "./models/EventManager";
3
- import { Logger } from "./models/Logger";
4
- import { Store } from "./models/Store";
5
- import { TaskRunner } from "./models/TaskRunner";
1
+ import { defineResource } from "../define";
2
+ import { EventManager } from "../models/EventManager";
3
+ import { Logger } from "../models/Logger";
4
+ import { Store } from "../models/Store";
5
+ import { TaskRunner } from "../models/TaskRunner";
6
+ import { cacheResource } from "./middleware/cache.middleware";
7
+ import { queueResource } from "./resources/queue.resource";
6
8
 
7
9
  const store = defineResource({
8
- id: "global.resources.store",
10
+ id: "globals.resources.store",
9
11
  init: async (store: Store) => store,
10
12
  meta: {
11
13
  title: "Store",
@@ -18,7 +20,7 @@ const store = defineResource({
18
20
  export const globalResources = {
19
21
  store,
20
22
  eventManager: defineResource({
21
- id: "global.resources.eventManager",
23
+ id: "globals.resources.eventManager",
22
24
  init: async (em: EventManager) => em,
23
25
  meta: {
24
26
  title: "Event Manager",
@@ -28,7 +30,7 @@ export const globalResources = {
28
30
  },
29
31
  }),
30
32
  taskRunner: defineResource({
31
- id: "global.resources.taskRunner",
33
+ id: "globals.resources.taskRunner",
32
34
  init: async (runner: TaskRunner) => runner,
33
35
  meta: {
34
36
  title: "Task Runner",
@@ -38,12 +40,14 @@ export const globalResources = {
38
40
  },
39
41
  }),
40
42
  logger: defineResource({
41
- id: "global.resources.logger",
43
+ id: "globals.resources.logger",
42
44
  init: async (logger: Logger) => logger,
43
45
  meta: {
44
46
  title: "Logger",
45
47
  description:
46
- "Logs all events and errors. This is meant to be used internally for most use-cases. Emits a global.log event for each log.",
48
+ "Logs all events and errors. This is meant to be used internally for most use-cases. Emits a globals.log event for each log.",
47
49
  },
48
50
  }),
51
+ cache: cacheResource,
52
+ queue: queueResource,
49
53
  };
@@ -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, 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, 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";
@@ -7,7 +7,8 @@ import {
7
7
  IEventDefinition,
8
8
  IEvent,
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
 
@@ -132,18 +139,6 @@ export class DependencyProcessor {
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,
@@ -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) {
184
179
  if (utils.isResource(object)) {
185
180
  return this.extractResourceDependency(object);
186
181
  } else if (utils.isTask(object)) {
@@ -201,15 +196,7 @@ export class DependencyProcessor {
201
196
  object: IEventDefinition<Record<string, any>>,
202
197
  source: string
203
198
  ) {
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
-
199
+ return async (input: any) => {
213
200
  return this.eventManager.emit(object, input, source);
214
201
  };
215
202
  }
@@ -232,7 +219,7 @@ export class DependencyProcessor {
232
219
  );
233
220
  }
234
221
 
235
- return (input) => {
222
+ return (input: any) => {
236
223
  return this.taskRunner.run(
237
224
  storeTask.task,
238
225
  input,
@@ -256,11 +243,20 @@ export class DependencyProcessor {
256
243
  storeResource.isInitialized = true;
257
244
 
258
245
  // 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
- );
246
+ if (resource.init) {
247
+ const { value, context } =
248
+ await this.resourceInitializer.initializeResource(
249
+ resource,
250
+ config,
251
+ await this.extractDependencies(
252
+ resource.dependencies || {},
253
+ resource.id
254
+ )
255
+ );
256
+
257
+ storeResource.context = context;
258
+ storeResource.value = value;
259
+ }
264
260
  }
265
261
 
266
262
  return storeResource.value;
@@ -22,6 +22,8 @@ export interface IEventHandlerOptions<T = any> {
22
22
  export class EventManager {
23
23
  private listeners: Map<string, IListenerStorage[]> = new Map();
24
24
  private globalListeners: IListenerStorage[] = [];
25
+ private cachedMergedListeners: Map<string, IListenerStorage[]> = new Map();
26
+ private globalListenersCacheValid = true;
25
27
  #isLocked = false;
26
28
 
27
29
  get isLocked() {
@@ -57,16 +59,47 @@ export class EventManager {
57
59
  return result;
58
60
  }
59
61
 
62
+ private getCachedMergedListeners(eventId: string): IListenerStorage[] {
63
+ if (!this.globalListenersCacheValid) {
64
+ this.cachedMergedListeners.clear();
65
+ this.globalListenersCacheValid = true;
66
+ }
67
+
68
+ let cached = this.cachedMergedListeners.get(eventId);
69
+ if (!cached) {
70
+ const eventListeners = this.listeners.get(eventId) || [];
71
+ if (eventListeners.length === 0 && this.globalListeners.length === 0) {
72
+ cached = [];
73
+ } else if (eventListeners.length === 0) {
74
+ cached = this.globalListeners;
75
+ } else if (this.globalListeners.length === 0) {
76
+ cached = eventListeners;
77
+ } else {
78
+ cached = this.mergeSortedListeners(eventListeners, this.globalListeners);
79
+ }
80
+ this.cachedMergedListeners.set(eventId, cached);
81
+ }
82
+ return cached;
83
+ }
84
+
85
+ private invalidateCache(eventId?: string): void {
86
+ if (eventId) {
87
+ this.cachedMergedListeners.delete(eventId);
88
+ } else {
89
+ this.globalListenersCacheValid = false;
90
+ }
91
+ }
92
+
60
93
  async emit<TInput>(
61
94
  eventDefinition: IEventDefinition<TInput>,
62
95
  data: TInput,
63
96
  source: string
64
97
  ): Promise<void> {
65
- const eventListeners = this.listeners.get(eventDefinition.id) || [];
66
- const allListeners = this.mergeSortedListeners(
67
- eventListeners,
68
- this.globalListeners
69
- );
98
+ const allListeners = this.getCachedMergedListeners(eventDefinition.id);
99
+
100
+ if (allListeners.length === 0) {
101
+ return;
102
+ }
70
103
 
71
104
  const event: IEvent = {
72
105
  id: eventDefinition.id,
@@ -121,6 +154,7 @@ export class EventManager {
121
154
  } else {
122
155
  this.listeners.set(eventId, [newListener]);
123
156
  }
157
+ this.invalidateCache(eventId);
124
158
  }
125
159
  }
126
160
 
@@ -135,5 +169,11 @@ export class EventManager {
135
169
  filter: options.filter,
136
170
  };
137
171
  this.insertListener(this.globalListeners, newListener);
172
+ this.invalidateCache();
173
+ }
174
+
175
+ hasListeners<T>(eventDefinition: IEventDefinition<T>): boolean {
176
+ const eventListeners = this.listeners.get(eventDefinition.id) || [];
177
+ return eventListeners.length > 0 || this.globalListeners.length > 0;
138
178
  }
139
179
  }