effect 4.0.2 → 4.0.4

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 (389) hide show
  1. package/AGENTS.md +24 -0
  2. package/CLAUDE.md +24 -0
  3. package/ai-docs/package.json +2 -2
  4. package/ai-docs/src/52_graphql/10_basics.ts +111 -0
  5. package/ai-docs/src/52_graphql/20_testing.ts +45 -0
  6. package/ai-docs/src/52_graphql/30_batching.ts +116 -0
  7. package/ai-docs/src/52_graphql/40_aliased-batching.ts +122 -0
  8. package/ai-docs/src/52_graphql/50_errors-and-retries.ts +74 -0
  9. package/ai-docs/src/52_graphql/index.md +6 -0
  10. package/dist/Context.d.ts.map +1 -1
  11. package/dist/Context.js +12 -3
  12. package/dist/Context.js.map +1 -1
  13. package/dist/Crypto.d.ts +33 -5
  14. package/dist/Crypto.d.ts.map +1 -1
  15. package/dist/Crypto.js +8 -2
  16. package/dist/Crypto.js.map +1 -1
  17. package/dist/DateTime.d.ts +9 -6
  18. package/dist/DateTime.d.ts.map +1 -1
  19. package/dist/DateTime.js +9 -6
  20. package/dist/DateTime.js.map +1 -1
  21. package/dist/Duration.d.ts +6 -6
  22. package/dist/Duration.d.ts.map +1 -1
  23. package/dist/Duration.js +27 -8
  24. package/dist/Duration.js.map +1 -1
  25. package/dist/FileSystem.d.ts +44 -2
  26. package/dist/FileSystem.d.ts.map +1 -1
  27. package/dist/FileSystem.js.map +1 -1
  28. package/dist/RequestResolver.d.ts.map +1 -1
  29. package/dist/RequestResolver.js +4 -10
  30. package/dist/RequestResolver.js.map +1 -1
  31. package/dist/Scheduler.d.ts +9 -0
  32. package/dist/Scheduler.d.ts.map +1 -1
  33. package/dist/Scheduler.js +9 -0
  34. package/dist/Scheduler.js.map +1 -1
  35. package/dist/Schema.d.ts +481 -1
  36. package/dist/Schema.d.ts.map +1 -1
  37. package/dist/Schema.js +362 -4
  38. package/dist/Schema.js.map +1 -1
  39. package/dist/SchemaGetter.d.ts +4 -0
  40. package/dist/SchemaGetter.d.ts.map +1 -1
  41. package/dist/SchemaGetter.js +52 -33
  42. package/dist/SchemaGetter.js.map +1 -1
  43. package/dist/Sink.d.ts.map +1 -1
  44. package/dist/Sink.js +38 -17
  45. package/dist/Sink.js.map +1 -1
  46. package/dist/Stdio.d.ts +7 -1
  47. package/dist/Stdio.d.ts.map +1 -1
  48. package/dist/Stdio.js +1 -0
  49. package/dist/Stdio.js.map +1 -1
  50. package/dist/Stream.d.ts +22 -11
  51. package/dist/Stream.d.ts.map +1 -1
  52. package/dist/Stream.js +21 -6
  53. package/dist/Stream.js.map +1 -1
  54. package/dist/Utils.js +8 -3
  55. package/dist/Utils.js.map +1 -1
  56. package/dist/ai/AnthropicStructuredOutput.js +0 -2
  57. package/dist/ai/AnthropicStructuredOutput.js.map +1 -1
  58. package/dist/ai/Chat.d.ts +2 -2
  59. package/dist/ai/Chat.d.ts.map +1 -1
  60. package/dist/ai/LanguageModel.d.ts +38 -7
  61. package/dist/ai/LanguageModel.d.ts.map +1 -1
  62. package/dist/ai/LanguageModel.js +25 -7
  63. package/dist/ai/LanguageModel.js.map +1 -1
  64. package/dist/ai/McpServer.d.ts +61 -0
  65. package/dist/ai/McpServer.d.ts.map +1 -1
  66. package/dist/ai/McpServer.js +196 -44
  67. package/dist/ai/McpServer.js.map +1 -1
  68. package/dist/ai/Prompt.d.ts.map +1 -1
  69. package/dist/ai/Prompt.js +28 -5
  70. package/dist/ai/Prompt.js.map +1 -1
  71. package/dist/ai/Response.d.ts +4 -3
  72. package/dist/ai/Response.d.ts.map +1 -1
  73. package/dist/ai/Response.js.map +1 -1
  74. package/dist/ai/internal/mcpProtocol/v2026_07_28.js +5 -0
  75. package/dist/ai/internal/mcpProtocol/v2026_07_28.js.map +1 -1
  76. package/dist/ai/internal/mcpProtocol.js.map +1 -1
  77. package/dist/ai/internal/mcpRuntime.js +1 -0
  78. package/dist/ai/internal/mcpRuntime.js.map +1 -1
  79. package/dist/cli/CliOutput.d.ts +27 -0
  80. package/dist/cli/CliOutput.d.ts.map +1 -1
  81. package/dist/cli/CliOutput.js +33 -10
  82. package/dist/cli/CliOutput.js.map +1 -1
  83. package/dist/cli/Command.d.ts.map +1 -1
  84. package/dist/cli/Command.js +6 -3
  85. package/dist/cli/Command.js.map +1 -1
  86. package/dist/cli/Completions.d.ts +3 -3
  87. package/dist/cli/Completions.d.ts.map +1 -1
  88. package/dist/cli/Completions.js +8 -5
  89. package/dist/cli/Completions.js.map +1 -1
  90. package/dist/cli/GlobalFlag.d.ts +5 -4
  91. package/dist/cli/GlobalFlag.d.ts.map +1 -1
  92. package/dist/cli/GlobalFlag.js +3 -3
  93. package/dist/cli/GlobalFlag.js.map +1 -1
  94. package/dist/cli/Prompt.d.ts +17 -1
  95. package/dist/cli/Prompt.d.ts.map +1 -1
  96. package/dist/cli/Prompt.js +119 -114
  97. package/dist/cli/Prompt.js.map +1 -1
  98. package/dist/cli/internal/ansi.d.ts +0 -1
  99. package/dist/cli/internal/ansi.d.ts.map +1 -1
  100. package/dist/cli/internal/ansi.js +20 -3
  101. package/dist/cli/internal/ansi.js.map +1 -1
  102. package/dist/cli/internal/completions/powershell.d.ts +2 -0
  103. package/dist/cli/internal/completions/powershell.d.ts.map +1 -0
  104. package/dist/cli/internal/completions/powershell.js +273 -0
  105. package/dist/cli/internal/completions/powershell.js.map +1 -0
  106. package/dist/cli/internal/completions/zsh.js +3 -2
  107. package/dist/cli/internal/completions/zsh.js.map +1 -1
  108. package/dist/cli/internal/wizard.d.ts +3 -3
  109. package/dist/cli/internal/wizard.d.ts.map +1 -1
  110. package/dist/cli/internal/wizard.js +21 -18
  111. package/dist/cli/internal/wizard.js.map +1 -1
  112. package/dist/cluster/ClusterSchema.d.ts +28 -0
  113. package/dist/cluster/ClusterSchema.d.ts.map +1 -1
  114. package/dist/cluster/ClusterSchema.js +30 -0
  115. package/dist/cluster/ClusterSchema.js.map +1 -1
  116. package/dist/cluster/ClusterWorkflowEngine.d.ts.map +1 -1
  117. package/dist/cluster/ClusterWorkflowEngine.js +56 -26
  118. package/dist/cluster/ClusterWorkflowEngine.js.map +1 -1
  119. package/dist/cluster/MessageStorage.d.ts +18 -3
  120. package/dist/cluster/MessageStorage.d.ts.map +1 -1
  121. package/dist/cluster/MessageStorage.js +31 -11
  122. package/dist/cluster/MessageStorage.js.map +1 -1
  123. package/dist/cluster/Runners.d.ts.map +1 -1
  124. package/dist/cluster/Runners.js +8 -2
  125. package/dist/cluster/Runners.js.map +1 -1
  126. package/dist/cluster/Sharding.d.ts.map +1 -1
  127. package/dist/cluster/Sharding.js +187 -66
  128. package/dist/cluster/Sharding.js.map +1 -1
  129. package/dist/cluster/SqlMessageStorage.d.ts +59 -19
  130. package/dist/cluster/SqlMessageStorage.d.ts.map +1 -1
  131. package/dist/cluster/SqlMessageStorage.js +84 -55
  132. package/dist/cluster/SqlMessageStorage.js.map +1 -1
  133. package/dist/cluster/SqlRunnerStorage.d.ts +68 -19
  134. package/dist/cluster/SqlRunnerStorage.d.ts.map +1 -1
  135. package/dist/cluster/SqlRunnerStorage.js +191 -113
  136. package/dist/cluster/SqlRunnerStorage.js.map +1 -1
  137. package/dist/cluster/internal/entityManager.js +104 -37
  138. package/dist/cluster/internal/entityManager.js.map +1 -1
  139. package/dist/encoding/Sse.d.ts +6 -0
  140. package/dist/encoding/Sse.d.ts.map +1 -1
  141. package/dist/encoding/Sse.js +62 -70
  142. package/dist/encoding/Sse.js.map +1 -1
  143. package/dist/graphql/GraphQL.d.ts +349 -0
  144. package/dist/graphql/GraphQL.d.ts.map +1 -0
  145. package/dist/graphql/GraphQL.js +207 -0
  146. package/dist/graphql/GraphQL.js.map +1 -0
  147. package/dist/graphql/GraphQLClient.d.ts +562 -0
  148. package/dist/graphql/GraphQLClient.d.ts.map +1 -0
  149. package/dist/graphql/GraphQLClient.js +498 -0
  150. package/dist/graphql/GraphQLClient.js.map +1 -0
  151. package/dist/graphql/GraphQLClientError.d.ts +445 -0
  152. package/dist/graphql/GraphQLClientError.d.ts.map +1 -0
  153. package/dist/graphql/GraphQLClientError.js +479 -0
  154. package/dist/graphql/GraphQLClientError.js.map +1 -0
  155. package/dist/graphql/GraphQLGroup.d.ts +118 -0
  156. package/dist/graphql/GraphQLGroup.d.ts.map +1 -0
  157. package/dist/graphql/GraphQLGroup.js +109 -0
  158. package/dist/graphql/GraphQLGroup.js.map +1 -0
  159. package/dist/graphql/GraphQLMiddleware.d.ts +249 -0
  160. package/dist/graphql/GraphQLMiddleware.d.ts.map +1 -0
  161. package/dist/graphql/GraphQLMiddleware.js +130 -0
  162. package/dist/graphql/GraphQLMiddleware.js.map +1 -0
  163. package/dist/graphql/GraphQLProtocol.d.ts +731 -0
  164. package/dist/graphql/GraphQLProtocol.d.ts.map +1 -0
  165. package/dist/graphql/GraphQLProtocol.js +719 -0
  166. package/dist/graphql/GraphQLProtocol.js.map +1 -0
  167. package/dist/graphql/GraphQLResolver.d.ts +453 -0
  168. package/dist/graphql/GraphQLResolver.d.ts.map +1 -0
  169. package/dist/graphql/GraphQLResolver.js +552 -0
  170. package/dist/graphql/GraphQLResolver.js.map +1 -0
  171. package/dist/graphql/index.d.ts +40 -0
  172. package/dist/graphql/index.d.ts.map +1 -0
  173. package/dist/graphql/index.js +41 -0
  174. package/dist/graphql/index.js.map +1 -0
  175. package/dist/http/FindMyWay/internal/router.js +3 -6
  176. package/dist/http/FindMyWay/internal/router.js.map +1 -1
  177. package/dist/http/HttpClient.d.ts +21 -0
  178. package/dist/http/HttpClient.d.ts.map +1 -1
  179. package/dist/http/HttpClient.js +63 -12
  180. package/dist/http/HttpClient.js.map +1 -1
  181. package/dist/http/HttpEffect.d.ts.map +1 -1
  182. package/dist/http/HttpEffect.js +14 -7
  183. package/dist/http/HttpEffect.js.map +1 -1
  184. package/dist/http/HttpMiddleware.d.ts.map +1 -1
  185. package/dist/http/HttpMiddleware.js +31 -32
  186. package/dist/http/HttpMiddleware.js.map +1 -1
  187. package/dist/http/HttpRouter.d.ts +35 -0
  188. package/dist/http/HttpRouter.d.ts.map +1 -1
  189. package/dist/http/HttpRouter.js +35 -0
  190. package/dist/http/HttpRouter.js.map +1 -1
  191. package/dist/http/HttpServerResponse.d.ts +8 -6
  192. package/dist/http/HttpServerResponse.d.ts.map +1 -1
  193. package/dist/http/HttpServerResponse.js +27 -16
  194. package/dist/http/HttpServerResponse.js.map +1 -1
  195. package/dist/http/HttpTraceContext.d.ts.map +1 -1
  196. package/dist/http/HttpTraceContext.js +4 -2
  197. package/dist/http/HttpTraceContext.js.map +1 -1
  198. package/dist/http/Multipart.d.ts.map +1 -1
  199. package/dist/http/Multipart.js +15 -1
  200. package/dist/http/Multipart.js.map +1 -1
  201. package/dist/http-api/HttpApiScalar.d.ts +5 -0
  202. package/dist/http-api/HttpApiScalar.d.ts.map +1 -1
  203. package/dist/http-api/HttpApiScalar.js.map +1 -1
  204. package/dist/internal/dateTime.js +40 -35
  205. package/dist/internal/dateTime.js.map +1 -1
  206. package/dist/internal/effect.js +19 -9
  207. package/dist/internal/effect.js.map +1 -1
  208. package/dist/internal/graphql.d.ts +2 -0
  209. package/dist/internal/graphql.d.ts.map +1 -0
  210. package/dist/internal/graphql.js +195 -0
  211. package/dist/internal/graphql.js.map +1 -0
  212. package/dist/internal/graphqlHeaders.d.ts +4 -0
  213. package/dist/internal/graphqlHeaders.d.ts.map +1 -0
  214. package/dist/internal/graphqlHeaders.js +11 -0
  215. package/dist/internal/graphqlHeaders.js.map +1 -0
  216. package/dist/internal/request.js +20 -1
  217. package/dist/internal/request.js.map +1 -1
  218. package/dist/internal/schema/toCodeDocument.js +4 -1
  219. package/dist/internal/schema/toCodeDocument.js.map +1 -1
  220. package/dist/internal/schema/toJsonSchemaDocument.js +29 -5
  221. package/dist/internal/schema/toJsonSchemaDocument.js.map +1 -1
  222. package/dist/internal/version.js +1 -1
  223. package/dist/net/AddressResolver.d.ts +145 -0
  224. package/dist/net/AddressResolver.d.ts.map +1 -0
  225. package/dist/net/AddressResolver.js +114 -0
  226. package/dist/net/AddressResolver.js.map +1 -0
  227. package/dist/net/Dns.d.ts +596 -0
  228. package/dist/net/Dns.d.ts.map +1 -0
  229. package/dist/net/Dns.js +549 -0
  230. package/dist/net/Dns.js.map +1 -0
  231. package/dist/net/Host.d.ts +369 -0
  232. package/dist/net/Host.d.ts.map +1 -0
  233. package/dist/net/Host.js +397 -0
  234. package/dist/net/Host.js.map +1 -0
  235. package/dist/net/IpInterface.d.ts +76 -3
  236. package/dist/net/IpInterface.d.ts.map +1 -1
  237. package/dist/net/IpInterface.js +53 -4
  238. package/dist/net/IpInterface.js.map +1 -1
  239. package/dist/net/IpNetwork.d.ts +82 -8
  240. package/dist/net/IpNetwork.d.ts.map +1 -1
  241. package/dist/net/IpNetwork.js +60 -4
  242. package/dist/net/IpNetwork.js.map +1 -1
  243. package/dist/net/NetAddress.d.ts +396 -35
  244. package/dist/net/NetAddress.d.ts.map +1 -1
  245. package/dist/net/NetAddress.js +358 -34
  246. package/dist/net/NetAddress.js.map +1 -1
  247. package/dist/net/index.d.ts +15 -0
  248. package/dist/net/index.d.ts.map +1 -1
  249. package/dist/net/index.js +15 -0
  250. package/dist/net/index.js.map +1 -1
  251. package/dist/observability/OtlpMetrics.d.ts.map +1 -1
  252. package/dist/observability/OtlpMetrics.js +15 -21
  253. package/dist/observability/OtlpMetrics.js.map +1 -1
  254. package/dist/observability/PrometheusMetrics.js +1 -0
  255. package/dist/observability/PrometheusMetrics.js.map +1 -1
  256. package/dist/persistence/PersistedQueue.d.ts +32 -3
  257. package/dist/persistence/PersistedQueue.d.ts.map +1 -1
  258. package/dist/persistence/PersistedQueue.js +75 -16
  259. package/dist/persistence/PersistedQueue.js.map +1 -1
  260. package/dist/rpc/RpcClient.d.ts +22 -6
  261. package/dist/rpc/RpcClient.d.ts.map +1 -1
  262. package/dist/rpc/RpcClient.js +12 -5
  263. package/dist/rpc/RpcClient.js.map +1 -1
  264. package/dist/rpc/RpcServer.d.ts +6 -0
  265. package/dist/rpc/RpcServer.d.ts.map +1 -1
  266. package/dist/rpc/RpcServer.js +78 -29
  267. package/dist/rpc/RpcServer.js.map +1 -1
  268. package/dist/socket/DatagramSocket.d.ts +456 -0
  269. package/dist/socket/DatagramSocket.d.ts.map +1 -0
  270. package/dist/socket/DatagramSocket.js +535 -0
  271. package/dist/socket/DatagramSocket.js.map +1 -0
  272. package/dist/socket/Socket.d.ts +20 -1
  273. package/dist/socket/Socket.d.ts.map +1 -1
  274. package/dist/socket/Socket.js +9 -1
  275. package/dist/socket/Socket.js.map +1 -1
  276. package/dist/socket/index.d.ts +5 -0
  277. package/dist/socket/index.d.ts.map +1 -1
  278. package/dist/socket/index.js +5 -0
  279. package/dist/socket/index.js.map +1 -1
  280. package/dist/sql/Migrator.d.ts.map +1 -1
  281. package/dist/sql/Migrator.js +7 -2
  282. package/dist/sql/Migrator.js.map +1 -1
  283. package/dist/sql/SqlClient.d.ts +7 -1
  284. package/dist/sql/SqlClient.d.ts.map +1 -1
  285. package/dist/sql/SqlClient.js +31 -6
  286. package/dist/sql/SqlClient.js.map +1 -1
  287. package/dist/testing/TestCrypto.d.ts +73 -0
  288. package/dist/testing/TestCrypto.d.ts.map +1 -0
  289. package/dist/testing/TestCrypto.js +87 -0
  290. package/dist/testing/TestCrypto.js.map +1 -0
  291. package/dist/testing/index.d.ts +5 -0
  292. package/dist/testing/index.d.ts.map +1 -1
  293. package/dist/testing/index.js +5 -0
  294. package/dist/testing/index.js.map +1 -1
  295. package/dist/workflow/DurableDeferred.d.ts +54 -0
  296. package/dist/workflow/DurableDeferred.d.ts.map +1 -1
  297. package/dist/workflow/DurableDeferred.js +108 -50
  298. package/dist/workflow/DurableDeferred.js.map +1 -1
  299. package/dist/workflow/WorkflowEngine.d.ts +24 -2
  300. package/dist/workflow/WorkflowEngine.d.ts.map +1 -1
  301. package/dist/workflow/WorkflowEngine.js +13 -3
  302. package/dist/workflow/WorkflowEngine.js.map +1 -1
  303. package/package.json +6 -3
  304. package/src/Context.ts +12 -3
  305. package/src/Crypto.ts +43 -9
  306. package/src/DateTime.ts +9 -6
  307. package/src/Duration.ts +36 -16
  308. package/src/FileSystem.ts +48 -2
  309. package/src/RequestResolver.ts +4 -10
  310. package/src/Scheduler.ts +9 -0
  311. package/src/Schema.ts +677 -5
  312. package/src/SchemaGetter.ts +47 -35
  313. package/src/Sink.ts +45 -18
  314. package/src/Stdio.ts +9 -2
  315. package/src/Stream.ts +44 -15
  316. package/src/Utils.ts +8 -5
  317. package/src/ai/AnthropicStructuredOutput.ts +2 -2
  318. package/src/ai/Chat.ts +2 -2
  319. package/src/ai/LanguageModel.ts +96 -24
  320. package/src/ai/McpServer.ts +288 -94
  321. package/src/ai/Prompt.ts +24 -10
  322. package/src/ai/Response.ts +6 -3
  323. package/src/ai/internal/mcpProtocol/v2026_07_28.ts +5 -0
  324. package/src/ai/internal/mcpProtocol.ts +3 -0
  325. package/src/ai/internal/mcpRuntime.ts +2 -0
  326. package/src/cli/CliOutput.ts +33 -16
  327. package/src/cli/Command.ts +4 -3
  328. package/src/cli/Completions.ts +9 -6
  329. package/src/cli/GlobalFlag.ts +19 -11
  330. package/src/cli/Prompt.ts +159 -149
  331. package/src/cli/internal/ansi.ts +26 -3
  332. package/src/cli/internal/completions/powershell.ts +322 -0
  333. package/src/cli/internal/completions/zsh.ts +4 -2
  334. package/src/cli/internal/wizard.ts +34 -26
  335. package/src/cluster/ClusterSchema.ts +32 -0
  336. package/src/cluster/ClusterWorkflowEngine.ts +64 -36
  337. package/src/cluster/MessageStorage.ts +45 -14
  338. package/src/cluster/Runners.ts +8 -2
  339. package/src/cluster/Sharding.ts +219 -58
  340. package/src/cluster/SqlMessageStorage.ts +118 -84
  341. package/src/cluster/SqlRunnerStorage.ts +224 -128
  342. package/src/cluster/internal/entityManager.ts +151 -55
  343. package/src/encoding/Sse.ts +69 -79
  344. package/src/graphql/GraphQL.ts +494 -0
  345. package/src/graphql/GraphQLClient.ts +912 -0
  346. package/src/graphql/GraphQLClientError.ts +542 -0
  347. package/src/graphql/GraphQLGroup.ts +167 -0
  348. package/src/graphql/GraphQLMiddleware.ts +253 -0
  349. package/src/graphql/GraphQLProtocol.ts +1286 -0
  350. package/src/graphql/GraphQLResolver.ts +1010 -0
  351. package/src/graphql/index.ts +48 -0
  352. package/src/http/FindMyWay/internal/router.ts +4 -7
  353. package/src/http/HttpClient.ts +110 -12
  354. package/src/http/HttpEffect.ts +11 -5
  355. package/src/http/HttpMiddleware.ts +33 -34
  356. package/src/http/HttpRouter.ts +35 -0
  357. package/src/http/HttpServerResponse.ts +28 -16
  358. package/src/http/HttpTraceContext.ts +8 -2
  359. package/src/http/Multipart.ts +17 -1
  360. package/src/http-api/HttpApiScalar.ts +5 -0
  361. package/src/internal/dateTime.ts +13 -10
  362. package/src/internal/effect.ts +22 -15
  363. package/src/internal/graphql.ts +263 -0
  364. package/src/internal/graphqlHeaders.ts +11 -0
  365. package/src/internal/request.ts +31 -3
  366. package/src/internal/schema/toCodeDocument.ts +7 -1
  367. package/src/internal/schema/toJsonSchemaDocument.ts +34 -2
  368. package/src/internal/version.ts +1 -1
  369. package/src/net/AddressResolver.ts +222 -0
  370. package/src/net/Dns.ts +1035 -0
  371. package/src/net/Host.ts +544 -0
  372. package/src/net/IpInterface.ts +117 -7
  373. package/src/net/IpNetwork.ts +116 -17
  374. package/src/net/NetAddress.ts +620 -76
  375. package/src/net/index.ts +18 -0
  376. package/src/observability/OtlpMetrics.ts +12 -23
  377. package/src/observability/PrometheusMetrics.ts +1 -0
  378. package/src/persistence/PersistedQueue.ts +146 -53
  379. package/src/rpc/RpcClient.ts +41 -19
  380. package/src/rpc/RpcServer.ts +115 -40
  381. package/src/socket/DatagramSocket.ts +919 -0
  382. package/src/socket/Socket.ts +28 -2
  383. package/src/socket/index.ts +6 -0
  384. package/src/sql/Migrator.ts +21 -5
  385. package/src/sql/SqlClient.ts +39 -15
  386. package/src/testing/TestCrypto.ts +94 -0
  387. package/src/testing/index.ts +6 -0
  388. package/src/workflow/DurableDeferred.ts +160 -47
  389. package/src/workflow/WorkflowEngine.ts +50 -5
@@ -15,7 +15,6 @@ import * as Effect from "../Effect.ts"
15
15
  import * as Exit from "../Exit.ts"
16
16
  import * as Fiber from "../Fiber.ts"
17
17
  import * as Headers from "../http/Headers.ts"
18
- import * as Latch from "../Latch.ts"
19
18
  import * as Layer from "../Layer.ts"
20
19
  import * as Option from "../Option.ts"
21
20
  import * as PrimaryKey from "../PrimaryKey.ts"
@@ -132,7 +131,6 @@ export const make = Effect.gen(function*() {
132
131
  readonly context: Context.Context<any>
133
132
  }>()
134
133
  const interruptedActivities = new Set<string>()
135
- const activityLatches = new Map<string, Latch.Latch>()
136
134
  const deferredState = WorkflowEngine.makeDeferredState()
137
135
  // Rebuilds share an activation; overlapping activations have separate results.
138
136
  // Weak keys do not retain scopes, but contexts captured by clients, activities,
@@ -293,10 +291,7 @@ export const make = Effect.gen(function*() {
293
291
  id: ""
294
292
  })
295
293
 
296
- const maybeSuspended = Option.filter(
297
- maybeReply,
298
- (reply) => reply.exit._tag === "Success" && reply.exit.value._tag === "Suspended"
299
- )
294
+ const maybeSuspended = Option.filter(maybeReply, isSuspendedReply)
300
295
 
301
296
  if (Option.isNone(maybeSuspended)) return
302
297
  if (
@@ -330,10 +325,14 @@ export const make = Effect.gen(function*() {
330
325
  yield* sharding.reset(requestId.value)
331
326
  }, Effect.scoped)
332
327
 
333
- // A child or deferred may complete while a run is still unwinding. Subscribe
334
- // to the run's reply before reading storage so the wake cannot fall between
335
- // the run's last read and its suspended reply.
336
- const waitForRunReply = Effect.fnUntraced(function*(workflow: Workflow.Any, request: Entity.Request<any>) {
328
+ // Subscribe to the run's next reply before reading storage, so a reply
329
+ // cannot fall between the read and the subscription. Returns `true` when the
330
+ // stored reply already settles the wait, `false` when a new reply arrived.
331
+ const awaitRunReply = Effect.fnUntraced(function*(
332
+ workflow: Workflow.Any,
333
+ request: Envelope.Request<any>,
334
+ isSettled: (reply: WithExitEncoded<Workflow.ResultEncoded<any, any>>) => boolean
335
+ ) {
337
336
  const waiter = yield* storage.registerReplyHandler(
338
337
  new Message.OutgoingRequest<any>({
339
338
  envelope: request,
@@ -345,12 +344,45 @@ export const make = Effect.gen(function*() {
345
344
  })
346
345
  ).pipe(Effect.forkScoped({ startImmediately: true }))
347
346
  const reply = yield* replyForRequestId(request.requestId)
348
- if (Option.isSome(reply)) return
349
- yield* Fiber.join(waiter).pipe(
347
+ if (Option.isSome(reply) && isSettled(reply.value)) return true
348
+ yield* Fiber.join(waiter)
349
+ return false
350
+ }, Effect.scoped)
351
+
352
+ // A child or deferred may complete while a run is still unwinding. Wait for
353
+ // the run's reply so the wake cannot fall between the run's last read and
354
+ // its suspended reply.
355
+ const waitForRunReply = (workflow: Workflow.Any, request: Entity.Request<any>) =>
356
+ awaitRunReply(workflow, request, () => true).pipe(
357
+ Effect.asVoid,
350
358
  Effect.catchTag("EntityNotAssignedToRunner", () => ClusterAbandon.interrupt),
351
359
  Effect.catchCause((cause) => ClusterAbandon.isCause(cause) ? ClusterAbandon.interrupt : Effect.failCause(cause))
352
360
  )
353
- }, Effect.scoped)
361
+
362
+ // Wake an external caller once its run publishes a reply other than
363
+ // `Suspended`. Replies are only observed on the runner that saves them, so
364
+ // callers on other runners, and any failure here, leave delivery to the
365
+ // caller's retry schedule.
366
+ const awaitComplete = (workflow: Workflow.Any, executionId: string) =>
367
+ Effect.gen(function*() {
368
+ const address = entityAddressFor({ workflow, entityType: `Workflow/${workflow._tag}`, executionId })
369
+ if (!sharding.hasShardId(address.shardId)) return yield* Effect.never
370
+ const requestId = yield* storage.requestIdForPrimaryKey({ address, tag: "run", id: "" })
371
+ if (Option.isNone(requestId)) return yield* Effect.never
372
+ const request = Envelope.makeRequest<any>({
373
+ requestId: requestId.value,
374
+ address,
375
+ tag: "run",
376
+ payload: {},
377
+ headers: Headers.empty
378
+ })
379
+ // A replay that suspends again re-arms the wait, so it does not spend a
380
+ // step of the caller's retry schedule.
381
+ let settled = false
382
+ while (!settled) {
383
+ settled = yield* awaitRunReply(workflow, request, (reply) => !isSuspendedReply(reply))
384
+ }
385
+ }).pipe(Effect.catchCause(() => Effect.never))
354
386
 
355
387
  const interrupt = Effect.fnUntraced(
356
388
  function*(workflow: Workflow.Any, executionId: string) {
@@ -369,7 +401,7 @@ export const make = Effect.gen(function*() {
369
401
 
370
402
  const nonSuspendedReply = Option.filter(
371
403
  reply,
372
- (reply) => reply.exit._tag !== "Success" || reply.exit.value._tag !== "Suspended"
404
+ (reply) => !isSuspendedReply(reply)
373
405
  )
374
406
  if (Option.isSome(nonSuspendedReply)) {
375
407
  return Option.none()
@@ -476,23 +508,19 @@ export const make = Effect.gen(function*() {
476
508
  const activityId = `${executionId}/${payload.name}`
477
509
  const instance = WorkflowEngine.WorkflowInstance.initial(workflow, executionId)
478
510
  interruptedActivities.delete(activityId)
479
- return Effect.gen(function*() {
480
- let entry = activities.get(activityId)
481
- while (!entry) {
482
- const latch = Latch.makeUnsafe()
483
- activityLatches.set(activityId, latch)
484
- yield* latch.await
485
- entry = activities.get(activityId)
486
- }
487
- const context = entry.context.pipe(
488
- Context.add(WorkflowEngine.WorkflowInstance, instance),
489
- Context.add(CurrentActivationScope, activation),
490
- Context.add(Activity.CurrentAttempt, payload.attempt)
491
- )
492
- return yield* entry.activity.executeEncoded.pipe(
493
- Effect.provideContext(context)
494
- )
495
- }).pipe(
511
+ const entry = activities.get(activityId)
512
+ if (!entry) {
513
+ // Replay may never register a losing race activity. Answer it
514
+ // without waiting; a later registration can reset this reply.
515
+ return Effect.succeed(new Workflow.Suspended({})).pipe(Rpc.wrap({ fork: true }))
516
+ }
517
+ const context = entry.context.pipe(
518
+ Context.add(WorkflowEngine.WorkflowInstance, instance),
519
+ Context.add(CurrentActivationScope, activation),
520
+ Context.add(Activity.CurrentAttempt, payload.attempt)
521
+ )
522
+ return entry.activity.executeEncoded.pipe(
523
+ Effect.provideContext(context),
496
524
  Workflow.intoResult,
497
525
  Effect.catchCause((cause) => {
498
526
  // we only want to store interrupts as suspends when the
@@ -623,6 +651,8 @@ export const make = Effect.gen(function*() {
623
651
 
624
652
  resume: (workflow, executionId) => ensureSuccess(resume(workflow, executionId)),
625
653
 
654
+ awaitComplete,
655
+
626
656
  activityExecute: Effect.fnUntraced(
627
657
  function*(activity, attempt) {
628
658
  const services = yield* Effect.context<WorkflowEngine.WorkflowInstance>()
@@ -633,11 +663,6 @@ export const make = Effect.gen(function*() {
633
663
  while (true) {
634
664
  if (!activities.has(activityId)) {
635
665
  activities.set(activityId, { activity, context: services })
636
- const latch = activityLatches.get(activityId)
637
- if (latch) {
638
- yield* latch.open
639
- activityLatches.delete(activityId)
640
- }
641
666
  }
642
667
  const result = yield* Effect.orDie(
643
668
  client.activity({
@@ -820,6 +845,9 @@ const DeferredRpc = Rpc.make("deferred", {
820
845
  .annotate(ClusterSchema.Persisted, true)
821
846
  .annotate(ClusterSchema.Uninterruptible, true)
822
847
 
848
+ const isSuspendedReply = (reply: WithExitEncoded<Workflow.ResultEncoded<any, any>>) =>
849
+ reply.exit._tag === "Success" && reply.exit.value._tag === "Suspended"
850
+
823
851
  const decodeDeferredWithExit = Schema.decodeSync(Schema.toCodecJson(Reply.WithExit.schema(DeferredRpc)))
824
852
 
825
853
  const ResumeRpc = Rpc.make("resume", {
@@ -207,7 +207,12 @@ export class MessageStorage extends Context.Service<MessageStorage, {
207
207
  ) => Effect.Effect<void, PersistenceError>
208
208
 
209
209
  /**
210
- * Used to wrap requests with transactions.
210
+ * Wraps requests in storage transactions.
211
+ *
212
+ * `WithExit` replies saved in a transaction notify callers after the outermost
213
+ * commit; stream chunks notify immediately. Replies saved in a failed
214
+ * transaction must be discarded. The wrapper may fail only with the wrapped
215
+ * effect's own failure or a defect.
211
216
  */
212
217
  readonly withTransaction: <A, E, R>(
213
218
  effect: Effect.Effect<A, E, R>
@@ -513,6 +518,10 @@ export type EncodedRepliesOptions<A> = {
513
518
  readonly cursor: Option.Option<A>
514
519
  }
515
520
 
521
+ const TransactionNotifications = Context.Reference<
522
+ Array<Effect.Effect<void, PersistenceError | MalformedMessage>> | undefined
523
+ >("effect/cluster/MessageStorage/TransactionNotifications", { defaultValue: () => undefined })
524
+
516
525
  /**
517
526
  * Wraps a concrete message storage implementation with reply-handler management.
518
527
  *
@@ -555,6 +564,20 @@ export const make = (
555
564
  const replyHandlersShard = new Map<string, Set<ReplyHandler>>()
556
565
  return MessageStorage.of({
557
566
  ...storage,
567
+ // Nested transactions defer notifications to the outermost commit.
568
+ withTransaction: (effect) =>
569
+ TransactionNotifications.use((outer) => {
570
+ const notifications: Array<Effect.Effect<void, PersistenceError | MalformedMessage>> = []
571
+ return storage.withTransaction(Effect.provideService(effect, TransactionNotifications, notifications)).pipe(
572
+ Effect.tap(() =>
573
+ outer
574
+ ? Effect.sync(() => {
575
+ outer.push(...notifications)
576
+ })
577
+ : Effect.orDie(Effect.forEach(notifications, identity, { discard: true }))
578
+ )
579
+ )
580
+ }),
558
581
  registerReplyHandler: (message) => {
559
582
  const requestId = message.envelope.requestId
560
583
  return Effect.callback<void, EntityNotAssignedToRunner>((resume) => {
@@ -619,20 +642,28 @@ export const make = (
619
642
  saveReply(reply) {
620
643
  const requestId = reply.reply.requestId
621
644
  return Effect.flatMap(storage.saveReply(reply), (persisted) => {
622
- const handlers = replyHandlers.get(requestId)
623
- if (!handlers) {
624
- return Effect.void
625
- } else if (persisted.reply._tag === "WithExit") {
626
- replyHandlers.delete(requestId)
627
- for (let i = 0; i < handlers.length; i++) {
628
- const handler = handlers[i]
629
- handler.shardSet.delete(handler)
630
- handler.resume(Effect.void)
645
+ const notify = Effect.suspend(() => {
646
+ const handlers = replyHandlers.get(requestId)
647
+ if (!handlers) {
648
+ return Effect.void
649
+ } else if (persisted.reply._tag === "WithExit") {
650
+ replyHandlers.delete(requestId)
651
+ for (let i = 0; i < handlers.length; i++) {
652
+ const handler = handlers[i]
653
+ handler.shardSet.delete(handler)
654
+ handler.resume(Effect.void)
655
+ }
631
656
  }
632
- }
633
- return handlers.length === 1
634
- ? handlers[0].respond(persisted)
635
- : Effect.forEach(handlers, (handler) => handler.respond(persisted))
657
+ return handlers.length === 1
658
+ ? handlers[0].respond(persisted)
659
+ : Effect.forEach(handlers, (handler) => handler.respond(persisted), { discard: true })
660
+ })
661
+ if (persisted.reply._tag !== "WithExit") return notify
662
+ return TransactionNotifications.use((notifications) =>
663
+ notifications === undefined ? notify : Effect.sync(() => {
664
+ notifications.push(notify)
665
+ })
666
+ )
636
667
  })
637
668
  }
638
669
  })
@@ -282,6 +282,10 @@ export const make: (
282
282
  const reply = entry.replies[i]
283
283
  // we have reached the end
284
284
  if (reply._tag === "WithExit") {
285
+ // A reply can immediately reset and resend the same request. Do not
286
+ // let that sender attach to this completed reply loop.
287
+ storageRequests.delete(message.envelope.requestId)
288
+ waitingStorageRequests.delete(message.envelope.requestId)
285
289
  for (const message of entry.messages) {
286
290
  yield* message.respond(reply)
287
291
  }
@@ -304,8 +308,10 @@ export const make: (
304
308
  effect,
305
309
  Effect.sync(() => {
306
310
  const entry = storageRequests.get(message.envelope.requestId)
307
- if (!entry || entry.messages.size > 1) {
308
- entry?.messages.delete(message)
311
+ // An old waiter must not clean up a reset request's new reply loop.
312
+ if (!entry?.messages.has(message)) return
313
+ if (entry.messages.size > 1) {
314
+ entry.messages.delete(message)
309
315
  return
310
316
  }
311
317
  storageRequests.delete(message.envelope.requestId)