@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
@@ -14,6 +14,7 @@ import {
14
14
  parseProcedureJson,
15
15
  stringifyProcedureJson,
16
16
  isAnyProcedureRef,
17
+ isProcedureRef,
17
18
  isOutputRef,
18
19
  isPlainObject,
19
20
  resolveOutputRef,
@@ -58,18 +59,37 @@ const ImportOutputSchema = z.looseObject({
58
59
 
59
60
  type ImportProcedure = Procedure<ImportInput, ImportOutput, { description: string; tags: string[] }>;
60
61
 
62
+ /**
63
+ * This function gives true for the runtime form of a procedure reference whose path is a list of
64
+ * texts. `client.import` accepts only such a value at the top level.
65
+ */
66
+ function isImportableRef(value: unknown): boolean {
67
+ if (!isProcedureRef(value)) return false;
68
+ const path = (value as { path?: unknown }).path;
69
+ return Array.isArray(path) && path.length > 0 && path.every((segment) => typeof segment === "string");
70
+ }
71
+
61
72
  const importProcedure: ImportProcedure = defineProcedure({
62
73
  path: ["import"],
63
74
  input: ImportInputSchema,
64
75
  output: ImportOutputSchema,
65
76
  metadata: {
66
- description: "Parse procedure aggregation from JSON string",
77
+ description:
78
+ "Parse a procedure reference ({ $proc, input }) from JSON text. Other JSON, for example a number or a list, gives success false.",
67
79
  tags: ["core", "meta"],
68
80
  },
69
81
  handler: async (input: ImportInput): Promise<ImportOutput> => {
70
82
  try {
71
83
  if (input.json) {
72
84
  const procedure = parseProcedureJson(input.json);
85
+ // Before, each JSON value gave success true, for example 42 (catalog test, roadmap R29)
86
+ if (!isImportableRef(procedure)) {
87
+ return {
88
+ procedure: null,
89
+ success: false,
90
+ error: "The JSON is not a procedure reference. A reference is an object whose $proc is a list of texts.",
91
+ };
92
+ }
73
93
  return { procedure, success: true };
74
94
  }
75
95
  if (input.path) {
@@ -107,8 +127,19 @@ interface ExportInput {
107
127
  pretty?: boolean | undefined;
108
128
  }
109
129
 
130
+ /**
131
+ * This function gives a schema of a required value: undefined has no JSON text, so the input
132
+ * schema refuses it with `VALIDATION_ERROR`. Before, the call failed with
133
+ * `OUTPUT_VALIDATION_ERROR` (catalog test, roadmap R29).
134
+ */
135
+ const definedValue = (description: string) =>
136
+ z
137
+ .unknown()
138
+ .refine((value) => value !== undefined, { message: "The value is missing: undefined has no JSON text" })
139
+ .describe(description);
140
+
110
141
  const ExportInputSchema = z.object({
111
- procedure: z.unknown().optional().describe("Procedure reference to serialize"),
142
+ procedure: definedValue("The procedure reference to serialize. The procedure refuses a missing value (undefined) with VALIDATION_ERROR, because undefined has no JSON text."),
112
143
  pretty: z.boolean().optional().describe("Pretty print JSON output"),
113
144
  });
114
145
 
@@ -134,13 +165,15 @@ const exportProcedure: ExportProcedure = defineProcedure({
134
165
  input: ExportInputSchema,
135
166
  output: ExportOutputSchema,
136
167
  metadata: {
137
- description: "Serialize procedure aggregation to JSON string",
168
+ description: "Serialize a procedure reference to JSON text. It refuses a missing procedure (undefined) with VALIDATION_ERROR.",
138
169
  // The refs of the input are data: exec() must not run them (deep dive CORE-5)
139
170
  tags: ["core", "meta", RAW_INPUT_TAG],
140
171
  },
141
172
  handler: async (input: ExportInput): Promise<ExportOutput> => {
142
173
  try {
143
- const json = stringifyProcedureJson(input.procedure, input.pretty ? 2 : undefined);
174
+ const json = stringifyProcedureJson(input.procedure, input.pretty ? 2 : undefined) as string | undefined;
175
+ // A function or a symbol has no JSON text either: an error result, not an invalid output
176
+ if (json === undefined) return { json: "", success: false, error: "The procedure has no JSON text" };
144
177
  return { json, success: true };
145
178
  } catch (error) {
146
179
  return {
@@ -268,7 +301,7 @@ interface StringifyJsonInput {
268
301
  }
269
302
 
270
303
  const StringifyJsonInputSchema = z.object({
271
- value: z.unknown().optional().describe("Value to stringify"),
304
+ value: definedValue("The value to write as JSON. The procedure refuses a missing value (undefined) with VALIDATION_ERROR, because undefined has no JSON text."),
272
305
  pretty: z.boolean().optional().describe("Pretty print with indentation"),
273
306
  indent: z.union([z.number(), z.string()]).optional().describe("Custom indentation (number of spaces or string)"),
274
307
  });
@@ -280,13 +313,16 @@ const stringifyJsonProcedure: StringifyJsonProcedure = defineProcedure({
280
313
  input: StringifyJsonInputSchema,
281
314
  output: stringOutput("The JSON text"),
282
315
  metadata: {
283
- description: "Stringify value to JSON (with procedure ref support)",
316
+ description: "Stringify value to JSON (with procedure ref support). It refuses a missing value (undefined) with VALIDATION_ERROR.",
284
317
  // The refs of the input are data: exec() must not run them (deep dive CORE-5)
285
318
  tags: ["core", "meta", RAW_INPUT_TAG],
286
319
  },
287
320
  handler: async (input: StringifyJsonInput): Promise<string> => {
288
321
  const indent = input.pretty ? (input.indent ?? 2) : input.indent;
289
- return stringifyProcedureJson(input.value, indent as number);
322
+ const json = stringifyProcedureJson(input.value, indent as number) as string | undefined;
323
+ // A function or a symbol has no JSON text either: a clear error, not an invalid output
324
+ if (json === undefined) throw new Error("The value has no JSON text");
325
+ return json;
290
326
  },
291
327
  });
292
328
 
@@ -331,7 +367,8 @@ const lookupProcedure: LookupProcedure = defineProcedure({
331
367
  handler: async (input: LookupInput, ctx): Promise<LookupOutput> => {
332
368
  // The registry of the caller (a server or a client with its own registry)
333
369
  const proc = (ctx?.registry ?? PROCEDURE_REGISTRY).get(input.path);
334
- if (proc) {
370
+ // A procedure outside the expose rule of the host does not exist for its callers (R17)
371
+ if (proc && (!ctx?.expose || ctx.expose(proc.path))) {
335
372
  return {
336
373
  exists: true,
337
374
  metadata: proc.metadata,
@@ -11,15 +11,46 @@ import { defineProcedure, namespace } from "../define.js";
11
11
  import type { AnyProcedure, Procedure } from "../types.js";
12
12
  import { PURE_EFFECTS, withEffects } from "../effects.js";
13
13
 
14
+ // =============================================================================
15
+ // Helpers: own keys only (roadmap R6)
16
+ // =============================================================================
17
+
18
+ /** The path segments that reach a prototype. The registry refuses them in paths too. */
19
+ const PROTOTYPE_KEYS = new Set(["__proto__", "constructor", "prototype"]);
20
+
21
+ /**
22
+ * The segments of a dotted path. A segment that reaches a prototype is an error: before,
23
+ * `client.set` with the path `__proto__.x` changed the prototype of its result (roadmap R6).
24
+ */
25
+ function segmentsOf(path: string): string[] {
26
+ const parts = path.split(".");
27
+ for (const part of parts) {
28
+ if (PROTOTYPE_KEYS.has(part)) {
29
+ throw new Error(`The path "${path}" has the segment "${part}", which reaches a prototype`);
30
+ }
31
+ }
32
+ return parts;
33
+ }
34
+
35
+ function hasOwn(obj: object, key: string): boolean {
36
+ return Object.prototype.hasOwnProperty.call(obj, key);
37
+ }
38
+
39
+ /** This function sets an own data property: a "__proto__" key of the data stays data. */
40
+ function put(target: Record<string, unknown>, key: string, value: unknown): void {
41
+ Object.defineProperty(target, key, { value, writable: true, enumerable: true, configurable: true });
42
+ }
43
+
14
44
  // =============================================================================
15
45
  // Helper: Get nested value by path
16
46
  // =============================================================================
17
47
 
18
48
  function getByPath(obj: unknown, path: string): unknown {
19
- const parts = path.split(".");
49
+ const parts = segmentsOf(path);
20
50
  let current: unknown = obj;
21
51
  for (const part of parts) {
22
- if (current === null || current === undefined) {
52
+ // Only own fields: before, "toString" gave a function of the prototype
53
+ if (current === null || typeof current !== "object" || !hasOwn(current, part)) {
23
54
  return undefined;
24
55
  }
25
56
  current = (current as Record<string, unknown>)[part];
@@ -33,22 +64,22 @@ function getByPath(obj: unknown, path: string): unknown {
33
64
 
34
65
  function setByPath(obj: Record<string, unknown>, path: string, value: unknown): Record<string, unknown> {
35
66
  const result = { ...obj };
36
- const parts = path.split(".");
67
+ const parts = segmentsOf(path);
37
68
  let current: Record<string, unknown> = result;
38
69
 
39
70
  for (let i = 0; i < parts.length - 1; i++) {
40
71
  const part = parts[i]!;
41
- if (!(part in current) || typeof current[part] !== "object" || current[part] === null) {
42
- current[part] = {};
72
+ if (!hasOwn(current, part) || typeof current[part] !== "object" || current[part] === null) {
73
+ put(current, part, {});
43
74
  } else {
44
- current[part] = { ...(current[part] as Record<string, unknown>) };
75
+ put(current, part, { ...(current[part] as Record<string, unknown>) });
45
76
  }
46
77
  current = current[part] as Record<string, unknown>;
47
78
  }
48
79
 
49
80
  const lastPart = parts[parts.length - 1];
50
81
  if (lastPart !== undefined) {
51
- current[lastPart] = value;
82
+ put(current, lastPart, value);
52
83
  }
53
84
  return result;
54
85
  }
@@ -139,16 +170,17 @@ function deepMerge(target: Record<string, unknown>, source: Record<string, unkno
139
170
  typeof source[key] === "object" &&
140
171
  source[key] !== null &&
141
172
  !Array.isArray(source[key]) &&
173
+ hasOwn(result, key) &&
142
174
  typeof result[key] === "object" &&
143
175
  result[key] !== null &&
144
176
  !Array.isArray(result[key])
145
177
  ) {
146
- result[key] = deepMerge(
178
+ put(result, key, deepMerge(
147
179
  result[key] as Record<string, unknown>,
148
180
  source[key] as Record<string, unknown>
149
- );
181
+ ));
150
182
  } else {
151
- result[key] = source[key];
183
+ put(result, key, source[key]);
152
184
  }
153
185
  }
154
186
  return result;
@@ -166,7 +198,8 @@ const mergeProcedure: MergeProcedure = defineProcedure({
166
198
  if (input.deep) {
167
199
  return input.objects.reduce((acc, obj) => deepMerge(acc, obj), {});
168
200
  }
169
- return Object.assign({}, ...input.objects);
201
+ // Object.assign sets a "__proto__" key of the data as the prototype: spread keeps it data
202
+ return input.objects.reduce<Record<string, unknown>>((acc, obj) => ({ ...acc, ...obj }), {});
170
203
  },
171
204
  });
172
205
 
@@ -307,8 +340,9 @@ const pickProcedure: PickProcedure = defineProcedure({
307
340
  handler: async (input: PickInput): Promise<Record<string, unknown>> => {
308
341
  const result: Record<string, unknown> = {};
309
342
  for (const key of input.keys) {
310
- if (key in input.object) {
311
- result[key] = input.object[key];
343
+ // Only own fields: before, "toString" picked a function of the prototype
344
+ if (hasOwn(input.object, key)) {
345
+ put(result, key, input.object[key]);
312
346
  }
313
347
  }
314
348
  return result;
@@ -344,7 +378,7 @@ const omitProcedure: OmitProcedure = defineProcedure({
344
378
  const result: Record<string, unknown> = {};
345
379
  for (const key of Object.keys(input.object)) {
346
380
  if (!keysToOmit.has(key)) {
347
- result[key] = input.object[key];
381
+ put(result, key, input.object[key]);
348
382
  }
349
383
  }
350
384
  return result;
@@ -7,6 +7,8 @@ import type { ProcedureContext, ProcedurePath } from "../types.js";
7
7
  import { PROCEDURE_REGISTRY } from "../registry.js";
8
8
  import {
9
9
  isAnyProcedureRef,
10
+ getRefWhen,
11
+ WHEN_NEVER,
10
12
  normalizeRef,
11
13
  hydrateInput,
12
14
  createRefScope,
@@ -73,6 +75,11 @@ export async function callOperand(
73
75
  ctx: ProcedureContext,
74
76
  options: { scope?: RefScope | undefined; extra?: Record<string, unknown> | undefined } = {},
75
77
  ): Promise<unknown> {
78
+ // A $never operand is data with its full input, as a $never call anywhere (roadmap R4: before,
79
+ // only a chain step obeyed it, and a $never branch, task or fn started)
80
+ if (getRefWhen(ref) === WHEN_NEVER) {
81
+ return ref;
82
+ }
76
83
  const normalized = normalizeRef(ref);
77
84
  const raw = rawRule(ctx)(normalized.path);
78
85
  let input: unknown = raw ? normalized.input : await hydrateOperand(normalized.input, ctx, options.scope);
@@ -132,7 +132,9 @@ const replaceProcedure: ReplaceProcedure = defineProcedure({
132
132
  if (input.all) {
133
133
  return input.value.split(input.search).join(input.replace);
134
134
  }
135
- return input.value.replace(input.search, input.replace);
135
+ // A function gives the replacement as text. Before, "$$" became "$" and "$&" became the match
136
+ // (catalog test, roadmap R29).
137
+ return input.value.replace(input.search, () => input.replace);
136
138
  },
137
139
  });
138
140
 
@@ -382,7 +384,7 @@ interface TemplateInput {
382
384
  }
383
385
 
384
386
  const TemplateInputSchema = z.object({
385
- template: z.string().describe("The template text, with {name} placeholders"),
387
+ template: z.string().describe("The template text, with {{name}} placeholders"),
386
388
  values: z.record(z.string(), z.unknown()).describe("The values"),
387
389
  });
388
390
 
@@ -10,6 +10,7 @@ import { anyOutput, booleanOutput, stringOutput } from "./outputs.js";
10
10
  import { defineProcedure, namespace } from "../define.js";
11
11
  import type { AnyProcedure, Procedure } from "../types.js";
12
12
  import { PURE_EFFECTS, withEffects } from "../effects.js";
13
+ import { isPlainObject } from "../ref.js";
13
14
 
14
15
  // =============================================================================
15
16
  // Typeof Procedure
@@ -168,11 +169,12 @@ const isObjectProcedure: IsObjectProcedure = defineProcedure({
168
169
  input: IsObjectInputSchema,
169
170
  output: booleanOutput("True for a plain object"),
170
171
  metadata: {
171
- description: "Check if value is a plain object (not array, not null)",
172
+ description: "Check if value is a plain object (not array, not null, not a Date or another class instance)",
172
173
  tags: ["core", "type"],
173
174
  },
174
175
  handler: async (input: IsObjectInput): Promise<boolean> => {
175
- return typeof input.value === "object" && input.value !== null && !Array.isArray(input.value);
176
+ // Before, a Date or a Map gave true (catalog test, roadmap R29)
177
+ return isPlainObject(input.value);
176
178
  },
177
179
  });
178
180
 
@@ -34,6 +34,7 @@ import type {
34
34
  ProcedureContext,
35
35
  AnyProcedure,
36
36
  ProcedureRegistryLike,
37
+ Effects,
37
38
  } from "./types.js";
38
39
  import { z } from "zod";
39
40
  import { anySchema, zodAdapter } from "./core/schemas.js";
@@ -47,7 +48,10 @@ import {
47
48
  rawInputRule,
48
49
  RUNS_REFS_TAG,
49
50
  } from "./ref.js";
50
- import { PURE_EFFECTS, REGISTRY_READ_EFFECTS, withEffects } from "./effects.js";
51
+ import { INFERRED_EFFECTS_METADATA_KEY, PURE_EFFECTS, REGISTRY_READ_EFFECTS, effectsValue, withEffects } from "./effects.js";
52
+ import { MANIFEST_FORMAT, describeProcedure as describeEntry, describeRegistry, type ProcedureManifest } from "./manifest.js";
53
+ import type { JsonSchema } from "./schema.js";
54
+ import { isRuntimeDefined, markRuntimeProgram, runtimeProgramOfHandler } from "./runtime-programs.js";
51
55
 
52
56
  // Input schemas of procedure.define and procedure.delete (roadmap 2.1)
53
57
  const procedurePathField = z.array(z.string().min(1)).min(1);
@@ -151,7 +155,24 @@ const GetOutputSchema = z.looseObject({
151
155
  metadata: descriptionFields.metadata,
152
156
  hasInputSchema: z.boolean().optional().describe("True when the procedure has an input schema"),
153
157
  hasOutputSchema: z.boolean().optional().describe("True when the procedure has an output schema"),
154
- }) as z.ZodType<{ found: false } | (ProcedureDescription & { found: true; hasInputSchema: boolean; hasOutputSchema: boolean })>;
158
+ input: z.record(z.string(), z.unknown()).optional().describe("The JSON Schema of the input"),
159
+ output: z.record(z.string(), z.unknown()).optional().describe("The JSON Schema of the output"),
160
+ effects: z.unknown().optional().describe("The declared effects: reads, writes, idempotent, destructive"),
161
+ }) as z.ZodType<{ found: false } | (ProcedureDescription & GetSchemas & { found: true; hasInputSchema: boolean; hasOutputSchema: boolean })>;
162
+
163
+ /** The JSON Schemas and the effects that `procedure.get` gives (roadmap R17). */
164
+ interface GetSchemas {
165
+ input?: JsonSchema | undefined;
166
+ output?: JsonSchema | undefined;
167
+ effects?: Effects | undefined;
168
+ }
169
+
170
+ /** The output of `procedure.manifest` */
171
+ const ManifestOutputSchema = z.looseObject({
172
+ format: z.literal(MANIFEST_FORMAT).describe("The format of the manifest"),
173
+ hash: z.string().describe("A hash of the entries: two manifests with the same hash describe the same procedures"),
174
+ procedures: z.array(z.looseObject({ path: z.array(z.string()), key: z.string() })).describe("The procedures, in the order of their keys"),
175
+ }) as unknown as z.ZodType<ProcedureManifest>;
155
176
 
156
177
  /** The output of `procedure.list` */
157
178
  const ListOutputSchema = z.looseObject({
@@ -204,26 +225,15 @@ function createAggregationHandler(
204
225
  // Runtime-Defined Procedures
205
226
  // =============================================================================
206
227
 
207
- /**
208
- * The handlers that `procedure.define` made, with the program (the aggregation) of each. The
209
- * registry is the only store of the procedures: a procedure is runtime-defined when its handler
210
- * is a key here. (Before, a separate map kept them, and `procedure.delete` removed only the map
211
- * entry: deep dive CORE-3.) The program is kept here, not only in the metadata: a caller that
212
- * gives its own metadata to `procedure.define` replaces the metadata.
213
- */
214
- const RUNTIME_PROGRAMS = new WeakMap<object, AggregationDefinition>();
215
-
216
- /** True for a procedure that `procedure.define` made. */
217
- export function isRuntimeDefined(procedure: { handler?: unknown }): boolean {
218
- return typeof procedure.handler === "function" && RUNTIME_PROGRAMS.has(procedure.handler);
219
- }
228
+ // The store of the programs of procedure.define (runtime-programs.ts, which imports nothing)
229
+ export { isRuntimeDefined } from "./runtime-programs.js";
220
230
 
221
231
  /**
222
232
  * The program (the aggregation) of a procedure that `procedure.define` made, or undefined for a
223
233
  * code procedure. A program store saves it and defines it again after a restart (roadmap 3.2).
224
234
  */
225
235
  export function runtimeProgramOf(procedure: { handler?: unknown }): AggregationDefinition | undefined {
226
- return typeof procedure.handler === "function" ? RUNTIME_PROGRAMS.get(procedure.handler) : undefined;
236
+ return runtimeProgramOfHandler(procedure.handler) as AggregationDefinition | undefined;
227
237
  }
228
238
 
229
239
  type RegistryOf = Pick<ProcedureRegistryLike, "get" | "getAll" | "unregister">;
@@ -327,6 +337,10 @@ export const defineProcedureProcedure: Procedure<
327
337
  throw new Error(`Procedure already exists at path: ${pathKey}`);
328
338
  }
329
339
 
340
+ // The effects of the body that the host found (roadmap R22). Only an interceptor of the host
341
+ // can give them: each transport removes the key from a request.
342
+ const inferred = effectsValue(ctx?.metadata?.[INFERRED_EFFECTS_METADATA_KEY]);
343
+
330
344
  // Create the procedure
331
345
  const procedure: AnyProcedure = defineProcedure({
332
346
  path,
@@ -341,13 +355,15 @@ export const defineProcedureProcedure: Procedure<
341
355
  // No effects of its own, as client.chain: the admission examines each nested call of a
342
356
  // data-driven procedure. The framework sets it, so the metadata of a caller cannot claim less
343
357
  effects: PURE_EFFECTS,
358
+ // The framework sets this field too: the effects of the body, for the tool hints
359
+ ...(inferred ? { inferredEffects: inferred } : { inferredEffects: undefined }),
344
360
  },
345
361
  handler: createAggregationHandler(aggregation),
346
362
  });
347
363
 
348
364
  // Its behavior comes from data: a server with an expose rule lets it call only exposed procedures
349
365
  markDataDriven(procedure);
350
- RUNTIME_PROGRAMS.set(procedure.handler!, aggregation);
366
+ markRuntimeProgram(procedure.handler!, aggregation);
351
367
 
352
368
  // Validate the procedure
353
369
  validateProcedure(procedure);
@@ -435,7 +451,7 @@ const getInputSchema: z.ZodType<ProcedureGetInput> = z.object({
435
451
  */
436
452
  export const getProcedureProcedure: Procedure<
437
453
  ProcedureGetInput,
438
- { found: false } | (ProcedureDescription & { found: true; hasInputSchema: boolean; hasOutputSchema: boolean }),
454
+ { found: false } | (ProcedureDescription & GetSchemas & { found: true; hasInputSchema: boolean; hasOutputSchema: boolean }),
439
455
  ProcedureMetadata
440
456
  > = defineProcedure({
441
457
  path: ["procedure", "get"],
@@ -450,14 +466,20 @@ export const getProcedureProcedure: Procedure<
450
466
  handler: async (input: ProcedureGetInput, ctx?: ProcedureContext) => {
451
467
  const path = typeof input.path === "string" ? keyToPath(input.path) : input.path;
452
468
  const procedure = (ctx?.registry ?? PROCEDURE_REGISTRY).get(path);
453
- if (!procedure) {
469
+ // A procedure outside the expose rule of the host is not found (R17)
470
+ if (!procedure || (ctx?.expose && !ctx.expose(procedure.path))) {
454
471
  return { found: false as const };
455
472
  }
473
+ const entry = describeEntry(procedure);
456
474
  return {
457
475
  found: true as const,
458
476
  ...describeProcedure(procedure, true),
459
477
  hasInputSchema: !!procedure.input,
460
478
  hasOutputSchema: !!procedure.output,
479
+ // The JSON Schemas and the effects of the one manifest type (R17)
480
+ ...(entry.input ? { input: entry.input } : {}),
481
+ ...(entry.output ? { output: entry.output } : {}),
482
+ ...(entry.effects ? { effects: entry.effects } : {}),
461
483
  };
462
484
  },
463
485
  });
@@ -484,6 +506,8 @@ export const listProceduresProcedure: Procedure<
484
506
  },
485
507
  handler: async (input: ProcedureListInput, ctx?: ProcedureContext) => {
486
508
  let all = (ctx?.registry ?? PROCEDURE_REGISTRY).getAll();
509
+ // Only the procedures that the host exposes (R17)
510
+ if (ctx?.expose) all = all.filter((procedure) => ctx.expose!(procedure.path));
487
511
  if (input.runtime) all = all.filter(isRuntimeDefined);
488
512
  if (input.namespace) all = all.filter((procedure) => procedure.path[0] === input.namespace);
489
513
  const procedures = all.map((procedure) => describeProcedure(procedure, input.includeMetadata ?? false));
@@ -491,6 +515,31 @@ export const listProceduresProcedure: Procedure<
491
515
  },
492
516
  });
493
517
 
518
+ /**
519
+ * The manifest of the caller's registry (roadmap R17): each procedure that the host exposes, with
520
+ * its JSON Schemas, effects and command-line metadata, and a hash. The compiler and the SDK
521
+ * generators read it.
522
+ */
523
+ export const manifestProcedure: Procedure<{ namespace?: string | undefined }, ProcedureManifest, ProcedureMetadata> = defineProcedure({
524
+ path: ["procedure", "manifest"],
525
+ input: zodAdapter(
526
+ z.object({ namespace: z.string().optional().describe("Only the procedures under this first path segment") }) as z.ZodType<{ namespace?: string | undefined }>,
527
+ ),
528
+ output: ManifestOutputSchema,
529
+ metadata: {
530
+ description: "The manifest of the registry: each exposed procedure with its schemas and effects",
531
+ tags: ["core", "meta"],
532
+ args: [],
533
+ shorts: { namespace: "n" },
534
+ output: "json",
535
+ },
536
+ handler: async (input: { namespace?: string | undefined }, ctx?: ProcedureContext) =>
537
+ describeRegistry(ctx?.registry ?? PROCEDURE_REGISTRY, {
538
+ ...(ctx?.expose ? { expose: ctx.expose } : {}),
539
+ ...(input.namespace ? { include: (procedure: AnyProcedure) => procedure.path[0] === input.namespace } : {}),
540
+ }),
541
+ });
542
+
494
543
  /**
495
544
  * Delete a runtime-defined procedure.
496
545
  */
@@ -532,6 +581,10 @@ export const deleteProcedureProcedure: Procedure<
532
581
  export const metaProcedures: AnyProcedure[] = [
533
582
  // They read the registry. define and delete change it (CAPABILITIES-2026-10).
534
583
  ...withEffects({ reads: ["registry"], writes: ["registry"] }, [defineProcedureProcedure as AnyProcedure]),
535
- ...withEffects(REGISTRY_READ_EFFECTS, [getProcedureProcedure as AnyProcedure, listProceduresProcedure as AnyProcedure]),
584
+ ...withEffects(REGISTRY_READ_EFFECTS, [
585
+ getProcedureProcedure as AnyProcedure,
586
+ listProceduresProcedure as AnyProcedure,
587
+ manifestProcedure as AnyProcedure,
588
+ ]),
536
589
  ...withEffects({ reads: ["registry"], writes: ["registry"] }, [deleteProcedureProcedure as AnyProcedure]),
537
590
  ];
@@ -1,13 +1,11 @@
1
1
  /**
2
2
  * Procedure Registration Helpers
3
3
  *
4
- * Manual registration helpers for procedures. For compile-time auto-discovery,
5
- * use the CLI: `npx client-registry discover`
4
+ * These helpers add procedures to the global registry.
6
5
  *
7
- * Libraries that depend on client should:
8
- * 1. Add `"client": { "procedures": "./dist/register.js" }` to package.json
9
- * 2. Create a register.ts that uses createAndRegister()
10
- * 3. Consumers run `npx client-registry discover` to generate imports
6
+ * A package with procedures declares `"client": { "procedures": "./dist/register.js" }` in its
7
+ * `package.json`. Its `src/register.ts` registers its module. `mark` finds the packages by the
8
+ * `client.procedures` field, and an app imports the `/register` entry of each package that it uses.
11
9
  */
12
10
 
13
11
  import { PROCEDURE_REGISTRY } from "./registry.js";
@@ -11,6 +11,41 @@ export const PURE_EFFECTS: Effects = Object.freeze({ reads: Object.freeze([]), w
11
11
  /** The effects of a procedure that reads the procedure registry and changes nothing. */
12
12
  export const REGISTRY_READ_EFFECTS: Effects = Object.freeze({ reads: Object.freeze(["registry"]), writes: Object.freeze([]) });
13
13
 
14
+ /**
15
+ * The metadata key of the effects that a host found for the body of `procedure.define`
16
+ * (`programCheckInterceptor` of `@procedures/compiler`, roadmap R22). The key starts with `__`, so
17
+ * each transport removes it from a request: only an interceptor of the host can set it.
18
+ */
19
+ export const INFERRED_EFFECTS_METADATA_KEY = "__inferredEffects";
20
+
21
+ function isNameList(value: unknown): value is string[] {
22
+ return Array.isArray(value) && value.every((name) => typeof name === "string");
23
+ }
24
+
25
+ /**
26
+ * The effects that a host found for a data-driven procedure (`metadata.inferredEffects`), or
27
+ * undefined. They are information for the tool hints and the explanations. The admission still
28
+ * examines each nested call of the procedure.
29
+ */
30
+ export function inferredEffectsOf(source: { readonly metadata?: unknown } | Record<string, unknown> | undefined): Effects | undefined {
31
+ const metadata = source && "metadata" in source ? (source as { metadata?: unknown }).metadata : source;
32
+ const value = (metadata as { inferredEffects?: unknown } | undefined)?.inferredEffects;
33
+ return effectsValue(value);
34
+ }
35
+
36
+ /** An `Effects` value with known reads and writes, or undefined. */
37
+ export function effectsValue(value: unknown): Effects | undefined {
38
+ if (value === null || typeof value !== "object") return undefined;
39
+ const { reads, writes, destructive, idempotent } = value as Record<string, unknown>;
40
+ if (!isNameList(reads) || !isNameList(writes)) return undefined;
41
+ return Object.freeze({
42
+ reads: Object.freeze([...reads]),
43
+ writes: Object.freeze([...writes]),
44
+ ...(typeof destructive === "boolean" ? { destructive } : {}),
45
+ ...(typeof idempotent === "boolean" ? { idempotent } : {}),
46
+ });
47
+ }
48
+
14
49
  /**
15
50
  * This function gives copies of the procedures with the effects in their metadata. A procedure
16
51
  * that declares its own effects keeps them.