@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,4 +1,4 @@
1
- import { globalEvents } from "../globalEvents";
1
+ import { globalEvents } from "../globals/globalEvents";
2
2
  import { EventManager } from "./EventManager";
3
3
 
4
4
  export type LogLevels =
@@ -9,17 +9,32 @@ export type LogLevels =
9
9
  | "error"
10
10
  | "critical";
11
11
 
12
+ export interface LogInfo {
13
+ source?: string;
14
+ error?: Error;
15
+ data?: Record<string, any>;
16
+ [key: string]: any;
17
+ }
18
+
12
19
  export interface ILog {
13
20
  level: string;
14
21
  source?: string;
15
- data: any;
22
+ message: any;
16
23
  timestamp: Date;
24
+ error?: {
25
+ name: string;
26
+ message: string;
27
+ stack?: string;
28
+ };
29
+ data?: Record<string, any>;
30
+ context?: Record<string, any>;
17
31
  }
18
32
 
19
33
  export class Logger {
20
34
  printThreshold: LogLevels | null = null;
35
+ private boundContext: Record<string, any> = {};
21
36
 
22
- public severity = {
37
+ public static Severity = {
23
38
  trace: 0,
24
39
  debug: 1,
25
40
  info: 2,
@@ -28,32 +43,71 @@ export class Logger {
28
43
  critical: 5,
29
44
  };
30
45
 
31
- constructor(protected eventManager: EventManager) {}
46
+ constructor(
47
+ protected eventManager: EventManager,
48
+ boundContext: Record<string, any> = {}
49
+ ) {
50
+ this.boundContext = { ...boundContext };
51
+ }
32
52
 
33
53
  /**
34
- * @param level
35
- * @param message
54
+ * Creates a new logger instance with additional bound context
36
55
  */
37
- public async log(
38
- level: LogLevels,
39
- data: any,
40
- source?: string
41
- ): Promise<void> {
56
+ public with(context: Record<string, any>): Logger {
57
+ return new Logger(this.eventManager, {
58
+ ...this.boundContext,
59
+ ...context,
60
+ });
61
+ }
62
+
63
+ private extractErrorInfo(error: Error): {
64
+ name: string;
65
+ message: string;
66
+ stack?: string;
67
+ } {
68
+ return {
69
+ name: error.name,
70
+ message: error.message,
71
+ stack: error.stack,
72
+ };
73
+ }
74
+
75
+ /**
76
+ * Core logging method with structured LogInfo
77
+ */
78
+ public log(level: LogLevels, message: any, logInfo: LogInfo = {}): void {
79
+ const { source, error, data, ...context } = logInfo;
80
+
42
81
  const log: ILog = {
43
82
  level,
44
- data,
45
- source: source,
83
+ message,
84
+ source: source || this.boundContext.source,
46
85
  timestamp: new Date(),
86
+ error: error ? this.extractErrorInfo(error) : undefined,
87
+ data: data || undefined,
88
+ context: { ...this.boundContext, ...context },
47
89
  };
48
90
 
49
91
  if (
50
92
  this.printThreshold &&
51
- this.severity[level] >= this.severity[this.printThreshold]
93
+ Logger.Severity[level] >= Logger.Severity[this.printThreshold]
52
94
  ) {
53
95
  this.print(log);
54
96
  }
55
97
 
56
- await this.eventManager.emit(globalEvents.log, log, source || "unknown");
98
+ if (this.eventManager.hasListeners(globalEvents.log)) {
99
+ setImmediate(() => {
100
+ this.eventManager
101
+ .emit(
102
+ globalEvents.log,
103
+ log,
104
+ source || this.boundContext.source || "unknown"
105
+ )
106
+ .catch((err) => {
107
+ console.error("Logger event emission failed:", err);
108
+ });
109
+ });
110
+ }
57
111
  }
58
112
 
59
113
  /**
@@ -65,56 +119,124 @@ export class Logger {
65
119
  }
66
120
 
67
121
  public print(log: ILog) {
68
- // Extract the relevant information from the log
69
- const { level, source: context, data, timestamp } = log;
70
-
71
- // Format the timestamp to a more readable format
72
- const formattedTimestamp = timestamp.toISOString();
122
+ const { level, source, message, timestamp, error, data, context } = log;
123
+
124
+ // Color codes for different log levels
125
+ const colors = {
126
+ trace: '\x1b[90m', // bright black/gray
127
+ debug: '\x1b[36m', // cyan
128
+ info: '\x1b[32m', // green
129
+ warn: '\x1b[33m', // yellow
130
+ error: '\x1b[31m', // red
131
+ critical: '\x1b[35m', // magenta
132
+ reset: '\x1b[0m', // reset
133
+ bold: '\x1b[1m', // bold
134
+ dim: '\x1b[2m', // dim
135
+ blue: '\x1b[34m', // blue
136
+ red: '\x1b[31m', // red
137
+ cyan: '\x1b[36m', // cyan
138
+ };
73
139
 
74
- // Format the log level for better visibility
75
- const levelStr = `[${level.toUpperCase()}]`;
140
+ const levelColor = colors[level as keyof typeof colors] || colors.info;
141
+
142
+ // Format timestamp
143
+ const time = timestamp.toLocaleTimeString('en-US', {
144
+ hour12: false,
145
+ hour: '2-digit',
146
+ minute: '2-digit',
147
+ second: '2-digit'
148
+ });
149
+ const ms = timestamp.getMilliseconds().toString().padStart(3, '0');
150
+ const formattedTime = `${colors.dim}${time}.${ms}${colors.reset}`;
151
+
152
+ // Format level with color and padding
153
+ const levelStr = `${levelColor}${colors.bold}${level.toUpperCase().padEnd(8)}${colors.reset}`;
154
+
155
+ // Format source
156
+ const sourceStr = source ? `${colors.blue}[${source}]${colors.reset} ` : '';
157
+
158
+ // Format the main message
159
+ let messageStr: string;
160
+ if (typeof message === 'object') {
161
+ messageStr = JSON.stringify(message, null, 2);
162
+ } else {
163
+ messageStr = String(message);
164
+ }
76
165
 
77
- // Format the context, if provided
78
- const contextStr = context ? `(${context})` : "";
166
+ // Build the main log line
167
+ const mainLine = `${formattedTime} ${levelStr} ${sourceStr}${messageStr}`;
168
+
169
+ // Start building output lines
170
+ const lines = [mainLine];
171
+
172
+ // Add error information if present
173
+ if (error) {
174
+ lines.push(`${colors.dim}├─ ${colors.red}Error: ${error.name}${colors.reset}`);
175
+ lines.push(`${colors.dim}├─ ${colors.red}${error.message}${colors.reset}`);
176
+ if (error.stack) {
177
+ const stackLines = error.stack.split('\n').slice(1, 4); // Show first 3 stack frames
178
+ stackLines.forEach((line, index) => {
179
+ const prefix = index === stackLines.length - 1 ? '└─' : '├─';
180
+ lines.push(`${colors.dim}${prefix} ${colors.red}${line.trim()}${colors.reset}`);
181
+ });
182
+ }
183
+ }
79
184
 
80
- // Handle different data types, especially if it's an error
81
- let dataStr: string;
82
- if (data instanceof Error) {
83
- dataStr = `Error: ${data.name} - ${data.message}\nStack Trace:\n${data.stack}`;
84
- } else if (typeof data === "object") {
85
- dataStr = JSON.stringify(data, null, 2); // Pretty-print JSON objects
86
- } else {
87
- dataStr = String(data); // Convert any other type to string
185
+ // Add structured data if present
186
+ if (data && Object.keys(data).length > 0) {
187
+ lines.push(`${colors.dim}├─ ${colors.cyan}Data:${colors.reset}`);
188
+ const dataStr = JSON.stringify(data, null, 2);
189
+ const dataLines = dataStr.split('\n');
190
+ dataLines.forEach((line, index) => {
191
+ const prefix = index === dataLines.length - 1 ? '└─' : '├─';
192
+ lines.push(`${colors.dim}${prefix} ${colors.cyan}${line}${colors.reset}`);
193
+ });
88
194
  }
89
195
 
90
- // Construct the final log message
91
- const logMessage = `${formattedTimestamp} ${levelStr} ${contextStr} - ${dataStr}`;
196
+ // Add context if present (excluding common context we already show)
197
+ const filteredContext = context ? { ...context } : {};
198
+ delete filteredContext.source; // Already shown in source
199
+
200
+ if (filteredContext && Object.keys(filteredContext).length > 0) {
201
+ lines.push(`${colors.dim}└─ ${colors.blue}Context:${colors.reset}`);
202
+ const contextStr = JSON.stringify(filteredContext, null, 2);
203
+ const contextLines = contextStr.split('\n');
204
+ contextLines.forEach((line, index) => {
205
+ const prefix = index === contextLines.length - 1 ? ' ' : ' ';
206
+ lines.push(`${colors.dim}${prefix} ${colors.blue}${line}${colors.reset}`);
207
+ });
208
+ }
92
209
 
93
- // Print the log message
94
- console.log(logMessage);
210
+ // Output all lines
211
+ lines.forEach(line => console.log(line));
212
+
213
+ // Add a subtle separator for multi-line logs
214
+ if (lines.length > 1) {
215
+ console.log(`${colors.dim}${colors.reset}`);
216
+ }
95
217
  }
96
218
 
97
- public async info(data: any, source?: string) {
98
- await this.log("info", data, source);
219
+ public info(message: any, logInfo: LogInfo = {}) {
220
+ this.log("info", message, logInfo);
99
221
  }
100
222
 
101
- public async error(data: any, source?: string) {
102
- await this.log("error", data, source);
223
+ public error(message: any, logInfo: LogInfo = {}) {
224
+ this.log("error", message, logInfo);
103
225
  }
104
226
 
105
- public async warn(data: any, source?: string) {
106
- await this.log("warn", data, source);
227
+ public warn(message: any, logInfo: LogInfo = {}) {
228
+ this.log("warn", message, logInfo);
107
229
  }
108
230
 
109
- public async debug(data: any, source?: string) {
110
- await this.log("debug", data, source);
231
+ public debug(message: any, logInfo: LogInfo = {}) {
232
+ this.log("debug", message, logInfo);
111
233
  }
112
234
 
113
- public async trace(data: any, source?: string) {
114
- await this.log("trace", data, source);
235
+ public trace(message: any, logInfo: LogInfo = {}) {
236
+ this.log("trace", message, logInfo);
115
237
  }
116
238
 
117
- public async critical(data: any, source?: string) {
118
- await this.log("critical", data, source);
239
+ public critical(message: any, logInfo: LogInfo = {}) {
240
+ this.log("critical", message, logInfo);
119
241
  }
120
242
  }
@@ -0,0 +1,84 @@
1
+ import {
2
+ IResource,
3
+ IMiddleware,
4
+ ITask,
5
+ IResourceWithConfig,
6
+ RegisterableItems,
7
+ } from "../defs";
8
+ import * as utils from "../define";
9
+ import { Errors } from "../errors";
10
+ import {
11
+ TaskStoreElementType,
12
+ MiddlewareStoreElementType,
13
+ ResourceStoreElementType,
14
+ } from "./StoreTypes";
15
+ import { StoreRegistry } from "./StoreRegistry";
16
+
17
+ export class OverrideManager {
18
+ public overrides: Map<
19
+ string,
20
+ IResource | IMiddleware | ITask | IResourceWithConfig
21
+ > = new Map();
22
+
23
+ public overrideRequests: Set<{
24
+ source: string;
25
+ override: RegisterableItems;
26
+ }> = new Set();
27
+
28
+ constructor(private readonly registry: StoreRegistry) {}
29
+
30
+ storeOverridesDeeply<C>(element: IResource<C, any, any>) {
31
+ element.overrides.forEach((override) => {
32
+ if (utils.isResource(override)) {
33
+ this.storeOverridesDeeply(override);
34
+ }
35
+
36
+ let id: string;
37
+ if (utils.isResourceWithConfig(override)) {
38
+ this.storeOverridesDeeply(override.resource);
39
+ id = override.resource.id;
40
+ } else {
41
+ id = override.id;
42
+ }
43
+
44
+ this.overrideRequests.add({ source: element.id, override });
45
+ this.overrides.set(id, override);
46
+ });
47
+ }
48
+
49
+ processOverrides() {
50
+ // If we are trying to use override on something that wasn't previously registered, we throw an error.
51
+ for (const override of this.overrides.values()) {
52
+ let hasAnyItem = false;
53
+ if (utils.isTask(override)) {
54
+ hasAnyItem = this.registry.tasks.has(override.id);
55
+ } else if (utils.isResource(override)) {
56
+ hasAnyItem = this.registry.resources.has(override.id);
57
+ } else if (utils.isMiddleware(override)) {
58
+ hasAnyItem = this.registry.middlewares.has(override.id);
59
+ } else if (utils.isResourceWithConfig(override)) {
60
+ hasAnyItem = this.registry.resources.has(override.resource.id);
61
+ }
62
+
63
+ if (!hasAnyItem) {
64
+ const id = utils.isResourceWithConfig(override)
65
+ ? override.resource.id
66
+ : override.id;
67
+
68
+ throw Errors.dependencyNotFound(id);
69
+ }
70
+ }
71
+
72
+ for (const override of this.overrides.values()) {
73
+ if (utils.isTask(override)) {
74
+ this.registry.storeTask(override, false);
75
+ } else if (utils.isResource(override)) {
76
+ this.registry.storeResource(override, false);
77
+ } else if (utils.isMiddleware(override)) {
78
+ this.registry.storeMiddleware(override, false);
79
+ } else if (utils.isResourceWithConfig(override)) {
80
+ this.registry.storeResourceWithConfig(override, false);
81
+ }
82
+ }
83
+ }
84
+ }
@@ -0,0 +1,66 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+
3
+ /**
4
+ * Cooperative task queue.
5
+ * • Tasks run one‑after‑another (FIFO ordering).
6
+ * • Dead‑lock detection prevents nesting.
7
+ * • dispose() drains or cancels outstanding tasks, then rejects new ones.
8
+ */
9
+ export class Queue {
10
+ private tail: Promise<unknown> = Promise.resolve();
11
+ private disposed = false;
12
+ private abortController = new AbortController();
13
+
14
+ // true while inside a queued task → helps detect "queue in queue"
15
+ private readonly executionContext = new AsyncLocalStorage<boolean>();
16
+
17
+ /**
18
+ * Schedule an asynchronous task.
19
+ * @param task – receives an AbortSignal so it can cancel early if desired.
20
+ */
21
+ public run<T>(task: (signal: AbortSignal) => Promise<T>): Promise<T> {
22
+ // 1. refuse new work if we've disposed
23
+ if (this.disposed) {
24
+ return Promise.reject(new Error("Queue has been disposed"));
25
+ }
26
+
27
+ // 2. detect dead‑locks (a queued task adding another queued task)
28
+ if (this.executionContext.getStore()) {
29
+ return Promise.reject(
30
+ new Error(
31
+ "Dead‑lock detected: a queued task attempted to queue another task"
32
+ )
33
+ );
34
+ }
35
+
36
+ const { signal } = this.abortController;
37
+
38
+ // 3. chain task after the current tail
39
+ const result = this.tail.then(() =>
40
+ this.executionContext.run(true, () => task(signal))
41
+ );
42
+
43
+ // 4. preserve the chain even if the task rejects (swallow internally)
44
+ this.tail = result.catch(() => {});
45
+
46
+ return result;
47
+ }
48
+
49
+ /**
50
+ * Disposes the queue.
51
+ * @param options.cancel – if true, broadcasts AbortSignal to running task.
52
+ * default: false (waits for tasks to finish).
53
+ */
54
+ public async dispose(options: { cancel?: boolean } = {}): Promise<void> {
55
+ if (this.disposed) return;
56
+
57
+ this.disposed = true;
58
+
59
+ if (options.cancel) {
60
+ this.abortController.abort(); // notify cooperative tasks
61
+ }
62
+
63
+ // wait for everything already chained to settle
64
+ await this.tail.catch(() => {});
65
+ }
66
+ }
@@ -5,8 +5,9 @@ import {
5
5
  IResource,
6
6
  } from "../defs";
7
7
  import { EventManager } from "./EventManager";
8
- import { globalEvents } from "../globalEvents";
9
- import { MiddlewareStoreElementType, Store } from "./Store";
8
+ import { globalEvents } from "../globals/globalEvents";
9
+ import { Store } from "./Store";
10
+ import { MiddlewareStoreElementType } from "./StoreTypes";
10
11
  import { Logger } from "./Logger";
11
12
 
12
13
  export class ResourceInitializer {
@@ -23,12 +24,14 @@ export class ResourceInitializer {
23
24
  public async initializeResource<
24
25
  TConfig = null,
25
26
  TValue = any,
26
- TDeps extends DependencyMapType = {}
27
+ TDeps extends DependencyMapType = {},
28
+ TContext = any
27
29
  >(
28
30
  resource: IResource<TConfig, TValue, TDeps>,
29
31
  config: TConfig,
30
32
  dependencies: DependencyValuesType<TDeps>
31
- ): Promise<TValue | undefined> {
33
+ ): Promise<{ value: TValue; context: TContext }> {
34
+ const context = resource.context?.();
32
35
  await this.eventManager.emit(
33
36
  globalEvents.resources.beforeInit,
34
37
  {
@@ -44,15 +47,22 @@ export class ResourceInitializer {
44
47
  resource.id
45
48
  );
46
49
 
47
- let error, value;
50
+ let error: any, value: TValue | undefined;
48
51
  try {
49
- value = await this.initWithMiddleware(resource, config, dependencies);
52
+ if (resource.init) {
53
+ value = await this.initWithMiddleware(
54
+ resource,
55
+ config,
56
+ dependencies,
57
+ context
58
+ );
59
+ }
50
60
 
51
61
  await this.eventManager.emit(
52
62
  resource.events.afterInit,
53
63
  {
54
64
  config,
55
- value,
65
+ value: value as TValue,
56
66
  },
57
67
  resource.id
58
68
  );
@@ -61,14 +71,14 @@ export class ResourceInitializer {
61
71
  {
62
72
  config,
63
73
  resource,
64
- value,
74
+ value: value as TValue,
65
75
  },
66
76
  resource.id
67
77
  );
68
78
 
69
- this.logger.debug(`Resource ${resource.id} initialized`, resource.id);
79
+ this.logger.debug(`Resource ${resource.id} initialized`, { source: resource.id });
70
80
 
71
- return value;
81
+ return { value: value as TValue, context };
72
82
  } catch (e) {
73
83
  error = e;
74
84
  let isSuppressed = false;
@@ -80,7 +90,7 @@ export class ResourceInitializer {
80
90
  await this.eventManager.emit(
81
91
  resource.events.onError,
82
92
  {
83
- error,
93
+ error: error as Error,
84
94
  suppress,
85
95
  },
86
96
  resource.id
@@ -88,7 +98,7 @@ export class ResourceInitializer {
88
98
  await this.eventManager.emit(
89
99
  globalEvents.resources.onError,
90
100
  {
91
- error,
101
+ error: error as Error,
92
102
  resource,
93
103
  suppress,
94
104
  },
@@ -96,23 +106,28 @@ export class ResourceInitializer {
96
106
  );
97
107
 
98
108
  if (!isSuppressed) throw e;
109
+
110
+ return { value: undefined as TValue, context: {} as TContext };
99
111
  }
100
112
  }
101
113
 
102
- public async initWithMiddleware<C, V, D extends DependencyMapType>(
103
- resource: IResource<C, V>,
114
+ public async initWithMiddleware<C, V, D extends DependencyMapType, TContext>(
115
+ resource: IResource<C, V, D, TContext>,
104
116
  config: C,
105
- dependencies: D
117
+ dependencies: DependencyValuesType<D>,
118
+ context: TContext
106
119
  ) {
107
120
  let next = async (config: C): Promise<V | undefined> => {
108
121
  if (resource.init) {
109
- return resource.init.call(null, config, dependencies);
122
+ return resource.init.call(null, config, dependencies, context);
110
123
  }
111
124
  };
112
125
 
113
126
  const existingMiddlewares = resource.middleware;
114
127
  const createdMiddlewares = [
115
- ...this.store.getGlobalMiddlewares(existingMiddlewares.map((x) => x.id)),
128
+ ...this.store.getEverywhereMiddlewareForResources(
129
+ existingMiddlewares.map((x) => x.id)
130
+ ),
116
131
  ...existingMiddlewares,
117
132
  ];
118
133
 
@@ -126,11 +141,14 @@ export class ResourceInitializer {
126
141
  next = async (config: C) => {
127
142
  return storeMiddleware.middleware.run(
128
143
  {
129
- resourceDefinition: resource as any,
130
- config: config,
144
+ resource: {
145
+ definition: resource,
146
+ config,
147
+ },
131
148
  next: nextFunction,
132
149
  },
133
- storeMiddleware.computedDependencies
150
+ storeMiddleware.computedDependencies,
151
+ middleware.config
134
152
  );
135
153
  };
136
154
  }