@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
@@ -19,6 +19,10 @@ export {
19
19
  hasOutputSchema,
20
20
  getAllInputSchema,
21
21
  getAllOutputSchema,
22
+ entriesInputSchema,
23
+ entriesOutputSchema,
24
+ changesInputSchema,
25
+ changesOutputSchema,
22
26
  sizeInputSchema,
23
27
  sizeOutputSchema,
24
28
  clearInputSchema,
@@ -37,6 +41,10 @@ export type {
37
41
  DeleteInput,
38
42
  HasInput,
39
43
  GetAllInput,
44
+ EntriesInput,
45
+ CollectionEntry,
46
+ ChangesInput,
47
+ ChangesOutput,
40
48
  SizeInput,
41
49
  ClearInput,
42
50
  GetBatchInput,
@@ -48,4 +56,4 @@ export type {
48
56
  export { createCollectionProcedures } from "./procedures.js";
49
57
 
50
58
  // The storage interface of the core (the shape of client-collections' CollectionStorage)
51
- export type { CollectionStorage, StorageMetadata } from "./storage.js";
59
+ export type { CollectionStorage, StorageChanges, StorageMetadata } from "./storage.js";
@@ -18,6 +18,10 @@ import {
18
18
  hasOutputSchema,
19
19
  getAllInputSchema,
20
20
  getAllOutputSchema,
21
+ entriesInputSchema,
22
+ entriesOutputSchema,
23
+ changesInputSchema,
24
+ changesOutputSchema,
21
25
  sizeInputSchema,
22
26
  sizeOutputSchema,
23
27
  clearInputSchema,
@@ -33,6 +37,9 @@ import {
33
37
  type DeleteInput,
34
38
  type HasInput,
35
39
  type GetAllInput,
40
+ type EntriesInput,
41
+ type ChangesInput,
42
+ type ChangesOutput,
36
43
  type SizeInput,
37
44
  type ClearInput,
38
45
  type GetBatchInput,
@@ -168,6 +175,56 @@ export function createCollectionProcedures(collectionName: string): AnyProcedure
168
175
  },
169
176
  }),
170
177
 
178
+ // Entries: each item with its id, for a sync (roadmap R2)
179
+ defineProcedure({
180
+ path: [...basePath, "entries"],
181
+ input: entriesInputSchema,
182
+ output: entriesOutputSchema,
183
+ metadata: {
184
+ effects: { reads: [resource], writes: [] },
185
+ description: `Get all items of ${collectionName} with their ids`,
186
+ tags: ["collections", collectionName, "read"],
187
+ },
188
+ handler: async (_input: EntriesInput, ctx: ProcedureContext) => {
189
+ const storage = getStorage(ctx);
190
+ if (!storage.getEntries) {
191
+ throw new Error(`The storage of ${collectionName} cannot give the ids of its items`);
192
+ }
193
+ return (await storage.getEntries()).map(([id, value]) => ({ id, value }));
194
+ },
195
+ }),
196
+
197
+ // Changes: the items that changed after a version, for a delta sync (roadmap R28)
198
+ defineProcedure({
199
+ path: [...basePath, "changes"],
200
+ input: changesInputSchema,
201
+ output: changesOutputSchema,
202
+ metadata: {
203
+ effects: { reads: [resource], writes: [] },
204
+ description: `Get the items of ${collectionName} that changed after a version`,
205
+ tags: ["collections", collectionName, "read"],
206
+ },
207
+ handler: async (input: ChangesInput, ctx: ProcedureContext): Promise<ChangesOutput> => {
208
+ const storage = getStorage(ctx);
209
+ if (storage.getChangesSince) {
210
+ const changes = await storage.getChangesSince(input.since);
211
+ return {
212
+ version: changes.version,
213
+ full: changes.full,
214
+ changed: changes.changed.map(([id, value]) => ({ id, value })),
215
+ deleted: changes.deleted,
216
+ };
217
+ }
218
+ // A storage without a change log gives all its items. The empty version asks for all
219
+ // items again at the next call.
220
+ if (storage.getEntries) {
221
+ const entries = await storage.getEntries();
222
+ return { version: "", full: true, changed: entries.map(([id, value]) => ({ id, value })), deleted: [] };
223
+ }
224
+ throw new Error(`The storage of ${collectionName} cannot give its changes or the ids of its items`);
225
+ },
226
+ }),
227
+
171
228
  // Size
172
229
  defineProcedure({
173
230
  path: [...basePath, "size"],
@@ -335,6 +335,116 @@ export const getAllOutputSchema : ZodLike<unknown[]> = createSchema<unknown[]>((
335
335
  };
336
336
  });
337
337
 
338
+ // =============================================================================
339
+ // Entries Operation Schemas
340
+ // =============================================================================
341
+
342
+ /**
343
+ * Input for entries operation - empty object.
344
+ */
345
+ export interface EntriesInput {}
346
+
347
+ export const entriesInputSchema : ZodLike<EntriesInput> = createSchema<EntriesInput>((data) => {
348
+ if (!isObject(data)) {
349
+ return {
350
+ success: false,
351
+ error: {
352
+ message: "Input must be an object",
353
+ errors: [{ path: [], message: "Input must be an object" }],
354
+ },
355
+ };
356
+ }
357
+ return { success: true, data: {} };
358
+ });
359
+
360
+ /** One item of a collection with its id. */
361
+ export interface CollectionEntry {
362
+ id: string;
363
+ value: unknown;
364
+ }
365
+
366
+ /**
367
+ * Output for entries operation - each item with its id. A sync needs the ids: `getAll` gives
368
+ * only the values (roadmap R2).
369
+ */
370
+ export const entriesOutputSchema : ZodLike<CollectionEntry[]> = createSchema<CollectionEntry[]>((data) => {
371
+ if (isArray(data) && data.every((entry) => isObject(entry) && isString(entry["id"]) && "value" in entry)) {
372
+ return { success: true, data: data as CollectionEntry[] };
373
+ }
374
+ return {
375
+ success: false,
376
+ error: {
377
+ message: "Output must be an array of { id, value } objects",
378
+ errors: [{ path: [], message: "Output must be an array of { id, value } objects" }],
379
+ },
380
+ };
381
+ });
382
+
383
+ // =============================================================================
384
+ // Changes Operation Schemas
385
+ // =============================================================================
386
+
387
+ /** Input for changes operation: the version of an earlier call (roadmap R28). */
388
+ export interface ChangesInput {
389
+ /** The `version` of an earlier result. Without it, the result has all items. */
390
+ since?: string;
391
+ }
392
+
393
+ export const changesInputSchema : ZodLike<ChangesInput> = createSchema<ChangesInput>((data) => {
394
+ if (!isObject(data)) {
395
+ return {
396
+ success: false,
397
+ error: {
398
+ message: "Input must be an object",
399
+ errors: [{ path: [], message: "Input must be an object" }],
400
+ },
401
+ };
402
+ }
403
+ const since = data["since"];
404
+ if (since !== undefined && !isString(since)) {
405
+ return {
406
+ success: false,
407
+ error: {
408
+ message: "since must be a string",
409
+ errors: [{ path: ["since"], message: "since must be a string" }],
410
+ },
411
+ };
412
+ }
413
+ return { success: true, data: since === undefined ? {} : { since } };
414
+ });
415
+
416
+ /**
417
+ * Output for changes operation: the items that changed after the version, the deleted ids, and
418
+ * the new version. When `full` is true, `changed` has each item (roadmap R28).
419
+ */
420
+ export interface ChangesOutput {
421
+ version: string;
422
+ full: boolean;
423
+ changed: CollectionEntry[];
424
+ deleted: string[];
425
+ }
426
+
427
+ export const changesOutputSchema : ZodLike<ChangesOutput> = createSchema<ChangesOutput>((data) => {
428
+ if (
429
+ isObject(data) &&
430
+ isString(data["version"]) &&
431
+ typeof data["full"] === "boolean" &&
432
+ isArray(data["changed"]) &&
433
+ data["changed"].every((entry) => isObject(entry) && isString(entry["id"]) && "value" in entry) &&
434
+ isArray(data["deleted"]) &&
435
+ data["deleted"].every((id) => isString(id))
436
+ ) {
437
+ return { success: true, data: data as unknown as ChangesOutput };
438
+ }
439
+ return {
440
+ success: false,
441
+ error: {
442
+ message: "Output must be { version, full, changed: [{ id, value }], deleted: [id] }",
443
+ errors: [{ path: [], message: "Output must be { version, full, changed: [{ id, value }], deleted: [id] }" }],
444
+ },
445
+ };
446
+ });
447
+
338
448
  // =============================================================================
339
449
  // Size Operation Schemas
340
450
  // =============================================================================
@@ -15,6 +15,18 @@ export interface CollectionStorage<T> {
15
15
  get(id: string): Promise<T | undefined>;
16
16
  /** Every item of the collection. */
17
17
  getAll(): Promise<T[]>;
18
+ /**
19
+ * Every item with its id. A sync needs the ids (roadmap R2). A storage that cannot give
20
+ * them does not have this method.
21
+ */
22
+ getEntries?(): Promise<Array<[string, T]>>;
23
+ /**
24
+ * The changes after a version (roadmap R28). A storage without a change log does not have
25
+ * this method, and a sync uses `getEntries()`.
26
+ *
27
+ * @param since - A `version` that an earlier call gave. Without it, the result has all items.
28
+ */
29
+ getChangesSince?(since?: string): Promise<StorageChanges<T>>;
18
30
  /** The items for which the predicate gives true. */
19
31
  find(predicate: (item: T) => boolean): Promise<T[]>;
20
32
  /** True when an item has this id. */
@@ -33,12 +45,38 @@ export interface CollectionStorage<T> {
33
45
  deleteBatch(ids: string[]): Promise<number>;
34
46
  /** The items of several ids. An id with no item is not in the map. */
35
47
  getBatch(ids: string[]): Promise<Map<string, T>>;
48
+ /**
49
+ * This method sends the writes that wait in the storage to their destination (roadmap R28). A storage
50
+ * that keeps no writes does not have this method. `ProcedureServer.useStorage` calls it.
51
+ */
52
+ flush?(): Promise<void>;
36
53
  /** Release the resources of the storage. */
37
54
  close(): Promise<void>;
38
55
  /** The kind, the size and the statistics of the storage. */
39
56
  getMetadata?(): Promise<StorageMetadata>;
40
57
  }
41
58
 
59
+ /**
60
+ * The changes of a storage after a version (roadmap R28).
61
+ *
62
+ * - `version` is opaque. The next call gives it back as `since`.
63
+ * - When `full` is true, `changed` has every item, and each id that it does not have was
64
+ * deleted. A storage gives a full result when it cannot find the changes since the version:
65
+ * the first call, a version of an older run, or a change log that dropped old deletes.
66
+ * - When `full` is false, `changed` has the items that changed after the version, and `deleted`
67
+ * has the ids that were deleted after it.
68
+ */
69
+ export interface StorageChanges<T> {
70
+ /** The version of the storage that includes these changes. */
71
+ version: string;
72
+ /** True when `changed` has every item of the storage. */
73
+ full: boolean;
74
+ /** The items that changed, with their ids. */
75
+ changed: Array<[string, T]>;
76
+ /** The ids that were deleted. Empty when `full` is true. */
77
+ deleted: string[];
78
+ }
79
+
42
80
  /** The metadata that a storage gives about itself. */
43
81
  export interface StorageMetadata {
44
82
  type: "memory" | "api" | "hybrid" | "synced" | "custom";
@@ -10,6 +10,17 @@ import { anyOutput, arrayOutput, booleanOutput, numberOutput } from "./outputs.j
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";
14
+
15
+ /**
16
+ * This function gives the own field `key` of an item, or undefined. A field of the prototype
17
+ * (`toString`, `constructor`) is not a field of the item: before, `count` with the key
18
+ * `toString` counted each item (catalog test, roadmap R29, as R6 for the object procedures).
19
+ */
20
+ function fieldOf(item: unknown, key: string): unknown {
21
+ if (item === null || item === undefined) return undefined;
22
+ return Object.hasOwn(Object(item), key) ? (item as Record<string, unknown>)[key] : undefined;
23
+ }
13
24
 
14
25
  // =============================================================================
15
26
  // First Procedure
@@ -209,8 +220,8 @@ const sortProcedure: SortProcedure = defineProcedure({
209
220
  const direction = input.desc ? -1 : 1;
210
221
 
211
222
  return items.sort((a, b) => {
212
- let aVal = input.key ? (a as Record<string, unknown>)[input.key] : a;
213
- let bVal = input.key ? (b as Record<string, unknown>)[input.key] : b;
223
+ let aVal = input.key ? fieldOf(a, input.key) : a;
224
+ let bVal = input.key ? fieldOf(b, input.key) : b;
214
225
 
215
226
  if (typeof aVal === "string" && typeof bVal === "string") {
216
227
  return aVal.localeCompare(bVal) * direction;
@@ -291,10 +302,47 @@ interface UniqueInput {
291
302
  }
292
303
 
293
304
  const UniqueInputSchema = z.object({
294
- items: z.array(z.unknown()).describe("The array"),
295
- key: z.string().optional().describe("The key (a field name of the items)"),
305
+ items: z.array(z.unknown()).describe("The array. Equal JSON values are duplicates: objects and arrays compare by value."),
306
+ key: z.string().optional().describe("The key (a field name of the items). The values of the field compare by value."),
296
307
  });
297
308
 
309
+ /**
310
+ * This function gives a function that makes a text key for a value: two equal JSON values get
311
+ * the same key. An array compares by its items in order, and a plain object by its own fields in
312
+ * any order. A number and its string get different keys. Another object (a `Date`, a `Map`) and
313
+ * a symbol compare by identity.
314
+ */
315
+ function valueKeys(): (value: unknown) => string {
316
+ const identities = new Map<unknown, number>();
317
+ const identity = (value: unknown): string => {
318
+ let id = identities.get(value);
319
+ if (id === undefined) {
320
+ id = identities.size;
321
+ identities.set(value, id);
322
+ }
323
+ return `#${id}`;
324
+ };
325
+ const keyOf = (value: unknown, parents: Set<unknown>): string => {
326
+ if (typeof value === "string") return JSON.stringify(value);
327
+ // -0 and 0 are one value, and NaN is one value, as in a Set
328
+ if (typeof value === "number") return Object.is(value, -0) ? "0" : String(value);
329
+ if (typeof value === "bigint") return `${value}n`;
330
+ if (value === null || value === undefined || typeof value === "boolean") return String(value);
331
+ // A cycle is not JSON: its object compares by identity
332
+ if (parents.has(value) || (!Array.isArray(value) && !isPlainObject(value))) return identity(value);
333
+ parents.add(value);
334
+ const text = Array.isArray(value)
335
+ ? `[${value.map((item) => keyOf(item, parents)).join(",")}]`
336
+ : `{${Object.keys(value)
337
+ .sort()
338
+ .map((field) => `${JSON.stringify(field)}:${keyOf(value[field], parents)}`)
339
+ .join(",")}}`;
340
+ parents.delete(value);
341
+ return text;
342
+ };
343
+ return (value) => keyOf(value, new Set());
344
+ }
345
+
298
346
  type UniqueProcedure = Procedure<UniqueInput, unknown[], { description: string; tags: string[] }>;
299
347
 
300
348
  const uniqueProcedure: UniqueProcedure = defineProcedure({
@@ -302,20 +350,19 @@ const uniqueProcedure: UniqueProcedure = defineProcedure({
302
350
  input: UniqueInputSchema,
303
351
  output: arrayOutput("The items without duplicates"),
304
352
  metadata: {
305
- description: "Remove duplicate values (optionally by key)",
353
+ description: "Remove duplicate values (optionally by key). It compares by value and keeps the first item of each value.",
306
354
  tags: ["core", "array"],
307
355
  },
308
356
  handler: async (input: UniqueInput): Promise<unknown[]> => {
309
- if (input.key) {
310
- const seen = new Set<unknown>();
311
- return input.items.filter((item) => {
312
- const val = (item as Record<string, unknown>)[input.key!];
313
- if (seen.has(val)) return false;
314
- seen.add(val);
315
- return true;
316
- });
317
- }
318
- return [...new Set(input.items)];
357
+ // Before, objects compared by reference, so two equal objects both stayed (catalog test, R29)
358
+ const keyOf = valueKeys();
359
+ const seen = new Set<string>();
360
+ return input.items.filter((item) => {
361
+ const key = keyOf(input.key ? fieldOf(item, input.key) : item);
362
+ if (seen.has(key)) return false;
363
+ seen.add(key);
364
+ return true;
365
+ });
319
366
  },
320
367
  });
321
368
 
@@ -346,11 +393,13 @@ const groupByProcedure: GroupByProcedure = defineProcedure({
346
393
  handler: async (input: GroupByInput): Promise<Record<string, unknown[]>> => {
347
394
  const result: Record<string, unknown[]> = {};
348
395
  for (const item of input.items) {
349
- const keyVal = String((item as Record<string, unknown>)[input.key]);
350
- if (!result[keyVal]) {
351
- result[keyVal] = [];
396
+ const keyVal = String(fieldOf(item, input.key));
397
+ // Only an own group: before, a group "constructor" or "toString" found the function of the
398
+ // prototype, and push failed (catalog test, roadmap R29)
399
+ if (!Object.hasOwn(result, keyVal)) {
400
+ Object.defineProperty(result, keyVal, { value: [], writable: true, enumerable: true, configurable: true });
352
401
  }
353
- result[keyVal].push(item);
402
+ result[keyVal]!.push(item);
354
403
  }
355
404
  return result;
356
405
  },
@@ -611,8 +660,8 @@ interface FilterInput {
611
660
  value?: unknown | undefined;
612
661
  /** Values to match (any of these) */
613
662
  values?: unknown[] | undefined;
614
- /** Match mode: 'equals', 'not', 'in', 'notIn', 'contains', 'startsWith', 'endsWith' */
615
- match?: 'equals' | 'not' | 'in' | 'notIn' | 'contains' | 'startsWith' | 'endsWith' | 'gt' | 'gte' | 'lt' | 'lte' | undefined;
663
+ /** Match mode: 'equals', 'not', 'in', 'notIn', 'contains', 'startsWith', 'endsWith', 'gt', 'gte', 'lt', 'lte' */
664
+ match?:'equals' | 'not' | 'in' | 'notIn' | 'contains' | 'startsWith' | 'endsWith' | 'gt' | 'gte' | 'lt' | 'lte' | undefined;
616
665
  /** Invert the filter */
617
666
  invert?: boolean | undefined;
618
667
  }
@@ -622,7 +671,7 @@ const FilterInputSchema = z.object({
622
671
  key: z.string().optional().describe("Property to match against"),
623
672
  value: z.unknown().optional().describe("Value to match (equality check)"),
624
673
  values: z.array(z.unknown()).optional().describe("Values to match (any of these)"),
625
- match: z.enum(["equals", "not", "in", "notIn", "contains", "startsWith", "endsWith", "gt", "gte", "lt", "lte"]).optional().describe("Match mode: 'equals', 'not', 'in', 'notIn', 'contains', 'startsWith', 'endsWith'"),
674
+ match: z.enum(["equals", "not", "in", "notIn", "contains", "startsWith", "endsWith", "gt", "gte", "lt", "lte"]).optional().describe("Match mode (default 'equals'): 'equals' and 'not' compare with value, 'in' and 'notIn' with values. 'contains', 'startsWith' and 'endsWith' compare strings. 'gt', 'gte', 'lt' and 'lte' compare numbers."),
626
675
  invert: z.boolean().optional().describe("Invert the filter"),
627
676
  });
628
677
 
@@ -640,7 +689,7 @@ const filterProcedure: FilterProcedure = defineProcedure({
640
689
  const { items, key, value, values, match = 'equals', invert = false } = input;
641
690
 
642
691
  const predicate = (item: unknown): boolean => {
643
- const itemValue = key ? (item as Record<string, unknown>)[key] : item;
692
+ const itemValue = key ? fieldOf(item, key) : item;
644
693
 
645
694
  let result: boolean;
646
695
  switch (match) {
@@ -716,10 +765,7 @@ const whereProcedure: WhereProcedure = defineProcedure({
716
765
  },
717
766
  handler: async (input: WhereInput): Promise<unknown[]> => {
718
767
  const { items, where } = input;
719
- return items.filter((item) => {
720
- const obj = item as Record<string, unknown>;
721
- return Object.entries(where).every(([key, value]) => obj[key] === value);
722
- });
768
+ return items.filter((item) => Object.entries(where).every(([key, value]) => fieldOf(item, key) === value));
723
769
  },
724
770
  });
725
771
 
@@ -750,7 +796,7 @@ const pluckProcedure: PluckProcedure = defineProcedure({
750
796
  tags: ["core", "array", "transform"],
751
797
  },
752
798
  handler: async (input: PluckInput): Promise<unknown[]> => {
753
- return input.items.map((item) => (item as Record<string, unknown>)[input.key]);
799
+ return input.items.map((item) => fieldOf(item, input.key));
754
800
  },
755
801
  });
756
802
 
@@ -785,7 +831,9 @@ const arrPickProcedure: ArrPickProcedure = defineProcedure({
785
831
  const obj = item as Record<string, unknown>;
786
832
  const result: Record<string, unknown> = {};
787
833
  for (const key of input.keys) {
788
- if (key in obj) {
834
+ // Only own fields, as client.pick: before, "toString" picked a function of the prototype
835
+ // (catalog test, roadmap R29)
836
+ if (Object.hasOwn(obj, key)) {
789
837
  result[key] = obj[key];
790
838
  }
791
839
  }
@@ -881,7 +929,7 @@ const partitionProcedure: PartitionProcedure = defineProcedure({
881
929
  const falsy: unknown[] = [];
882
930
 
883
931
  for (const item of input.items) {
884
- const itemValue = (item as Record<string, unknown>)[input.key];
932
+ const itemValue = fieldOf(item, input.key);
885
933
  const matches = input.value !== undefined ? itemValue === input.value : Boolean(itemValue);
886
934
  if (matches) {
887
935
  truthy.push(item);
@@ -929,7 +977,7 @@ const countProcedure: CountProcedure = defineProcedure({
929
977
  }
930
978
 
931
979
  return input.items.filter((item) => {
932
- const itemValue = (item as Record<string, unknown>)[input.key!];
980
+ const itemValue = fieldOf(item, input.key!);
933
981
  return input.value !== undefined ? itemValue === input.value : Boolean(itemValue);
934
982
  }).length;
935
983
  },
@@ -84,9 +84,16 @@ const ConditionalInputSchema = z.looseObject({
84
84
  const LogicInputSchema = z.looseObject({
85
85
  values: z.array(z.unknown()).describe("The operands, in order: values or calls. The evaluation stops at the deciding value."),
86
86
  });
87
- const NotInputSchema = z.looseObject({ value: operand("The value or call to negate") });
87
+ // not, all, any and none take values (roadmap R29, the owner's choice). They do not run a call in
88
+ // their operands: a call is an object, thus truthy. In a program, the calls of their input run
89
+ // first, as for each procedure that is not control flow. client.and and client.or run calls.
90
+ const NotInputSchema = z.looseObject({
91
+ value: operand("The value to negate. This procedure does not run a call: a call is an object, thus truthy."),
92
+ });
88
93
  const VariadicBoolInputSchema = z.looseObject({
89
- values: z.array(z.unknown()).describe("The values or calls to evaluate"),
94
+ values: z
95
+ .array(z.unknown())
96
+ .describe("The values to evaluate. This procedure does not run calls: a call is an object, thus truthy. client.and and client.or run calls."),
90
97
  });
91
98
  const MapInputSchema = z.looseObject({
92
99
  items: operand("The array, or a call that gives it"),
@@ -582,6 +589,10 @@ const mapProcedure: MapProcedure = defineProcedure({
582
589
  // map is a control-flow procedure: its operands arrive raw, and it runs them here.
583
590
  // Before, the handler only returned the raw items, so their refs never ran and no
584
591
  // function was applied.
592
+ if (input.fn !== undefined && !isAnyProcedureRef(input.fn)) {
593
+ // Before, a fn that was not a call was ignored without a word (roadmap R4)
594
+ throw new Error("client.map: fn must be one call ({ $proc, input }), or absent");
595
+ }
585
596
  const outer = scopeOf(input);
586
597
  const items = await hydrateItems(input.items, ctx, outer);
587
598
  if (!isAnyProcedureRef(input.fn) || !ctx?.client) {
@@ -625,6 +636,10 @@ const reduceProcedure: ReduceProcedure = defineProcedure({
625
636
  tags: controlFlowTags("collection"),
626
637
  },
627
638
  handler: async (input: ReduceInput, ctx?: ProcedureContext): Promise<unknown> => {
639
+ if (input.fn !== undefined && !isAnyProcedureRef(input.fn)) {
640
+ // Before, a fn that was not a call was ignored without a word (roadmap R4)
641
+ throw new Error("client.reduce: fn must be one call ({ $proc, input }), or absent");
642
+ }
628
643
  if (!ctx?.client) {
629
644
  return input.accumulated ?? input.initial;
630
645
  }
@@ -184,7 +184,7 @@ export const orMetadata: LogicMetadata = {
184
184
  * Metadata for the NOT operator.
185
185
  */
186
186
  export const notMetadata: LogicMetadata = {
187
- description: "Logical NOT",
187
+ description: "Logical NOT of a value",
188
188
  tags: ["core", "logic"],
189
189
  arity: "unary",
190
190
  commutative: false, // n/a for unary
@@ -151,10 +151,14 @@ const modProcedure: ModProcedure = defineProcedure({
151
151
  input: ModInputSchema,
152
152
  output: numberOutput("The remainder of a divided by b"),
153
153
  metadata: {
154
- description: "Modulo (remainder of a / b)",
154
+ description: "Modulo (remainder of a / b). It refuses b = 0, as client.divide does.",
155
155
  tags: ["core", "math"],
156
156
  },
157
157
  handler: async (input: ModInput): Promise<number> => {
158
+ // Before, b = 0 gave NaN, and client.divide refused it (catalog test, roadmap R29)
159
+ if (input.b === 0) {
160
+ throw new Error("Division by zero");
161
+ }
158
162
  return input.a % input.b;
159
163
  },
160
164
  });