@bluelibs/runner 3.4.3 → 4.1.1

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 (375) hide show
  1. package/AI.md +638 -0
  2. package/README.md +1071 -586
  3. package/dist/context.d.ts +4 -8
  4. package/dist/context.js +5 -12
  5. package/dist/context.js.map +1 -1
  6. package/dist/define.d.ts +9 -113
  7. package/dist/define.js +29 -364
  8. package/dist/define.js.map +1 -1
  9. package/dist/definers/defineEvent.d.ts +2 -0
  10. package/dist/definers/defineEvent.js +23 -0
  11. package/dist/definers/defineEvent.js.map +1 -0
  12. package/dist/definers/defineHook.d.ts +6 -0
  13. package/dist/definers/defineHook.js +24 -0
  14. package/dist/definers/defineHook.js.map +1 -0
  15. package/dist/definers/defineOverride.d.ts +14 -0
  16. package/dist/definers/defineOverride.js +13 -0
  17. package/dist/definers/defineOverride.js.map +1 -0
  18. package/dist/definers/defineResource.d.ts +2 -0
  19. package/dist/definers/defineResource.js +69 -0
  20. package/dist/definers/defineResource.js.map +1 -0
  21. package/dist/definers/defineResourceMiddleware.d.ts +2 -0
  22. package/dist/definers/defineResourceMiddleware.js +42 -0
  23. package/dist/definers/defineResourceMiddleware.js.map +1 -0
  24. package/dist/definers/defineTag.d.ts +12 -0
  25. package/dist/definers/defineTag.js +106 -0
  26. package/dist/definers/defineTag.js.map +1 -0
  27. package/dist/definers/defineTask.d.ts +15 -0
  28. package/dist/definers/defineTask.js +42 -0
  29. package/dist/definers/defineTask.js.map +1 -0
  30. package/dist/definers/defineTaskMiddleware.d.ts +2 -0
  31. package/dist/definers/defineTaskMiddleware.js +42 -0
  32. package/dist/definers/defineTaskMiddleware.js.map +1 -0
  33. package/dist/definers/tools.d.ts +45 -0
  34. package/dist/definers/tools.js +75 -0
  35. package/dist/definers/tools.js.map +1 -0
  36. package/dist/defs.d.ts +16 -424
  37. package/dist/defs.js +26 -38
  38. package/dist/defs.js.map +1 -1
  39. package/dist/errors.d.ts +23 -8
  40. package/dist/errors.js +50 -10
  41. package/dist/errors.js.map +1 -1
  42. package/dist/globals/globalEvents.d.ts +15 -39
  43. package/dist/globals/globalEvents.js +20 -81
  44. package/dist/globals/globalEvents.js.map +1 -1
  45. package/dist/globals/globalMiddleware.d.ts +24 -17
  46. package/dist/globals/globalMiddleware.js +12 -4
  47. package/dist/globals/globalMiddleware.js.map +1 -1
  48. package/dist/globals/globalResources.d.ts +13 -28
  49. package/dist/globals/globalResources.js +15 -7
  50. package/dist/globals/globalResources.js.map +1 -1
  51. package/dist/globals/globalTags.d.ts +9 -0
  52. package/dist/globals/globalTags.js +23 -0
  53. package/dist/globals/globalTags.js.map +1 -0
  54. package/dist/globals/middleware/cache.middleware.d.ts +10 -17
  55. package/dist/globals/middleware/cache.middleware.js +4 -16
  56. package/dist/globals/middleware/cache.middleware.js.map +1 -1
  57. package/dist/globals/middleware/requireContext.middleware.d.ts +1 -1
  58. package/dist/globals/middleware/requireContext.middleware.js +5 -14
  59. package/dist/globals/middleware/requireContext.middleware.js.map +1 -1
  60. package/dist/globals/middleware/retry.middleware.d.ts +2 -1
  61. package/dist/globals/middleware/retry.middleware.js +32 -5
  62. package/dist/globals/middleware/retry.middleware.js.map +1 -1
  63. package/dist/globals/middleware/timeout.middleware.d.ts +2 -1
  64. package/dist/globals/middleware/timeout.middleware.js +31 -5
  65. package/dist/globals/middleware/timeout.middleware.js.map +1 -1
  66. package/dist/globals/resources/debug/debug.resource.d.ts +7 -0
  67. package/dist/globals/resources/debug/debug.resource.js +29 -0
  68. package/dist/globals/resources/debug/debug.resource.js.map +1 -0
  69. package/dist/globals/resources/debug/debug.tag.d.ts +2 -0
  70. package/dist/globals/resources/debug/debug.tag.js +12 -0
  71. package/dist/globals/resources/debug/debug.tag.js.map +1 -0
  72. package/dist/globals/resources/debug/debugConfig.resource.d.ts +22 -0
  73. package/dist/globals/resources/debug/debugConfig.resource.js +20 -0
  74. package/dist/globals/resources/debug/debugConfig.resource.js.map +1 -0
  75. package/dist/globals/resources/debug/executionTracker.middleware.d.ts +50 -0
  76. package/dist/globals/resources/debug/executionTracker.middleware.js +87 -0
  77. package/dist/globals/resources/debug/executionTracker.middleware.js.map +1 -0
  78. package/dist/globals/resources/debug/globalEvent.hook.d.ts +27 -0
  79. package/dist/globals/resources/debug/globalEvent.hook.js +38 -0
  80. package/dist/globals/resources/debug/globalEvent.hook.js.map +1 -0
  81. package/dist/globals/resources/debug/hook.hook.d.ts +25 -0
  82. package/dist/globals/resources/debug/hook.hook.js +42 -0
  83. package/dist/globals/resources/debug/hook.hook.js.map +1 -0
  84. package/dist/globals/resources/debug/index.d.ts +6 -0
  85. package/dist/{types → globals/resources/debug}/index.js +6 -11
  86. package/dist/globals/resources/debug/index.js.map +1 -0
  87. package/dist/globals/resources/debug/middleware.hook.d.ts +25 -0
  88. package/dist/globals/resources/debug/middleware.hook.js +71 -0
  89. package/dist/globals/resources/debug/middleware.hook.js.map +1 -0
  90. package/dist/globals/resources/debug/types.d.ts +25 -0
  91. package/dist/globals/resources/debug/types.js +65 -0
  92. package/dist/globals/resources/debug/types.js.map +1 -0
  93. package/dist/globals/resources/debug/utils.d.ts +2 -0
  94. package/dist/globals/resources/debug/utils.js +9 -0
  95. package/dist/globals/resources/debug/utils.js.map +1 -0
  96. package/dist/globals/resources/queue.resource.d.ts +3 -3
  97. package/dist/globals/resources/queue.resource.js.map +1 -1
  98. package/dist/globals/types.d.ts +1 -0
  99. package/dist/{task.types.js → globals/types.js} +2 -7
  100. package/dist/globals/types.js.map +1 -0
  101. package/dist/index.d.ts +58 -85
  102. package/dist/index.js +23 -10
  103. package/dist/index.js.map +1 -1
  104. package/dist/models/DependencyProcessor.d.ts +8 -6
  105. package/dist/models/DependencyProcessor.js +129 -30
  106. package/dist/models/DependencyProcessor.js.map +1 -1
  107. package/dist/models/EventManager.d.ts +127 -7
  108. package/dist/models/EventManager.js +251 -78
  109. package/dist/models/EventManager.js.map +1 -1
  110. package/dist/models/LogPrinter.d.ts +55 -0
  111. package/dist/models/LogPrinter.js +196 -0
  112. package/dist/models/LogPrinter.js.map +1 -0
  113. package/dist/models/Logger.d.ts +47 -27
  114. package/dist/models/Logger.js +133 -155
  115. package/dist/models/Logger.js.map +1 -1
  116. package/dist/models/MiddlewareManager.d.ts +86 -0
  117. package/dist/models/MiddlewareManager.js +409 -0
  118. package/dist/models/MiddlewareManager.js.map +1 -0
  119. package/dist/models/OverrideManager.d.ts +3 -3
  120. package/dist/models/OverrideManager.js +22 -7
  121. package/dist/models/OverrideManager.js.map +1 -1
  122. package/dist/models/ResourceInitializer.d.ts +4 -3
  123. package/dist/models/ResourceInitializer.js +12 -68
  124. package/dist/models/ResourceInitializer.js.map +1 -1
  125. package/dist/models/RunResult.d.ts +35 -0
  126. package/dist/models/RunResult.js +68 -0
  127. package/dist/models/RunResult.js.map +1 -0
  128. package/dist/models/Store.d.ts +30 -17
  129. package/dist/models/Store.js +87 -25
  130. package/dist/models/Store.js.map +1 -1
  131. package/dist/models/StoreRegistry.d.ts +34 -19
  132. package/dist/models/StoreRegistry.js +248 -100
  133. package/dist/models/StoreRegistry.js.map +1 -1
  134. package/dist/models/StoreValidator.d.ts +5 -7
  135. package/dist/models/StoreValidator.js +50 -17
  136. package/dist/models/StoreValidator.js.map +1 -1
  137. package/dist/models/TaskRunner.d.ts +3 -2
  138. package/dist/models/TaskRunner.js +6 -103
  139. package/dist/models/TaskRunner.js.map +1 -1
  140. package/dist/models/UnhandledError.d.ts +11 -0
  141. package/dist/models/UnhandledError.js +30 -0
  142. package/dist/models/UnhandledError.js.map +1 -0
  143. package/dist/models/index.d.ts +3 -0
  144. package/dist/models/index.js +3 -0
  145. package/dist/models/index.js.map +1 -1
  146. package/dist/{tools → models/utils}/findCircularDependencies.js +8 -16
  147. package/dist/models/utils/findCircularDependencies.js.map +1 -0
  148. package/dist/models/utils/safeStringify.d.ts +3 -0
  149. package/dist/models/utils/safeStringify.js +45 -0
  150. package/dist/models/utils/safeStringify.js.map +1 -0
  151. package/dist/processHooks.d.ts +2 -0
  152. package/dist/processHooks.js +70 -0
  153. package/dist/processHooks.js.map +1 -0
  154. package/dist/run.d.ts +14 -27
  155. package/dist/run.js +100 -36
  156. package/dist/run.js.map +1 -1
  157. package/dist/testing.d.ts +5 -4
  158. package/dist/testing.js +3 -2
  159. package/dist/testing.js.map +1 -1
  160. package/dist/tools/getCallerFile.d.ts +0 -8
  161. package/dist/tools/getCallerFile.js +0 -51
  162. package/dist/tools/getCallerFile.js.map +1 -1
  163. package/dist/types/contracts.d.ts +55 -0
  164. package/dist/types/contracts.js +4 -0
  165. package/dist/types/contracts.js.map +1 -0
  166. package/dist/types/event.d.ts +32 -7
  167. package/dist/types/event.js +14 -1
  168. package/dist/types/event.js.map +1 -1
  169. package/dist/types/hook.d.ts +23 -0
  170. package/dist/{models/StoreTypes.js → types/hook.js} +2 -1
  171. package/dist/types/hook.js.map +1 -0
  172. package/dist/types/meta.d.ts +6 -1
  173. package/dist/types/meta.js +4 -2
  174. package/dist/types/meta.js.map +1 -1
  175. package/dist/types/resource.d.ts +40 -52
  176. package/dist/types/resource.js +1 -0
  177. package/dist/types/resource.js.map +1 -1
  178. package/dist/types/resourceMiddleware.d.ts +47 -0
  179. package/dist/{middleware.types.js → types/resourceMiddleware.js} +1 -1
  180. package/dist/types/resourceMiddleware.js.map +1 -0
  181. package/dist/types/runner.d.ts +37 -0
  182. package/dist/types/{base.js → runner.js} +1 -1
  183. package/dist/types/runner.js.map +1 -0
  184. package/dist/types/storeTypes.d.ts +40 -0
  185. package/dist/types/{metadata.js → storeTypes.js} +1 -1
  186. package/dist/types/storeTypes.js.map +1 -0
  187. package/dist/types/symbols.d.ts +10 -21
  188. package/dist/types/symbols.js +17 -22
  189. package/dist/types/symbols.js.map +1 -1
  190. package/dist/types/tag.d.ts +46 -0
  191. package/dist/{resource.types.js → types/tag.js} +2 -1
  192. package/dist/types/tag.js.map +1 -0
  193. package/dist/types/task.d.ts +28 -52
  194. package/dist/types/task.js +1 -0
  195. package/dist/types/task.js.map +1 -1
  196. package/dist/types/taskMiddleware.d.ts +48 -0
  197. package/dist/{event.types.js → types/taskMiddleware.js} +1 -1
  198. package/dist/types/taskMiddleware.js.map +1 -0
  199. package/dist/types/utilities.d.ts +109 -6
  200. package/dist/types/utilities.js +16 -2
  201. package/dist/types/utilities.js.map +1 -1
  202. package/package.json +14 -5
  203. package/dist/cli/extract-docs.d.ts +0 -2
  204. package/dist/cli/extract-docs.js +0 -88
  205. package/dist/cli/extract-docs.js.map +0 -1
  206. package/dist/common.types.d.ts +0 -20
  207. package/dist/common.types.js +0 -4
  208. package/dist/common.types.js.map +0 -1
  209. package/dist/defs/core.d.ts +0 -144
  210. package/dist/defs/core.js +0 -6
  211. package/dist/defs/core.js.map +0 -1
  212. package/dist/defs/symbols.d.ts +0 -42
  213. package/dist/defs/symbols.js +0 -45
  214. package/dist/defs/symbols.js.map +0 -1
  215. package/dist/defs/tags.d.ts +0 -70
  216. package/dist/defs/tags.js +0 -6
  217. package/dist/defs/tags.js.map +0 -1
  218. package/dist/defs.returnTag.d.ts +0 -36
  219. package/dist/defs.returnTag.js +0 -4
  220. package/dist/defs.returnTag.js.map +0 -1
  221. package/dist/docs/introspect.d.ts +0 -7
  222. package/dist/docs/introspect.js +0 -199
  223. package/dist/docs/introspect.js.map +0 -1
  224. package/dist/docs/markdown.d.ts +0 -2
  225. package/dist/docs/markdown.js +0 -148
  226. package/dist/docs/markdown.js.map +0 -1
  227. package/dist/docs/model.d.ts +0 -62
  228. package/dist/docs/model.js +0 -33
  229. package/dist/docs/model.js.map +0 -1
  230. package/dist/event.types.d.ts +0 -18
  231. package/dist/event.types.js.map +0 -1
  232. package/dist/examples/express-mongo/index.d.ts +0 -0
  233. package/dist/examples/express-mongo/index.js +0 -3
  234. package/dist/examples/express-mongo/index.js.map +0 -1
  235. package/dist/examples/registrator-example.d.ts +0 -122
  236. package/dist/examples/registrator-example.js +0 -147
  237. package/dist/examples/registrator-example.js.map +0 -1
  238. package/dist/express/docsRouter.d.ts +0 -12
  239. package/dist/express/docsRouter.js +0 -54
  240. package/dist/express/docsRouter.js.map +0 -1
  241. package/dist/globalEvents.d.ts +0 -40
  242. package/dist/globalEvents.js +0 -94
  243. package/dist/globalEvents.js.map +0 -1
  244. package/dist/globalResources.d.ts +0 -10
  245. package/dist/globalResources.js +0 -43
  246. package/dist/globalResources.js.map +0 -1
  247. package/dist/middleware.types.d.ts +0 -40
  248. package/dist/middleware.types.js.map +0 -1
  249. package/dist/models/StoreConstants.d.ts +0 -14
  250. package/dist/models/StoreConstants.js +0 -19
  251. package/dist/models/StoreConstants.js.map +0 -1
  252. package/dist/models/StoreTypes.d.ts +0 -21
  253. package/dist/models/StoreTypes.js.map +0 -1
  254. package/dist/models/VarStore.d.ts +0 -17
  255. package/dist/models/VarStore.js +0 -60
  256. package/dist/models/VarStore.js.map +0 -1
  257. package/dist/resource.types.d.ts +0 -31
  258. package/dist/resource.types.js.map +0 -1
  259. package/dist/symbols.d.ts +0 -24
  260. package/dist/symbols.js +0 -29
  261. package/dist/symbols.js.map +0 -1
  262. package/dist/t1.d.ts +0 -1
  263. package/dist/t1.js +0 -13
  264. package/dist/t1.js.map +0 -1
  265. package/dist/task.types.d.ts +0 -55
  266. package/dist/task.types.js.map +0 -1
  267. package/dist/tools/findCircularDependencies.js.map +0 -1
  268. package/dist/tools/registratorId.d.ts +0 -4
  269. package/dist/tools/registratorId.js +0 -40
  270. package/dist/tools/registratorId.js.map +0 -1
  271. package/dist/tools/simpleHash.d.ts +0 -9
  272. package/dist/tools/simpleHash.js +0 -34
  273. package/dist/tools/simpleHash.js.map +0 -1
  274. package/dist/types/base-interfaces.d.ts +0 -18
  275. package/dist/types/base-interfaces.js +0 -6
  276. package/dist/types/base-interfaces.js.map +0 -1
  277. package/dist/types/base.d.ts +0 -13
  278. package/dist/types/base.js.map +0 -1
  279. package/dist/types/dependencies.d.ts +0 -51
  280. package/dist/types/dependencies.js +0 -3
  281. package/dist/types/dependencies.js.map +0 -1
  282. package/dist/types/dependency-core.d.ts +0 -14
  283. package/dist/types/dependency-core.js +0 -5
  284. package/dist/types/dependency-core.js.map +0 -1
  285. package/dist/types/events.d.ts +0 -52
  286. package/dist/types/events.js +0 -6
  287. package/dist/types/events.js.map +0 -1
  288. package/dist/types/hooks.d.ts +0 -16
  289. package/dist/types/hooks.js +0 -5
  290. package/dist/types/hooks.js.map +0 -1
  291. package/dist/types/index.d.ts +0 -8
  292. package/dist/types/index.js.map +0 -1
  293. package/dist/types/metadata.d.ts +0 -75
  294. package/dist/types/metadata.js.map +0 -1
  295. package/dist/types/middleware.d.ts +0 -63
  296. package/dist/types/middleware.js +0 -3
  297. package/dist/types/middleware.js.map +0 -1
  298. package/dist/types/registerable.d.ts +0 -10
  299. package/dist/types/registerable.js +0 -5
  300. package/dist/types/registerable.js.map +0 -1
  301. package/dist/types/resources.d.ts +0 -44
  302. package/dist/types/resources.js +0 -5
  303. package/dist/types/resources.js.map +0 -1
  304. package/dist/types/tasks.d.ts +0 -41
  305. package/dist/types/tasks.js +0 -5
  306. package/dist/types/tasks.js.map +0 -1
  307. package/src/__tests__/benchmark/benchmark.test.ts +0 -148
  308. package/src/__tests__/benchmark/task-benchmark.test.ts +0 -132
  309. package/src/__tests__/context.test.ts +0 -91
  310. package/src/__tests__/createTestResource.test.ts +0 -139
  311. package/src/__tests__/errors.test.ts +0 -341
  312. package/src/__tests__/globalEvents.test.ts +0 -542
  313. package/src/__tests__/globals/cache.middleware.test.ts +0 -772
  314. package/src/__tests__/globals/queue.resource.test.ts +0 -141
  315. package/src/__tests__/globals/requireContext.middleware.test.ts +0 -98
  316. package/src/__tests__/globals/retry.middleware.test.ts +0 -157
  317. package/src/__tests__/globals/timeout.middleware.test.ts +0 -88
  318. package/src/__tests__/index.helper.test.ts +0 -55
  319. package/src/__tests__/models/EventManager.test.ts +0 -585
  320. package/src/__tests__/models/Logger.test.ts +0 -519
  321. package/src/__tests__/models/Queue.test.ts +0 -189
  322. package/src/__tests__/models/ResourceInitializer.test.ts +0 -148
  323. package/src/__tests__/models/Semaphore.test.ts +0 -713
  324. package/src/__tests__/models/Store.test.ts +0 -227
  325. package/src/__tests__/models/TaskRunner.test.ts +0 -221
  326. package/src/__tests__/override.test.ts +0 -104
  327. package/src/__tests__/recursion/README.md +0 -3
  328. package/src/__tests__/recursion/a.resource.ts +0 -25
  329. package/src/__tests__/recursion/b.resource.ts +0 -33
  330. package/src/__tests__/recursion/c.resource.ts +0 -18
  331. package/src/__tests__/run.anonymous.test.ts +0 -706
  332. package/src/__tests__/run.dynamic-register-and-dependencies.test.ts +0 -1185
  333. package/src/__tests__/run.middleware.test.ts +0 -620
  334. package/src/__tests__/run.overrides.test.ts +0 -424
  335. package/src/__tests__/run.test.ts +0 -1040
  336. package/src/__tests__/setOutput.test.ts +0 -244
  337. package/src/__tests__/tags.test.ts +0 -396
  338. package/src/__tests__/tools/findCircularDependencies.test.ts +0 -217
  339. package/src/__tests__/tools/getCallerFile.test.ts +0 -179
  340. package/src/__tests__/typesafety.test.ts +0 -423
  341. package/src/__tests__/validation-edge-cases.test.ts +0 -111
  342. package/src/__tests__/validation-interface.test.ts +0 -428
  343. package/src/context.ts +0 -86
  344. package/src/define.ts +0 -492
  345. package/src/defs.returnTag.ts +0 -91
  346. package/src/defs.ts +0 -596
  347. package/src/errors.ts +0 -105
  348. package/src/globals/globalEvents.ts +0 -125
  349. package/src/globals/globalMiddleware.ts +0 -16
  350. package/src/globals/globalResources.ts +0 -53
  351. package/src/globals/middleware/cache.middleware.ts +0 -115
  352. package/src/globals/middleware/requireContext.middleware.ts +0 -36
  353. package/src/globals/middleware/retry.middleware.ts +0 -56
  354. package/src/globals/middleware/timeout.middleware.ts +0 -46
  355. package/src/globals/resources/queue.resource.ts +0 -34
  356. package/src/index.ts +0 -39
  357. package/src/models/DependencyProcessor.ts +0 -257
  358. package/src/models/EventManager.ts +0 -210
  359. package/src/models/Logger.ts +0 -282
  360. package/src/models/OverrideManager.ts +0 -79
  361. package/src/models/Queue.ts +0 -66
  362. package/src/models/ResourceInitializer.ts +0 -165
  363. package/src/models/Semaphore.ts +0 -208
  364. package/src/models/Store.ts +0 -193
  365. package/src/models/StoreConstants.ts +0 -18
  366. package/src/models/StoreRegistry.ts +0 -253
  367. package/src/models/StoreTypes.ts +0 -47
  368. package/src/models/StoreValidator.ts +0 -43
  369. package/src/models/TaskRunner.ts +0 -203
  370. package/src/models/index.ts +0 -8
  371. package/src/run.ts +0 -116
  372. package/src/testing.ts +0 -66
  373. package/src/tools/findCircularDependencies.ts +0 -69
  374. package/src/tools/getCallerFile.ts +0 -96
  375. /package/dist/{tools → models/utils}/findCircularDependencies.d.ts +0 -0
package/AI.md ADDED
@@ -0,0 +1,638 @@
1
+ # BlueLibs Runner: Minimal Guide
2
+
3
+ ## Install
4
+
5
+ ```bash
6
+ npm install @bluelibs/runner
7
+ ```
8
+
9
+ ## Core Philosophy
10
+
11
+ BlueLibs Runner is a **powerful and integrated** framework. It provides a comprehensive set of tools for building robust, testable, and maintainable applications by combining a predictable Dependency Injection (DI) container with a dynamic metadata and eventing system.
12
+
13
+ ## DI Container Guarantees
14
+
15
+ This is the foundation of trust for any DI framework.
16
+
17
+ - **Circular Dependencies**: A runtime circular dependency (e.g., `A → B → A`) is a fatal error. The runner **will fail to start** and will throw a descriptive error showing the full dependency chain, forcing you to fix the architecture.
18
+ - **Override Precedence**: Overrides are applied top-down. In case of conflicting overrides for the same `id`, the one defined closest to the root `run()` call wins. The "root is the boss."
19
+
20
+ ## TL;DR
21
+
22
+ - **Lifecycle**: `run() → init resources (deps first) → 'ready' event → dispose() (reverse order)`
23
+ - **Tasks**: Functions with DI and middleware. Flow: `call → middleware → input validation → run() → result validation → return`
24
+ - **Resources**: Managed singletons (init/dispose).
25
+ - **Events**: Decoupled communication. Flow: `emit → validation → find & order hooks → run hooks (stoppable)`
26
+ - **Hooks**: Lightweight event listeners. Async and awaited by default.
27
+ - **Middleware**: Cross-cutting concerns. Async and awaited by default.
28
+
29
+ ## Quick Start
30
+
31
+ ```ts
32
+ import express from "express";
33
+ import { resource, task, run } from "@bluelibs/runner";
34
+
35
+ const server = resource({
36
+ id: "app.server",
37
+ // "context" is for private state between init() and dispose()
38
+ context: () => ({ value: null }),
39
+ init: async (config: { port: number }, dependencies, ctx) => {
40
+ ctx.value = "some-value"; // Store private state for dispose()
41
+
42
+ const app = express();
43
+ const server = app.listen(config.port);
44
+ return { app, server };
45
+ },
46
+ dispose: async ({ server }, config, deps, ctx) => server.close(),
47
+ });
48
+
49
+ const createUser = task({
50
+ id: "app.tasks.createUser",
51
+ dependencies: { server },
52
+ run: async (user: { name: string }, deps) => ({ id: "u1", ...user }),
53
+ });
54
+
55
+ const app = resource({
56
+ id: "app",
57
+ // Resources with configurations must be registered with with() unless the configuration allows all optional
58
+ // All elements must be registered for them to be used in the system
59
+ register: [server.with({ port: 3000 }), createUser],
60
+ dependencies: { server, createUser },
61
+ init: async (_, { server, createUser }) => {
62
+ server.app.post("/users", async (req, res) =>
63
+ res.json(await createUser(req.body)),
64
+ );
65
+ },
66
+ });
67
+
68
+ // Run with optional debug/logs
69
+ // If app had a config app.with(config) for 1st arg
70
+ await run(app, {
71
+ debug: "normal", // "normal" | "verbose" | DebugConfig
72
+ logs: { printThreshold: "info", printStrategy: "pretty" },
73
+ });
74
+ ```
75
+
76
+ ## Events & Hooks
77
+
78
+ ```ts
79
+ import { event, hook, globals } from "@bluelibs/runner";
80
+
81
+ const userRegistered = event<{ userId: string; email: string }>({
82
+ id: "app.events.userRegistered",
83
+ });
84
+
85
+ const sendWelcome = hook({
86
+ id: "app.hooks.sendWelcome",
87
+ on: userRegistered,
88
+ run: async (e) => console.log(`Welcome ${e.data.email}`),
89
+ });
90
+
91
+ // Wildcard listener
92
+ const audit = hook({
93
+ id: "app.hooks.audit",
94
+ on: "*",
95
+ run: (e) => console.log(e.id),
96
+ });
97
+
98
+ // Exclude internal events from "*"
99
+ const internal = event({
100
+ id: "app.events.internal",
101
+ tags: [globals.tags.excludeFromGlobalHooks],
102
+ });
103
+ ```
104
+
105
+ ### Multiple Events per Hook
106
+
107
+ Listen to multiple events with type-safe common fields:
108
+
109
+ ```ts
110
+ const h = hook({
111
+ id: "app.hooks.multi",
112
+ on: [event1, event2, event3],
113
+ run: async (ev) => {
114
+ // helper utility
115
+ if (isOneOf(ev, [event1, event2])) {
116
+ // all common fields from event1 and event2, if just event1, it will be just event1
117
+ }
118
+ },
119
+ });
120
+ ```
121
+
122
+ ### Interception APIs
123
+
124
+ Low-level interception is available for advanced observability and control:
125
+
126
+ - `eventManager.intercept((next, event) => Promise<void>)` — wraps event emission
127
+ - `eventManager.interceptHook((next, hook, event) => Promise<any>)` — wraps hook execution
128
+ - `middlewareManager.intercept("task" | "resource", (next, input) => Promise<any>)` — wraps middleware execution
129
+ - `middlewareManager.interceptMiddleware(middleware, interceptor)` — per-middleware interception
130
+
131
+ Prefer task-level `task.intercept()` for application logic; use the above for cross-cutting concerns.
132
+
133
+ ## Unhandled Errors
134
+
135
+ By default, unhandled errors are just logged. You can customize this via `run(app, { onUnhandledError })`:
136
+
137
+ ```ts
138
+ await run(app, {
139
+ errorBoundary: true, // Catch process-level errors (default: true)
140
+ onUnhandledError: async ({ error, kind, source }) => {
141
+ // kind: "task" | "middleware" | "resourceInit" | "hook" | "process" | "run"
142
+ // source: optional origin hint (ex: "uncaughtException")
143
+ await telemetry.capture(error as Error, { kind, source });
144
+
145
+ // Optionally decide on remediation strategy
146
+ if (kind === "process") {
147
+ // For hard process faults, prefer fast, clean exit after flushing logs
148
+ await flushAll();
149
+ process.exit(1);
150
+ }
151
+ },
152
+ });
153
+ ```
154
+
155
+ If you prefer event-driven handling, you can still emit your own custom events from this callback.
156
+
157
+ ## Debug (zero‑overhead when disabled)
158
+
159
+ Enable globally at run time:
160
+
161
+ ```ts
162
+ await run(app, { debug: "verbose" }); // "normal" or DebugConfig
163
+ ```
164
+
165
+ or per‑component via tag:
166
+
167
+ ```ts
168
+ import { globals, task } from "@bluelibs/runner";
169
+
170
+ const critical = task({
171
+ id: "app.tasks.critical",
172
+ meta: {
173
+ tags: [
174
+ globals.tags.debug.with({ logTaskInput: true, logTaskResult: true }),
175
+ ],
176
+ },
177
+ run: async () => "ok",
178
+ });
179
+ ```
180
+
181
+ ## Logger (direct API)
182
+
183
+ ```ts
184
+ import { resource, globals } from "@bluelibs/runner";
185
+
186
+ const logsExtension = resource({
187
+ id: "app.logs",
188
+ dependencies: { logger: globals.resources.logger },
189
+ init: async (_, { logger }) => {
190
+ logger.info("test", { data }); // "trace", "debug", "info", "warn", "error", "critical"
191
+ const sublogger = logger.with({
192
+ source: "app.logs",
193
+ context: {},
194
+ });
195
+ logger.onLog((log) => {
196
+ // ship or transform
197
+ });
198
+ },
199
+ });
200
+ ```
201
+
202
+ ## Middleware (global or local)
203
+
204
+ Middleware now supports type contracts with `<Config, Input, Output>` signature:
205
+
206
+ ```ts
207
+ import {
208
+ taskMiddleware,
209
+ resourceMiddleware,
210
+ resource,
211
+ task,
212
+ globals,
213
+ } from "@bluelibs/runner";
214
+
215
+ // Custom task middleware with type contracts
216
+ const auth = taskMiddleware<
217
+ { role: string },
218
+ { user: { role: string } },
219
+ { user: { role: string; verified: boolean } }
220
+ >({
221
+ id: "app.middleware.auth",
222
+ run: async ({ task, next }, _, cfg) => {
223
+ if (task.input?.user?.role !== cfg.role) throw new Error("Unauthorized");
224
+ const result = await next(task.input);
225
+ return { user: { ...task.input.user, verified: true } };
226
+ },
227
+ });
228
+
229
+ // Resource middleware can augment a resource's behavior after it's initialized.
230
+ // For example, this `softDelete` middleware intercepts the `delete` method
231
+ // of a resource and replaces it with a non-destructive update.
232
+ const softDelete = resourceMiddleware({
233
+ id: "app.middleware.softDelete",
234
+ run: async ({ next }) => {
235
+ const resourceInstance = await next(); // The original resource instance
236
+
237
+ // This example assumes the resource has `update` and `delete` methods.
238
+ // A more robust implementation would check for their existence.
239
+
240
+ // Monkey-patch the 'delete' method
241
+ const originalDelete = resourceInstance.delete;
242
+ resourceInstance.delete = async (id: string, ...args) => {
243
+ // Instead of deleting, call 'update' to mark as deleted
244
+ return resourceInstance.update(id, { deletedAt: new Date() }, ...args);
245
+ };
246
+
247
+ return resourceInstance;
248
+ },
249
+ });
250
+
251
+ const adminOnly = task({
252
+ id: "app.tasks.adminOnly",
253
+ middleware: [auth.with({ role: "admin" })],
254
+ run: async () => "secret",
255
+ });
256
+
257
+ // Built-in middleware patterns
258
+ const {
259
+ task: { retry, timeout, cache },
260
+ // available: resource: { retry, timeout, cache } as well, same configs.
261
+ } = globals.middleware;
262
+
263
+ // Example of custom middleware with full type contracts
264
+ const validationMiddleware = taskMiddleware<
265
+ { strict: boolean },
266
+ { data: unknown },
267
+ { data: any; validated: boolean }
268
+ >({
269
+ id: "app.middleware.validation",
270
+ run: async ({ task, next }, _, config) => {
271
+ // Validation logic here
272
+ const result = await next(task.input);
273
+ return { ...result, validated: true };
274
+ },
275
+ });
276
+
277
+ const resilientTask = task({
278
+ id: "app.tasks.resilient",
279
+ middleware: [
280
+ // Retry with exponential backoff, allow each with timeout
281
+ retry.with({
282
+ retries: 3,
283
+ delayStrategy: (attempt) => 1000 * attempt,
284
+ stopRetryIf: (error) => error.message === "Invalid credentials",
285
+ }),
286
+ // Timeout protection (propose-timeout)
287
+ timeout.with({ ttl: 10000 }),
288
+ // Caching first (onion-level)
289
+ cache.with({
290
+ ttl: 60000,
291
+ keyBuilder: (taskId, input) => `${taskId}-${JSON.stringify(input)}`,
292
+ }),
293
+ ],
294
+ run: async () => expensiveApiCall(),
295
+ });
296
+
297
+ // Global middleware
298
+ const globalTaskMiddleware = taskMiddleware({
299
+ id: "...",
300
+ everywhere: true, // Use everywhere: (task) => boolean, where true means it gets applied
301
+ // ... rest as usual ...
302
+ // if you have dependencies as task, exclude them via everywhere filter.
303
+ });
304
+
305
+ // Similar behavior for resourceMiddleware({ ... });
306
+ ```
307
+
308
+ ## Context (request-scoped values)
309
+
310
+ ```ts
311
+ import { createContext } from "@bluelibs/runner";
312
+
313
+ const UserCtx = createContext<{ userId: string }>("app.userContext");
314
+
315
+ // In middleware or entry-point
316
+ UserCtx.provide({ userId: "u1" }, async () => {
317
+ await someTask(); // has access to the context
318
+ });
319
+
320
+ // In a task or hook
321
+ const user = UserCtx.use(); // -> { userId: "u1" }
322
+
323
+ // In a task definition
324
+ const task = {
325
+ middleware: [UserCtx.require()], // This middleware works only in tasks.
326
+ };
327
+ ```
328
+
329
+ ## System Shutdown & Error Boundary
330
+
331
+ The framework includes built-in support for graceful shutdowns:
332
+
333
+ ```ts
334
+ const { dispose } = await run(app, {
335
+ shutdownHooks: true, // Automatically handle SIGTERM/SIGINT (default: true)
336
+ errorBoundary: true, // Catch unhandled errors and rejections (default: true)
337
+ });
338
+ ```
339
+
340
+ ## Run Options (high‑level)
341
+
342
+ - debug: "normal" | "verbose" | DebugConfig
343
+ - logs: {
344
+ - printThreshold?: LogLevel | null;
345
+ - printStrategy?: "pretty" | "json" | "json-pretty" | "plain";
346
+ - bufferLogs?: boolean
347
+ - }
348
+ - errorBoundary: boolean (default true)
349
+ - shutdownHooks: boolean (default true)
350
+ - onUnhandledError(error) {}
351
+
352
+ Note: `globals` is a convenience object exposing framework internals:
353
+
354
+ - `globals.events` (ready)
355
+ - `globals.resources` (store, taskRunner, eventManager, logger, cache, queue)
356
+ - `globals.middleware` (retry, cache, timeout, requireContext)
357
+ - `globals.tags` (system, debug, excludeFromGlobalHooks)
358
+
359
+ ## Overrides
360
+
361
+ ```ts
362
+ import { override, resource } from "@bluelibs/runner";
363
+
364
+ const emailer = resource({ id: "app.emailer", init: async () => new SMTP() });
365
+ const testEmailer = override(emailer, { init: async () => new MockSMTP() });
366
+
367
+ const app = resource({
368
+ id: "app",
369
+ register: [emailer],
370
+ overrides: [testEmailer],
371
+ });
372
+ ```
373
+
374
+ ## Namespacing
375
+
376
+ As your app grows, use a consistent naming convention. This is the recommended format:
377
+
378
+ | Type | Format |
379
+ | ---------- | -------------------------------------- |
380
+ | Tasks | `{domain}.tasks.{taskName}` |
381
+ | Hooks | `{domain}.hooks.on{EventName}` |
382
+ | Resources | `{domain}.resources.{resourceName}` |
383
+ | Events | `{domain}.events.{eventName}` |
384
+ | Middleware | `{domain}.middleware.{middlewareName}` |
385
+
386
+ ## Factory Pattern
387
+
388
+ Use a resource to act as a factory for creating class instances. The resource is configured once, and the resulting function can be used throughout the app.
389
+
390
+ ```ts
391
+ const myFactory = resource({
392
+ id: "app.factories.myFactory",
393
+ init: async (config: SomeConfigType) => {
394
+ // The resource's value is a factory function
395
+ return (input: any) => {
396
+ return new MyClass(input, config.someOption);
397
+ };
398
+ },
399
+ });
400
+
401
+ const app = resource({
402
+ id: "app",
403
+ register: [myFactory.with({ someOption: "configured" })],
404
+ dependencies: { myFactory },
405
+ init: async (_, { myFactory }) => {
406
+ const instance = myFactory({ someInput: "hello" });
407
+ },
408
+ });
409
+ ```
410
+
411
+ ## Testing
412
+
413
+ ```ts
414
+ import { resource, run, override } from "@bluelibs/runner";
415
+
416
+ const app = resource({
417
+ id: "app",
418
+ register: [
419
+ /* tasks/resources */
420
+ ],
421
+ });
422
+ const harness = resource({
423
+ id: "test",
424
+ register: [app],
425
+ overrides: [
426
+ /* test overrides */
427
+ ],
428
+ });
429
+
430
+ const rr = await run(harness);
431
+ await rr.runTask(id | task, { input: 1 });
432
+ // rr.getResourceValue(id | resource)
433
+ // await rr.emitEvent(event, payload)
434
+ // rr.logger.info("xxx")
435
+ await rr.dispose();
436
+ ```
437
+
438
+ ## Metadata & Tags
439
+
440
+ ```ts
441
+ import { tag, globals, task, resource } from "@bluelibs/runner";
442
+
443
+ // Simple tags and debug/system globals
444
+ const perf = tag<{ warnAboveMs: number }>({ id: "perf" });
445
+ const contractTag = tag<void, void, { result: string }>({ id: "contract" });
446
+
447
+ const processPayment = task({
448
+ id: "app.tasks.pay",
449
+ tags: [perf.with({ warnAboveMs: 1000 })],
450
+ meta: {
451
+ title: "Process Payment",
452
+ description: "Detailed",
453
+ },
454
+ run: async () => {
455
+ /* ... */
456
+ },
457
+ });
458
+
459
+ const internalSvc = resource({
460
+ id: "app.resources.internal",
461
+ register: [perf],
462
+ tags: [globals.tags.system],
463
+ init: async () => ({}),
464
+ });
465
+ ```
466
+
467
+ ### Tag Contracts (type‑enforced returns)
468
+
469
+ ```ts
470
+ // Contract enforces the awaited return type (Config, Input, Output)
471
+ const userContract = tag<void, void, { name: string }>({ id: "contract.user" });
472
+
473
+ const getProfile = task({
474
+ id: "app.tasks.getProfile",
475
+ tags: [userContract],
476
+ run: async () => ({ name: "Ada" }), // must contain { name: string }
477
+ });
478
+
479
+ const profileService = resource({
480
+ id: "app.resources.profile",
481
+ tags: [userContract],
482
+ init: async () => ({ name: "Ada" }),
483
+ });
484
+ ```
485
+
486
+ ### Tag Extraction (behavior flags)
487
+
488
+ ```ts
489
+ const perf = tag<{ warnAboveMs: number }>({ id: "perf" });
490
+
491
+ const perfMiddleware = taskMiddleware({
492
+ id: "app.middleware.perf",
493
+ run: async ({ task, next }) => {
494
+ const cfg = perf.extract(task.definition);
495
+ // use perf.exists(task.definition) to check for existence
496
+ if (!cfg) return next(task.input);
497
+ // performance hooks here ...
498
+ },
499
+ });
500
+ ```
501
+
502
+ ### Intercept Tasks via Tags (programmatic wiring)
503
+
504
+ Use a hook on `globals.events.ready` to discover and intercept tasks by tag:
505
+
506
+ ```ts
507
+ import { hook, globals, tag } from "@bluelibs/runner";
508
+
509
+ const apiTag = tag<void>({ id: "api" });
510
+
511
+ const addTracingToApiTasks = hook({
512
+ id: "app.hooks.traceApis",
513
+ on: globals.events.ready,
514
+ dependencies: { store: globals.resources.store },
515
+ run: async (_, { store }) => {
516
+ const apiTasks = store.getTasksWithTag(apiTag); // tag object or string id
517
+ apiTasks.forEach((taskDef) => {
518
+ taskDef.intercept(async (next, input) => {
519
+ // ...
520
+ });
521
+ });
522
+ // Apply same concept to routing like fastify, express routes based on tag config to your fastify instance.
523
+ },
524
+ });
525
+ ```
526
+
527
+ ### Route registration via tags (ready hook)
528
+
529
+ ```ts
530
+ import { hook, globals } from "@bluelibs/runner";
531
+ import { httpTag } from "./http.tag"; // your structured tag (method, path, schemas, etc.)
532
+ import { expressServer } from "./expressServer"; // resource that returns { app, port }
533
+
534
+ const registerRoutes = hook({
535
+ id: "app.hooks.registerRoutes",
536
+ on: globals.events.ready,
537
+ dependencies: { store: globals.resources.store, server: expressServer },
538
+ run: async (_, { store, server }) => {
539
+ const tasks = store.getTasksWithTag(httpTag);
540
+ tasks.forEach((t) => {
541
+ const cfg = httpTag.extract(t.meta?.tags || []);
542
+ if (!cfg?.config) return;
543
+ const { method, path } = cfg.config;
544
+ if (!method || !path) return;
545
+ (server.app as any)[method.toLowerCase()](path, async (req, res) => {
546
+ const result = await t({ ...req.body, ...req.query, ...req.params });
547
+ res.json(result);
548
+ });
549
+ });
550
+ },
551
+ });
552
+ ```
553
+
554
+ ## Key Patterns & Features
555
+
556
+ - **Optional Dependencies**: Gracefully handle missing services by defining dependencies as optional. The dependency will be `null` if not registered.
557
+ `dependencies: { analytics: analyticsService.optional() }`
558
+
559
+ - **Stop Propagation**: Prevent other hooks from running for a specific event.
560
+ `// inside a hook`
561
+ `event.stopPropagation()`
562
+
563
+ That’s it. Small surface area, strong primitives, great DX.
564
+
565
+ ## Concurrency: Semaphore & Queue
566
+
567
+ ```ts
568
+ import { Semaphore, Queue } from "@bluelibs/runner";
569
+
570
+ // Semaphore: limit parallelism
571
+ const dbSem = new Semaphore(5);
572
+ const users = await dbSem.withPermit(async () =>
573
+ db.query("SELECT * FROM users"),
574
+ );
575
+
576
+ // Queue: FIFO with cooperative cancellation
577
+ const queue = new Queue();
578
+ const result = await queue.run(async (signal) => {
579
+ signal.throwIfAborted();
580
+ return await doWork();
581
+ });
582
+ await queue.dispose({ cancel: true });
583
+ ```
584
+
585
+ ## Handling Circular Types (even if runtime is fine)
586
+
587
+ Rarely, when TypeScript struggles with circular type inference, break the chain with an explicit interface:
588
+
589
+ ```ts
590
+ import type { IResource } from "@bluelibs/runner";
591
+
592
+ export const cResource = resource({
593
+ id: "c.resource",
594
+ dependencies: { a: aResource },
595
+ init: async (_, { a }) => `C depends on ${a}`,
596
+ }) as IResource<void, string>; // void config, returns string
597
+ ```
598
+
599
+ ## Validation (optional and library‑agnostic)
600
+
601
+ Interface any library can implement:
602
+
603
+ ```ts
604
+ interface IValidationSchema<T> {
605
+ parse(input: unknown): T;
606
+ }
607
+ ```
608
+
609
+ Works out of the box with Zod (`z.object(...).parse`), and can be adapted for Yup/Joi with small wrappers.
610
+
611
+ ```ts
612
+ import { z } from "zod";
613
+
614
+ // Task input/result validation
615
+ const inputSchema = z.object({ email: z.string().email() });
616
+ const resultSchema = z.object({ id: z.string(), email: z.string().email() });
617
+
618
+ task({
619
+ // ...
620
+ inputSchema, // validates before run
621
+ resultSchema, // validates awaited return
622
+ });
623
+
624
+ resource({
625
+ configSchema, // Resource config validation (runs on .with())
626
+ resultSchema, // Runs after initialization
627
+ });
628
+
629
+ event({
630
+ payloadSchema, // Runs on event emission
631
+ });
632
+
633
+ // Middleware config validation (runs on .with())
634
+ middleware({
635
+ // ...
636
+ configSchema, // runs on .with()
637
+ });
638
+ ```