@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
@@ -95,7 +95,7 @@ export interface HeaderConverter {
95
95
  * Implements standard conventions for converting metadata to headers:
96
96
  * - Auth: Bearer token, API key
97
97
  * - Tracing: X-Trace-Id, X-Span-Id format
98
- * - Timeouts: X-Timeout header
98
+ * - The deadline: `procedure-timeout` in X-Metadata (no X-Timeout header)
99
99
  * - Custom fields: Pass through as-is
100
100
  */
101
101
  export class DefaultHeaderConverter implements HeaderConverter {
@@ -120,7 +120,10 @@ export class DefaultHeaderConverter implements HeaderConverter {
120
120
  headers[HTTPHeaders.API_KEY] = metadata.auth.apiKey;
121
121
  }
122
122
 
123
- // Distributed Tracing
123
+ // Distributed Tracing: the W3C trace context, and the older headers (roadmap R16)
124
+ if (typeof metadata["traceparent"] === "string") {
125
+ headers["traceparent"] = metadata["traceparent"];
126
+ }
124
127
  if (metadata.tracing?.traceId) {
125
128
  headers[HTTPHeaders.TRACE_ID] = metadata.tracing.traceId;
126
129
  }
@@ -131,10 +134,8 @@ export class DefaultHeaderConverter implements HeaderConverter {
131
134
  headers[HTTPHeaders.PARENT_SPAN_ID] = metadata.tracing.parentSpanId;
132
135
  }
133
136
 
134
- // Timeouts
135
- if (metadata.timeout?.overall) {
136
- headers[HTTPHeaders.TIMEOUT] = String(metadata.timeout.overall);
137
- }
137
+ // No X-Timeout: the remaining time of the deadline goes as procedure-timeout in X-Metadata,
138
+ // which the server uses (roadmap R10). The server ignored X-Timeout.
138
139
 
139
140
  const custom: Record<string, unknown> = {};
140
141
  for (const [key, value] of Object.entries(metadata)) {
@@ -9,8 +9,41 @@ import type { Handler, LocalTransportOptions } from "./types.js";
9
9
  import { methodKey } from "./types.js";
10
10
  import type { ProcedureRegistry } from "../../../procedures/registry.js";
11
11
  import type { AnyProcedure } from "../../../procedures/types.js";
12
- import { InvocationError, invokeProcedure, isAsyncIterable } from "../../../procedures/invoke.js";
12
+ import { InvocationError, invokeProcedure, isAsyncIterable, type Interceptor } from "../../../procedures/invoke.js";
13
+ import { TIMEOUT_METADATA_KEY } from "../../../client/deadline.js";
14
+ import { narrowCapability } from "../../../procedures/capability.js";
15
+ import type { Capability, ClearanceOrder } from "../../../procedures/types.js";
13
16
  import { methodToPath } from "../../../server/method.js";
17
+ import { errorCodeOf } from "../../../protocol.js";
18
+
19
+ /** The value of `untilAborted` when the signal aborts first. */
20
+ const ABORTED: unique symbol = Symbol("aborted");
21
+
22
+ /** The value of a promise, or ABORTED when the signal aborts first. The promise runs on. */
23
+ function untilAborted<T>(promise: Promise<T>, signal: AbortSignal | undefined): Promise<T | typeof ABORTED> {
24
+ if (!signal) return promise;
25
+ if (signal.aborted) {
26
+ promise.catch(() => undefined);
27
+ return Promise.resolve(ABORTED);
28
+ }
29
+ return new Promise((resolve, reject) => {
30
+ const onAbort = (): void => {
31
+ promise.catch(() => undefined);
32
+ resolve(ABORTED);
33
+ };
34
+ signal.addEventListener("abort", onAbort, { once: true });
35
+ promise.then(
36
+ (value) => {
37
+ signal.removeEventListener("abort", onAbort);
38
+ resolve(value);
39
+ },
40
+ (error: unknown) => {
41
+ signal.removeEventListener("abort", onAbort);
42
+ reject(error);
43
+ },
44
+ );
45
+ });
46
+ }
14
47
 
15
48
  function successItem<TRes>(id: string, payload: unknown): ResponseItem<TRes> {
16
49
  return { id, status: { type: "success", code: 200 }, payload: payload as TRes, metadata: {} };
@@ -53,10 +86,16 @@ export class LocalTransport implements Transport {
53
86
  private readonly handlers = new Map<string, Handler>();
54
87
  private readonly throwOnMissing: boolean;
55
88
  private readonly registry: ProcedureRegistry | undefined;
89
+ private readonly interceptors: readonly Interceptor[] | undefined;
90
+ private readonly capability: Capability | undefined;
91
+ private readonly clearanceOrder: ClearanceOrder | undefined;
56
92
 
57
93
  constructor(options: LocalTransportOptions = {}) {
58
94
  this.throwOnMissing = options.throwOnMissing !== false;
59
95
  this.registry = options.registry;
96
+ this.interceptors = options.interceptors;
97
+ this.capability = options.capability;
98
+ this.clearanceOrder = options.clearanceOrder;
60
99
 
61
100
  // Register pre-configured handlers. A key without a version ("users.get") gets the
62
101
  // empty version, as methodKey() makes it: before, such a key was never found.
@@ -136,21 +175,44 @@ export class LocalTransport implements Transport {
136
175
  let values: AsyncIterable<unknown> | undefined;
137
176
  let value: unknown;
138
177
  if (procedure) {
139
- const output = await invokeProcedure(procedure, message.payload, {
178
+ // The interceptors, the capability and the deadline of the call, as on a server (R14)
179
+ const timeoutMs = message.metadata[TIMEOUT_METADATA_KEY];
180
+ const capability =
181
+ typeof timeoutMs === "number" && Number.isFinite(timeoutMs) && timeoutMs >= 0
182
+ ? narrowCapability(this.capability, { timeoutMs }, this.clearanceOrder)
183
+ : this.capability;
184
+ const output = await untilAborted(invokeProcedure(procedure, message.payload, {
140
185
  registry: this.registry,
141
186
  metadata: message.metadata,
142
187
  signal: message.signal,
143
- });
188
+ ...(this.interceptors ? { interceptors: this.interceptors } : {}),
189
+ ...(capability ? { capability } : {}),
190
+ ...(this.clearanceOrder ? { clearanceOrder: this.clearanceOrder } : {}),
191
+ }), message.signal);
192
+ if (output === ABORTED) {
193
+ // The caller stops waiting at once, as over HTTP and WebSocket (the conformance suite
194
+ // of roadmap R18 found this: before, a local call waited for its handler)
195
+ yield errorItem<TRes>(message.id, "ABORTED", "Request was aborted");
196
+ return;
197
+ }
144
198
  if (output.kind === "stream") values = output.items;
145
199
  else value = output.value;
146
200
  } else {
147
201
  // Execute handler (may be sync, async or an async generator)
148
- const result = await handler!(message.payload, message);
202
+ const result = await untilAborted(Promise.resolve(handler!(message.payload, message)), message.signal);
203
+ if (result === ABORTED) {
204
+ yield errorItem<TRes>(message.id, "ABORTED", "Request was aborted");
205
+ return;
206
+ }
149
207
  if (isAsyncIterable(result)) values = result;
150
208
  else value = result;
151
209
  }
152
210
 
153
211
  if (values === undefined) {
212
+ if (message.signal?.aborted) {
213
+ yield errorItem<TRes>(message.id, "ABORTED", "Request was aborted");
214
+ return;
215
+ }
154
216
  yield successItem<TRes>(message.id, value);
155
217
  return;
156
218
  }
@@ -162,9 +224,13 @@ export class LocalTransport implements Transport {
162
224
  yield successItem<TRes>(message.id, item);
163
225
  }
164
226
  } catch (error) {
165
- // The handler threw, or the invocation failed (its code: VALIDATION_ERROR, ABORTED and so on)
166
- const code = error instanceof InvocationError ? error.code : "HANDLER_ERROR";
167
- yield errorItem<TRes>(message.id, code, error instanceof Error ? error.message : "Unknown error");
227
+ // The handler threw, or the invocation failed (its code: VALIDATION_ERROR, ABORTED and so
228
+ // on). A handler error keeps its own code, as on the other faces (WIRE-PROTOCOL.md).
229
+ const code = errorCodeOf(error);
230
+ const item = errorItem<TRes>(message.id, code, error instanceof Error ? error.message : "Unknown error");
231
+ // The reason of a FORBIDDEN error travels on each face (roadmap R25)
232
+ if (error instanceof InvocationError && error.reason) item.metadata = { reason: error.reason };
233
+ yield item;
168
234
  }
169
235
  }
170
236
 
@@ -172,6 +238,7 @@ export class LocalTransport implements Transport {
172
238
  // No resources to clean up
173
239
  }
174
240
 
241
+
175
242
  /**
176
243
  * Clear all handlers.
177
244
  */
@@ -6,6 +6,8 @@
6
6
 
7
7
  import type { Message, Method } from "../../../client/types.js";
8
8
  import type { ProcedureRegistry } from "../../../procedures/registry.js";
9
+ import type { Interceptor } from "../../../procedures/invoke.js";
10
+ import type { Capability, ClearanceOrder } from "../../../procedures/types.js";
9
11
 
10
12
  /**
11
13
  * Handler function for a method.
@@ -47,4 +49,20 @@ export interface LocalTransportOptions {
47
49
  * The procedure runs through `invokeProcedure()`, as on a server.
48
50
  */
49
51
  registry?: ProcedureRegistry;
52
+
53
+ /**
54
+ * The interceptors of each procedure that the transport runs, as on a server: policy, tracing,
55
+ * timeouts, metrics (roadmap R14). Before, a local call had none.
56
+ */
57
+ interceptors?: readonly Interceptor[] | undefined;
58
+
59
+ /**
60
+ * The capability of each call. The remaining time that a call sends (`procedure-timeout`)
61
+ * makes its deadline earlier, as on a server: a `Client` with a timeout gives a local call a
62
+ * deadline too (roadmap R14).
63
+ */
64
+ capability?: Capability | undefined;
65
+
66
+ /** The order of the clearance values of the capability. */
67
+ clearanceOrder?: ClearanceOrder | undefined;
50
68
  }
@@ -11,6 +11,7 @@ import type {
11
11
  MockTransportOptions,
12
12
  CallHistoryEntry,
13
13
  } from "./types.js";
14
+ import { optionalMs } from "../../../duration.js";
14
15
 
15
16
  /**
16
17
  * Mock transport implementation for testing.
@@ -26,8 +27,8 @@ import type {
26
27
  * ```typescript
27
28
  * // Create mock transport with latency
28
29
  * const mock = new MockTransport({
29
- * minLatency: 10,
30
- * maxLatency: 50,
30
+ * minLatency: ms(10),
31
+ * maxLatency: ms(50),
31
32
  * });
32
33
  *
33
34
  * // Mock a specific response
@@ -57,14 +58,15 @@ import type {
57
58
  export class MockTransport implements Transport {
58
59
  readonly name = "mock";
59
60
 
60
- private options: Required<MockTransportOptions>;
61
+ /** The options, with the latencies in milliseconds */
62
+ private options: Required<Omit<MockTransportOptions, "minLatency" | "maxLatency">> & { minLatency: number; maxLatency: number };
61
63
  private responses = new Map<ResponseMatcher, MockResponse>();
62
64
  private history: CallHistoryEntry[] = [];
63
65
 
64
66
  constructor(options: MockTransportOptions = {}) {
65
67
  this.options = {
66
- minLatency: options.minLatency ?? 0,
67
- maxLatency: options.maxLatency ?? 0,
68
+ minLatency: optionalMs(options.minLatency, "minLatency") ?? 0,
69
+ maxLatency: optionalMs(options.maxLatency, "maxLatency") ?? 0,
68
70
  failureRate: options.failureRate ?? 0,
69
71
  defaultErrorMessage: options.defaultErrorMessage ?? "Mock transport error",
70
72
  trackHistory: options.trackHistory ?? true,
@@ -247,7 +249,8 @@ export class MockTransport implements Transport {
247
249
 
248
250
  // Apply custom delay if specified
249
251
  if (mockResponse.delay !== undefined) {
250
- await new Promise((resolve) => setTimeout(resolve, mockResponse.delay));
252
+ const delay = optionalMs(mockResponse.delay, "delay");
253
+ await new Promise((resolve) => setTimeout(resolve, delay));
251
254
  }
252
255
 
253
256
  // Handle error response
@@ -5,6 +5,7 @@
5
5
  */
6
6
 
7
7
  import type { Message, ResponseItem } from "../../../client/types.js";
8
+ import type { Duration } from "../../../duration.js";
8
9
 
9
10
  /**
10
11
  * Mock response configuration.
@@ -26,10 +27,10 @@ export interface MockResponse<TRes = unknown> {
26
27
  error?: Error;
27
28
 
28
29
  /**
29
- * Delay in milliseconds before responding
30
+ * Delay before responding
30
31
  * @default Uses transport's latency config
31
32
  */
32
- delay?: number;
33
+ delay?: Duration;
33
34
 
34
35
  /**
35
36
  * Number of times this response can be used
@@ -48,16 +49,16 @@ export type ResponseMatcher = (method: any, payload: unknown) => boolean;
48
49
  */
49
50
  export interface MockTransportOptions {
50
51
  /**
51
- * Minimum latency in milliseconds
52
+ * Minimum latency
52
53
  * @default 0
53
54
  */
54
- minLatency?: number;
55
+ minLatency?: Duration;
55
56
 
56
57
  /**
57
- * Maximum latency in milliseconds
58
+ * Maximum latency
58
59
  * @default 0
59
60
  */
60
- maxLatency?: number;
61
+ maxLatency?: Duration;
61
62
 
62
63
  /**
63
64
  * Random failure rate (0-1)
@@ -0,0 +1,124 @@
1
+ /**
2
+ * The transport over a message port (roadmap R27): a Web Worker, a `MessagePort` of a
3
+ * `MessageChannel`, an iframe, or a `worker_threads` port of Node.
4
+ *
5
+ * The two sides of a port send calls to each other with the frames of the WebSocket transport
6
+ * (`websocket/shared/rpc.ts`): a request, then a response, an error, or a stream with credit, and
7
+ * a cancel. A port sends objects, not text, so a frame needs no JSON. A side serves the calls of
8
+ * the other side with its `serve` handler, for example a `ProcedureServer`. Before, the site had
9
+ * two worker protocols of its own.
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * // In the worker: serve the procedures of a registry
14
+ * new PortTransport(self, { serve: new ProcedureServer({ registry, autoRegister: true }) });
15
+ *
16
+ * // On the page: call them
17
+ * const client = new Client(new PortTransport(new Worker(url, { type: "module" })));
18
+ * for await (const item of client.stream({ service: "site", operation: "run" }, input)) { ... }
19
+ * ```
20
+ */
21
+
22
+ import type { Message, ResponseItem, Transport } from "../../client/types.js";
23
+ import type { RequestHandler } from "../../server/types.js";
24
+ import type { WebSocketMessage } from "../websocket/server/types.js";
25
+ import { IncomingCalls, OutgoingCalls } from "../websocket/shared/rpc.js";
26
+ import { withoutInternalKeys } from "../metadata.js";
27
+
28
+ /**
29
+ * A message port: the object that a worker, a `MessagePort` or a `worker_threads` port gives. It
30
+ * has `postMessage`, and `addEventListener` (the browser form) or `on` (the Node form).
31
+ *
32
+ * The event of a listener has the type `any`: the DOM and Node give it different types, and a
33
+ * port of each must fit without a cast. The transport reads only its `data`.
34
+ */
35
+ export interface PortLike {
36
+ postMessage(message: unknown): void;
37
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
38
+ addEventListener?(type: "message", listener: (event: any) => void): void;
39
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
40
+ removeEventListener?(type: "message", listener: (event: any) => void): void;
41
+ on?(type: "message", listener: (data: unknown) => void): unknown;
42
+ off?(type: "message", listener: (data: unknown) => void): unknown;
43
+ /** A `MessagePort` delivers its messages to `addEventListener` only after `start()`. */
44
+ start?(): void;
45
+ }
46
+
47
+ /** The options of a `PortTransport`. */
48
+ export interface PortTransportOptions {
49
+ /** The handler of the calls of the other side, for example a `ProcedureServer`. */
50
+ serve?: RequestHandler | undefined;
51
+ /** The number of stream items that the reader holds. The default is 64. */
52
+ streamWindow?: number | undefined;
53
+ }
54
+
55
+ /** A frame of the protocol: an object with an id and a type. */
56
+ function isFrame(value: unknown): value is WebSocketMessage {
57
+ return value !== null && typeof value === "object" && typeof (value as { type?: unknown }).type === "string";
58
+ }
59
+
60
+ /** The transport over a message port, in the two directions. */
61
+ export class PortTransport implements Transport {
62
+ readonly name = "port";
63
+ private readonly outgoing: OutgoingCalls;
64
+ private readonly incoming: IncomingCalls | undefined;
65
+ private open = true;
66
+ private readonly onBrowserMessage = (event: { data: unknown }): void => this.receive(event.data);
67
+ private readonly onNodeMessage = (data: unknown): void => this.receive(data);
68
+
69
+ constructor(
70
+ private readonly port: PortLike,
71
+ options: PortTransportOptions = {},
72
+ ) {
73
+ const send = (frame: WebSocketMessage): void => this.port.postMessage(frame);
74
+ const isOpen = (): boolean => this.open;
75
+ this.outgoing = new OutgoingCalls({
76
+ send,
77
+ isOpen,
78
+ // No time limit: a worker can run a long program. The `signal` of a call stops it. This file
79
+ // does not import the Durations (quantity, 6 kB of gzip), so the page of a worker stays small.
80
+ requestTimeoutMs: 0,
81
+ streamWindow: Math.max(2, options.streamWindow ?? 64),
82
+ idPrefix: "",
83
+ });
84
+ if (options.serve) {
85
+ this.incoming = new IncomingCalls({
86
+ handler: options.serve,
87
+ send,
88
+ isOpen,
89
+ // A call over a port comes from a part of this application: it has no credentials
90
+ requestOf: (frame, signal) => ({
91
+ id: frame.id,
92
+ method: frame.method!,
93
+ payload: frame.payload === undefined ? {} : frame.payload,
94
+ metadata: withoutInternalKeys(frame.metadata),
95
+ signal,
96
+ }),
97
+ });
98
+ }
99
+ if (port.addEventListener) port.addEventListener("message", this.onBrowserMessage);
100
+ else port.on?.("message", this.onNodeMessage);
101
+ port.start?.();
102
+ }
103
+
104
+ private receive(data: unknown): void {
105
+ if (!this.open || !isFrame(data)) return;
106
+ if (this.outgoing.receive(data)) return;
107
+ this.incoming?.receive(data);
108
+ }
109
+
110
+ /** This function sends a call to the other side, and gives its response items. */
111
+ send<TReq, TRes>(message: Message<TReq>): AsyncIterable<ResponseItem<TRes>> {
112
+ return this.outgoing.call<TReq, TRes>(message);
113
+ }
114
+
115
+ /** This function stops the calls in the two directions, and stops to listen. It does not close the port. */
116
+ async close(): Promise<void> {
117
+ if (!this.open) return;
118
+ this.open = false;
119
+ this.outgoing.failAll("The port transport closed");
120
+ this.incoming?.abortAll();
121
+ if (this.port.removeEventListener) this.port.removeEventListener("message", this.onBrowserMessage);
122
+ else this.port.off?.("message", this.onNodeMessage);
123
+ }
124
+ }
@@ -7,6 +7,7 @@
7
7
  // Types
8
8
  export type {
9
9
  WebSocketTransportOptions,
10
+ WebSocketClass,
10
11
  WebSocketMessage,
11
12
  ServerRequestMessage,
12
13
  ServerRequestHandler,
@@ -0,0 +1,34 @@
1
+ /**
2
+ * The WebSocket client transport for Node (finding of roadmap R36).
3
+ *
4
+ * This class is the `WebSocketTransport` of the browser entry with one other default: its sockets
5
+ * are of the `WebSocket` class of the `ws` package. A drop by the heartbeat or by the time limit
6
+ * of the handshake then ends the socket at once with `terminate()`. The global `WebSocket` of Node
7
+ * has only `close()`, which waits for the answer of the peer. With a dead peer, that socket stays
8
+ * open until the kernel stops it: 924.6 seconds in the harness cells of R36.
9
+ *
10
+ * Only the Node entry `@procedures/client/node` exports this class. The browser entry does not
11
+ * import `ws`.
12
+ */
13
+
14
+ import { WebSocket as NodeWebSocket } from "ws";
15
+ import { WebSocketTransport as PortableWebSocketTransport } from "./transport.js";
16
+ import type { WebSocketTransportOptions } from "./types.js";
17
+
18
+ /**
19
+ * The WebSocket client transport with the `WebSocket` class of the `ws` package as the default
20
+ * class of its sockets. The option `webSocket` gives another class.
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * import { Client } from "@procedures/client";
25
+ * import { WebSocketTransport } from "@procedures/client/node";
26
+ *
27
+ * const client = new Client({ transport: new WebSocketTransport({ url: "ws://localhost:3000/ws" }) });
28
+ * ```
29
+ */
30
+ export class WebSocketTransport extends PortableWebSocketTransport {
31
+ constructor(options: WebSocketTransportOptions) {
32
+ super({ ...options, webSocket: options.webSocket ?? NodeWebSocket });
33
+ }
34
+ }