@procedures/client 0.2.3 → 0.4.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 (369) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/README.md +77 -44
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/adapters/http/client/transport.d.ts.map +1 -1
  5. package/dist/adapters/http/client/transport.js +35 -3
  6. package/dist/adapters/http/client/transport.js.map +1 -1
  7. package/dist/adapters/http/client/types.d.ts +3 -2
  8. package/dist/adapters/http/client/types.d.ts.map +1 -1
  9. package/dist/adapters/http/server/transport.d.ts +2 -2
  10. package/dist/adapters/http/server/transport.d.ts.map +1 -1
  11. package/dist/adapters/http/server/transport.js +54 -22
  12. package/dist/adapters/http/server/transport.js.map +1 -1
  13. package/dist/adapters/http/shared/headers.d.ts +1 -1
  14. package/dist/adapters/http/shared/headers.d.ts.map +1 -1
  15. package/dist/adapters/http/shared/headers.js +7 -6
  16. package/dist/adapters/http/shared/headers.js.map +1 -1
  17. package/dist/adapters/local/client/transport.d.ts +3 -0
  18. package/dist/adapters/local/client/transport.d.ts.map +1 -1
  19. package/dist/adapters/local/client/transport.js +67 -6
  20. package/dist/adapters/local/client/transport.js.map +1 -1
  21. package/dist/adapters/local/client/types.d.ts +15 -0
  22. package/dist/adapters/local/client/types.d.ts.map +1 -1
  23. package/dist/adapters/local/client/types.js.map +1 -1
  24. package/dist/adapters/mock/client/transport.d.ts +3 -2
  25. package/dist/adapters/mock/client/transport.d.ts.map +1 -1
  26. package/dist/adapters/mock/client/transport.js +8 -5
  27. package/dist/adapters/mock/client/transport.js.map +1 -1
  28. package/dist/adapters/mock/client/types.d.ts +7 -6
  29. package/dist/adapters/mock/client/types.d.ts.map +1 -1
  30. package/dist/adapters/port/transport.d.ts +62 -0
  31. package/dist/adapters/port/transport.d.ts.map +1 -0
  32. package/dist/adapters/port/transport.js +94 -0
  33. package/dist/adapters/port/transport.js.map +1 -0
  34. package/dist/adapters/websocket/client/index.d.ts +1 -1
  35. package/dist/adapters/websocket/client/index.d.ts.map +1 -1
  36. package/dist/adapters/websocket/client/index.js.map +1 -1
  37. package/dist/adapters/websocket/client/node.d.ts +30 -0
  38. package/dist/adapters/websocket/client/node.d.ts.map +1 -0
  39. package/dist/adapters/websocket/client/node.js +32 -0
  40. package/dist/adapters/websocket/client/node.js.map +1 -0
  41. package/dist/adapters/websocket/client/transport.d.ts +47 -7
  42. package/dist/adapters/websocket/client/transport.d.ts.map +1 -1
  43. package/dist/adapters/websocket/client/transport.js +229 -75
  44. package/dist/adapters/websocket/client/transport.js.map +1 -1
  45. package/dist/adapters/websocket/client/types.d.ts +71 -21
  46. package/dist/adapters/websocket/client/types.d.ts.map +1 -1
  47. package/dist/adapters/websocket/client/types.js.map +1 -1
  48. package/dist/adapters/websocket/server/transport.d.ts +65 -24
  49. package/dist/adapters/websocket/server/transport.d.ts.map +1 -1
  50. package/dist/adapters/websocket/server/transport.js +192 -263
  51. package/dist/adapters/websocket/server/transport.js.map +1 -1
  52. package/dist/adapters/websocket/server/types.d.ts +20 -1
  53. package/dist/adapters/websocket/server/types.d.ts.map +1 -1
  54. package/dist/adapters/websocket/shared/rpc.d.ts +129 -0
  55. package/dist/adapters/websocket/shared/rpc.d.ts.map +1 -0
  56. package/dist/adapters/websocket/shared/rpc.js +425 -0
  57. package/dist/adapters/websocket/shared/rpc.js.map +1 -0
  58. package/dist/browser.d.ts +29 -7
  59. package/dist/browser.d.ts.map +1 -1
  60. package/dist/browser.js +26 -3
  61. package/dist/browser.js.map +1 -1
  62. package/dist/client/call-types.d.ts +10 -10
  63. package/dist/client/call-types.d.ts.map +1 -1
  64. package/dist/client/call-types.js +1 -6
  65. package/dist/client/call-types.js.map +1 -1
  66. package/dist/client/client.d.ts +6 -1
  67. package/dist/client/client.d.ts.map +1 -1
  68. package/dist/client/client.js +23 -9
  69. package/dist/client/client.js.map +1 -1
  70. package/dist/client/deadline.d.ts +38 -0
  71. package/dist/client/deadline.d.ts.map +1 -0
  72. package/dist/client/deadline.js +60 -0
  73. package/dist/client/deadline.js.map +1 -0
  74. package/dist/client/errors/factory.d.ts.map +1 -1
  75. package/dist/client/errors/factory.js +11 -7
  76. package/dist/client/errors/factory.js.map +1 -1
  77. package/dist/client/errors/http-errors.d.ts.map +1 -1
  78. package/dist/client/errors/http-errors.js +6 -0
  79. package/dist/client/errors/http-errors.js.map +1 -1
  80. package/dist/client/errors/index.d.ts +1 -1
  81. package/dist/client/errors/index.js +1 -1
  82. package/dist/client/errors/registry.d.ts +2 -1
  83. package/dist/client/errors/registry.d.ts.map +1 -1
  84. package/dist/client/errors/registry.js +72 -22
  85. package/dist/client/errors/registry.js.map +1 -1
  86. package/dist/client/index.d.ts +9 -2
  87. package/dist/client/index.d.ts.map +1 -1
  88. package/dist/client/index.js +8 -2
  89. package/dist/client/index.js.map +1 -1
  90. package/dist/client/middleware/cache.d.ts +19 -5
  91. package/dist/client/middleware/cache.d.ts.map +1 -1
  92. package/dist/client/middleware/cache.js +106 -18
  93. package/dist/client/middleware/cache.js.map +1 -1
  94. package/dist/client/middleware/circuit-breaker.d.ts +28 -10
  95. package/dist/client/middleware/circuit-breaker.d.ts.map +1 -1
  96. package/dist/client/middleware/circuit-breaker.js +35 -24
  97. package/dist/client/middleware/circuit-breaker.js.map +1 -1
  98. package/dist/client/middleware/contexts.d.ts +16 -10
  99. package/dist/client/middleware/contexts.d.ts.map +1 -1
  100. package/dist/client/middleware/contexts.js.map +1 -1
  101. package/dist/client/middleware/items.d.ts +31 -2
  102. package/dist/client/middleware/items.d.ts.map +1 -1
  103. package/dist/client/middleware/items.js +86 -6
  104. package/dist/client/middleware/items.js.map +1 -1
  105. package/dist/client/middleware/lru.d.ts +2 -0
  106. package/dist/client/middleware/lru.d.ts.map +1 -1
  107. package/dist/client/middleware/lru.js +11 -0
  108. package/dist/client/middleware/lru.js.map +1 -1
  109. package/dist/client/middleware/rate-limit.d.ts +7 -6
  110. package/dist/client/middleware/rate-limit.d.ts.map +1 -1
  111. package/dist/client/middleware/rate-limit.js +9 -7
  112. package/dist/client/middleware/rate-limit.js.map +1 -1
  113. package/dist/client/middleware/resilience.d.ts +61 -0
  114. package/dist/client/middleware/resilience.d.ts.map +1 -0
  115. package/dist/client/middleware/resilience.js +59 -0
  116. package/dist/client/middleware/resilience.js.map +1 -0
  117. package/dist/client/middleware/retry.d.ts +45 -11
  118. package/dist/client/middleware/retry.d.ts.map +1 -1
  119. package/dist/client/middleware/retry.js +102 -22
  120. package/dist/client/middleware/retry.js.map +1 -1
  121. package/dist/client/middleware/timeout.d.ts +15 -12
  122. package/dist/client/middleware/timeout.d.ts.map +1 -1
  123. package/dist/client/middleware/timeout.js +48 -46
  124. package/dist/client/middleware/timeout.js.map +1 -1
  125. package/dist/client/middleware/tracing.d.ts.map +1 -1
  126. package/dist/client/middleware/tracing.js +11 -3
  127. package/dist/client/middleware/tracing.js.map +1 -1
  128. package/dist/client/types.d.ts +9 -1
  129. package/dist/client/types.d.ts.map +1 -1
  130. package/dist/client/types.js.map +1 -1
  131. package/dist/clock.d.ts +19 -0
  132. package/dist/clock.d.ts.map +1 -0
  133. package/dist/clock.js +18 -0
  134. package/dist/clock.js.map +1 -0
  135. package/dist/duration.d.ts +37 -0
  136. package/dist/duration.d.ts.map +1 -0
  137. package/dist/duration.js +44 -0
  138. package/dist/duration.js.map +1 -0
  139. package/dist/index.d.ts +4 -0
  140. package/dist/index.d.ts.map +1 -1
  141. package/dist/index.js +4 -0
  142. package/dist/index.js.map +1 -1
  143. package/dist/machines.d.ts +90 -0
  144. package/dist/machines.d.ts.map +1 -0
  145. package/dist/machines.js +127 -0
  146. package/dist/machines.js.map +1 -0
  147. package/dist/node.d.ts +11 -0
  148. package/dist/node.d.ts.map +1 -0
  149. package/dist/node.js +10 -0
  150. package/dist/node.js.map +1 -0
  151. package/dist/procedures/capability.d.ts +3 -6
  152. package/dist/procedures/capability.d.ts.map +1 -1
  153. package/dist/procedures/capability.js +7 -8
  154. package/dist/procedures/capability.js.map +1 -1
  155. package/dist/procedures/collection/index.d.ts +3 -3
  156. package/dist/procedures/collection/index.d.ts.map +1 -1
  157. package/dist/procedures/collection/index.js +1 -1
  158. package/dist/procedures/collection/index.js.map +1 -1
  159. package/dist/procedures/collection/procedures.d.ts.map +1 -1
  160. package/dist/procedures/collection/procedures.js +49 -1
  161. package/dist/procedures/collection/procedures.js.map +1 -1
  162. package/dist/procedures/collection/schemas.d.ts +33 -0
  163. package/dist/procedures/collection/schemas.d.ts.map +1 -1
  164. package/dist/procedures/collection/schemas.js +68 -0
  165. package/dist/procedures/collection/schemas.js.map +1 -1
  166. package/dist/procedures/collection/storage.d.ts +37 -0
  167. package/dist/procedures/collection/storage.d.ts.map +1 -1
  168. package/dist/procedures/core/array.d.ts +1 -1
  169. package/dist/procedures/core/array.d.ts.map +1 -1
  170. package/dist/procedures/core/array.js +81 -29
  171. package/dist/procedures/core/array.js.map +1 -1
  172. package/dist/procedures/core/index.d.ts.map +1 -1
  173. package/dist/procedures/core/index.js +17 -2
  174. package/dist/procedures/core/index.js.map +1 -1
  175. package/dist/procedures/core/logic.js +1 -1
  176. package/dist/procedures/core/logic.js.map +1 -1
  177. package/dist/procedures/core/math.d.ts.map +1 -1
  178. package/dist/procedures/core/math.js +5 -1
  179. package/dist/procedures/core/math.js.map +1 -1
  180. package/dist/procedures/core/meta.d.ts.map +1 -1
  181. package/dist/procedures/core/meta.js +43 -8
  182. package/dist/procedures/core/meta.js.map +1 -1
  183. package/dist/procedures/core/object.d.ts.map +1 -1
  184. package/dist/procedures/core/object.js +42 -13
  185. package/dist/procedures/core/object.js.map +1 -1
  186. package/dist/procedures/core/operands.d.ts.map +1 -1
  187. package/dist/procedures/core/operands.js +6 -1
  188. package/dist/procedures/core/operands.js.map +1 -1
  189. package/dist/procedures/core/string.d.ts.map +1 -1
  190. package/dist/procedures/core/string.js +4 -2
  191. package/dist/procedures/core/string.js.map +1 -1
  192. package/dist/procedures/core/type.d.ts.map +1 -1
  193. package/dist/procedures/core/type.js +4 -2
  194. package/dist/procedures/core/type.js.map +1 -1
  195. package/dist/procedures/define-procedure.d.ts +19 -7
  196. package/dist/procedures/define-procedure.d.ts.map +1 -1
  197. package/dist/procedures/define-procedure.js +57 -17
  198. package/dist/procedures/define-procedure.js.map +1 -1
  199. package/dist/procedures/discovery.d.ts +4 -6
  200. package/dist/procedures/discovery.d.ts.map +1 -1
  201. package/dist/procedures/discovery.js +4 -6
  202. package/dist/procedures/discovery.js.map +1 -1
  203. package/dist/procedures/effects.d.ts +16 -0
  204. package/dist/procedures/effects.d.ts.map +1 -1
  205. package/dist/procedures/effects.js +33 -0
  206. package/dist/procedures/effects.js.map +1 -1
  207. package/dist/procedures/idempotency.d.ts +73 -0
  208. package/dist/procedures/idempotency.d.ts.map +1 -0
  209. package/dist/procedures/idempotency.js +131 -0
  210. package/dist/procedures/idempotency.js.map +1 -0
  211. package/dist/procedures/index.d.ts +7 -3
  212. package/dist/procedures/index.d.ts.map +1 -1
  213. package/dist/procedures/index.js +6 -2
  214. package/dist/procedures/index.js.map +1 -1
  215. package/dist/procedures/interceptors.d.ts +17 -4
  216. package/dist/procedures/interceptors.d.ts.map +1 -1
  217. package/dist/procedures/interceptors.js +18 -7
  218. package/dist/procedures/interceptors.js.map +1 -1
  219. package/dist/procedures/invoke.d.ts +15 -3
  220. package/dist/procedures/invoke.d.ts.map +1 -1
  221. package/dist/procedures/invoke.js +8 -3
  222. package/dist/procedures/invoke.js.map +1 -1
  223. package/dist/procedures/manifest.d.ts +83 -0
  224. package/dist/procedures/manifest.d.ts.map +1 -0
  225. package/dist/procedures/manifest.js +90 -0
  226. package/dist/procedures/manifest.js.map +1 -0
  227. package/dist/procedures/ref.d.ts +4 -2
  228. package/dist/procedures/ref.d.ts.map +1 -1
  229. package/dist/procedures/ref.js +2 -1
  230. package/dist/procedures/ref.js.map +1 -1
  231. package/dist/procedures/runtime-programs.d.ts +19 -0
  232. package/dist/procedures/runtime-programs.d.ts.map +1 -0
  233. package/dist/procedures/runtime-programs.js +24 -0
  234. package/dist/procedures/runtime-programs.js.map +1 -0
  235. package/dist/procedures/types.d.ts +16 -0
  236. package/dist/procedures/types.d.ts.map +1 -1
  237. package/dist/procedures/types.js.map +1 -1
  238. package/dist/protocol.d.ts +47 -0
  239. package/dist/protocol.d.ts.map +1 -0
  240. package/dist/protocol.js +68 -0
  241. package/dist/protocol.js.map +1 -0
  242. package/dist/server/host-policy.d.ts +4 -3
  243. package/dist/server/host-policy.d.ts.map +1 -1
  244. package/dist/server/host-policy.js +6 -3
  245. package/dist/server/host-policy.js.map +1 -1
  246. package/dist/server/host-procedures.d.ts +31 -0
  247. package/dist/server/host-procedures.d.ts.map +1 -0
  248. package/dist/server/host-procedures.js +75 -0
  249. package/dist/server/host-procedures.js.map +1 -0
  250. package/dist/server/index.d.ts +3 -3
  251. package/dist/server/index.d.ts.map +1 -1
  252. package/dist/server/index.js +1 -1
  253. package/dist/server/index.js.map +1 -1
  254. package/dist/server/procedure-server.d.ts +112 -5
  255. package/dist/server/procedure-server.d.ts.map +1 -1
  256. package/dist/server/procedure-server.js +273 -19
  257. package/dist/server/procedure-server.js.map +1 -1
  258. package/dist/server/request-auth.d.ts +19 -0
  259. package/dist/server/request-auth.d.ts.map +1 -0
  260. package/dist/server/request-auth.js +32 -0
  261. package/dist/server/request-auth.js.map +1 -0
  262. package/dist/server/types.d.ts +32 -0
  263. package/dist/server/types.d.ts.map +1 -1
  264. package/dist/server/types.js.map +1 -1
  265. package/dist/timers.d.ts +60 -0
  266. package/dist/timers.d.ts.map +1 -0
  267. package/dist/timers.js +33 -0
  268. package/dist/timers.js.map +1 -0
  269. package/dist/tracing/traceparent.d.ts +26 -0
  270. package/dist/tracing/traceparent.d.ts.map +1 -0
  271. package/dist/tracing/traceparent.js +53 -0
  272. package/dist/tracing/traceparent.js.map +1 -0
  273. package/package.json +11 -4
  274. package/src/adapters/http/client/transport.ts +31 -3
  275. package/src/adapters/http/client/types.ts +3 -2
  276. package/src/adapters/http/server/transport.ts +61 -26
  277. package/src/adapters/http/shared/headers.ts +7 -6
  278. package/src/adapters/local/client/transport.ts +74 -7
  279. package/src/adapters/local/client/types.ts +18 -0
  280. package/src/adapters/mock/client/transport.ts +9 -6
  281. package/src/adapters/mock/client/types.ts +7 -6
  282. package/src/adapters/port/transport.ts +124 -0
  283. package/src/adapters/websocket/client/index.ts +1 -0
  284. package/src/adapters/websocket/client/node.ts +34 -0
  285. package/src/adapters/websocket/client/transport.ts +251 -81
  286. package/src/adapters/websocket/client/types.ts +81 -21
  287. package/src/adapters/websocket/server/transport.ts +199 -301
  288. package/src/adapters/websocket/server/types.ts +20 -1
  289. package/src/adapters/websocket/shared/rpc.ts +453 -0
  290. package/src/browser.ts +51 -3
  291. package/src/client/call-types.ts +10 -14
  292. package/src/client/client.ts +28 -9
  293. package/src/client/deadline.ts +64 -0
  294. package/src/client/errors/README.md +2 -0
  295. package/src/client/errors/factory.ts +12 -8
  296. package/src/client/errors/http-errors.ts +5 -0
  297. package/src/client/errors/index.ts +1 -1
  298. package/src/client/errors/registry.ts +79 -22
  299. package/src/client/index.ts +21 -3
  300. package/src/client/middleware/cache.ts +120 -23
  301. package/src/client/middleware/circuit-breaker.ts +65 -35
  302. package/src/client/middleware/contexts.ts +17 -10
  303. package/src/client/middleware/items.ts +101 -6
  304. package/src/client/middleware/lru.ts +12 -0
  305. package/src/client/middleware/rate-limit.ts +9 -9
  306. package/src/client/middleware/resilience.ts +81 -0
  307. package/src/client/middleware/retry.ts +146 -34
  308. package/src/client/middleware/timeout.ts +57 -55
  309. package/src/client/middleware/tracing.ts +13 -4
  310. package/src/client/types.ts +9 -1
  311. package/src/clock.ts +21 -0
  312. package/src/duration.ts +56 -0
  313. package/src/index.ts +4 -0
  314. package/src/machines.ts +186 -0
  315. package/src/node.ts +11 -0
  316. package/src/procedures/capability.ts +7 -8
  317. package/src/procedures/collection/index.ts +9 -1
  318. package/src/procedures/collection/procedures.ts +57 -0
  319. package/src/procedures/collection/schemas.ts +110 -0
  320. package/src/procedures/collection/storage.ts +38 -0
  321. package/src/procedures/core/array.ts +79 -31
  322. package/src/procedures/core/index.ts +17 -2
  323. package/src/procedures/core/logic.ts +1 -1
  324. package/src/procedures/core/math.ts +5 -1
  325. package/src/procedures/core/meta.ts +45 -8
  326. package/src/procedures/core/object.ts +48 -14
  327. package/src/procedures/core/operands.ts +7 -0
  328. package/src/procedures/core/string.ts +4 -2
  329. package/src/procedures/core/type.ts +4 -2
  330. package/src/procedures/define-procedure.ts +73 -20
  331. package/src/procedures/discovery.ts +4 -6
  332. package/src/procedures/effects.ts +35 -0
  333. package/src/procedures/idempotency.ts +152 -0
  334. package/src/procedures/index.ts +18 -2
  335. package/src/procedures/interceptors.ts +28 -11
  336. package/src/procedures/invoke.ts +19 -5
  337. package/src/procedures/manifest.ts +158 -0
  338. package/src/procedures/ref.ts +4 -2
  339. package/src/procedures/runtime-programs.ts +27 -0
  340. package/src/procedures/types.ts +16 -0
  341. package/src/protocol.ts +108 -0
  342. package/src/server/host-policy.ts +8 -5
  343. package/src/server/host-procedures.ts +102 -0
  344. package/src/server/index.ts +5 -2
  345. package/src/server/procedure-server.ts +328 -12
  346. package/src/server/request-auth.ts +35 -0
  347. package/src/server/types.ts +37 -0
  348. package/src/timers.ts +82 -0
  349. package/src/tracing/traceparent.ts +61 -0
  350. package/dist/procedures/cli/announce.d.ts +0 -67
  351. package/dist/procedures/cli/announce.d.ts.map +0 -1
  352. package/dist/procedures/cli/announce.js +0 -392
  353. package/dist/procedures/cli/announce.js.map +0 -1
  354. package/dist/procedures/cli/discover.d.ts +0 -66
  355. package/dist/procedures/cli/discover.d.ts.map +0 -1
  356. package/dist/procedures/cli/discover.js +0 -310
  357. package/dist/procedures/cli/discover.js.map +0 -1
  358. package/dist/procedures/cli/index.d.ts +0 -13
  359. package/dist/procedures/cli/index.d.ts.map +0 -1
  360. package/dist/procedures/cli/index.js +0 -203
  361. package/dist/procedures/cli/index.js.map +0 -1
  362. package/dist/procedures/cli/init.d.ts +0 -17
  363. package/dist/procedures/cli/init.d.ts.map +0 -1
  364. package/dist/procedures/cli/init.js +0 -336
  365. package/dist/procedures/cli/init.js.map +0 -1
  366. package/src/procedures/cli/announce.ts +0 -470
  367. package/src/procedures/cli/discover.ts +0 -390
  368. package/src/procedures/cli/index.ts +0 -231
  369. package/src/procedures/cli/init.ts +0 -370
@@ -4,8 +4,10 @@
4
4
  * Protocol-agnostic retry with exponential backoff and jitter.
5
5
  * Works with any transport!
6
6
  */
7
- import type { ResponseItem, TypedClientMiddleware } from "../types.js";
7
+ import type { Method, ResponseItem, TypedClientMiddleware } from "../types.js";
8
8
  import type { RetryContext } from "./contexts.js";
9
+ import { type Clock } from "../../clock.js";
10
+ import { type Duration } from "../../duration.js";
9
11
  /**
10
12
  * Retry middleware options.
11
13
  */
@@ -16,30 +18,62 @@ export interface RetryOptions {
16
18
  */
17
19
  maxRetries?: number;
18
20
  /**
19
- * Base delay in milliseconds for exponential backoff.
21
+ * The clock of the backoff and of the budget (roadmap R35). The default is the clock of the
22
+ * platform. A test gives a fake clock.
23
+ */
24
+ clock?: Clock;
25
+ /**
26
+ * Base delay for exponential backoff, for example `ms(500)`.
20
27
  * Actual delay will be: baseDelay * 2^attempt + jitter
21
- * @default 1000
28
+ * @default 1 second
22
29
  */
23
- retryDelay?: number;
30
+ retryDelay?: Duration;
24
31
  /**
25
32
  * The factor of the delay from one attempt to the next.
26
33
  * @default 2
27
34
  */
28
35
  backoffMultiplier?: number;
29
36
  /**
30
- * The longest delay between two attempts, in milliseconds.
37
+ * The longest delay between two attempts.
31
38
  * @default no limit
32
39
  */
33
- maxDelay?: number;
40
+ maxDelay?: Duration;
34
41
  /**
35
- * Maximum jitter as a fraction of the delay (0-1).
36
- * Adds randomness to prevent thundering herd.
37
- * @default 0.1 (10% jitter)
42
+ * The random part of each delay, from 0 to 1. With 1 (full jitter), the delay is a random time
43
+ * from 0 to the backoff, so the clients that failed together do not retry together. 0 gives the
44
+ * backoff exactly.
45
+ * @default 1 (full jitter; roadmap R12: before, 0.1 gave a delay within 10 percent of the backoff)
38
46
  */
39
47
  jitter?: number;
48
+ /**
49
+ * The retry budget of this middleware: each call adds `ratio` of a retry to the budget, and each
50
+ * retry takes one. When the budget is empty, an error goes to the caller without a retry, so a
51
+ * failing service gets at most `ratio` more requests, not `maxRetries` times more. Besides the
52
+ * budget, `minPerSecond` retries a second are always allowed. `false` turns the budget off.
53
+ * @default { ratio: 0.2, minPerSecond: 10 }
54
+ */
55
+ budget?: {
56
+ ratio?: number;
57
+ minPerSecond?: number;
58
+ } | false;
59
+ /**
60
+ * The calls that the middleware can send again after a try with an indeterminate outcome: the
61
+ * request left, and no answer came (a reset, a closed connection, the timeout of one try). The
62
+ * server can have run such a call, so only an idempotent call goes again (roadmap R1).
63
+ *
64
+ * - a function: true for an idempotent method. `idempotentIn(registry)` reads the effects of
65
+ * the procedures that the client knows.
66
+ * - `true`: every call. Use it only with a server that removes duplicate calls by their
67
+ * idempotency key (`ProcedureServer` does).
68
+ * - absent: only a call with `retry: { idempotent: true }` in its context.
69
+ *
70
+ * A try that did not leave the client (a refused connection) and an error that the server
71
+ * marks retryable go again in each case.
72
+ */
73
+ idempotent?: boolean | ((method: Method) => boolean | undefined);
40
74
  /**
41
75
  * Custom function to determine if a request should be retried.
42
- * Takes precedence over Status.retryable flag.
76
+ * Takes precedence over Status.retryable flag. The rule of `idempotent` still applies.
43
77
  *
44
78
  * @param item - The response item
45
79
  * @param attempt - Current attempt number (0-indexed)
@@ -88,7 +122,7 @@ export interface RetryOptions {
88
122
  * @example
89
123
  * ```typescript
90
124
  * // Create middleware with defaults
91
- * client.use(createRetryMiddleware({ maxRetries: 3 }));
125
+ * client.use(createRetryMiddleware({ maxRetries: 3, retryDelay: ms(500) }));
92
126
  *
93
127
  * // Override per-call via context
94
128
  * await client.call(method, payload, {
@@ -1 +1 @@
1
- {"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../../../src/client/middleware/retry.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAA+B,YAAY,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACpG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAGlD;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;IAExE;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,CACd,IAAI,EAAE,YAAY,CAAC,OAAO,CAAC,EAC3B,OAAO,EAAE,MAAM,KACZ,OAAO,CAAC;QAAE,WAAW,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG;QAAE,WAAW,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAEtG;;;;;OAKG;IACH,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAC5E;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,YAAiB,GAAG,qBAAqB,CAAC,YAAY,EAAE,EAAE,CAAC,CAiIzG"}
1
+ {"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../../../src/client/middleware/retry.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAA+B,MAAM,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAC5G,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,EAAa,KAAK,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAGvD,OAAO,EAA0B,KAAK,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAG1E;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC;IAEd;;;;OAIG;IACH,UAAU,CAAC,EAAE,QAAQ,CAAC;IAEtB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;;OAGG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC;IAEpB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;;;;OAMG;IACH,MAAM,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,KAAK,CAAC;IAE3D;;;;;;;;;;;;;OAaG;IACH,UAAU,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,GAAG,SAAS,CAAC,CAAC;IAEjE;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;IAExE;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,CACd,IAAI,EAAE,YAAY,CAAC,OAAO,CAAC,EAC3B,OAAO,EAAE,MAAM,KACZ,OAAO,CAAC;QAAE,WAAW,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG;QAAE,WAAW,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAEtG;;;;;OAKG;IACH,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAC5E;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,YAAiB,GAAG,qBAAqB,CAAC,YAAY,EAAE,EAAE,CAAC,CA4MzG"}
@@ -4,7 +4,12 @@
4
4
  * Protocol-agnostic retry with exponential backoff and jitter.
5
5
  * Works with any transport!
6
6
  */
7
- import { abortedItem, sleep, thrownItem } from "./items.js";
7
+ import { abortedItem, deliveryOf, retryAfterOf, sleep, thrownItem } from "./items.js";
8
+ import { realClock } from "../../clock.js";
9
+ import { backoffDelay } from "@mark1russell7/service/backoff";
10
+ import { IDEMPOTENCY_KEY_METADATA, idempotencyKeyOf, newIdempotencyKey } from "../../procedures/idempotency.js";
11
+ import { isDuration, optionalMs } from "../../duration.js";
12
+ import { TIMER_DEFAULTS } from "../../timers.js";
8
13
  /**
9
14
  * Create retry middleware with exponential backoff and jitter.
10
15
  *
@@ -24,7 +29,7 @@ import { abortedItem, sleep, thrownItem } from "./items.js";
24
29
  * @example
25
30
  * ```typescript
26
31
  * // Create middleware with defaults
27
- * client.use(createRetryMiddleware({ maxRetries: 3 }));
32
+ * client.use(createRetryMiddleware({ maxRetries: 3, retryDelay: ms(500) }));
28
33
  *
29
34
  * // Override per-call via context
30
35
  * await client.call(method, payload, {
@@ -33,45 +38,112 @@ import { abortedItem, sleep, thrownItem } from "./items.js";
33
38
  * ```
34
39
  */
35
40
  export function createRetryMiddleware(options = {}) {
36
- const { retryDelay: defaultRetryDelay = 1000, jitter: defaultJitter = 0.1, shouldRetry: customShouldRetry, onBeforeRetry, onAfterRetry, } = options;
37
- const defaultMaxRetries = options.maxRetries ?? 3;
38
- const defaultMultiplier = options.backoffMultiplier ?? 2;
39
- const defaultMaxDelay = options.maxDelay ?? Number.POSITIVE_INFINITY;
40
- /** The overrides of one call (`retry: { ... }` in its context). Before, only maxAttempts was read. */
41
+ const defaultRetryDelay = optionalMs(options.retryDelay, "retryDelay") ?? TIMER_DEFAULTS.retry.retryDelayMs;
42
+ const { jitter: defaultJitter = 1, shouldRetry: customShouldRetry, onBeforeRetry, onAfterRetry, idempotent: idempotentOption, } = options;
43
+ const defaultMaxRetries = options.maxRetries ?? TIMER_DEFAULTS.retry.maxRetries;
44
+ const defaultMultiplier = options.backoffMultiplier ?? TIMER_DEFAULTS.retry.backoffMultiplier;
45
+ const defaultMaxDelay = optionalMs(options.maxDelay, "maxDelay") ?? TIMER_DEFAULTS.retry.maxDelayMs;
46
+ /**
47
+ * The overrides of one call (`retry: { ... }` in its context), with the delays in milliseconds.
48
+ * Before, only maxAttempts was read.
49
+ */
41
50
  const overridesOf = (value) => {
42
51
  if (typeof value !== "object" || value === null)
43
52
  return {};
44
53
  const out = {};
45
- for (const key of ["maxAttempts", "delay", "backoffMultiplier", "maxDelay"]) {
54
+ for (const key of ["maxAttempts", "backoffMultiplier"]) {
46
55
  const field = value[key];
47
56
  if (typeof field === "number" && Number.isFinite(field) && field >= 0)
48
57
  out[key] = field;
49
58
  }
59
+ for (const key of ["delay", "maxDelay"]) {
60
+ const field = value[key];
61
+ if (field !== undefined) {
62
+ if (!isDuration(field))
63
+ throw new TypeError(`retry.${key} must be a duration such as ms(500) (roadmap R11)`);
64
+ out[key] = optionalMs(field, `retry.${key}`);
65
+ }
66
+ }
50
67
  return out;
51
68
  };
52
- /** The backoff delay of an attempt: exponential, with jitter, at most the maximum delay */
53
- const backoffDelay = (attempt, overrides) => {
54
- const baseDelay = (overrides.delay ?? defaultRetryDelay) * Math.pow(overrides.backoffMultiplier ?? defaultMultiplier, attempt);
55
- const jitterAmount = defaultJitter * baseDelay * (Math.random() * 2 - 1);
56
- return Math.max(0, Math.min(overrides.maxDelay ?? defaultMaxDelay, baseDelay + jitterAmount));
69
+ /**
70
+ * The backoff delay of an attempt (from 0): exponential, at most the maximum delay, with the
71
+ * random part of `jitter` (full jitter by default). The backoff is `backoffDelay` of
72
+ * `@mark1russell7/service/backoff` (roadmap R37), whose tries start at 1.
73
+ */
74
+ const retryDelayOf = (attempt, overrides) => backoffDelay({
75
+ base: overrides.delay ?? defaultRetryDelay,
76
+ factor: overrides.backoffMultiplier ?? defaultMultiplier,
77
+ cap: overrides.maxDelay ?? defaultMaxDelay,
78
+ jitter: { kind: "subtract", fraction: Math.min(1, Math.max(0, defaultJitter)) },
79
+ }, attempt + 1);
80
+ // The retry budget (R12): a balance of retries that each call fills by `ratio`
81
+ const budget = options.budget === false ? undefined : { ratio: options.budget?.ratio ?? 0.2, minPerSecond: options.budget?.minPerSecond ?? 10 };
82
+ const budgetCap = budget ? Math.max(1, budget.ratio * 100) : 0;
83
+ let balance = budgetCap;
84
+ const clock = options.clock ?? realClock;
85
+ let secondStart = clock.now();
86
+ let retriesThisSecond = 0;
87
+ /** This function takes one retry from the budget, or gives false when the budget is empty. */
88
+ const takeRetry = () => {
89
+ if (!budget)
90
+ return true;
91
+ if (balance >= 1) {
92
+ balance -= 1;
93
+ return true;
94
+ }
95
+ const now = clock.now();
96
+ if (now - secondStart >= 1000) {
97
+ secondStart = now;
98
+ retriesThisSecond = 0;
99
+ }
100
+ if (retriesThisSecond < budget.minPerSecond) {
101
+ retriesThisSecond++;
102
+ return true;
103
+ }
104
+ return false;
105
+ };
106
+ /** True when the call can go again after an indeterminate try: the call says so, or the option. */
107
+ const isIdempotent = (method, value) => {
108
+ const own = typeof value === "object" && value !== null ? value.idempotent : undefined;
109
+ if (typeof own === "boolean")
110
+ return own;
111
+ if (typeof idempotentOption === "function")
112
+ return idempotentOption(method) === true;
113
+ return idempotentOption === true;
57
114
  };
58
115
  return (next) => {
59
116
  return async function* (context) {
60
117
  // Read from metadata first (user-provided context), then fall back to options
61
- const overrides = overridesOf(context.message.metadata.retry);
62
- const maxRetries = overrides.maxAttempts ?? defaultMaxRetries;
63
- const shouldRetryItem = (item, attempt) => customShouldRetry
64
- ? customShouldRetry(item, attempt)
65
- : item.status.type === "error" && item.status.retryable;
118
+ const given = context.message.metadata.retry;
119
+ const overrides = overridesOf(given);
120
+ // maxAttempts counts the attempts: the first try and the retries (R12: before, the override
121
+ // was used as the number of retries, against its name and its documentation)
122
+ const maxRetries = overrides.maxAttempts !== undefined ? Math.max(0, overrides.maxAttempts - 1) : defaultMaxRetries;
123
+ if (budget)
124
+ balance = Math.min(budgetCap, balance + budget.ratio);
125
+ const idempotent = isIdempotent(context.message.method, context.message.metadata.retry);
126
+ const shouldRetryItem = (item, attempt) => {
127
+ const wanted = customShouldRetry
128
+ ? customShouldRetry(item, attempt)
129
+ : item.status.type === "error" && item.status.retryable;
130
+ // A request that can have run goes again only when the call is idempotent (R1). Before,
131
+ // a reset or a timeout of one try sent a write again.
132
+ return wanted && (deliveryOf(item) !== "indeterminate" || idempotent);
133
+ };
66
134
  const signal = context.message.signal;
67
135
  const baseId = context.message.id;
136
+ // One key for all the tries of the call: a server that removes duplicates runs it one time
137
+ const idempotencyKey = idempotencyKeyOf(context.message.metadata) ?? newIdempotencyKey();
138
+ context.message.metadata[IDEMPOTENCY_KEY_METADATA] = idempotencyKey;
68
139
  let attempt = 0;
69
140
  for (;;) {
70
141
  // Update retry metadata with current state
71
142
  context.message.metadata.retry = {
72
- ...overrides,
143
+ ...(typeof given === "object" && given !== null ? given : {}),
73
144
  attempt,
74
- maxAttempts: maxRetries,
145
+ maxAttempts: maxRetries + 1,
146
+ idempotent,
75
147
  };
76
148
  // Each attempt gets its own request id. The id correlates the frames of one attempt
77
149
  // on the wire: a cancelled attempt can still answer under its id (deep dive TRN-8/9).
@@ -120,7 +192,15 @@ export function createRetryMiddleware(options = {}) {
120
192
  }
121
193
  return;
122
194
  }
123
- let delay = backoffDelay(attempt, overrides);
195
+ // The budget is empty: the error goes to the caller (R12)
196
+ if (!takeRetry()) {
197
+ yield retryItem;
198
+ if (onAfterRetry)
199
+ await onAfterRetry(false, attempt);
200
+ return;
201
+ }
202
+ // A server that asks for a wait (Retry-After) gets at least that wait (R12)
203
+ let delay = Math.max(retryDelayOf(attempt, overrides), retryAfterOf(retryItem) ?? 0);
124
204
  // Before retry hook
125
205
  if (onBeforeRetry) {
126
206
  const hookResult = await onBeforeRetry(retryItem, attempt);
@@ -138,7 +218,7 @@ export function createRetryMiddleware(options = {}) {
138
218
  }
139
219
  }
140
220
  // The backoff stops at once when the caller aborts (deep dive TRN-8)
141
- if (!(await sleep(delay, signal))) {
221
+ if (!(await sleep(delay, signal, clock))) {
142
222
  yield abortedItem(context.message.id);
143
223
  return;
144
224
  }
@@ -1 +1 @@
1
- {"version":3,"file":"retry.js","sourceRoot":"","sources":["../../../src/client/middleware/retry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"retry.js","sourceRoot":"","sources":["../../../src/client/middleware/retry.ts"],"names":[],"mappings":"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"}
@@ -6,21 +6,22 @@
6
6
  */
7
7
  import type { TypedClientMiddleware } from "../types.js";
8
8
  import type { TimeoutContext } from "./contexts.js";
9
+ import { type Duration } from "../../duration.js";
9
10
  /**
10
11
  * Timeout middleware options.
11
12
  */
12
13
  export interface TimeoutOptions {
13
14
  /**
14
- * Timeout in milliseconds for the entire request (including retries).
15
+ * The time of the entire request (including retries), for example `seconds(10)`.
15
16
  * @default undefined (no overall timeout)
16
17
  */
17
- overall?: number;
18
+ overall?: Duration | undefined;
18
19
  /**
19
- * Timeout in milliseconds for each individual attempt.
20
+ * The time of each individual attempt, for example `seconds(2)`.
20
21
  * Useful with retry middleware for per-attempt timeouts.
21
22
  * @default undefined (no per-attempt timeout)
22
23
  */
23
- perAttempt?: number;
24
+ perAttempt?: Duration | undefined;
24
25
  /**
25
26
  * Custom timeout error message.
26
27
  * @default "Request timeout"
@@ -41,11 +42,11 @@ export interface TimeoutOptions {
41
42
  * @example
42
43
  * ```typescript
43
44
  * // Create middleware with default timeout
44
- * client.use(createOverallTimeoutMiddleware({ overall: 5000 }));
45
+ * client.use(createOverallTimeoutMiddleware({ overall: seconds(5) }));
45
46
  *
46
47
  * // Override per-call via context
47
48
  * await client.call(method, payload, {
48
- * context: { timeout: { overall: 10000 } }
49
+ * context: { timeout: { overall: seconds(10) } }
49
50
  * });
50
51
  * ```
51
52
  */
@@ -64,11 +65,11 @@ export declare function createOverallTimeoutMiddleware(options: Pick<TimeoutOpti
64
65
  * @example
65
66
  * ```typescript
66
67
  * // Create middleware with default timeout
67
- * client.use(createTimeoutMiddleware({ perAttempt: 1000 }));
68
+ * client.use(createTimeoutMiddleware({ perAttempt: seconds(1) }));
68
69
  *
69
70
  * // Override per-call via context
70
71
  * await client.call(method, payload, {
71
- * context: { timeout: { perAttempt: 2000 } }
72
+ * context: { timeout: { perAttempt: seconds(2) } }
72
73
  * });
73
74
  * ```
74
75
  */
@@ -76,15 +77,17 @@ export declare function createTimeoutMiddleware(options: Pick<TimeoutOptions, "p
76
77
  /**
77
78
  * Create combined timeout middleware with both overall and per-attempt timeouts.
78
79
  *
80
+ * @deprecated Use `resilience({ overall, perAttempt, retry })`. This function puts the two
81
+ * timeouts in one slot, so a retry middleware that you add with another `use()` cannot sit
82
+ * between them: the retry is outside both, or inside both. `resilience` puts the retry between
83
+ * them (roadmap R10).
84
+ *
79
85
  * @param options - Timeout configuration
80
86
  * @returns Middleware function
81
87
  *
82
88
  * @example
83
89
  * ```typescript
84
- * client.use(createCombinedTimeoutMiddleware({
85
- * overall: 5000, // 5 seconds total
86
- * perAttempt: 1000 // 1 second per attempt
87
- * }));
90
+ * client.use(resilience({ overall: seconds(5), perAttempt: seconds(1), retry: { maxRetries: 2 } }));
88
91
  * ```
89
92
  */
90
93
  export declare function createCombinedTimeoutMiddleware(options: TimeoutOptions): TypedClientMiddleware<TimeoutContext, {}>;
@@ -1 +1 @@
1
- {"version":3,"file":"timeout.d.ts","sourceRoot":"","sources":["../../../src/client/middleware/timeout.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAA+D,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEtH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AA2JD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE,SAAS,GAAG,SAAS,CAAC,GAAG,qBAAqB,CAAC,cAAc,EAAE,EAAE,CAAC,CAmB9I;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE,YAAY,GAAG,SAAS,CAAC,GAAG,qBAAqB,CAAC,cAAc,EAAE,EAAE,CAAC,CAmB1I;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,+BAA+B,CAAC,OAAO,EAAE,cAAc,GAAG,qBAAqB,CAAC,cAAc,EAAE,EAAE,CAAC,CAmBlH"}
1
+ {"version":3,"file":"timeout.d.ts","sourceRoot":"","sources":["../../../src/client/middleware/timeout.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAA6C,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEpG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGpD,OAAO,EAAc,KAAK,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE9D;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,OAAO,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAE/B;;;;OAIG;IACH,UAAU,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAElC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAgKD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE,SAAS,GAAG,SAAS,CAAC,GAAG,qBAAqB,CAAC,cAAc,EAAE,EAAE,CAAC,CAqB9I;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,IAAI,CAAC,cAAc,EAAE,YAAY,GAAG,SAAS,CAAC,GAAG,qBAAqB,CAAC,cAAc,EAAE,EAAE,CAAC,CAyB1I;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,+BAA+B,CAAC,OAAO,EAAE,cAAc,GAAG,qBAAqB,CAAC,cAAc,EAAE,EAAE,CAAC,CAGlH"}
@@ -4,7 +4,10 @@
4
4
  * Protocol-agnostic timeout handling with per-request and overall timeouts.
5
5
  * Works with any transport!
6
6
  */
7
- import { ABORTED_FIRST, errorItem, nextOrAbort } from "./items.js";
7
+ import { ABORTED_FIRST, abortedItem, errorItem, nextOrAbort, withDelivery } from "./items.js";
8
+ import { deadlineOf, narrowDeadline, restoreDeadline } from "../deadline.js";
9
+ import { resilience } from "./resilience.js";
10
+ import { optionalMs } from "../../duration.js";
8
11
  /**
9
12
  * Compose multiple AbortSignals into one.
10
13
  *
@@ -55,15 +58,16 @@ function composeAbortSignals(...signals) {
55
58
  // Fall through to manual composition if AbortSignal.any() fails
56
59
  }
57
60
  }
58
- // Fallback: Manual composition for older environments
61
+ // Fallback: Manual composition for older environments. The composed signal keeps the reason of
62
+ // the signal that aborted, as with AbortSignal.any() (before, it got a new AbortError).
59
63
  const controller = new AbortController();
60
64
  const abortListeners = [];
61
65
  for (const signal of validSignals) {
62
66
  if (signal.aborted) {
63
- controller.abort();
67
+ controller.abort(signal.reason);
64
68
  break;
65
69
  }
66
- const listener = () => controller.abort();
70
+ const listener = () => controller.abort(signal.reason);
67
71
  signal.addEventListener("abort", listener, { once: true });
68
72
  abortListeners.push(() => signal.removeEventListener("abort", listener));
69
73
  }
@@ -77,7 +81,8 @@ function composeAbortSignals(...signals) {
77
81
  /**
78
82
  * Run `next` with a timeout. When the time runs out, the request's signal aborts, and the
79
83
  * reader gets one TIMEOUT error item in place of the transport's ABORTED item or thrown error.
80
- * The middleware does not wait for a transport that ignores the signal.
84
+ * The middleware does not wait for a transport that ignores the signal: not after the timeout,
85
+ * and not after an abort of the caller, which gives an ABORTED item.
81
86
  *
82
87
  * Before, the middleware waited for a thrown error, but the transports yield error items: the
83
88
  * reader got "ABORTED" (not retryable), so a per-attempt timeout with retry never retried, and
@@ -86,47 +91,49 @@ function composeAbortSignals(...signals) {
86
91
  async function* runWithTimeout(next, context, ms, message, retryable) {
87
92
  const timeoutController = new AbortController();
88
93
  const timeoutId = setTimeout(() => timeoutController.abort(), ms);
94
+ // The overall timeout is the deadline of the call: the transport sends its remaining time to
95
+ // the server (roadmap R10). The timeout of one try is not: the server keeps a keyed write
96
+ // until the next try can get its result, and the request signal stops another call.
97
+ const deadlineBefore = retryable ? deadlineOf(context.message.metadata) : narrowDeadline(context.message.metadata, Date.now() + ms);
89
98
  // Compose with existing signal
90
99
  const originalSignal = context.message.signal;
91
100
  const { controller: composedController, cleanup } = composeAbortSignals(originalSignal, timeoutController.signal);
92
101
  context.message.signal = composedController.signal;
93
102
  const timedOut = () => timeoutController.signal.aborted && !originalSignal?.aborted;
94
- const timeoutItem = () => errorItem(context.message.id, "TIMEOUT", message, retryable);
103
+ // The request left before the time ran out: the server can still run it (R1)
104
+ const timeoutItem = () => withDelivery(errorItem(context.message.id, "TIMEOUT", message, retryable), "indeterminate");
95
105
  const iterator = next(context)[Symbol.asyncIterator]();
96
106
  // "running": a next() may be pending; "done": the iterator ended; "abandoned": the timeout won
97
107
  let phase = "running";
98
108
  try {
99
109
  for (;;) {
100
- const result = await nextOrAbort(iterator, timeoutController.signal);
110
+ // The timeout and the abort of the caller both end the wait (roadmap R30: before, the wait
111
+ // ignored the abort of the caller, so a transport that ignored the signal kept the caller
112
+ // waiting until the timeout, and the caller got TIMEOUT for its own abort)
113
+ const result = await nextOrAbort(iterator, composedController.signal);
101
114
  if (result === ABORTED_FIRST) {
102
115
  phase = "abandoned";
103
- // The transport did not stop in time: end it in the background, and give the timeout
116
+ // The transport did not stop in time: end it in the background, and give the timeout or the abort
104
117
  void Promise.resolve(iterator.return?.()).catch(() => undefined);
105
- yield timeoutItem();
118
+ yield timedOut() ? timeoutItem() : abortedItem(context.message.id);
106
119
  return;
107
120
  }
108
121
  if (result.done) {
109
122
  phase = "done";
110
123
  return;
111
124
  }
112
- if (result.value.status.type === "error" && timedOut()) {
113
- // The transport saw the aborted signal and reported ABORTED: it is a timeout
114
- yield timeoutItem();
115
- return;
116
- }
125
+ // An item or an error of the transport comes before the abort. `nextOrAbort` adds its abort
126
+ // listener before it calls `next()`, so a later abort gives ABORTED_FIRST, not an item.
117
127
  yield result.value;
118
128
  }
119
129
  }
120
130
  catch (error) {
121
131
  phase = "done";
122
- if (timedOut()) {
123
- yield timeoutItem();
124
- return;
125
- }
126
132
  throw error;
127
133
  }
128
134
  finally {
129
135
  clearTimeout(timeoutId);
136
+ restoreDeadline(context.message.metadata, deadlineBefore);
130
137
  cleanup();
131
138
  // Restore the caller's original signal; we only swapped in the composed signal for this
132
139
  // attempt. Leaving the (possibly aborted) composed signal in place poisons subsequent
@@ -156,21 +163,22 @@ async function* runWithTimeout(next, context, ms, message, retryable) {
156
163
  * @example
157
164
  * ```typescript
158
165
  * // Create middleware with default timeout
159
- * client.use(createOverallTimeoutMiddleware({ overall: 5000 }));
166
+ * client.use(createOverallTimeoutMiddleware({ overall: seconds(5) }));
160
167
  *
161
168
  * // Override per-call via context
162
169
  * await client.call(method, payload, {
163
- * context: { timeout: { overall: 10000 } }
170
+ * context: { timeout: { overall: seconds(10) } }
164
171
  * });
165
172
  * ```
166
173
  */
167
174
  export function createOverallTimeoutMiddleware(options) {
168
- const { overall: defaultOverall, message = "Overall request timeout" } = options;
175
+ const { message = "Overall request timeout" } = options;
176
+ const defaultOverall = optionalMs(options.overall, "overall");
169
177
  return (next) => {
170
178
  return async function* (context) {
171
179
  // Read from metadata first (user-provided context), then fall back to options
172
- const overall = context.message.metadata.timeout?.overall
173
- ?? defaultOverall;
180
+ const overall = optionalMs(context.message.metadata.timeout?.overall, "timeout.overall") ??
181
+ defaultOverall;
174
182
  if (!overall) {
175
183
  // No timeout - passthrough
176
184
  yield* next(context);
@@ -195,59 +203,53 @@ export function createOverallTimeoutMiddleware(options) {
195
203
  * @example
196
204
  * ```typescript
197
205
  * // Create middleware with default timeout
198
- * client.use(createTimeoutMiddleware({ perAttempt: 1000 }));
206
+ * client.use(createTimeoutMiddleware({ perAttempt: seconds(1) }));
199
207
  *
200
208
  * // Override per-call via context
201
209
  * await client.call(method, payload, {
202
- * context: { timeout: { perAttempt: 2000 } }
210
+ * context: { timeout: { perAttempt: seconds(2) } }
203
211
  * });
204
212
  * ```
205
213
  */
206
214
  export function createTimeoutMiddleware(options) {
207
- const { perAttempt: defaultPerAttempt, message = "Request timeout" } = options;
215
+ const { message = "Request timeout" } = options;
216
+ const defaultPerAttempt = optionalMs(options.perAttempt, "perAttempt");
208
217
  return (next) => {
209
218
  return async function* (context) {
210
219
  // Read from metadata first (user-provided context), then fall back to options
211
- const perAttempt = context.message.metadata.timeout?.perAttempt
212
- ?? defaultPerAttempt;
220
+ const perAttempt = optionalMs(context.message.metadata.timeout?.perAttempt, "timeout.perAttempt") ??
221
+ defaultPerAttempt;
213
222
  if (!perAttempt) {
214
223
  // No timeout - passthrough
215
224
  yield* next(context);
216
225
  return;
217
226
  }
227
+ // The limit of one try is at most the remaining time of the call (roadmap R10)
228
+ const deadline = deadlineOf(context.message.metadata);
229
+ const limit = deadline === undefined ? perAttempt : Math.max(0, Math.min(perAttempt, deadline - Date.now()));
218
230
  // A retry middleware outside this one can start a new attempt
219
- yield* runWithTimeout(next, context, perAttempt, message, true);
231
+ yield* runWithTimeout(next, context, limit, message, true);
220
232
  };
221
233
  };
222
234
  }
223
235
  /**
224
236
  * Create combined timeout middleware with both overall and per-attempt timeouts.
225
237
  *
238
+ * @deprecated Use `resilience({ overall, perAttempt, retry })`. This function puts the two
239
+ * timeouts in one slot, so a retry middleware that you add with another `use()` cannot sit
240
+ * between them: the retry is outside both, or inside both. `resilience` puts the retry between
241
+ * them (roadmap R10).
242
+ *
226
243
  * @param options - Timeout configuration
227
244
  * @returns Middleware function
228
245
  *
229
246
  * @example
230
247
  * ```typescript
231
- * client.use(createCombinedTimeoutMiddleware({
232
- * overall: 5000, // 5 seconds total
233
- * perAttempt: 1000 // 1 second per attempt
234
- * }));
248
+ * client.use(resilience({ overall: seconds(5), perAttempt: seconds(1), retry: { maxRetries: 2 } }));
235
249
  * ```
236
250
  */
237
251
  export function createCombinedTimeoutMiddleware(options) {
238
252
  const { overall, perAttempt, message = "Request timeout" } = options;
239
- return (next) => {
240
- // Compose both timeout middlewares
241
- let composed = next;
242
- if (perAttempt) {
243
- const perAttemptMiddleware = createTimeoutMiddleware({ perAttempt, message: `${message} (per-attempt)` });
244
- composed = perAttemptMiddleware(composed);
245
- }
246
- if (overall) {
247
- const overallMiddleware = createOverallTimeoutMiddleware({ overall, message: `${message} (overall)` });
248
- composed = overallMiddleware(composed);
249
- }
250
- return composed;
251
- };
253
+ return resilience({ overall, perAttempt, message });
252
254
  }
253
255
  //# sourceMappingURL=timeout.js.map