@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
package/CHANGELOG.md CHANGED
@@ -1,5 +1,99 @@
1
1
  # @procedures/client
2
2
 
3
+ ## 0.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - d919678: `ERROR_REGISTRY` agrees with `ERROR_CODES` of the wire protocol. `ERROR_CODES` is the source: the registry has an entry for each code of the protocol, and that entry takes its HTTP status and its retry flag from `ERROR_CODES`. Changes that a caller sees:
8
+
9
+ - `TIMEOUT` has the status 504 and is not retryable (before: 408, retryable).
10
+ - `INTERNAL_ERROR` is not retryable (before: retryable).
11
+ - `ABORTED` has the status 499 (before: none).
12
+ - The codes of the protocol that the registry did not have (`HANDLER_ERROR`, `VALIDATION_ERROR`, `UNAVAILABLE` and the others) have entries, with the meaning of the protocol as their texts.
13
+ - `errorCodeToHTTPStatus` gives the status of `ERROR_CODES` for a code of the protocol, for example 504 for `TIMEOUT`.
14
+ - b2a1a1e: A handler error keeps its own code on each face, as `WIRE-PROTOCOL.md` says. `errorResponse` (the server, and through it HTTP, WebSocket and MCP) and `LocalTransport` give the code of the thrown error when it is a string. Before, they changed it to `HANDLER_ERROR` for a value, and `LocalTransport` also for a stream. An error without a code is still `HANDLER_ERROR`.
15
+
16
+ The HTTP status follows the code: a code of `ERROR_CODES` gets its status, another code of `ERROR_REGISTRY` gets the status of the registry, and each other code gets 500. For example, a handler error with the code `UNAVAILABLE` now gives 503.
17
+ - 057d69a: `client.import` gives `success: true` only for a procedure reference: an object whose `$proc` is a list of texts. Other JSON, for example `42`, an object without `$proc` or a list of calls, gives `success: false` and an error. Before, each JSON value gave `success: true`.
18
+ - 057d69a: `client.isObject` gives true only for a plain object: an object literal, or an object without a prototype. A `Date`, a `Map` or another class instance gives false. Before, each object that is not an array gave true.
19
+ - 057d69a: `client.stringifyJson` and `client.export` refuse a missing value (undefined) with `VALIDATION_ERROR`, because undefined has no JSON text. Before, the call failed with `OUTPUT_VALIDATION_ERROR`. The `value` field of `client.stringifyJson` and the `procedure` field of `client.export` are now required. A value without JSON text, for example a function, gives an error from `client.stringifyJson` and an error result from `client.export`.
20
+ - 057d69a: `client.mod` refuses `b = 0` with the error `Division by zero`, as `client.divide` does. Before, it gave `NaN`.
21
+ - afd6a19: The `client-registry` command is removed. `mark` finds the packages, and the `/register` entries register them.
22
+ - 057d69a: `client.unique` compares the items by value: two equal JSON values are duplicates. An array compares by its items in order, and a plain object by its fields in any order. With `key`, the values of the field compare by value too. The result keeps the first item of each value, in the order of the input. Before, objects and arrays compared by reference, so two equal objects both stayed.
23
+ - 4e751da: The new entry `@procedures/client/node` exports a `WebSocketTransport` whose sockets are of the `WebSocket` class of the `ws` package. A Node application gets `terminate()` without the option `webSocket`: a drop by the heartbeat or by the time limit of the handshake ends the socket at once. The option `webSocket` still gives another class. The other entries keep the global `WebSocket`, and the browser entry does not import `ws`.
24
+
25
+ A socket of the `ws` package that closes during its handshake emits an error. Before, the transport removed its error handler first, so a handshake timeout or a `close()` during the handshake ended the process with an uncaught error. The transport now keeps a handler that ignores that error.
26
+
27
+ ### Patch Changes
28
+
29
+ - 057d69a: The description of the `match` field of `client.filter` lists all 11 modes. Before, it did not name `gt`, `gte`, `lt` and `lte`.
30
+ - 057d69a: The input schemas of `client.not`, `client.all`, `client.any` and `client.none` say "values", not "values or calls". These procedures do not run a call in their operands: a call is an object, thus truthy. In a program, the calls of their input run first, as for each procedure that is not control flow. `client.and` and `client.or` run calls. The behavior does not change.
31
+
32
+ ## 0.3.0
33
+
34
+ ### Minor Changes
35
+
36
+ - b2a3e3a: The bidirectional peer (roadmap R24). The calls over one WebSocket go in the two directions with the same frames: a request, then a response, an error or a stream with credit, and a cancel. The client serves the calls of the server with the new `serve` option of `WebSocketTransport` (for example a `ProcedureServer` with its registry), so a call gets validation, the expose rule, the capability, streaming, cancel, the signal and the deadline. The server sends them with `callConnection()`, or with a `Client` over `transportFor(connectionId)`, and a peer has `connections()` and `clientFor(connectionId)`. `callClient()` and `onServerRequest` stay as wrappers of the new calls, and the old `server-request` frames are no longer sent. The shared code of the two sides (`OutgoingCalls`, `IncomingCalls`) serves the calls of the client too.
37
+ - a8c47f1: The cache (roadmap R13). Two calls with the same key at the same time make one request: the second one waits for the first and gets a copy. With the new option `effects` (for example `effectsIn(registry)`), a write that succeeds deletes the cached entries of the methods that read one of its resources. `invalidateCache(middleware, matches?)` deletes entries by method. A response whose key changed during the call is not stored, so an auth layer inside the cache cannot give the response of one user to another. The idempotency key and the remaining time of a call are not part of the key.
38
+ - 381ce3a: Cancellation scopes and a graceful stop (roadmap R14). `ProcedureServer.stop({ grace })` refuses new calls with a retryable `UNAVAILABLE` and lets the calls in flight end until the grace time ends, then aborts them (`peer.stop({ grace })` passes it; `mark --server` and the `server` bin give 5 seconds). The signal of each call joins its request and the server, and `callsInFlight` counts the calls, streams included. `exec()` and `execStream()` take a `signal`. `LocalTransport` takes `interceptors`, `capability` and `clearanceOrder`, and gives a call the deadline that its client sends, so a client timeout ends a local handler too. `mark` aborts a command in its own process on the first Ctrl+C. A remote leaf of `route()` and `server.call` send the deadline of their caller, and `server.call` relays the signal.
39
+ - ae7f05f: The clock of the timers, and the state machines as data (roadmap R35). The retry, the circuit breaker and the WebSocket client take a `clock` option: a `Clock` gives the monotonic time and starts the timers, and `realClock` is the default. The shape is the `Clock` of `@mark1russell7/service`, so its `fakeClock` fits, and a test gets exact times. `WEBSOCKET_CONNECTION_MACHINE` and `CIRCUIT_BREAKER_MACHINE` are `mark1russell7.machine/1` documents with their knobs, events and timers. The WebSocket client and the circuit breaker report each change of their state to `onTransition`, and `replayTransitions()` replays the reports against a document. The heartbeat of the WebSocket client uses one timer for each interval in place of `setInterval`.
40
+ - 7873940: The collection mode switch (roadmap R28). `HybridStorage` has four modes: `local-only` (no remote call, each write waits in the queue), `write-through`, `write-back` and `remote-only` (no local copy). The option `mode` gives the first mode, and `writeStrategy` stays as a deprecated name of it. `setMode()` changes the mode while the storage operates. The switch waits for the operations that run, sends the queue to the remote first, and syncs the local copy when it leaves `local-only` or `remote-only`. A switch to `remote-only` drops the local copy only after the flush. When the remote fails, the mode stays and `setMode()` throws `ModeSwitchError`. A switch to `local-only` makes no remote call.
41
+
42
+ The queue is durable: the option `queueStore` takes a `QueueStore` (`load`, `append`, `replace`, `clear`). `MemoryQueueStore` is the default, and `FileQueueStore` of the new entry `@procedures/store/file-queue` keeps the queue in a JSON Lines file. A write ends only after the store kept it (`QueueStoreError` when it cannot). A new storage with the same store loads the queue, shows its writes and sends them. The modes `write-back` and `local-only` always use the queue: before, `write-back` with `offlineQueue: false` never sent a write.
43
+
44
+ A sync asks for the changes since the version of the last sync. Storages get the optional `getChangesSince(version)` (`InMemoryStorage` keeps a change log with tombstones, `ApiStorage` calls the new collection procedure `collections.<name>.changes`), and a remote without it gives all its entries. The option `versionOf` gives the version of an item: with versions, a conflict is a queued local write of an id whose remote version changed, and `conflictResolution` decides (`error` throws `ConflictError`). The option `pullInterval` starts a periodic pull. Storages get the optional `flush()`, and `ProcedureServer.useStorage(name, storage, { drain, close })` flushes the old storage before the change. `createSyncedRegistry({ type: "hybrid", mode })` takes the mode, and the synced registry has `setMode()` and `getMode()`.
45
+ - a70d445: `procedure.define` keeps the effects of its body (roadmap R22). `programCheckInterceptor()` checks the body of each definition, refuses a body with an error, and gives its effects to `procedure.define` in the internal metadata key `__inferredEffects`. Each transport removes `__` keys from a request, so a remote caller cannot claim effects. The defined procedure keeps them as `metadata.inferredEffects`, and `toolHints()` uses them: before, a defined procedure had no hints. The admission still examines each nested call. New exports: `INFERRED_EFFECTS_METADATA_KEY`, `inferredEffectsOf()`, `effectsValue()`.
46
+ - bc70357: The file procedures can have a root (roadmap R33). `createFsModule({ root })` gives the procedures of `fsModule`, and each path of each input must be in the root folder. A relative path starts at the root. Without a root, the procedures operate as before.
47
+
48
+ The procedures refuse a path that goes up with `..`, an absolute path outside the root and another drive. They also refuse a symlink or a junction that goes out of the root. The error has the code `FORBIDDEN` and the new reason `outside-root` (the type `ForbiddenReason` of `@procedures/client`). For a path that does not exist, the check uses the real path of the nearest existing folder. `registerFsProcedures(registry, { root })` takes the root too.
49
+ - 07cc998: The procedures of a host (roadmap R19). `hostProcedures(source)` gives `host.health` (the state, the uptime, the procedures and the calls in flight), `host.info` (the id, the start time, the endpoints and the runtime) and `host.metrics` (the counts of `metricsInterceptor` for each procedure). A peer pins them on its server, so the registry of its caller does not change, and counts its calls (option `metrics`, default true). `ProcedureServer.stopping` tells when a graceful stop runs.
50
+ - a6b01a9: The new package `@procedures/http` (roadmap R38) has four procedures: `http.request` (each method, a body of text, JSON or base64, and a response as text, JSON or base64), `http.json` (a GET or a POST of JSON), `http.ndjson` (a stream of the JSON values of the lines) and `http.sse` (a stream of server-sent events). `http.request` and `http.json` declare `reads: ["network"]` and `writes: ["network"]`. The two streams send only a GET, so they declare `writes: []`, and they declare `stream: { unbounded: true }`.
51
+
52
+ The default module reaches each public host and refuses loopback, private, link-local, shared, multicast and reserved addresses. `createHttpModule({ allow, publicHosts, maxRequestBytes, maxResponseBytes, resolve })` gives the procedures with more hosts: host names, wildcards, IP addresses and CIDR ranges, each with an optional port. The check resolves the host name, and the connection uses only the addresses that passed the check. Each redirect gets the same check. A refusal has the code `FORBIDDEN` and the new reason `host-not-allowed` (the type `ForbiddenReason` of `@procedures/client`).
53
+
54
+ A call stops at the earliest of its deadline, the abort of the caller and `timeoutMs`. A stream also stops after `idleTimeoutMs` without data. The body of a response has a limit of 10 MiB by default, and the limit counts the decoded bytes of a compressed body.
55
+ - c799cdd: `HybridStorage` cannot lose data (roadmap R2). A sync gets the ids of the remote items from the new `getEntries()` of a storage (the collection procedure `collections.<name>.entries`), or else from `keyOf`. A remote item without a key stops the sync with `MissingKeysError` before any change: before, such a list emptied the local cache. A write-through that the remote cannot take waits in the queue and throws `PendingWriteError`, and a timer sends the queue. A write that the remote refuses is undone in the local cache. A full queue refuses a write with `QueueFullError`: before, it dropped the oldest operation. A second try of a write replaces its queued operation. A write-through sends the older queued operations first. The reads wait for the first sync.
56
+ - 5b9264e: One deadline model (roadmap R10, REV-6). The overall timeout and the remaining time that a caller gives (`procedure-timeout`) set the deadline of a call, and each try sends its remaining time as `procedure-timeout`. So the server stops the handler when the client stops waiting: before, the timeouts of the client middleware did not reach the server. The timeout of one try is at most the remaining time. The client no longer sends `X-Timeout`, which the server ignored. The new preset `resilience({ overall, perAttempt, retry, breaker, cache, rateLimit })` puts the layers in the correct order, and `createCombinedTimeoutMiddleware` is now that preset (deprecated).
57
+ - 956e88b: One manifest type (roadmap R17). `describeRegistry(registry, { expose })` and `describeProcedure` (exported as `describeProcedureEntry`) describe each procedure: the path, the description, the tags, the effects, the JSON Schemas of the input and the output, the calls, an unbounded stream, the command-line metadata, and a hash of the manifest (`procedures.manifest/1`). The new procedure `procedure.manifest` gives it, and it is an MCP tool of the dev tools (63 tools). `procedure.get` gives the JSON Schemas and the effects. The context of a handler has the expose rule of its host (`ctx.expose`), and `procedure.get`, `procedure.list`, `procedure.manifest`, `procedure.export`, `client.lookup`, `mcp.list-tools`, `manifest.generate` and `_discovery.announce` show only the exposed procedures: before, an MCP tool could list the procedures that the host hides. `_discovery.announce` gives the JSON Schemas, and `manifest.generate`, `getProcedureInfos` and the manifest of `mark` use the one description.
58
+ - ff62281: One trace from the client to the handler (roadmap R16). The tracing middleware makes W3C ids by default and sends a W3C `traceparent` in the metadata (and as the HTTP header `traceparent`). The tracing interceptor of a server continues that trace: the span of a call of the host has the trace of the client and the span of the client as its parent (`remoteParent: true`). The nested calls of a handler carry its span, also to a remote server. The spans of the interceptor have W3C ids. Before, the client and the server made two traces that nothing joined.
59
+ - d81d0ed: The transport over a message port (roadmap R27). `PortTransport` sends calls over a Web Worker, a `MessageChannel` or a `worker_threads` port, in the two directions, with the frames of the WebSocket transport: streams with credit, cancel and errors. Its `serve` option takes a `RequestHandler`, for example a `ProcedureServer`. The site uses it for its two workers in place of two protocols of its own.
60
+ - 061fa22: The timers of the client come from `@mark1russell7/service` 0.4.0 and `@mark1russell7/algebra` 0.5.0 (roadmap R37). The two packages are dependencies of the client now.
61
+
62
+ - The clock is `platformClock()` of `@mark1russell7/service/clock`. The types `Clock` and `Cancel` come from there too, and `Cancel.unref` is read-only.
63
+ - The heartbeat of the WebSocket client is `heartbeat` of `@mark1russell7/service/heartbeat`. A ping whose send throws is a miss. Before, the error left the timer.
64
+ - `reconnectDelay` and the retry middleware use `backoffDelay` of `@mark1russell7/service/backoff`. The delays are the same.
65
+ - `replayTransitions` uses `replay` of `@mark1russell7/algebra/replay`. A jump message says "the runtime left", not "the code left".
66
+
67
+ The four entries import nothing else, and together they have 1.3 kB of gzip.
68
+ - cfb9d69: Retry obeys the effects (roadmap R1). After a try with an indeterminate outcome (a reset, a closed WebSocket, the timeout of one try), the retry middleware sends the call again only when it is idempotent: the `idempotent` option (`true`, or a function such as `idempotentIn(registry)`), or `retry: { idempotent: true }` in the context of the call. A refused connection and a retryable error of the server still go again. Each try of one call sends the same `idempotency-key`, and `ProcedureServer` runs a keyed call to a procedure that is not idempotent one time (option `idempotency`). The error items of the transports tell the delivery of a failed try (`deliveryOf`).
69
+ - 43f7de5: Retry and the circuit breaker (roadmap R12). The retry middleware has a retry budget (`budget`, default: 0.2 of the calls and at least 10 retries a second), so a failing service gets a fraction more requests and not `maxRetries` times more. The delay has full jitter by default (`jitter: 1`: a random time from 0 to the backoff, before ±10 percent). A server that asks for a wait gets it: `retryAfterMs` of a thrown error becomes the `Retry-After` header and the `retry-after` metadata, and the retry waits at least that long. `maxAttempts` in a context counts the tries, as its name says (before, it counted the retries). The circuit breaker counts only the failures of availability (`isAvailabilityFailure`: UNAVAILABLE, TIMEOUT, a stopped server, a gateway error, network errors): an abort or a validation error no longer opens it. Its windows use a monotonic clock. `ApiStorage` asks a retry middleware of its client for one try, so the two loops cannot multiply.
70
+ - b07bb50: The server-transport contract (roadmap R18). `RequestHandler` (`handle(request)`) is the contract of a server transport, and the HTTP and WebSocket transports take one. A `ServerRequest` has `credentials` (the bearer token of `Authorization`, or of the `token` query of a WebSocket) and `principal`. `ProcedureServer` takes `token` (a host policy gives its token) and examines it for each network request, and `authenticate` gives the principal. Before, only the Express middleware of the peer examined the token. The secret headers (`Authorization`, `Cookie`, `Proxy-Authorization`, `X-Api-Key`) no longer go into the metadata of a request, so a handler and a nested call do not see them. `LocalTransport` answers `ABORTED` at once when its caller aborts, as HTTP and WebSocket do. A conformance suite runs the same calls through Local, `handle()`, HTTP, WebSocket and MCP.
71
+ - 4a3bfa1: The timer law of systems for the client (roadmap R34). The new package `@procedures/systems` binds the times of the middleware, the transports and the server to the symbols of the law of `@mark1russell7/service`, and examines six rules of the client: `lintTimers()`, `bindingOf()`, `CLIENT_RULES` and the procedure `systems.timers`. The core exports its default times as data: `TIMER_DEFAULTS`. The middleware, the transports and the server read them. The law found two defaults that broke it, and they change: the WebSocket client pings each 20 s, not 30 s, so it finds a dead connection before a call on it times out. The WebSocket server pings each 15 s, not 30 s, so it finds a dead client before its call to the client times out.
72
+ - fb30718: Typed durations (roadmap R11). This change breaks the API. Each public option that is a time takes a `Duration` of `@mark1russell7/quantity`, made with `ms()`, `seconds()`, `minutes()` or `hours()` of `@procedures/client`. A plain number is a type error, and at run time the option throws a `TypeError` that names it. This covers the timeouts and `resilience()`, the retry delays, the cache, the circuit breaker, the rate limit, the HTTP, WebSocket and mock transports, the WebSocket server heartbeat, `timeoutInterceptor({ limit })` (was `ms`), `hostPolicy({ timeout })` (was `timeoutMs`), the idempotency cache (`ttl`, was `ttlMs`), `ApiStorage`, `HybridStorage`, the synced registry and `connect()` of the peer. The per-call overrides in a context (`timeout`, `retry.delay`, `cache.ttl`) and the `route()` overrides (`timeout.overall`, was `timeout.ms`) take durations too. The wire and the inputs of procedures keep milliseconds.
73
+
74
+ Migration: `createOverallTimeoutMiddleware({ overall: 5000 })` becomes `createOverallTimeoutMiddleware({ overall: seconds(5) })`.
75
+ - c5528e4: Streams that can run without an end (roadmap R15). A procedure declares `stream: { unbounded: true }` in its metadata (`docker.logs`, for `follow`, and `shell.stream` do). `ProcedureServer` marks its stream response, and the faces that collect a stream obey the mark: HTTP without NDJSON answers 406 `NOT_ACCEPTABLE` at once (before, the request waited until a timeout), and the MCP transport stops the stream at its `streamLimits` (default 100 items or 30 seconds) and says so in the result (before, `docker.logs` with `follow` ended only at the time limit of 10 minutes).
76
+ - fee5d9c: WebSocket time limits (roadmap R3). The handshake of the client has a time limit (`connectionTimeout`): an address that never answers no longer keeps the socket in `CONNECTING`. The reconnect delay has full jitter by default (`reconnect.jitter`), so the clients of a server that restarted do not come back at the same moments. The client drops a connection after `heartbeat.misses` pings without a pong (default 1, as before). The server pings each connection and closes one that misses 2 pongs (`WebSocketServerTransport` option `heartbeat`), so it finds a client that went silent.
77
+ - 21f7743: Wire protocol v1 (roadmap R25). `WIRE-PROTOCOL.md` specifies the HTTP and WebSocket formats. A WebSocket connection starts with a `hello` frame of each side (`getServerProtocol()` on the client), and HTTP sends the header `Procedures-Protocol: 1` on each request and response. A side refuses a newer version with `PROTOCOL_VERSION`. `ERROR_CODES` is the one list of error codes and HTTP statuses of all faces. The reason of a `FORBIDDEN` error travels on each face: in the HTTP error body, in the item metadata of the HTTP and local transports, and in the `_meta` of an MCP error result. `Method.version` is deprecated: the server ignores it. Golden tests in `packages/client/test/wire/` pin the frames.
78
+ - 31a0b46: The WebSocket client has the option `webSocket`: the class of its sockets. In Node, give the `WebSocket` of the `ws` package. A drop by the heartbeat or by the time limit of the handshake then ends the socket at once with `terminate()`.
79
+
80
+ The global `WebSocket` has only `close()`, which waits for the answer of the peer. The harness cells of roadmap R36 showed that a socket to a dead peer then stays open until the kernel stops it: 924.6 seconds with the default settings of Linux.
81
+
82
+ ### Patch Changes
83
+
84
+ - ddea529: The array procedures read only the own fields of an item (roadmap R29, as R6 for the object procedures). Before, a key such as `toString` read the function of the prototype: `client.count` counted each item, and `client.pluck` gave functions. This applies to `sort`, `unique`, `groupBy`, `filter`, `where`, `pluck`, `partition` and `count`.
85
+ - 0c627f8: The catalog test of the core procedures found four defects (roadmap R29). `client.replace` gives the replacement as text: before, the replacement of the first match changed `$$` to `$` and `$&` to the match. `client.groupBy` accepts a group with the name of a field of `Object.prototype`: before, a group `constructor` or `toString` failed. `client.arrPick` picks only own fields, as `client.pick` does: before, `toString` picked a function of the prototype. The input schema of `client.template` describes its `{{name}}` placeholders: before, it described `{name}`.
86
+ - 540f8d9: A `$never` operand of a control-flow procedure is data (roadmap R4). Before, only a step of `client.chain` obeyed `$never`: a `$never` branch of `client.conditional`, a try of `client.tryCatch`, a task of `client.parallel` and the `fn` of `client.map` started. A `fn` of `client.map` or `client.reduce` that is not one call is an error: before, it was ignored.
87
+ - b6daaec: The object procedures do not reach a prototype (roadmap R6). `client.set`, `client.get` and `client.has` refuse a path segment `__proto__`, `constructor` or `prototype`: before, `client.set` with the path `__proto__.x` changed the prototype of its result. `client.get`, `client.has` and `client.pick` read only own fields: before, `toString` gave a function of the prototype. `client.merge` and `client.omit` set own data properties.
88
+ - aa67cc7: The client uses `@mark1russell7/quantity` 0.2.0. Its root entry does not import zod. The entries of the client that use zod for their schemas still have it, but a bundle that takes only the Durations no longer gets zod.
89
+ - bd0b626: Four fixes that the tests of roadmap R30 found.
90
+
91
+ - An abort of the caller ends the wait of the timeout middlewares at once, also when the transport ignores the signal. The caller gets `ABORTED`. Before, the caller waited for the timeout and got `TIMEOUT`, and the circuit breaker counted a failure.
92
+ - `WebSocketTransport` starts its reconnects at once when a handshake fails on Node 22. The WebSocket of Node 22 gives an error event and no close event, so before, each try waited for `connectionTimeout`.
93
+ - `createErrorFromException` gives `TIMEOUT` for the reason of `AbortSignal.timeout()`, and `SERIALIZE_ERROR` for a circular structure. Before, it gave `ABORTED` and `PARSE_ERROR`.
94
+ - `isAvailabilityFailure` is true for `CONNECTION_CLOSED`, so the circuit breaker counts a closed connection.
95
+ - a6be873: The timeout middleware keeps the reason of an abort when the platform has no `AbortSignal.any`. Its own composition of the signals now gives the transport the reason of the signal that aborted, as `AbortSignal.any` does. Before, the transport got a new `AbortError` in place of the reason of the caller.
96
+
3
97
  ## 0.2.3
4
98
 
5
99
  ### Patch Changes
package/README.md CHANGED
@@ -250,11 +250,40 @@ new WebSocketTransport(options: WebSocketTransportOptions)
250
250
 
251
251
  interface WebSocketTransportOptions {
252
252
  url: string;
253
- protocols?: string[];
254
- reconnect?: boolean; // default: true
255
- reconnectDelay?: number; // default: 1000ms
256
- heartbeatInterval?: number; // default: 30000ms
253
+ connectionTimeout?: number; // default: 10000ms; a handshake that takes longer closes the socket
254
+ requestTimeout?: number; // default: 30000ms
255
+ streamWindow?: number; // default: 64 items
256
+ reconnect?: {
257
+ enabled?: boolean; // default: true
258
+ maxAttempts?: number; // default: Infinity
259
+ initialDelay?: number; // default: 1000ms
260
+ maxDelay?: number; // default: 30000ms
261
+ backoffMultiplier?: number; // default: 1.5
262
+ jitter?: number; // default: 1 (full jitter: a random delay from 0 to the backoff)
263
+ };
264
+ heartbeat?: {
265
+ enabled?: boolean; // default: true
266
+ interval?: number; // default: 30000ms
267
+ timeout?: number; // default: 5000ms
268
+ misses?: number; // default: 1; pings without a pong that drop the connection
269
+ };
270
+ webSocket?: WebSocketClass; // the class of the sockets; default: the global WebSocket
257
271
  }
272
+
273
+ // In Node, import the transport of the Node entry. Its sockets are of the `ws` package, so a
274
+ // drop ends a socket at once with terminate(). The global WebSocket has only close(): with a
275
+ // dead peer, its socket stays open until the kernel stops it (924.6 s in the cells of R36).
276
+ import { WebSocketTransport } from "@procedures/client/node";
277
+
278
+ // The server pings each connection, and closes one that misses 2 pongs (option heartbeat)
279
+ new WebSocketServerTransport(server, { server: http, heartbeat: { interval: seconds(30), misses: 2 } });
280
+
281
+ // Calls in the two directions over one WebSocket (roadmap R24). The client serves its own
282
+ // procedures with a ProcedureServer; the server calls them with the same frames: streams with
283
+ // credit, cancel, the signal and the deadline.
284
+ const transport = new WebSocketTransport({ url, serve: new ProcedureServer({ registry: uiRegistry, autoRegister: true }) });
285
+ const toClient = new Client({ transport: wsServer.transportFor(connectionId) }); // or peer.clientFor(id)
286
+ await toClient.call({ service: "ui", operation: "notify" }, { text: "Build done" });
258
287
  ```
259
288
 
260
289
  #### `LocalTransport`
@@ -290,12 +319,17 @@ interface RetryOptions {
290
319
  retryDelay?: number; // default: 1000ms (the base delay)
291
320
  backoffMultiplier?: number; // default: 2
292
321
  maxDelay?: number; // default: no limit
293
- jitter?: number; // default: 0.1 (a fraction of the delay)
322
+ jitter?: number; // default: 1 (full jitter: a random delay from 0 to the backoff)
323
+ budget?: { ratio?: number; minPerSecond?: number } | false; // default: 0.2 of the calls, at least 10 a second
324
+ // After a try with an indeterminate outcome (a reset, the timeout of one try), only an
325
+ // idempotent call goes again: true, or a function such as idempotentIn(registry)
326
+ idempotent?: boolean | ((method: Method) => boolean | undefined);
294
327
  shouldRetry?: (item: ResponseItem<unknown>, attempt: number) => boolean;
295
328
  }
296
329
 
297
- // The overrides of one call, in its context or as route() overrides
298
- client.withContext({ retry: { maxAttempts: 5, delay: 200, backoffMultiplier: 3, maxDelay: 2000 } });
330
+ // A Retry-After of the server makes the delay at least that long.
331
+ // The overrides of one call, in its context or as route() overrides (maxAttempts counts the tries)
332
+ client.withContext({ retry: { maxAttempts: 5, delay: ms(200), backoffMultiplier: 3, maxDelay: seconds(2), idempotent: true } });
299
333
  ```
300
334
 
301
335
  #### Cache Middleware
@@ -311,21 +345,19 @@ interface CacheOptions {
311
345
  }
312
346
 
313
347
  // The overrides of one call, in its context or as route() overrides
314
- client.withContext({ cache: { ttl: 5000 } }); // the life of the stored entry
348
+ client.withContext({ cache: { ttl: seconds(5) } }); // the life of the stored entry
315
349
  client.withContext({ cache: { bypass: true } }); // no read, no write
316
350
  client.withContext({ cache: { refresh: true } }); // fetch, then replace the stored entry
317
351
  ```
318
352
 
319
- #### Timeout Middleware
353
+ #### Timeout Middleware and the resilience preset
320
354
  ```typescript
321
- createTimeoutMiddleware(options: TimeoutOptions)
322
- createOverallTimeoutMiddleware(timeout: number)
323
- createCombinedTimeoutMiddleware(options: { overall: number; perAttempt: number })
355
+ createOverallTimeoutMiddleware({ overall: seconds(10) }) // the whole call, retries included
356
+ createTimeoutMiddleware({ perAttempt: seconds(2) }) // each try
357
+ resilience({ overall, perAttempt, retry, breaker, cache, rateLimit }) // the layers in the correct order
324
358
 
325
- interface TimeoutOptions {
326
- overall?: number; // milliseconds
327
- perAttempt?: number; // milliseconds
328
- }
359
+ // Each time is a Duration of @mark1russell7/quantity: ms(500), seconds(2), minutes(1), hours(1).
360
+ // A plain number is a type error (roadmap R11). The wire keeps milliseconds (procedure-timeout).
329
361
  ```
330
362
 
331
363
  #### Auth Middleware
@@ -347,9 +379,12 @@ interface AuthOptions {
347
379
  createCircuitBreakerMiddleware(options?: CircuitBreakerOptions)
348
380
 
349
381
  interface CircuitBreakerOptions {
350
- threshold?: number; // default: 5 failures
351
- timeout?: number; // default: 60000ms
352
- resetTimeout?: number; // default: 30000ms
382
+ failureThreshold?: number; // default: 5 failures
383
+ failureWindow?: Duration; // default: seconds(10)
384
+ resetTimeout?: Duration; // default: seconds(30)
385
+ successThreshold?: number; // default: 2
386
+ halfOpenMaxRequests?: number;
387
+ isFailure?: (error: Error) => boolean;
353
388
  }
354
389
  ```
355
390
 
@@ -358,19 +393,10 @@ interface CircuitBreakerOptions {
358
393
  createRateLimitMiddleware(options?: RateLimitOptions)
359
394
 
360
395
  interface RateLimitOptions {
361
- maxTokens?: number; // default: 10
362
- refillRate?: number; // tokens per second, default: 1
363
- refillInterval?: number; // milliseconds, default: 1000
364
- }
365
- ```
366
-
367
- #### Batching Middleware
368
- ```typescript
369
- createBatchingMiddleware(options?: BatchingOptions)
370
-
371
- interface BatchingOptions {
372
- maxBatchSize?: number; // default: 10
373
- maxWaitTime?: number; // default: 10ms
396
+ maxRequests?: number; // default: 100
397
+ window?: Duration; // default: minutes(1)
398
+ strategy?: "reject" | "queue"; // default: "reject"
399
+ maxQueueSize?: number; // default: 100
374
400
  }
375
401
  ```
376
402
 
@@ -567,22 +593,26 @@ import { ... } from "@procedures/client/procedures";
567
593
  import { ... } from "@procedures/client/events";
568
594
  ```
569
595
 
570
- ## Procedure Auto-Discovery
596
+ | Entry | Use |
597
+ |---|---|
598
+ | `@procedures/client` | Node. The browser entry and the server transports. Its `WebSocketTransport` uses the global `WebSocket`. |
599
+ | `@procedures/client/browser` | A browser bundle. It does not import `http` or `ws`. |
600
+ | `@procedures/client/node` | Node. A `WebSocketTransport` whose sockets are of the `ws` package: a drop ends a socket at once. |
601
+ | `@procedures/client/register` | It registers the core procedures in the global registry. |
602
+
603
+ ## Procedure Discovery
571
604
 
572
- Packages can declare procedures in `package.json`:
605
+ A package declares its procedures in `package.json`:
573
606
 
574
607
  ```json
575
608
  {
576
609
  "client": {
577
610
  "procedures": "./dist/register.js"
578
- },
579
- "scripts": {
580
- "postinstall": "client-registry announce"
581
611
  }
582
612
  }
583
613
  ```
584
614
 
585
- When installed, the package announces its procedures in `.client-registry.json`. Run `client-registry discover` to generate a file that imports the register entry of each announced package.
615
+ The `mark` CLI finds the packages by the `client.procedures` field. An app imports the `/register` entry of each package that it uses, for example `import "@procedures/fs/register"`.
586
616
 
587
617
  ## Type-Safe Middleware
588
618
 
@@ -667,13 +697,16 @@ const error = createError("E_TIMEOUT", {
667
697
 
668
698
  ### Advanced Middleware Composition
669
699
  ```typescript
700
+ // The first use() is the outermost layer. The overall timeout covers the retries, and the
701
+ // timeout of each try sits inside the retry, so the retry can start the next try.
670
702
  const client = new Client({ transport: new HttpTransport({ baseUrl: "/api" }) })
671
- .use(createTimeoutMiddleware({ overall: 10000, perAttempt: 5000 }))
672
- .use(createRetryMiddleware({ maxAttempts: 3, initialDelay: 1000 }))
673
- .use(createCacheMiddleware({ methods: ["users.get"], capacity: 100, ttl: 60000 }))
674
- .use(createCircuitBreakerMiddleware({ threshold: 5, timeout: 60000 }))
675
- .use(createRateLimitMiddleware({ maxTokens: 10, refillRate: 1 }))
676
703
  .use(createTracingMiddleware())
704
+ .use(createOverallTimeoutMiddleware({ overall: seconds(10) }))
705
+ .use(createCacheMiddleware({ methods: ["users.get"], capacity: 100, ttl: minutes(1) }))
706
+ .use(createRetryMiddleware({ maxRetries: 3, retryDelay: seconds(1), idempotent: (method) => method.operation === "get" }))
707
+ .use(createCircuitBreakerMiddleware({ failureThreshold: 5, resetTimeout: minutes(1) }))
708
+ .use(createRateLimitMiddleware({ maxRequests: 10, window: seconds(1) }))
709
+ .use(createTimeoutMiddleware({ perAttempt: seconds(5) }))
677
710
  .use(createZodMiddleware());
678
711
 
679
712
  // Context inheritance
@@ -713,7 +746,7 @@ const result = await client.route({
713
746
  batch: { strategy: "all", maxConcurrency: 5 },
714
747
  middlewares: {
715
748
  retry: { attempts: 3 },
716
- timeout: { overall: 10000 },
749
+ timeout: { overall: seconds(10) },
717
750
  },
718
751
  route: {
719
752
  users: {