@ddd-ts-kit/aspects 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (346) hide show
  1. package/README.md +381 -2
  2. package/dist/aspect/advice-factory.d.ts +9 -0
  3. package/dist/aspect/advice-factory.d.ts.map +1 -0
  4. package/dist/aspect/advice-factory.js +2 -0
  5. package/dist/aspect/advice-factory.js.map +1 -0
  6. package/dist/aspect/advice.d.ts +3 -0
  7. package/dist/aspect/advice.d.ts.map +1 -0
  8. package/dist/aspect/advice.js +2 -0
  9. package/dist/aspect/advice.js.map +1 -0
  10. package/dist/aspect/aspect-info.d.ts +12 -0
  11. package/dist/aspect/aspect-info.d.ts.map +1 -0
  12. package/dist/aspect/aspect-info.js +2 -0
  13. package/dist/aspect/aspect-info.js.map +1 -0
  14. package/dist/aspect/aspect-kind.d.ts +4 -0
  15. package/dist/aspect/aspect-kind.d.ts.map +1 -0
  16. package/dist/aspect/aspect-kind.js +2 -0
  17. package/dist/aspect/aspect-kind.js.map +1 -0
  18. package/dist/aspect/aspect-origins.d.ts +10 -0
  19. package/dist/aspect/aspect-origins.d.ts.map +1 -0
  20. package/dist/aspect/aspect-origins.js +3 -0
  21. package/dist/aspect/aspect-origins.js.map +1 -0
  22. package/dist/aspect/aspect.d.ts +9 -0
  23. package/dist/aspect/aspect.d.ts.map +1 -0
  24. package/dist/aspect/aspect.js +2 -0
  25. package/dist/aspect/aspect.js.map +1 -0
  26. package/dist/aspect/compose.d.ts +7 -0
  27. package/dist/aspect/compose.d.ts.map +1 -0
  28. package/dist/aspect/compose.js +11 -0
  29. package/dist/aspect/compose.js.map +1 -0
  30. package/dist/aspect/define-aspect.d.ts +5 -0
  31. package/dist/aspect/define-aspect.d.ts.map +1 -0
  32. package/dist/aspect/define-aspect.js +17 -0
  33. package/dist/aspect/define-aspect.js.map +1 -0
  34. package/dist/aspect/invocation.d.ts +6 -0
  35. package/dist/aspect/invocation.d.ts.map +1 -0
  36. package/dist/aspect/invocation.js +2 -0
  37. package/dist/aspect/invocation.js.map +1 -0
  38. package/dist/aspect/join-point.d.ts +13 -0
  39. package/dist/aspect/join-point.d.ts.map +1 -0
  40. package/dist/aspect/join-point.js +2 -0
  41. package/dist/aspect/join-point.js.map +1 -0
  42. package/dist/aspect/selector.d.ts +3 -0
  43. package/dist/aspect/selector.d.ts.map +1 -0
  44. package/dist/aspect/selector.js +2 -0
  45. package/dist/aspect/selector.js.map +1 -0
  46. package/dist/aspect/selectors.d.ts +14 -0
  47. package/dist/aspect/selectors.d.ts.map +1 -0
  48. package/dist/aspect/selectors.js +32 -0
  49. package/dist/aspect/selectors.js.map +1 -0
  50. package/dist/aspect/when.d.ts +8 -0
  51. package/dist/aspect/when.d.ts.map +1 -0
  52. package/dist/aspect/when.js +21 -0
  53. package/dist/aspect/when.js.map +1 -0
  54. package/dist/bulkhead/bulkhead-options.d.ts +10 -0
  55. package/dist/bulkhead/bulkhead-options.d.ts.map +1 -0
  56. package/dist/bulkhead/bulkhead-options.js +2 -0
  57. package/dist/bulkhead/bulkhead-options.js.map +1 -0
  58. package/dist/bulkhead/bulkhead.d.ts +11 -0
  59. package/dist/bulkhead/bulkhead.d.ts.map +1 -0
  60. package/dist/bulkhead/bulkhead.js +22 -0
  61. package/dist/bulkhead/bulkhead.js.map +1 -0
  62. package/dist/bulkhead/compartment.d.ts +13 -0
  63. package/dist/bulkhead/compartment.d.ts.map +1 -0
  64. package/dist/bulkhead/compartment.js +40 -0
  65. package/dist/bulkhead/compartment.js.map +1 -0
  66. package/dist/cached/cached.d.ts +26 -0
  67. package/dist/cached/cached.d.ts.map +1 -0
  68. package/dist/cached/cached.js +64 -0
  69. package/dist/cached/cached.js.map +1 -0
  70. package/dist/circuit-breaker/circuit-breaker-options.d.ts +20 -0
  71. package/dist/circuit-breaker/circuit-breaker-options.d.ts.map +1 -0
  72. package/dist/circuit-breaker/circuit-breaker-options.js +2 -0
  73. package/dist/circuit-breaker/circuit-breaker-options.js.map +1 -0
  74. package/dist/circuit-breaker/circuit-breaker.d.ts +11 -0
  75. package/dist/circuit-breaker/circuit-breaker.d.ts.map +1 -0
  76. package/dist/circuit-breaker/circuit-breaker.js +27 -0
  77. package/dist/circuit-breaker/circuit-breaker.js.map +1 -0
  78. package/dist/circuit-breaker/circuit-state-change.d.ts +10 -0
  79. package/dist/circuit-breaker/circuit-state-change.d.ts.map +1 -0
  80. package/dist/circuit-breaker/circuit-state-change.js +2 -0
  81. package/dist/circuit-breaker/circuit-state-change.js.map +1 -0
  82. package/dist/circuit-breaker/circuit-state-gauge.d.ts +8 -0
  83. package/dist/circuit-breaker/circuit-state-gauge.d.ts.map +1 -0
  84. package/dist/circuit-breaker/circuit-state-gauge.js +14 -0
  85. package/dist/circuit-breaker/circuit-state-gauge.js.map +1 -0
  86. package/dist/circuit-breaker/circuit-state.d.ts +2 -0
  87. package/dist/circuit-breaker/circuit-state.d.ts.map +1 -0
  88. package/dist/circuit-breaker/circuit-state.js +2 -0
  89. package/dist/circuit-breaker/circuit-state.js.map +1 -0
  90. package/dist/circuit-breaker/circuit.d.ts +22 -0
  91. package/dist/circuit-breaker/circuit.d.ts.map +1 -0
  92. package/dist/circuit-breaker/circuit.js +79 -0
  93. package/dist/circuit-breaker/circuit.js.map +1 -0
  94. package/dist/decorators/async-method-decorator.d.ts +13 -0
  95. package/dist/decorators/async-method-decorator.d.ts.map +1 -0
  96. package/dist/decorators/async-method-decorator.js +2 -0
  97. package/dist/decorators/async-method-decorator.js.map +1 -0
  98. package/dist/decorators/bulkhead.d.ts +5 -0
  99. package/dist/decorators/bulkhead.d.ts.map +1 -0
  100. package/dist/decorators/bulkhead.js +7 -0
  101. package/dist/decorators/bulkhead.js.map +1 -0
  102. package/dist/decorators/cached.d.ts +5 -0
  103. package/dist/decorators/cached.d.ts.map +1 -0
  104. package/dist/decorators/cached.js +7 -0
  105. package/dist/decorators/cached.js.map +1 -0
  106. package/dist/decorators/circuit-breaker.d.ts +5 -0
  107. package/dist/decorators/circuit-breaker.d.ts.map +1 -0
  108. package/dist/decorators/circuit-breaker.js +7 -0
  109. package/dist/decorators/circuit-breaker.js.map +1 -0
  110. package/dist/decorators/decorator-of.d.ts +9 -0
  111. package/dist/decorators/decorator-of.d.ts.map +1 -0
  112. package/dist/decorators/decorator-of.js +43 -0
  113. package/dist/decorators/decorator-of.js.map +1 -0
  114. package/dist/decorators/logged.d.ts +5 -0
  115. package/dist/decorators/logged.d.ts.map +1 -0
  116. package/dist/decorators/logged.js +7 -0
  117. package/dist/decorators/logged.js.map +1 -0
  118. package/dist/decorators/retry.d.ts +5 -0
  119. package/dist/decorators/retry.d.ts.map +1 -0
  120. package/dist/decorators/retry.js +7 -0
  121. package/dist/decorators/retry.js.map +1 -0
  122. package/dist/decorators/timed.d.ts +5 -0
  123. package/dist/decorators/timed.d.ts.map +1 -0
  124. package/dist/decorators/timed.js +7 -0
  125. package/dist/decorators/timed.js.map +1 -0
  126. package/dist/decorators/timeout.d.ts +5 -0
  127. package/dist/decorators/timeout.d.ts.map +1 -0
  128. package/dist/decorators/timeout.js +7 -0
  129. package/dist/decorators/timeout.js.map +1 -0
  130. package/dist/decorators.d.ts +10 -0
  131. package/dist/decorators.d.ts.map +1 -0
  132. package/dist/decorators.js +9 -0
  133. package/dist/decorators.js.map +1 -0
  134. package/dist/di/aspects.d.ts +19 -0
  135. package/dist/di/aspects.d.ts.map +1 -0
  136. package/dist/di/aspects.js +22 -0
  137. package/dist/di/aspects.js.map +1 -0
  138. package/dist/errors/aspect-not-allowed-error.d.ts +6 -0
  139. package/dist/errors/aspect-not-allowed-error.d.ts.map +1 -0
  140. package/dist/errors/aspect-not-allowed-error.js +9 -0
  141. package/dist/errors/aspect-not-allowed-error.js.map +1 -0
  142. package/dist/errors/bulkhead-full-error.d.ts +7 -0
  143. package/dist/errors/bulkhead-full-error.d.ts.map +1 -0
  144. package/dist/errors/bulkhead-full-error.js +10 -0
  145. package/dist/errors/bulkhead-full-error.js.map +1 -0
  146. package/dist/errors/circuit-open-error.d.ts +11 -0
  147. package/dist/errors/circuit-open-error.d.ts.map +1 -0
  148. package/dist/errors/circuit-open-error.js +14 -0
  149. package/dist/errors/circuit-open-error.js.map +1 -0
  150. package/dist/errors/is-business-refusal.d.ts +3 -0
  151. package/dist/errors/is-business-refusal.d.ts.map +1 -0
  152. package/dist/errors/is-business-refusal.js +6 -0
  153. package/dist/errors/is-business-refusal.js.map +1 -0
  154. package/dist/errors/timeout-error.d.ts +8 -0
  155. package/dist/errors/timeout-error.d.ts.map +1 -0
  156. package/dist/errors/timeout-error.js +12 -0
  157. package/dist/errors/timeout-error.js.map +1 -0
  158. package/dist/index.d.ts +46 -0
  159. package/dist/index.d.ts.map +1 -0
  160. package/dist/index.js +30 -0
  161. package/dist/index.js.map +1 -0
  162. package/dist/observability/log-call.d.ts +13 -0
  163. package/dist/observability/log-call.d.ts.map +1 -0
  164. package/dist/observability/log-call.js +22 -0
  165. package/dist/observability/log-call.js.map +1 -0
  166. package/dist/observability/logged.d.ts +22 -0
  167. package/dist/observability/logged.d.ts.map +1 -0
  168. package/dist/observability/logged.js +38 -0
  169. package/dist/observability/logged.js.map +1 -0
  170. package/dist/observability/logging-middleware.d.ts +13 -0
  171. package/dist/observability/logging-middleware.d.ts.map +1 -0
  172. package/dist/observability/logging-middleware.js +16 -0
  173. package/dist/observability/logging-middleware.js.map +1 -0
  174. package/dist/observability/metrics-middleware.d.ts +10 -0
  175. package/dist/observability/metrics-middleware.d.ts.map +1 -0
  176. package/dist/observability/metrics-middleware.js +12 -0
  177. package/dist/observability/metrics-middleware.js.map +1 -0
  178. package/dist/observability/observed.d.ts +14 -0
  179. package/dist/observability/observed.d.ts.map +1 -0
  180. package/dist/observability/observed.js +27 -0
  181. package/dist/observability/observed.js.map +1 -0
  182. package/dist/observability/safely.d.ts +3 -0
  183. package/dist/observability/safely.d.ts.map +1 -0
  184. package/dist/observability/safely.js +10 -0
  185. package/dist/observability/safely.js.map +1 -0
  186. package/dist/observability/stdout-logger.d.ts +45 -0
  187. package/dist/observability/stdout-logger.d.ts.map +1 -0
  188. package/dist/observability/stdout-logger.js +85 -0
  189. package/dist/observability/stdout-logger.js.map +1 -0
  190. package/dist/observability/time-call.d.ts +10 -0
  191. package/dist/observability/time-call.d.ts.map +1 -0
  192. package/dist/observability/time-call.js +11 -0
  193. package/dist/observability/time-call.js.map +1 -0
  194. package/dist/observability/timed.d.ts +19 -0
  195. package/dist/observability/timed.d.ts.map +1 -0
  196. package/dist/observability/timed.js +33 -0
  197. package/dist/observability/timed.js.map +1 -0
  198. package/dist/retry/backoff.d.ts +12 -0
  199. package/dist/retry/backoff.d.ts.map +1 -0
  200. package/dist/retry/backoff.js +2 -0
  201. package/dist/retry/backoff.js.map +1 -0
  202. package/dist/retry/delay-before-retry.d.ts +4 -0
  203. package/dist/retry/delay-before-retry.d.ts.map +1 -0
  204. package/dist/retry/delay-before-retry.js +10 -0
  205. package/dist/retry/delay-before-retry.js.map +1 -0
  206. package/dist/retry/full-jitter.d.ts +7 -0
  207. package/dist/retry/full-jitter.d.ts.map +1 -0
  208. package/dist/retry/full-jitter.js +8 -0
  209. package/dist/retry/full-jitter.js.map +1 -0
  210. package/dist/retry/is-transient.d.ts +6 -0
  211. package/dist/retry/is-transient.d.ts.map +1 -0
  212. package/dist/retry/is-transient.js +11 -0
  213. package/dist/retry/is-transient.js.map +1 -0
  214. package/dist/retry/jitter.d.ts +3 -0
  215. package/dist/retry/jitter.d.ts.map +1 -0
  216. package/dist/retry/jitter.js +2 -0
  217. package/dist/retry/jitter.js.map +1 -0
  218. package/dist/retry/no-jitter.d.ts +3 -0
  219. package/dist/retry/no-jitter.d.ts.map +1 -0
  220. package/dist/retry/no-jitter.js +2 -0
  221. package/dist/retry/no-jitter.js.map +1 -0
  222. package/dist/retry/retry.d.ts +20 -0
  223. package/dist/retry/retry.d.ts.map +1 -0
  224. package/dist/retry/retry.js +38 -0
  225. package/dist/retry/retry.js.map +1 -0
  226. package/dist/runtime/aspect-runtime-error.d.ts +6 -0
  227. package/dist/runtime/aspect-runtime-error.d.ts.map +1 -0
  228. package/dist/runtime/aspect-runtime-error.js +9 -0
  229. package/dist/runtime/aspect-runtime-error.js.map +1 -0
  230. package/dist/runtime/aspect-runtime.d.ts +15 -0
  231. package/dist/runtime/aspect-runtime.d.ts.map +1 -0
  232. package/dist/runtime/aspect-runtime.js +2 -0
  233. package/dist/runtime/aspect-runtime.js.map +1 -0
  234. package/dist/runtime/cache-or.d.ts +3 -0
  235. package/dist/runtime/cache-or.d.ts.map +1 -0
  236. package/dist/runtime/cache-or.js +6 -0
  237. package/dist/runtime/cache-or.js.map +1 -0
  238. package/dist/runtime/clock-or.d.ts +3 -0
  239. package/dist/runtime/clock-or.d.ts.map +1 -0
  240. package/dist/runtime/clock-or.js +7 -0
  241. package/dist/runtime/clock-or.js.map +1 -0
  242. package/dist/runtime/installed-runtime.d.ts +8 -0
  243. package/dist/runtime/installed-runtime.d.ts.map +1 -0
  244. package/dist/runtime/installed-runtime.js +16 -0
  245. package/dist/runtime/installed-runtime.js.map +1 -0
  246. package/dist/runtime/logger-or.d.ts +3 -0
  247. package/dist/runtime/logger-or.d.ts.map +1 -0
  248. package/dist/runtime/logger-or.js +6 -0
  249. package/dist/runtime/logger-or.js.map +1 -0
  250. package/dist/runtime/metrics-or.d.ts +3 -0
  251. package/dist/runtime/metrics-or.d.ts.map +1 -0
  252. package/dist/runtime/metrics-or.js +6 -0
  253. package/dist/runtime/metrics-or.js.map +1 -0
  254. package/dist/runtime/missing-collaborator.d.ts +2 -0
  255. package/dist/runtime/missing-collaborator.d.ts.map +1 -0
  256. package/dist/runtime/missing-collaborator.js +5 -0
  257. package/dist/runtime/missing-collaborator.js.map +1 -0
  258. package/dist/runtime/scheduler-or.d.ts +3 -0
  259. package/dist/runtime/scheduler-or.d.ts.map +1 -0
  260. package/dist/runtime/scheduler-or.js +6 -0
  261. package/dist/runtime/scheduler-or.js.map +1 -0
  262. package/dist/runtime/scheduler.d.ts +7 -0
  263. package/dist/runtime/scheduler.d.ts.map +1 -0
  264. package/dist/runtime/scheduler.js +5 -0
  265. package/dist/runtime/scheduler.js.map +1 -0
  266. package/dist/runtime/system-scheduler.d.ts +3 -0
  267. package/dist/runtime/system-scheduler.d.ts.map +1 -0
  268. package/dist/runtime/system-scheduler.js +9 -0
  269. package/dist/runtime/system-scheduler.js.map +1 -0
  270. package/dist/state/state-for.d.ts +5 -0
  271. package/dist/state/state-for.d.ts.map +1 -0
  272. package/dist/state/state-for.js +17 -0
  273. package/dist/state/state-for.js.map +1 -0
  274. package/dist/state/state-scope.d.ts +3 -0
  275. package/dist/state/state-scope.d.ts.map +1 -0
  276. package/dist/state/state-scope.js +2 -0
  277. package/dist/state/state-scope.js.map +1 -0
  278. package/dist/testing/create-aspect-fakes.d.ts +13 -0
  279. package/dist/testing/create-aspect-fakes.d.ts.map +1 -0
  280. package/dist/testing/create-aspect-fakes.js +15 -0
  281. package/dist/testing/create-aspect-fakes.js.map +1 -0
  282. package/dist/testing/manual-scheduler.d.ts +14 -0
  283. package/dist/testing/manual-scheduler.d.ts.map +1 -0
  284. package/dist/testing/manual-scheduler.js +30 -0
  285. package/dist/testing/manual-scheduler.js.map +1 -0
  286. package/dist/testing/with-aspect-runtime.d.ts +8 -0
  287. package/dist/testing/with-aspect-runtime.d.ts.map +1 -0
  288. package/dist/testing/with-aspect-runtime.js +10 -0
  289. package/dist/testing/with-aspect-runtime.js.map +1 -0
  290. package/dist/testing.d.ts +4 -0
  291. package/dist/testing.d.ts.map +1 -0
  292. package/dist/testing.js +4 -0
  293. package/dist/testing.js.map +1 -0
  294. package/dist/timeout/timeout.d.ts +21 -0
  295. package/dist/timeout/timeout.d.ts.map +1 -0
  296. package/dist/timeout/timeout.js +42 -0
  297. package/dist/timeout/timeout.js.map +1 -0
  298. package/dist/weaving/advise-method.d.ts +7 -0
  299. package/dist/weaving/advise-method.d.ts.map +1 -0
  300. package/dist/weaving/advise-method.js +19 -0
  301. package/dist/weaving/advise-method.js.map +1 -0
  302. package/dist/weaving/advise-methods.d.ts +13 -0
  303. package/dist/weaving/advise-methods.d.ts.map +1 -0
  304. package/dist/weaving/advise-methods.js +119 -0
  305. package/dist/weaving/advise-methods.js.map +1 -0
  306. package/dist/weaving/advise-prototype-method.d.ts +8 -0
  307. package/dist/weaving/advise-prototype-method.d.ts.map +1 -0
  308. package/dist/weaving/advise-prototype-method.js +30 -0
  309. package/dist/weaving/advise-prototype-method.js.map +1 -0
  310. package/dist/weaving/advised.d.ts +6 -0
  311. package/dist/weaving/advised.d.ts.map +1 -0
  312. package/dist/weaving/advised.js +19 -0
  313. package/dist/weaving/advised.js.map +1 -0
  314. package/dist/weaving/from-outside.d.ts +10 -0
  315. package/dist/weaving/from-outside.d.ts.map +1 -0
  316. package/dist/weaving/from-outside.js +29 -0
  317. package/dist/weaving/from-outside.js.map +1 -0
  318. package/dist/weaving/is-wrappable.d.ts +3 -0
  319. package/dist/weaving/is-wrappable.d.ts.map +1 -0
  320. package/dist/weaving/is-wrappable.js +7 -0
  321. package/dist/weaving/is-wrappable.js.map +1 -0
  322. package/dist/weaving/known-async.d.ts +4 -0
  323. package/dist/weaving/known-async.d.ts.map +1 -0
  324. package/dist/weaving/known-async.js +3 -0
  325. package/dist/weaving/known-async.js.map +1 -0
  326. package/dist/weaving/lifecycle-hooks.d.ts +3 -0
  327. package/dist/weaving/lifecycle-hooks.d.ts.map +1 -0
  328. package/dist/weaving/lifecycle-hooks.js +9 -0
  329. package/dist/weaving/lifecycle-hooks.js.map +1 -0
  330. package/dist/weaving/method.d.ts +2 -0
  331. package/dist/weaving/method.d.ts.map +1 -0
  332. package/dist/weaving/method.js +2 -0
  333. package/dist/weaving/method.js.map +1 -0
  334. package/dist/weaving/named.d.ts +4 -0
  335. package/dist/weaving/named.d.ts.map +1 -0
  336. package/dist/weaving/named.js +7 -0
  337. package/dist/weaving/named.js.map +1 -0
  338. package/dist/weaving/shadowed.d.ts +4 -0
  339. package/dist/weaving/shadowed.d.ts.map +1 -0
  340. package/dist/weaving/shadowed.js +14 -0
  341. package/dist/weaving/shadowed.js.map +1 -0
  342. package/dist/weaving/site-of.d.ts +5 -0
  343. package/dist/weaving/site-of.d.ts.map +1 -0
  344. package/dist/weaving/site-of.js +15 -0
  345. package/dist/weaving/site-of.js.map +1 -0
  346. package/package.json +40 -4
package/README.md CHANGED
@@ -1,3 +1,382 @@
1
- # Temporary Holding Version
1
+ # @ddd-ts-kit/aspects
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Logging, metrics, retries, timeouts, circuit breakers and caching are not business logic, so
4
+ they are never written inside it. Each one is an aspect: one implementation, put around an
5
+ adapter from outside, the way `@ddd-ts-kit/tracing` puts spans around it. The wrapped class
6
+ imports nothing, and `domain/` and `application/` never see this package.
7
+
8
+ ```
9
+ npm i @ddd-ts-kit/aspects
10
+ ```
11
+
12
+ ## Three forms, one implementation
13
+
14
+ The same policy, written three ways. All three run the same code.
15
+
16
+ ### 1. At the composition root, on one binding (preferred)
17
+
18
+ ```ts
19
+ // contexts/billing/application/ports/payment-gateway.port.ts
20
+ export abstract class PaymentGateway {
21
+ abstract charge(payment: Payment, signal?: AbortSignal): Promise<Receipt>;
22
+ }
23
+
24
+ // contexts/billing/application/ports/payment-settings.port.ts
25
+ export abstract class PaymentSettings {
26
+ abstract readonly baseUrl: string;
27
+
28
+ abstract readonly apiKey: string;
29
+ }
30
+
31
+ // contexts/billing/infrastructure/external/stripe.gateway.ts
32
+ export class StripeGateway implements PaymentGateway {
33
+ constructor(private readonly settings: PaymentSettings) {}
34
+
35
+ async charge(payment: Payment, signal?: AbortSignal): Promise<Receipt> {
36
+ return post(`${this.settings.baseUrl}/charges`, payment, { apiKey: this.settings.apiKey, signal });
37
+ }
38
+ }
39
+
40
+ // contexts/billing/billing.module.ts
41
+ import { retry, timeout } from '@ddd-ts-kit/aspects';
42
+
43
+ builder.bind(PaymentSettings).toValue({ baseUrl: config.stripe.url, apiKey: config.stripe.key });
44
+ builder
45
+ .bind(PaymentGateway)
46
+ .toClass(StripeGateway, [PaymentSettings])
47
+ .as('external')
48
+ .with(retry({ attempts: 3 }), timeout(2000, { signal: true }));
49
+ ```
50
+
51
+ The port is an abstract class and the class is what is bound. `StripeGateway` has no import
52
+ from this package and no knowledge of the policy. In the declared style, the default of a
53
+ generated project, there is no module file and the same policy is stated in the declaration
54
+ of the adapter: `@Adapter(PaymentGateway, { kind: 'external', aspects: [retry({ attempts: 3 }), timeout(2000)] }, [PaymentSettings])`. Without a container an aspect is a function:
55
+ `retry({ attempts: 3 })(new StripeGateway(settings), { kind: 'external' })`.
56
+
57
+ ### 2. At the composition root, on every binding
58
+
59
+ ```ts
60
+ // main.ts
61
+ import { Cache, Clock, Logger, Metrics } from '@ddd-ts-kit/kernel';
62
+ import { createApplication, kernelModule } from '@ddd-ts-kit/di';
63
+ import { StdoutLogger, aspects, forKinds, installAspectRuntime, logged, retry, timed, timeout, when } from '@ddd-ts-kit/aspects';
64
+
65
+ const app = createApplication({
66
+ modules: [kernelModule({ logger: new StdoutLogger() }), platformModule, billingModule],
67
+ decorate: aspects(
68
+ tracing,
69
+ when(forKinds('repository', 'gateway', 'external'), logged(), timed()),
70
+ when(forKinds('external'), retry({ attempts: 3 }), timeout(2000)),
71
+ ),
72
+ });
73
+
74
+ installAspectRuntime({
75
+ logger: app.resolve(Logger),
76
+ metrics: app.resolve(Metrics),
77
+ cache: app.resolve(Cache),
78
+ clock: app.resolve(Clock),
79
+ });
80
+ ```
81
+
82
+ `tracing` is the hook of the tracing package written as an aspect:
83
+
84
+ ```ts
85
+ const tracing: Aspect = (instance, info) =>
86
+ info?.kind === undefined ? instance : traceDependency(instance, { kind: info.kind, ...(info.context === undefined ? {} : { context: info.context }) });
87
+ ```
88
+
89
+ An application on `kernelModule({ instrumentation: traceApplication() })`, which is what the
90
+ CLI generates, does not write this `tracing` aspect: the kernel module traces the bindings
91
+ itself. Its span is inside whatever the `decorate` hook adds and outside the aspects of a
92
+ binding (see "Order"). A generated project does not use the `decorate` hook either: its
93
+ platform module puts the default policy around the adapters that leave the process with
94
+ `builder.around(['external', 'gateway'], () => when(forKinds('external', 'gateway'), retry(...), timeout(...)))`.
95
+
96
+ ### 3. As a decorator, on an adapter
97
+
98
+ ```ts
99
+ // contexts/billing/infrastructure/external/stripe.gateway.ts
100
+ import { Retry, Timeout } from '@ddd-ts-kit/aspects/decorators';
101
+
102
+ export class StripeGateway implements PaymentGateway {
103
+ constructor(private readonly settings: PaymentSettings) {}
104
+
105
+ @Retry({ attempts: 3 })
106
+ @Timeout(2000, { signal: true })
107
+ async charge(payment: Payment, signal?: AbortSignal): Promise<Receipt> {
108
+ return post(`${this.settings.baseUrl}/charges`, payment, { apiKey: this.settings.apiKey, signal });
109
+ }
110
+ }
111
+ ```
112
+
113
+ `@Retry`, `@Timeout`, `@Cached`, `@Logged`, `@Timed`, `@CircuitBreaker`, `@Bulkhead`, and
114
+ `decoratorOf(adviceFactory)` for an aspect of the project. Several on one method nest as they
115
+ read: the first written is outermost. The class takes no logger, metrics or cache in its
116
+ constructor: the decorators read them from the aspect runtime when a call is made.
117
+
118
+ ### Which form
119
+
120
+ - **Prefer the composition root** (forms 1 and 2). The adapter stays free of any import, the
121
+ policy of a whole application is read in one place, and it changes without touching a class.
122
+ - **A decorator is acceptable** on an adapter in `infrastructure/` or `platform/` whose policy
123
+ is part of what the adapter is: a client of a provider that documents its own retry and
124
+ timeout rules. Do not apply the same aspect in both forms: it would run twice.
125
+ - **Never in `domain/` or `application/`**, in any form: `layer/domain-purity` and
126
+ `layer/application-inward` forbid the import. A use case gets its cross-cutting behaviour
127
+ from a bus middleware, below.
128
+
129
+ ## Aspects
130
+
131
+ | Aspect | What it does | Default policy | Never records |
132
+ |---|---|---|---|
133
+ | `logged({ level?, fields?, logger?, clock? })` | entries `<Class>.<method> started` (debug), then `succeeded` (`level`, default info) or `failed` | a `DomainError` fails at `warn`, a fault at `error`; fields `class`, `method`, `kind`, `context`, `durationMs`, `errorType`, `errorCode` | arguments, results, error messages |
134
+ | `timed({ name?, metrics?, clock? })` | histogram `<name>_duration_ms`, counter `<name>_total` | `name` is `dependency_call`; labels `class`, `method`, `kind`, `context`, `outcome`, `error_code` | arguments, results, error messages, identifiers |
135
+ | `retry({ attempts, backoff?, retryOn?, sleep? })` | calls again after a failure | never after a `DomainError`, an open circuit or a full bulkhead (`isTransient`); 100 ms doubling up to 30 s with full jitter | nothing: it records nothing |
136
+ | `timeout(ms, { signal?, scheduler? })` | rejects with `TimeoutError` (`TIMEOUT`) | the underlying call keeps running unless `signal: true` | the message names the class and method only |
137
+ | `circuitBreaker({ failureThreshold, resetAfterMs, halfOpenMax?, scope?, isFailure?, onStateChange?, clock? })` | refuses calls with `CircuitOpenError` (`CIRCUIT_OPEN`) after consecutive failures, then lets trial calls through | one circuit per instance and method; a `DomainError` is not a failure; one trial call | `onStateChange` gets class, method, context and the two states |
138
+ | `cached({ key, ttlMs, cache?, allowKinds? })` | answers a repeated read from the `Cache` port | refuses kinds `repository` and `use-case`; a call whose key is `undefined` is not cached; failures are not cached; concurrent identical calls share one call; a failing cache is a miss | the key is built by the project from the arguments and goes to the cache only |
139
+ | `bulkhead({ maxConcurrent, maxQueued?, scope? })` | bounds the calls running at once, refuses beyond with `BulkheadFullError` (`BULKHEAD_FULL`) | no queue; one compartment per instance and method | nothing |
140
+
141
+ **Privacy.** The rule is the one of tracing: an entry, a label or an error raised by this
142
+ package carries names from the code (class, method, context, kind), a duration, the class of
143
+ an error and its short `code`. Never an argument, a result or the message of a caught error:
144
+ they quote data. Errors are rethrown untouched.
145
+
146
+ **`timeout` and cancellation.** With `signal: true` the method is handed an `AbortSignal` as its
147
+ last declared parameter, aborted when the time is up. Declare `signal?: AbortSignal` last,
148
+ without a default value, and pass it to the underlying call. A signal the caller passed there is
149
+ kept: either one aborts. A method without parameter receives none.
150
+
151
+ **`retry` and jitter.** `backoff: { baseMs, factor, maxMs, jitter }`. `jitter` is a function
152
+ `(delayMs, attempt) => delayMs`: `fullJitter()` by default, `fullJitter(seededRandom)` or
153
+ `noJitter` for a deterministic schedule. `sleep` replaces the wait altogether.
154
+
155
+ **`circuitBreaker` and metrics.** `onStateChange: circuitStateGauge()` publishes the state as
156
+ the gauge `circuit_breaker_state`: 0 closed, 1 half-open, 2 open.
157
+
158
+ **`cached` is for reads of gateways and external adapters.** An aggregate served from a cache
159
+ behind a repository hides that it is stale, and the next command would decide on old state.
160
+ Wrapping a repository or a use case therefore takes an explicit `allowKinds: ['repository']`,
161
+ which is a decision to record.
162
+
163
+ A project writes its own aspect with `defineAspect((joinPoint) => async ({ args, proceed }) => ...)`.
164
+
165
+ ## Selecting what is wrapped
166
+
167
+ `when(selector, ...aspects)` applies aspects to the methods a selector picks:
168
+
169
+ | Selector | Picks |
170
+ |---|---|
171
+ | `forKinds('repository', 'external')` | instances bound with `.as(kind)` |
172
+ | `forContext('billing')` | instances of a module |
173
+ | `forClasses('Http*')` | instances by class name |
174
+ | `forMethods('find*', 'get*')` | methods by name |
175
+ | `allOf(...)`, `anyOf(...)`, `not(...)` | combinations |
176
+
177
+ ```ts
178
+ when(allOf(forKinds('gateway', 'external'), forMethods('find*')), cached({ key, ttlMs: 60_000 }));
179
+ ```
180
+
181
+ ## Order
182
+
183
+ `compose(a, b)`, `.with(a, b)`, `aspects(a, b)` and stacked decorators all read the same way:
184
+ **the first is outermost**. Around one instance the application nests, outermost first: the
185
+ `decorate` hook, the aspects modules put around a kind with `builder.around(kinds, ...)` in
186
+ the order of the modules (the span of `kernelModule({ instrumentation })` comes first, then
187
+ for a repository the hand-over of its domain events, then the ones of the platform module),
188
+ and last the aspects of the binding itself (`.with(...)`, or `aspects` of a declaration).
189
+ Order changes meaning:
190
+
191
+ - `retry` then `timeout`: each attempt has its own time limit. The other way round, one limit
192
+ covers all the attempts and the waits between them.
193
+ - `timed` and `logged` outside `retry`: one measurement per call as the caller lived it,
194
+ retries included. Inside, one per attempt.
195
+ - `cached` outside `retry` and `circuitBreaker`: a hit costs nothing and is not refused by an
196
+ open circuit.
197
+ - `circuitBreaker` inside `retry`: each failed attempt counts, and an open circuit ends the
198
+ retries at once, since `retry` does not retry a `CircuitOpenError`.
199
+
200
+ The order that fits most adapters, outermost first:
201
+
202
+ ```
203
+ tracing, logged, timed, cached, retry, circuitBreaker, bulkhead, timeout, the adapter
204
+ ```
205
+
206
+ With the instrumentation of the kernel module, the tracing span is where the kernel module
207
+ puts it: `logged` and `timed` given to the `decorate` hook are outside the span, the ones
208
+ given to `builder.around` or to a binding are inside it.
209
+
210
+ A repository has one more wrapper, put by `kernelModule()` between the `decorate` hook and the
211
+ aspects of its binding: the hand-over of the domain events of what it saves, which the unit of
212
+ work dispatches at its end, before the commit. So the aspects of a binding
213
+ (`.with(retry(...))`) are inside it, where a retried save announces its events once. Inside a
214
+ unit of work a handler of those events no longer runs during the save, so no retry of a save
215
+ ever covers it. Outside of any, the save dispatches before it resolves: a `retry` given to the
216
+ `decorate` hook would then run the save again after a handler failed, with no event left to
217
+ dispatch. Put the retry of a repository on its binding.
218
+
219
+ ## The aspect runtime
220
+
221
+ `logged`, `timed` and `cached` need a logger, metrics and a cache. They take them from their
222
+ options, or from the runtime that the composition root installs once with
223
+ `installAspectRuntime({ logger, metrics, cache, clock, scheduler })`. The runtime is read when
224
+ a call is made, never when a class is decorated or an instance wrapped, so the order of the
225
+ statements in `main.ts` does not matter. A missing collaborator raises `AspectRuntimeError`,
226
+ which names it. The clock and the scheduler default to the system ones. `Scheduler` is the port
227
+ of this package for "run this later" (`schedule(work, delayMs)`), an abstract class like the
228
+ ports of the kernel: `systemScheduler` is its default, `ManualScheduler` of `./testing` the one
229
+ of a spec.
230
+
231
+ ## Use cases: bus middlewares
232
+
233
+ Handlers are not wrapped one by one; the buses are:
234
+
235
+ ```ts
236
+ new InMemoryCommandBus()
237
+ .use(loggingMiddleware(logger))
238
+ .use(metricsMiddleware(metrics));
239
+ ```
240
+
241
+ Same entries and series as `logged` and `timed` (prefix `use_case`), named after the message
242
+ class, never its content. There is no middleware opening an `ExecutionScope`: a command does not
243
+ carry a correlation id, and the scope must already be open when the route or the consumer runs,
244
+ before any bus. Open it at the boundary.
245
+
246
+ ## StdoutLogger
247
+
248
+ A small adapter of the kernel `Logger` port writing JSON lines:
249
+
250
+ ```ts
251
+ const logger = new StdoutLogger({
252
+ level: 'info',
253
+ base: { service: 'shop-api' },
254
+ fields: () => {
255
+ const context = scope.current();
256
+ return { ...traceLogFields(), ...(context === undefined ? {} : logFieldsOf(context)) };
257
+ },
258
+ });
259
+ // {"time":"2026-03-01T10:00:00.000Z","level":"info","message":"...","service":"shop-api","trace_id":"..."}
260
+ ```
261
+
262
+ `fields` is read at each entry: here the trace ids of the tracing package and, through
263
+ `logFieldsOf` of the kernel, the correlation id, actor and tenant of the current execution
264
+ context. This package depends on neither. When the same field comes from several places, the
265
+ later wins: `base`, then the fields of `child(...)`, then `fields()`, then the fields of the
266
+ log call. None can overwrite `time`, `level` or `message`.
267
+
268
+ It implements the whole port, held by `loggerContract` like the pino and winston adapters:
269
+
270
+ - **Errors.** An `Error` passed as a field (`logger.error('payment failed', { error })`) is
271
+ written as `describeError` of the kernel describes it: `type`, `code` for a `DomainError`,
272
+ `message`, `stack`, then `cause` the same way. `scrubMessages: true` writes
273
+ `[message removed]` for the message of every error that is not a `DomainError`: turn it on
274
+ when the logs leave your hands. `logged` and `loggingMiddleware` are not concerned: they
275
+ never pass an error, only `errorType` and `errorCode`.
276
+ - **`isLevelEnabled(level)`** answers the threshold (`level`, default `info`). `logged` and
277
+ `loggingMiddleware` ask it first: below the threshold no field is built and the `fields`
278
+ function of `logged` is not called.
279
+ - **`flush()`** resolves once the standard output took every line written so far, and never
280
+ rejects; the logger stays usable afterwards. `app.stop()` of the di package calls it last.
281
+ - **A log call never throws.** Fields that cannot be read or serialized (a getter that throws,
282
+ a circular object), on an entry or on a child, give the entry without them, marked
283
+ `logError: "unserializable fields"`.
284
+
285
+ It redacts no credential: `@ddd-ts-kit/pino` and `@ddd-ts-kit/winston` do.
286
+
287
+ ## What wrapping guarantees
288
+
289
+ - The instance is wrapped in place: same object, `instanceof`, `this` and `private` members
290
+ intact. A frozen instance is fronted by a proxy of the same type, and its methods run with
291
+ that proxy as `this`.
292
+ - **An aspect applied to an instance advises the calls that reach it from outside.** `private`
293
+ is erased by the compiler, and a name does not say whether a method is public (an underscore
294
+ is a habit, not a rule), so the public API cannot be picked out reliably: every asynchronous
295
+ method reachable by name, a `private async` method and a field holding an asynchronous
296
+ function included, is wrapped. What tells the calls apart is where they come from. While an
297
+ advised call is running on an instance, a call that instance makes on itself, to a public or
298
+ a `private` method, is the inside of that call and is not advised again by the same aspect:
299
+ `retry({ attempts: 3 })` around a public method that goes through a private step makes three
300
+ attempts, not nine, and `logged()` and `timed()` record one call. The guard is kept per
301
+ instance and per aspect, and follows the asynchronous flow of the call
302
+ (`AsyncLocalStorage`): two calls made from outside at the same time are both advised, and so
303
+ is whatever an advised call left behind (a timer, a listener) once that call is over.
304
+ - What follows from it: a call that comes back to the instance from a collaborator while the
305
+ advised call is running is the inside of that call too; `cached` serves the callers from
306
+ outside, not a method reading another through `this`; `bulkhead` and `circuitBreaker` count
307
+ one call. A method decorator is not concerned: it advises the one method it is put on, at
308
+ every call, the ones made through `this` included.
309
+ - Tracing differs on purpose: `traceDependency` of `@ddd-ts-kit/tracing` opens a child span for
310
+ a call an instance makes on itself. A span describes a step, where an advice acts on it.
311
+ - A frozen instance stays frozen: its fields, `private` ones included, cannot be assigned. State
312
+ it must change lives in an object it holds (a `Map`, an array).
313
+ - Applying the same aspect twice to an instance is a no-op.
314
+ - Getters, symbols, generators, synchronous methods and lifecycle hooks (`LIFECYCLE_HOOKS`) are
315
+ left alone.
316
+ - A method declared `async` is advised from its first call. A method that merely returns a
317
+ promise is advised from its second call: declare adapter methods `async`. A decorated method
318
+ is always advised.
319
+
320
+ ## Testing
321
+
322
+ Wrappers take their collaborators as options, so a spec passes fakes of the kernel. For
323
+ decorated classes and for aspects that read the runtime, `@ddd-ts-kit/aspects/testing` gives:
324
+
325
+ ```ts
326
+ import { createAspectFakes, withAspectRuntime } from '@ddd-ts-kit/aspects/testing';
327
+
328
+ const fakes = createAspectFakes(); // logger, metrics, cache, clock, scheduler
329
+ const gateway = new StripeGateway({ baseUrl: 'http://stripe.test', apiKey: 'test' });
330
+
331
+ const pending = withAspectRuntime(fakes, () => gateway.charge(payment));
332
+ fakes.scheduler.advance(2000); // time moves when the test says so
333
+
334
+ await expect(pending).rejects.toMatchObject({ code: 'TIMEOUT' });
335
+ expect(fakes.logger.entries).toEqual([...]);
336
+ ```
337
+
338
+ `withAspectRuntime` holds for the work and everything it awaits, and concurrent tests do not
339
+ see each other's runtime. With `createTestApplication` of the di package the application is the
340
+ runtime: `withAspectRuntime(app, () => ...)`, then assert `app.logger.entries` and `app.metrics`.
341
+
342
+ ## Decorator modes
343
+
344
+ The decorators of `./decorators` work under both decorator modes of TypeScript, with the same
345
+ source line.
346
+
347
+ | Stack | `tsconfig.json` |
348
+ |---|---|
349
+ | class-validator, NestJS, TypeORM entities, any library with legacy decorators | `"experimentalDecorators": true` |
350
+ | anything else | leave `experimentalDecorators` unset: standard (TC39) decorators need no flag |
351
+
352
+ - **How the mode is detected.** By the call: the standard one is `(method, context)` with a
353
+ context object, the legacy one `(prototype, name, descriptor)`. `decoratorOf` reads which it
354
+ received, so one function serves both and an aspect has one implementation.
355
+ - **What differs.** In the standard mode the method of each instance is wrapped when the
356
+ instance is constructed. The legacy mode has no initializer, so the method is replaced on
357
+ the prototype and the advice of each instance is built at its first call. State (a circuit,
358
+ a compartment, a single flight) is per instance and per method in both, several decorators
359
+ nest as they read in both, and neither needs `emitDecoratorMetadata`.
360
+ - **Compilers.** TypeScript 5, 6 and the native TypeScript 7 compile both modes. Node does not
361
+ run decorator syntax, so the code is compiled either way.
362
+ - **Vitest.** It lowers legacy decorators by itself: a project with `experimentalDecorators`
363
+ needs nothing. It does not lower standard ones (a spec importing a `.ts` file that uses
364
+ `@Retry` fails with a syntax error): add `decorators()` of `@ddd-ts-kit/testing/vitest` to
365
+ the Vitest config. Files wrapped at the composition root need nothing in either mode.
366
+ - **With a declaration.** `@Adapter(...)` on the class and `@Retry(...)` on its methods
367
+ compose, with the `aspects` of the declaration around the decorators of the methods:
368
+ `@Adapter(PaymentGateway, { kind: 'external' }, [PaymentSettings])`. A static method, a
369
+ method named by a symbol and a hard-private (`#`) method are refused.
370
+
371
+ The `./decorators` entry is separate: nothing else in the package imports it.
372
+
373
+ ## Limits
374
+
375
+ - State (circuits, compartments, single-flight) lives in the process, per wrapped instance. A
376
+ transient binding gets a new state with each instance.
377
+ - `retry` repeats the call as it is: only for operations that are safe to repeat.
378
+ - `timeout` without `signal` does not stop the underlying call.
379
+ - A wrapped call always answers a promise: do not wrap an object whose methods return a
380
+ chainable query builder.
381
+ - `when` selects methods of aspects made by `defineAspect`, `compose` or `when`; a plain
382
+ function used as an aspect filters by itself.
@@ -0,0 +1,9 @@
1
+ import type { Advice } from './advice.js';
2
+ import type { JoinPoint } from './join-point.js';
3
+ /**
4
+ * Builds the advice of one method of one instance, once, when the instance is wrapped: state
5
+ * kept in the closure is per instance and per method. Answering `undefined` leaves the method
6
+ * alone.
7
+ */
8
+ export type AdviceFactory = (joinPoint: JoinPoint) => Advice | undefined;
9
+ //# sourceMappingURL=advice-factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"advice-factory.d.ts","sourceRoot":"","sources":["../../src/aspect/advice-factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,SAAS,EAAE,SAAS,KAAK,MAAM,GAAG,SAAS,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=advice-factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"advice-factory.js","sourceRoot":"","sources":["../../src/aspect/advice-factory.ts"],"names":[],"mappings":""}
@@ -0,0 +1,3 @@
1
+ import type { Invocation } from './invocation.js';
2
+ export type Advice = (invocation: Invocation) => Promise<unknown>;
3
+ //# sourceMappingURL=advice.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"advice.d.ts","sourceRoot":"","sources":["../../src/aspect/advice.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,MAAM,MAAM,MAAM,GAAG,CAAC,UAAU,EAAE,UAAU,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=advice.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"advice.js","sourceRoot":"","sources":["../../src/aspect/advice.ts"],"names":[],"mappings":""}
@@ -0,0 +1,12 @@
1
+ import type { AspectKind } from './aspect-kind.js';
2
+ /** What the composition root knows about the instance it wraps. Everything is optional. */
3
+ export interface AspectInfo {
4
+ readonly kind?: AspectKind;
5
+ /** Defaults to the constructor name; set it when the build minifies class names. */
6
+ readonly className?: string;
7
+ /** Bounded context owning the instance. */
8
+ readonly context?: string;
9
+ /** Description of the port the instance is bound to. */
10
+ readonly token?: string;
11
+ }
12
+ //# sourceMappingURL=aspect-info.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aspect-info.d.ts","sourceRoot":"","sources":["../../src/aspect/aspect-info.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,2FAA2F;AAC3F,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC;IAC3B,oFAAoF;IACpF,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,2CAA2C;IAC3C,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,wDAAwD;IACxD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=aspect-info.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aspect-info.js","sourceRoot":"","sources":["../../src/aspect/aspect-info.ts"],"names":[],"mappings":""}
@@ -0,0 +1,4 @@
1
+ import type { DependencyKind } from '@ddd-ts-kit/kernel';
2
+ /** The roles a binding declares with `.as(kind)`: the one list of the kernel. */
3
+ export type AspectKind = DependencyKind;
4
+ //# sourceMappingURL=aspect-kind.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aspect-kind.d.ts","sourceRoot":"","sources":["../../src/aspect/aspect-kind.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzD,iFAAiF;AACjF,MAAM,MAAM,UAAU,GAAG,cAAc,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=aspect-kind.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aspect-kind.js","sourceRoot":"","sources":["../../src/aspect/aspect-kind.ts"],"names":[],"mappings":""}
@@ -0,0 +1,10 @@
1
+ import type { AdviceFactory } from './advice-factory.js';
2
+ import type { Aspect } from './aspect.js';
3
+ export type AspectOrigin = {
4
+ readonly advise: AdviceFactory;
5
+ } | {
6
+ readonly parts: readonly Aspect[];
7
+ };
8
+ /** How each aspect of this package was made, so that `when` can select the methods of one. */
9
+ export declare const aspectOrigins: WeakMap<Aspect, AspectOrigin>;
10
+ //# sourceMappingURL=aspect-origins.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aspect-origins.d.ts","sourceRoot":"","sources":["../../src/aspect/aspect-origins.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,MAAM,YAAY,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAC;AAEtG,8FAA8F;AAC9F,eAAO,MAAM,aAAa,+BAAsC,CAAC"}
@@ -0,0 +1,3 @@
1
+ /** How each aspect of this package was made, so that `when` can select the methods of one. */
2
+ export const aspectOrigins = new WeakMap();
3
+ //# sourceMappingURL=aspect-origins.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aspect-origins.js","sourceRoot":"","sources":["../../src/aspect/aspect-origins.ts"],"names":[],"mappings":"AAKA,8FAA8F;AAC9F,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,OAAO,EAAwB,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { AspectInfo } from './aspect-info.js';
2
+ /**
3
+ * Cross-cutting behaviour applied from outside: takes an instance, answers it with its
4
+ * asynchronous methods wrapped. The instance is the same object (a proxy stands in for a
5
+ * frozen one), so `this`, `instanceof` and its private members keep working, and applying the
6
+ * same aspect twice is a no-op.
7
+ */
8
+ export type Aspect = <T extends object>(instance: T, info?: AspectInfo) => T;
9
+ //# sourceMappingURL=aspect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aspect.d.ts","sourceRoot":"","sources":["../../src/aspect/aspect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD;;;;;GAKG;AACH,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,SAAS,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,UAAU,KAAK,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=aspect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aspect.js","sourceRoot":"","sources":["../../src/aspect/aspect.ts"],"names":[],"mappings":""}
@@ -0,0 +1,7 @@
1
+ import type { Aspect } from './aspect.js';
2
+ /**
3
+ * One aspect out of several, the first outermost: `compose(retry(...), timeout(2000))` bounds
4
+ * each attempt, the other order bounds them all together.
5
+ */
6
+ export declare function compose(...aspects: readonly Aspect[]): Aspect;
7
+ //# sourceMappingURL=compose.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compose.d.ts","sourceRoot":"","sources":["../../src/aspect/compose.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C;;;GAGG;AACH,wBAAgB,OAAO,CAAC,GAAG,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAO7D"}
@@ -0,0 +1,11 @@
1
+ import { aspectOrigins } from './aspect-origins.js';
2
+ /**
3
+ * One aspect out of several, the first outermost: `compose(retry(...), timeout(2000))` bounds
4
+ * each attempt, the other order bounds them all together.
5
+ */
6
+ export function compose(...aspects) {
7
+ const composed = (instance, info = {}) => aspects.reduceRight((wrapped, aspect) => aspect(wrapped, info), instance);
8
+ aspectOrigins.set(composed, { parts: aspects });
9
+ return composed;
10
+ }
11
+ //# sourceMappingURL=compose.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compose.js","sourceRoot":"","sources":["../../src/aspect/compose.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGpD;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,GAAG,OAA0B;IACnD,MAAM,QAAQ,GAAW,CAAC,QAAQ,EAAE,IAAI,GAAG,EAAE,EAAE,EAAE,CAC/C,OAAO,CAAC,WAAW,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,QAAQ,CAAC,CAAC;IAE5E,aAAa,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;IAEhD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { AdviceFactory } from './advice-factory.js';
2
+ import type { Aspect } from './aspect.js';
3
+ /** Makes an aspect out of an advice factory. Every aspect of this package is made this way. */
4
+ export declare function defineAspect(advise: AdviceFactory): Aspect;
5
+ //# sourceMappingURL=define-aspect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define-aspect.d.ts","sourceRoot":"","sources":["../../src/aspect/define-aspect.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,+FAA+F;AAC/F,wBAAgB,YAAY,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAkB1D"}
@@ -0,0 +1,17 @@
1
+ import { adviseMethods } from '../weaving/advise-methods.js';
2
+ import { aspectOrigins } from './aspect-origins.js';
3
+ /** Makes an aspect out of an advice factory. Every aspect of this package is made this way. */
4
+ export function defineAspect(advise) {
5
+ const applied = new WeakSet();
6
+ const aspect = (instance, info = {}) => {
7
+ if (applied.has(instance)) {
8
+ return instance;
9
+ }
10
+ const result = adviseMethods(instance, info, advise);
11
+ applied.add(result);
12
+ return result;
13
+ };
14
+ aspectOrigins.set(aspect, { advise });
15
+ return aspect;
16
+ }
17
+ //# sourceMappingURL=define-aspect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define-aspect.js","sourceRoot":"","sources":["../../src/aspect/define-aspect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAE7D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGpD,+FAA+F;AAC/F,MAAM,UAAU,YAAY,CAAC,MAAqB;IAChD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAU,CAAC;IAEtC,MAAM,MAAM,GAAW,CAAC,QAAQ,EAAE,IAAI,GAAG,EAAE,EAAE,EAAE;QAC7C,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC1B,OAAO,QAAQ,CAAC;QAClB,CAAC;QAED,MAAM,MAAM,GAAG,aAAa,CAAC,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QAErD,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAEpB,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;IAEF,aAAa,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IAEtC,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,6 @@
1
+ /** One call. `proceed` runs what is inside, with the same arguments unless others are given. */
2
+ export interface Invocation {
3
+ readonly args: readonly unknown[];
4
+ readonly proceed: (args?: readonly unknown[]) => Promise<unknown>;
5
+ }
6
+ //# sourceMappingURL=invocation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invocation.d.ts","sourceRoot":"","sources":["../../src/aspect/invocation.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,SAAS,OAAO,EAAE,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,EAAE,SAAS,OAAO,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACnE"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=invocation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invocation.js","sourceRoot":"","sources":["../../src/aspect/invocation.ts"],"names":[],"mappings":""}
@@ -0,0 +1,13 @@
1
+ import type { AspectKind } from './aspect-kind.js';
2
+ /** One method of one instance: what a selector tests and what an advice is built for. */
3
+ export interface JoinPoint {
4
+ readonly instance: object;
5
+ readonly className: string;
6
+ readonly method: string;
7
+ /** Number of declared parameters of the method. */
8
+ readonly arity: number;
9
+ readonly kind?: AspectKind;
10
+ readonly context?: string;
11
+ readonly token?: string;
12
+ }
13
+ //# sourceMappingURL=join-point.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"join-point.d.ts","sourceRoot":"","sources":["../../src/aspect/join-point.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,yFAAyF;AACzF,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,mDAAmD;IACnD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=join-point.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"join-point.js","sourceRoot":"","sources":["../../src/aspect/join-point.ts"],"names":[],"mappings":""}
@@ -0,0 +1,3 @@
1
+ import type { JoinPoint } from './join-point.js';
2
+ export type Selector = (joinPoint: JoinPoint) => boolean;
3
+ //# sourceMappingURL=selector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selector.d.ts","sourceRoot":"","sources":["../../src/aspect/selector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,MAAM,MAAM,QAAQ,GAAG,CAAC,SAAS,EAAE,SAAS,KAAK,OAAO,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=selector.js.map