@bluelibs/runner 3.4.2 → 4.0.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 +621 -0
  2. package/README.md +1024 -577
  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 -358
  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 +116 -33
  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 +26 -7
  167. package/dist/types/event.js +1 -1
  168. package/dist/types/event.js.map +1 -1
  169. package/dist/types/hook.d.ts +21 -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 +105 -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 -549
  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 -480
  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,621 @@
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
+ ### Interception APIs
106
+
107
+ Low-level interception is available for advanced observability and control:
108
+
109
+ - `eventManager.intercept((next, event) => Promise<void>)` — wraps event emission
110
+ - `eventManager.interceptHook((next, hook, event) => Promise<any>)` — wraps hook execution
111
+ - `middlewareManager.intercept("task" | "resource", (next, input) => Promise<any>)` — wraps middleware execution
112
+ - `middlewareManager.interceptMiddleware(middleware, interceptor)` — per-middleware interception
113
+
114
+ Prefer task-level `task.intercept()` for application logic; use the above for cross-cutting concerns.
115
+
116
+ ## Unhandled Errors
117
+
118
+ By default, unhandled errors are just logged. You can customize this via `run(app, { onUnhandledError })`:
119
+
120
+ ```ts
121
+ await run(app, {
122
+ errorBoundary: true, // Catch process-level errors (default: true)
123
+ onUnhandledError: async ({ error, kind, source }) => {
124
+ // kind: "task" | "middleware" | "resourceInit" | "hook" | "process" | "run"
125
+ // source: optional origin hint (ex: "uncaughtException")
126
+ await telemetry.capture(error as Error, { kind, source });
127
+
128
+ // Optionally decide on remediation strategy
129
+ if (kind === "process") {
130
+ // For hard process faults, prefer fast, clean exit after flushing logs
131
+ await flushAll();
132
+ process.exit(1);
133
+ }
134
+ },
135
+ });
136
+ ```
137
+
138
+ If you prefer event-driven handling, you can still emit your own custom events from this callback.
139
+
140
+ ## Debug (zero‑overhead when disabled)
141
+
142
+ Enable globally at run time:
143
+
144
+ ```ts
145
+ await run(app, { debug: "verbose" }); // "normal" or DebugConfig
146
+ ```
147
+
148
+ or per‑component via tag:
149
+
150
+ ```ts
151
+ import { globals, task } from "@bluelibs/runner";
152
+
153
+ const critical = task({
154
+ id: "app.tasks.critical",
155
+ meta: {
156
+ tags: [
157
+ globals.tags.debug.with({ logTaskInput: true, logTaskResult: true }),
158
+ ],
159
+ },
160
+ run: async () => "ok",
161
+ });
162
+ ```
163
+
164
+ ## Logger (direct API)
165
+
166
+ ```ts
167
+ import { resource, globals } from "@bluelibs/runner";
168
+
169
+ const logsExtension = resource({
170
+ id: "app.logs",
171
+ dependencies: { logger: globals.resources.logger },
172
+ init: async (_, { logger }) => {
173
+ logger.info("test", { data }); // "trace", "debug", "info", "warn", "error", "critical"
174
+ const sublogger = logger.with({
175
+ source: "app.logs",
176
+ context: {},
177
+ });
178
+ logger.onLog((log) => {
179
+ // ship or transform
180
+ });
181
+ },
182
+ });
183
+ ```
184
+
185
+ ## Middleware (global or local)
186
+
187
+ Middleware now supports type contracts with `<Config, Input, Output>` signature:
188
+
189
+ ```ts
190
+ import {
191
+ taskMiddleware,
192
+ resourceMiddleware,
193
+ resource,
194
+ task,
195
+ globals,
196
+ } from "@bluelibs/runner";
197
+
198
+ // Custom task middleware with type contracts
199
+ const auth = taskMiddleware<
200
+ { role: string },
201
+ { user: { role: string } },
202
+ { user: { role: string; verified: boolean } }
203
+ >({
204
+ id: "app.middleware.auth",
205
+ run: async ({ task, next }, _, cfg) => {
206
+ if (task.input?.user?.role !== cfg.role) throw new Error("Unauthorized");
207
+ const result = await next(task.input);
208
+ return { user: { ...task.input.user, verified: true } };
209
+ },
210
+ });
211
+
212
+ // Resource middleware can augment a resource's behavior after it's initialized.
213
+ // For example, this `softDelete` middleware intercepts the `delete` method
214
+ // of a resource and replaces it with a non-destructive update.
215
+ const softDelete = resourceMiddleware({
216
+ id: "app.middleware.softDelete",
217
+ run: async ({ next }) => {
218
+ const resourceInstance = await next(); // The original resource instance
219
+
220
+ // This example assumes the resource has `update` and `delete` methods.
221
+ // A more robust implementation would check for their existence.
222
+
223
+ // Monkey-patch the 'delete' method
224
+ const originalDelete = resourceInstance.delete;
225
+ resourceInstance.delete = async (id: string, ...args) => {
226
+ // Instead of deleting, call 'update' to mark as deleted
227
+ return resourceInstance.update(id, { deletedAt: new Date() }, ...args);
228
+ };
229
+
230
+ return resourceInstance;
231
+ },
232
+ });
233
+
234
+ const adminOnly = task({
235
+ id: "app.tasks.adminOnly",
236
+ middleware: [auth.with({ role: "admin" })],
237
+ run: async () => "secret",
238
+ });
239
+
240
+ // Built-in middleware patterns
241
+ const {
242
+ task: { retry, timeout, cache },
243
+ // available: resource: { retry, timeout, cache } as well, same configs.
244
+ } = globals.middleware;
245
+
246
+ // Example of custom middleware with full type contracts
247
+ const validationMiddleware = taskMiddleware<
248
+ { strict: boolean },
249
+ { data: unknown },
250
+ { data: any; validated: boolean }
251
+ >({
252
+ id: "app.middleware.validation",
253
+ run: async ({ task, next }, _, config) => {
254
+ // Validation logic here
255
+ const result = await next(task.input);
256
+ return { ...result, validated: true };
257
+ },
258
+ });
259
+
260
+ const resilientTask = task({
261
+ id: "app.tasks.resilient",
262
+ middleware: [
263
+ // Retry with exponential backoff, allow each with timeout
264
+ retry.with({
265
+ retries: 3,
266
+ delayStrategy: (attempt) => 1000 * attempt,
267
+ stopRetryIf: (error) => error.message === "Invalid credentials",
268
+ }),
269
+ // Timeout protection (propose-timeout)
270
+ timeout.with({ ttl: 10000 }),
271
+ // Caching first (onion-level)
272
+ cache.with({
273
+ ttl: 60000,
274
+ keyBuilder: (taskId, input) => `${taskId}-${JSON.stringify(input)}`,
275
+ }),
276
+ ],
277
+ run: async () => expensiveApiCall(),
278
+ });
279
+
280
+ // Global middleware
281
+ const globalTaskMiddleware = taskMiddleware({
282
+ id: "...",
283
+ everywhere: true, // Use everywhere: (task) => boolean, where true means it gets applied
284
+ // ... rest as usual ...
285
+ // if you have dependencies as task, exclude them via everywhere filter.
286
+ });
287
+
288
+ // Similar behavior for resourceMiddleware({ ... });
289
+ ```
290
+
291
+ ## Context (request-scoped values)
292
+
293
+ ```ts
294
+ import { createContext } from "@bluelibs/runner";
295
+
296
+ const UserCtx = createContext<{ userId: string }>("app.userContext");
297
+
298
+ // In middleware or entry-point
299
+ UserCtx.provide({ userId: "u1" }, async () => {
300
+ await someTask(); // has access to the context
301
+ });
302
+
303
+ // In a task or hook
304
+ const user = UserCtx.use(); // -> { userId: "u1" }
305
+
306
+ // In a task definition
307
+ const task = {
308
+ middleware: [UserCtx.require()], // This middleware works only in tasks.
309
+ };
310
+ ```
311
+
312
+ ## System Shutdown & Error Boundary
313
+
314
+ The framework includes built-in support for graceful shutdowns:
315
+
316
+ ```ts
317
+ const { dispose } = await run(app, {
318
+ shutdownHooks: true, // Automatically handle SIGTERM/SIGINT (default: true)
319
+ errorBoundary: true, // Catch unhandled errors and rejections (default: true)
320
+ });
321
+ ```
322
+
323
+ ## Run Options (high‑level)
324
+
325
+ - debug: "normal" | "verbose" | DebugConfig
326
+ - logs: {
327
+ - printThreshold?: LogLevel | null;
328
+ - printStrategy?: "pretty" | "json" | "json-pretty" | "plain";
329
+ - bufferLogs?: boolean
330
+ - }
331
+ - errorBoundary: boolean (default true)
332
+ - shutdownHooks: boolean (default true)
333
+ - onUnhandledError(error) {}
334
+
335
+ Note: `globals` is a convenience object exposing framework internals:
336
+
337
+ - `globals.events` (ready)
338
+ - `globals.resources` (store, taskRunner, eventManager, logger, cache, queue)
339
+ - `globals.middleware` (retry, cache, timeout, requireContext)
340
+ - `globals.tags` (system, debug, excludeFromGlobalHooks)
341
+
342
+ ## Overrides
343
+
344
+ ```ts
345
+ import { override, resource } from "@bluelibs/runner";
346
+
347
+ const emailer = resource({ id: "app.emailer", init: async () => new SMTP() });
348
+ const testEmailer = override(emailer, { init: async () => new MockSMTP() });
349
+
350
+ const app = resource({
351
+ id: "app",
352
+ register: [emailer],
353
+ overrides: [testEmailer],
354
+ });
355
+ ```
356
+
357
+ ## Namespacing
358
+
359
+ As your app grows, use a consistent naming convention. This is the recommended format:
360
+
361
+ | Type | Format |
362
+ | ---------- | -------------------------------------- |
363
+ | Tasks | `{domain}.tasks.{taskName}` |
364
+ | Hooks | `{domain}.hooks.on{EventName}` |
365
+ | Resources | `{domain}.resources.{resourceName}` |
366
+ | Events | `{domain}.events.{eventName}` |
367
+ | Middleware | `{domain}.middleware.{middlewareName}` |
368
+
369
+ ## Factory Pattern
370
+
371
+ 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.
372
+
373
+ ```ts
374
+ const myFactory = resource({
375
+ id: "app.factories.myFactory",
376
+ init: async (config: SomeConfigType) => {
377
+ // The resource's value is a factory function
378
+ return (input: any) => {
379
+ return new MyClass(input, config.someOption);
380
+ };
381
+ },
382
+ });
383
+
384
+ const app = resource({
385
+ id: "app",
386
+ register: [myFactory.with({ someOption: "configured" })],
387
+ dependencies: { myFactory },
388
+ init: async (_, { myFactory }) => {
389
+ const instance = myFactory({ someInput: "hello" });
390
+ },
391
+ });
392
+ ```
393
+
394
+ ## Testing
395
+
396
+ ```ts
397
+ import { resource, run, override } from "@bluelibs/runner";
398
+
399
+ const app = resource({
400
+ id: "app",
401
+ register: [
402
+ /* tasks/resources */
403
+ ],
404
+ });
405
+ const harness = resource({
406
+ id: "test",
407
+ register: [app],
408
+ overrides: [
409
+ /* test overrides */
410
+ ],
411
+ });
412
+
413
+ const rr = await run(harness);
414
+ await rr.runTask(id | task, { input: 1 });
415
+ // rr.getResourceValue(id | resource)
416
+ // await rr.emitEvent(event, payload)
417
+ // rr.logger.info("xxx")
418
+ await rr.dispose();
419
+ ```
420
+
421
+ ## Metadata & Tags
422
+
423
+ ```ts
424
+ import { tag, globals, task, resource } from "@bluelibs/runner";
425
+
426
+ // Simple tags and debug/system globals
427
+ const perf = tag<{ warnAboveMs: number }>({ id: "perf" });
428
+ const contractTag = tag<void, void, { result: string }>({ id: "contract" });
429
+
430
+ const processPayment = task({
431
+ id: "app.tasks.pay",
432
+ tags: [perf.with({ warnAboveMs: 1000 })],
433
+ meta: {
434
+ title: "Process Payment",
435
+ description: "Detailed",
436
+ },
437
+ run: async () => {
438
+ /* ... */
439
+ },
440
+ });
441
+
442
+ const internalSvc = resource({
443
+ id: "app.resources.internal",
444
+ register: [perf],
445
+ tags: [globals.tags.system],
446
+ init: async () => ({}),
447
+ });
448
+ ```
449
+
450
+ ### Tag Contracts (type‑enforced returns)
451
+
452
+ ```ts
453
+ // Contract enforces the awaited return type (Config, Input, Output)
454
+ const userContract = tag<void, void, { name: string }>({ id: "contract.user" });
455
+
456
+ const getProfile = task({
457
+ id: "app.tasks.getProfile",
458
+ tags: [userContract],
459
+ run: async () => ({ name: "Ada" }), // must contain { name: string }
460
+ });
461
+
462
+ const profileService = resource({
463
+ id: "app.resources.profile",
464
+ tags: [userContract],
465
+ init: async () => ({ name: "Ada" }),
466
+ });
467
+ ```
468
+
469
+ ### Tag Extraction (behavior flags)
470
+
471
+ ```ts
472
+ const perf = tag<{ warnAboveMs: number }>({ id: "perf" });
473
+
474
+ const perfMiddleware = taskMiddleware({
475
+ id: "app.middleware.perf",
476
+ run: async ({ task, next }) => {
477
+ const cfg = perf.extract(task.definition);
478
+ // use perf.exists(task.definition) to check for existence
479
+ if (!cfg) return next(task.input);
480
+ // performance hooks here ...
481
+ },
482
+ });
483
+ ```
484
+
485
+ ### Intercept Tasks via Tags (programmatic wiring)
486
+
487
+ Use a hook on `globals.events.ready` to discover and intercept tasks by tag:
488
+
489
+ ```ts
490
+ import { hook, globals, tag } from "@bluelibs/runner";
491
+
492
+ const apiTag = tag<void>({ id: "api" });
493
+
494
+ const addTracingToApiTasks = hook({
495
+ id: "app.hooks.traceApis",
496
+ on: globals.events.ready,
497
+ dependencies: { store: globals.resources.store },
498
+ run: async (_, { store }) => {
499
+ const apiTasks = store.getTasksWithTag(apiTag); // tag object or string id
500
+ apiTasks.forEach((taskDef) => {
501
+ taskDef.intercept(async (next, input) => {
502
+ // ...
503
+ });
504
+ });
505
+ // Apply same concept to routing like fastify, express routes based on tag config to your fastify instance.
506
+ },
507
+ });
508
+ ```
509
+
510
+ ### Route registration via tags (ready hook)
511
+
512
+ ```ts
513
+ import { hook, globals } from "@bluelibs/runner";
514
+ import { httpTag } from "./http.tag"; // your structured tag (method, path, schemas, etc.)
515
+ import { expressServer } from "./expressServer"; // resource that returns { app, port }
516
+
517
+ const registerRoutes = hook({
518
+ id: "app.hooks.registerRoutes",
519
+ on: globals.events.ready,
520
+ dependencies: { store: globals.resources.store, server: expressServer },
521
+ run: async (_, { store, server }) => {
522
+ const tasks = store.getTasksWithTag(httpTag);
523
+ tasks.forEach((t) => {
524
+ const cfg = httpTag.extract(t.meta?.tags || []);
525
+ if (!cfg?.config) return;
526
+ const { method, path } = cfg.config;
527
+ if (!method || !path) return;
528
+ (server.app as any)[method.toLowerCase()](path, async (req, res) => {
529
+ const result = await t({ ...req.body, ...req.query, ...req.params });
530
+ res.json(result);
531
+ });
532
+ });
533
+ },
534
+ });
535
+ ```
536
+
537
+ ## Key Patterns & Features
538
+
539
+ - **Optional Dependencies**: Gracefully handle missing services by defining dependencies as optional. The dependency will be `null` if not registered.
540
+ `dependencies: { analytics: analyticsService.optional() }`
541
+
542
+ - **Stop Propagation**: Prevent other hooks from running for a specific event.
543
+ `// inside a hook`
544
+ `event.stopPropagation()`
545
+
546
+ That’s it. Small surface area, strong primitives, great DX.
547
+
548
+ ## Concurrency: Semaphore & Queue
549
+
550
+ ```ts
551
+ import { Semaphore, Queue } from "@bluelibs/runner";
552
+
553
+ // Semaphore: limit parallelism
554
+ const dbSem = new Semaphore(5);
555
+ const users = await dbSem.withPermit(async () =>
556
+ db.query("SELECT * FROM users"),
557
+ );
558
+
559
+ // Queue: FIFO with cooperative cancellation
560
+ const queue = new Queue();
561
+ const result = await queue.run(async (signal) => {
562
+ signal.throwIfAborted();
563
+ return await doWork();
564
+ });
565
+ await queue.dispose({ cancel: true });
566
+ ```
567
+
568
+ ## Handling Circular Types (even if runtime is fine)
569
+
570
+ Rarely, when TypeScript struggles with circular type inference, break the chain with an explicit interface:
571
+
572
+ ```ts
573
+ import type { IResource } from "@bluelibs/runner";
574
+
575
+ export const cResource = resource({
576
+ id: "c.resource",
577
+ dependencies: { a: aResource },
578
+ init: async (_, { a }) => `C depends on ${a}`,
579
+ }) as IResource<void, string>; // void config, returns string
580
+ ```
581
+
582
+ ## Validation (optional and library‑agnostic)
583
+
584
+ Interface any library can implement:
585
+
586
+ ```ts
587
+ interface IValidationSchema<T> {
588
+ parse(input: unknown): T;
589
+ }
590
+ ```
591
+
592
+ Works out of the box with Zod (`z.object(...).parse`), and can be adapted for Yup/Joi with small wrappers.
593
+
594
+ ```ts
595
+ import { z } from "zod";
596
+
597
+ // Task input/result validation
598
+ const inputSchema = z.object({ email: z.string().email() });
599
+ const resultSchema = z.object({ id: z.string(), email: z.string().email() });
600
+
601
+ task({
602
+ // ...
603
+ inputSchema, // validates before run
604
+ resultSchema, // validates awaited return
605
+ });
606
+
607
+ resource({
608
+ configSchema, // Resource config validation (runs on .with())
609
+ resultSchema, // Runs after initialization
610
+ });
611
+
612
+ event({
613
+ payloadSchema, // Runs on event emission
614
+ });
615
+
616
+ // Middleware config validation (runs on .with())
617
+ middleware({
618
+ // ...
619
+ configSchema, // runs on .with()
620
+ });
621
+ ```