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
@@ -0,0 +1,542 @@
1
+ /**
2
+ * Serializable GraphQL client errors with an operation name and a
3
+ * `Schema.TaggedError` reason.
4
+ *
5
+ * @stability experimental
6
+ * @since 4.0.0
7
+ */
8
+ import * as Duration from "../Duration.ts"
9
+ import type * as Headers from "../http/Headers.ts"
10
+ import type * as HttpClientError from "../http/HttpClientError.ts"
11
+ import { HeadersSchema } from "../internal/graphqlHeaders.ts"
12
+ import { hasProperty } from "../Predicate.ts"
13
+ import * as Schema from "../Schema.ts"
14
+
15
+ const TypeId = "~effect/graphql/GraphQLClientError"
16
+
17
+ /**
18
+ * One entry of a GraphQL response's `errors` array, as described by the
19
+ * GraphQL specification.
20
+ *
21
+ * **Details**
22
+ *
23
+ * `path[0]` is the response key the error belongs to, which is the alias when
24
+ * the document used one, so callers can match an error to the field it hit.
25
+ * Unknown top-level fields are preserved as JSON values, including GitHub's
26
+ * `type` field.
27
+ *
28
+ * **Example** (Finding which alias an error belongs to)
29
+ *
30
+ * ```ts import.meta.vitest
31
+ * import { Schema } from "effect"
32
+ * import { GraphQLClientError } from "effect/graphql"
33
+ *
34
+ * const error = Schema.decodeUnknownSync(GraphQLClientError.GraphQLError)({
35
+ * message: "Could not resolve to a Repository",
36
+ * path: ["missing", "issues"],
37
+ * type: "NOT_FOUND"
38
+ * })
39
+ *
40
+ * error.path?.[0] // => "missing"
41
+ * ```
42
+ *
43
+ * @stability experimental
44
+ * @category schemas
45
+ * @since 4.0.0
46
+ */
47
+ export const GraphQLError = Schema.StructWithRest(
48
+ Schema.Struct({
49
+ message: Schema.String,
50
+ locations: Schema.optional(Schema.Array(Schema.Struct({ line: Schema.Number, column: Schema.Number }))),
51
+ path: Schema.optional(Schema.Array(Schema.Union([Schema.String, Schema.Number]))),
52
+ extensions: Schema.optional(Schema.Record(Schema.String, Schema.Json))
53
+ }),
54
+ [Schema.Record(Schema.String, Schema.Json)]
55
+ )
56
+
57
+ /**
58
+ * The decoded type of {@link GraphQLError}.
59
+ *
60
+ * @stability experimental
61
+ * @category models
62
+ * @since 4.0.0
63
+ */
64
+ export type GraphQLError = typeof GraphQLError.Type
65
+
66
+ /**
67
+ * The server answered with a non-empty `errors` array.
68
+ *
69
+ * **Details**
70
+ *
71
+ * Queries and mutations fail even when partial `data` is present, since a
72
+ * field error's `null` cannot be distinguished from a genuine `null`. Use
73
+ * `{ partial: true }` to receive decoded `{ data, errors }` instead.
74
+ *
75
+ * On this error, `data` is raw. `GraphQLError.path[0]` identifies the response
76
+ * key (or alias). Non-2xx HTTP responses with a GraphQL body also use this
77
+ * reason rather than `TransportError`.
78
+ *
79
+ * `status` and `headers` are the HTTP status and response headers, as the
80
+ * transport reported them and middleware left them. Headers use
81
+ * `Headers.Headers` at runtime and encode as a plain string record.
82
+ * Decoding restores the normalized header collection. Both are unset over
83
+ * graphql-ws, and `status` is unset for graphql-sse events. `retryAfter`
84
+ * parses the `Retry-After` response header as whole seconds or an HTTP date,
85
+ * measured from `Date.now()` when the error is constructed. `isRetryable` is
86
+ * true for HTTP status `429` or `500`–`599`, and for any response with a
87
+ * valid `Retry-After`, whatever its status. It is false otherwise, including
88
+ * when there is no status.
89
+ *
90
+ * For queries and mutations, callers opt into retries with `Effect.retry`,
91
+ * and should wait for `retryAfter` when it is set. With
92
+ * `GraphQLClient.pages`, retry each page's call, not the stream:
93
+ * `Stream.retry` starts again from the first page. Subscriptions retry a
94
+ * retryable `ResponseError` on their own, by resubscribing. graphql-sse events
95
+ * carry the stream response's headers, so an event with `errors` is retried
96
+ * when that response had a valid `Retry-After`, and ends the stream
97
+ * otherwise.
98
+ *
99
+ * **Example** (Inspecting the errors of a failed call)
100
+ *
101
+ * ```ts import.meta.vitest
102
+ * import { GraphQLClientError } from "effect/graphql"
103
+ *
104
+ * const reason = new GraphQLClientError.ResponseError({
105
+ * errors: [{ message: "Not found", path: ["repository"] }],
106
+ * data: { repository: null },
107
+ * status: 200
108
+ * })
109
+ *
110
+ * reason.isRetryable // => false
111
+ * reason.status // => 200
112
+ * reason.errors.map((error) => error.path?.[0]) // => ["repository"]
113
+ * ```
114
+ *
115
+ * **Example** (Retrying a call on retryable errors)
116
+ *
117
+ * ```ts import.meta.vitest
118
+ * import { Effect } from "effect"
119
+ * import { GraphQLClientError } from "effect/graphql"
120
+ *
121
+ * const call = Effect.fail(new GraphQLClientError.GraphQLClientError({
122
+ * operation: "Viewer",
123
+ * reason: new GraphQLClientError.ResponseError({
124
+ * errors: [{ message: "Query timed out" }],
125
+ * status: 502
126
+ * })
127
+ * }))
128
+ *
129
+ * const retried = Effect.retry(call, {
130
+ * while: (error) => error.reason.isRetryable,
131
+ * times: 3
132
+ * })
133
+ * ```
134
+ *
135
+ * @stability experimental
136
+ * @category errors
137
+ * @since 4.0.0
138
+ */
139
+ export class ResponseError
140
+ extends Schema.TaggedError<ResponseError>("effect/graphql/GraphQLClientError/ResponseError")("ResponseError", {
141
+ errors: Schema.NonEmptyArray(GraphQLError),
142
+ data: Schema.optional(Schema.Json),
143
+ extensions: Schema.optional(Schema.Record(Schema.String, Schema.Json)),
144
+ status: Schema.optional(Schema.Int),
145
+ headers: Schema.optional(HeadersSchema)
146
+ })
147
+ {
148
+ /**
149
+ * `true` for HTTP status `429` or `500`–`599`, or when the response has a
150
+ * `Retry-After` header that parses, at any status, including a graphql-sse
151
+ * event whose stream response had one; `false` otherwise, including when
152
+ * there is no status and no `Retry-After`. Use this as the `while` predicate
153
+ * of `Effect.retry` on the call, and {@link retryAfter} as the delay when it
154
+ * is set. Subscriptions resubscribe on it.
155
+ *
156
+ * @since 4.0.0
157
+ */
158
+ get isRetryable(): boolean {
159
+ if (this.retryAfter !== undefined) return true
160
+ return this.status !== undefined && isRetryableStatus(this.status)
161
+ }
162
+
163
+ readonly #retryAfter = parseRetryAfter(this.headers?.["retry-after"], Date.now())
164
+
165
+ /**
166
+ * The `Retry-After` response header parsed as whole seconds or an HTTP
167
+ * date. An HTTP date is measured from `Date.now()` when the error is
168
+ * constructed, so every read returns the same delay. Missing or invalid
169
+ * values yield `undefined`. `Effect.retry` does not apply this delay
170
+ * automatically.
171
+ *
172
+ * @since 4.0.0
173
+ */
174
+ get retryAfter(): Duration.Duration | undefined {
175
+ return this.#retryAfter
176
+ }
177
+
178
+ override get message(): string {
179
+ return this.errors.map((error) => error.message).join("; ")
180
+ }
181
+ }
182
+
183
+ const retryableStandardCloseCodes: ReadonlySet<number> = new Set([1000, 1001, 1005, 1006, 1012, 1013, 1014])
184
+
185
+ const fatalGraphQLWsCloseCodes: ReadonlySet<number> = new Set([4400, 4401, 4403, 4406, 4409, 4429, 4500])
186
+
187
+ /**
188
+ * Tests whether a WebSocket close code is fatal, meaning a `TransportError`
189
+ * carrying it is not retryable.
190
+ *
191
+ * **Details**
192
+ *
193
+ * Fatal codes are every standard `1xxx` code except `1000`, `1001`, `1005`,
194
+ * `1006`, `1012`, `1013` and `1014`, plus the graphql-ws codes `4400` (bad
195
+ * request), `4401` (unauthorized), `4403` (forbidden), `4406` (subprotocol
196
+ * not acceptable), `4409` (subscriber already exists), `4429` (too many
197
+ * initialisation requests) and `4500` (internal server error). Any other code
198
+ * is retryable.
199
+ *
200
+ * `4403` is fatal because reconnecting with the same credentials is refused
201
+ * again.
202
+ *
203
+ * **Example** (Checking close codes)
204
+ *
205
+ * ```ts import.meta.vitest
206
+ * import { GraphQLClientError } from "effect/graphql"
207
+ *
208
+ * GraphQLClientError.isFatalCloseCode(1006) // => false
209
+ * GraphQLClientError.isFatalCloseCode(1011) // => true
210
+ * GraphQLClientError.isFatalCloseCode(4401) // => true
211
+ * ```
212
+ *
213
+ * @stability experimental
214
+ * @category predicates
215
+ * @since 4.0.0
216
+ */
217
+ export const isFatalCloseCode = (code: number): boolean =>
218
+ code >= 1000 && code < 2000 ? !retryableStandardCloseCodes.has(code) : fatalGraphQLWsCloseCodes.has(code)
219
+
220
+ const isRetryableStatus = (status: number): boolean => status === 429 || (status >= 500 && status < 600)
221
+
222
+ /**
223
+ * Parses a `Retry-After` header value, either whole seconds or an HTTP date,
224
+ * into a `Duration` measured from `now` (epoch milliseconds). Anything else,
225
+ * such as `1.5` or `-1`, yields `undefined`.
226
+ */
227
+ const parseRetryAfter = (value: string | undefined, now: number): Duration.Duration | undefined => {
228
+ if (value === undefined) return undefined
229
+ const trimmed = value.trim()
230
+ if (/^\d+$/.test(trimmed)) {
231
+ return Duration.seconds(Number(trimmed))
232
+ }
233
+ // Every HTTP date names a day or month; `Date.parse` alone also accepts
234
+ // bare numbers such as `1.5` or `-1`.
235
+ if (!/[a-z]/i.test(trimmed)) return undefined
236
+ const date = Date.parse(trimmed)
237
+ if (Number.isNaN(date)) return undefined
238
+ return Duration.millis(Math.max(0, date - now))
239
+ }
240
+
241
+ /**
242
+ * The client could not get a GraphQL response from the server: a network
243
+ * failure, an HTTP response that is not a GraphQL response, or a closed
244
+ * WebSocket connection.
245
+ *
246
+ * **Details**
247
+ *
248
+ * - `status` and `headers` are only set for an HTTP response that is not a
249
+ * GraphQL response. Headers use `Headers.Headers` at runtime and encode
250
+ * as a plain string record; decoding restores the header collection.
251
+ * - `retryAfter` comes from the `Retry-After` header (whole seconds or an
252
+ * HTTP date) and is encoded as milliseconds. When it is set, the error is
253
+ * retryable at any status, unless the close code is fatal.
254
+ * - `closeCode` is set when a WebSocket connection closed. See
255
+ * {@link isFatalCloseCode} for the codes that are not retryable.
256
+ * - `fatal: true` marks a failure that retrying cannot fix, such as an
257
+ * operation on a graphql-ws transport whose scope has closed.
258
+ * - `cause` holds the original error, such as the `HttpClientError`. Once
259
+ * encoded it shrinks to `{ name, message }`, the standard `Schema.Defect`
260
+ * behaviour.
261
+ *
262
+ * **Example** (Retryability of transport failures)
263
+ *
264
+ * ```ts import.meta.vitest
265
+ * import { GraphQLClientError } from "effect/graphql"
266
+ *
267
+ * new GraphQLClientError.TransportError({ description: "ECONNREFUSED" }).isRetryable // => true
268
+ * new GraphQLClientError.TransportError({ description: "503", status: 503 }).isRetryable // => true
269
+ * new GraphQLClientError.TransportError({ description: "404", status: 404 }).isRetryable // => false
270
+ * new GraphQLClientError.TransportError({ description: "closed", closeCode: 4401 }).isRetryable // => false
271
+ * new GraphQLClientError.TransportError({ description: "shut down", fatal: true }).isRetryable // => false
272
+ * ```
273
+ *
274
+ * @stability experimental
275
+ * @category errors
276
+ * @since 4.0.0
277
+ */
278
+ export class TransportError
279
+ extends Schema.TaggedError<TransportError>("effect/graphql/GraphQLClientError/TransportError")("TransportError", {
280
+ description: Schema.String,
281
+ status: Schema.optional(Schema.Int),
282
+ headers: Schema.optional(HeadersSchema),
283
+ retryAfter: Schema.optional(Schema.DurationFromMillis),
284
+ closeCode: Schema.optional(Schema.Int),
285
+ fatal: Schema.optional(Schema.Boolean),
286
+ cause: Schema.optional(Schema.Defect())
287
+ })
288
+ {
289
+ /**
290
+ * `true` for a network failure (no `status`), a `429` or `500`–`599`
291
+ * status, a `retryAfter` at any status, and any close code that is not
292
+ * fatal (see {@link isFatalCloseCode}). An error marked `fatal` or with a
293
+ * fatal close code is never retryable. When retrying, wait for
294
+ * `retryAfter` when it is set.
295
+ *
296
+ * @since 4.0.0
297
+ */
298
+ get isRetryable(): boolean {
299
+ if (this.fatal === true) return false
300
+ if (this.closeCode !== undefined && isFatalCloseCode(this.closeCode)) return false
301
+ if (this.retryAfter !== undefined) return true
302
+ return this.status === undefined || isRetryableStatus(this.status)
303
+ }
304
+
305
+ override get message(): string {
306
+ return this.description
307
+ }
308
+
309
+ /**
310
+ * Converts an `HttpClientError` into a `TransportError`, reading `status`,
311
+ * `headers` (as `Headers.Headers`) and `Retry-After` from the response
312
+ * when there is one.
313
+ *
314
+ * **Details**
315
+ *
316
+ * `Retry-After` is read as whole seconds or an HTTP date; any other value
317
+ * is ignored. An HTTP date is measured from `options.now` (epoch
318
+ * milliseconds), which defaults to `Date.now()`. Transports pass the Effect
319
+ * clock's time when the response arrives.
320
+ *
321
+ * @since 4.0.0
322
+ */
323
+ static fromHttpClientError(
324
+ error: HttpClientError.HttpClientError,
325
+ options?: { readonly now?: number | undefined }
326
+ ): TransportError {
327
+ const response = error.response
328
+ return new TransportError({
329
+ description: error.message,
330
+ status: response?.status,
331
+ headers: response?.headers,
332
+ retryAfter: parseRetryAfter(response?.headers["retry-after"], options?.now ?? Date.now()),
333
+ cause: error
334
+ })
335
+ }
336
+ }
337
+
338
+ /**
339
+ * The variables Schema rejected the input, so nothing was sent.
340
+ *
341
+ * @stability experimental
342
+ * @category errors
343
+ * @since 4.0.0
344
+ */
345
+ export class EncodeError
346
+ extends Schema.TaggedError<EncodeError>("effect/graphql/GraphQLClientError/EncodeError")("EncodeError", {
347
+ description: Schema.String
348
+ })
349
+ {
350
+ /**
351
+ * An `EncodeError` is never retryable.
352
+ *
353
+ * @since 4.0.0
354
+ */
355
+ get isRetryable(): boolean {
356
+ return false
357
+ }
358
+
359
+ override get message(): string {
360
+ return this.description
361
+ }
362
+ }
363
+
364
+ /**
365
+ * The response was not a GraphQL response, or `data` did not match the result
366
+ * Schema.
367
+ *
368
+ * **Details**
369
+ *
370
+ * `data: null` without `errors` also produces a `DecodeError`, since
371
+ * `ResponseError` requires a non-empty errors array.
372
+ *
373
+ * @stability experimental
374
+ * @category errors
375
+ * @since 4.0.0
376
+ */
377
+ export class DecodeError
378
+ extends Schema.TaggedError<DecodeError>("effect/graphql/GraphQLClientError/DecodeError")("DecodeError", {
379
+ description: Schema.String
380
+ })
381
+ {
382
+ /**
383
+ * A `DecodeError` is never retryable.
384
+ *
385
+ * @since 4.0.0
386
+ */
387
+ get isRetryable(): boolean {
388
+ return false
389
+ }
390
+
391
+ override get message(): string {
392
+ return this.description
393
+ }
394
+ }
395
+
396
+ /**
397
+ * Cursor paging could not continue. Only `GraphQLClient.pages` and
398
+ * `GraphQLClient.items` raise it, when a connection becomes `null` on a page
399
+ * after the first, or when `hasNextPage` is `true` but `endCursor` is `null`
400
+ * or did not advance.
401
+ *
402
+ * **Details**
403
+ *
404
+ * `cursor` is the last cursor that was sent, `null` on the first page.
405
+ *
406
+ * @stability experimental
407
+ * @category errors
408
+ * @since 4.0.0
409
+ */
410
+ export class PaginationError
411
+ extends Schema.TaggedError<PaginationError>("effect/graphql/GraphQLClientError/PaginationError")("PaginationError", {
412
+ description: Schema.String,
413
+ cursor: Schema.NullOr(Schema.String)
414
+ })
415
+ {
416
+ /**
417
+ * A `PaginationError` is never retryable.
418
+ *
419
+ * @since 4.0.0
420
+ */
421
+ get isRetryable(): boolean {
422
+ return false
423
+ }
424
+
425
+ override get message(): string {
426
+ return this.description
427
+ }
428
+ }
429
+
430
+ /**
431
+ * The union of every reason a {@link GraphQLClientError} can carry.
432
+ *
433
+ * @stability experimental
434
+ * @category models
435
+ * @since 4.0.0
436
+ */
437
+ export type Reason = ResponseError | TransportError | EncodeError | DecodeError | PaginationError
438
+
439
+ const httpReason = (reason: Reason): ResponseError | TransportError | undefined =>
440
+ reason._tag === "ResponseError" || reason._tag === "TransportError" ? reason : undefined
441
+
442
+ /**
443
+ * The error every `GraphQLClient` method fails with. It names the operation
444
+ * and carries one of the five reasons; `cause` is the reason itself.
445
+ *
446
+ * **Example** (Retrying only what is retryable)
447
+ *
448
+ * ```ts import.meta.vitest
449
+ * import { Duration, Schema } from "effect"
450
+ * import { GraphQLClientError } from "effect/graphql"
451
+ *
452
+ * const error = new GraphQLClientError.GraphQLClientError({
453
+ * operation: "Viewer",
454
+ * reason: new GraphQLClientError.TransportError({
455
+ * description: "503 Service Unavailable",
456
+ * status: 503,
457
+ * retryAfter: Duration.seconds(30)
458
+ * })
459
+ * })
460
+ *
461
+ * error.isRetryable // => true
462
+ * error.retryAfter // => Duration.seconds(30)
463
+ *
464
+ * // Errors round-trip through their Schema
465
+ * const encoded = Schema.encodeSync(GraphQLClientError.GraphQLClientError)(error)
466
+ * Schema.decodeUnknownSync(GraphQLClientError.GraphQLClientError)(encoded).reason._tag // => "TransportError"
467
+ * ```
468
+ *
469
+ * @stability experimental
470
+ * @category errors
471
+ * @since 4.0.0
472
+ */
473
+ export class GraphQLClientError extends Schema.TaggedError<GraphQLClientError>(TypeId)("GraphQLClientError", {
474
+ operation: Schema.String,
475
+ reason: Schema.Union([ResponseError, TransportError, EncodeError, DecodeError, PaginationError])
476
+ }) {
477
+ /**
478
+ * Marks this value as a GraphQL client error for runtime guards.
479
+ *
480
+ * @stability experimental
481
+ * @since 4.0.0
482
+ */
483
+ readonly [TypeId] = TypeId
484
+
485
+ /**
486
+ * The reason, exposed as the error's cause.
487
+ *
488
+ * @since 4.0.0
489
+ */
490
+ override readonly cause = this.reason
491
+
492
+ /**
493
+ * Delegates to the reason's `isRetryable`.
494
+ *
495
+ * @since 4.0.0
496
+ */
497
+ get isRetryable(): boolean {
498
+ return this.reason.isRetryable
499
+ }
500
+
501
+ /**
502
+ * The `Retry-After` hint of a `ResponseError` or `TransportError` reason,
503
+ * if any. Use it as the delay when retrying on {@link isRetryable}.
504
+ *
505
+ * @since 4.0.0
506
+ */
507
+ get retryAfter(): Duration.Duration | undefined {
508
+ return httpReason(this.reason)?.retryAfter
509
+ }
510
+
511
+ /**
512
+ * The HTTP status of a `ResponseError` or `TransportError` reason, if any.
513
+ *
514
+ * @since 4.0.0
515
+ */
516
+ get status(): number | undefined {
517
+ return httpReason(this.reason)?.status
518
+ }
519
+
520
+ /**
521
+ * The response headers of a `ResponseError` or `TransportError` reason, if
522
+ * any.
523
+ *
524
+ * @since 4.0.0
525
+ */
526
+ get headers(): Headers.Headers | undefined {
527
+ return httpReason(this.reason)?.headers
528
+ }
529
+
530
+ override get message(): string {
531
+ return `${this.operation}: ${this.reason._tag}: ${this.reason.message}`
532
+ }
533
+ }
534
+
535
+ /**
536
+ * Tests whether a value is a {@link GraphQLClientError}.
537
+ *
538
+ * @stability experimental
539
+ * @category guards
540
+ * @since 4.0.0
541
+ */
542
+ export const isGraphQLClientError = (u: unknown): u is GraphQLClientError => hasProperty(u, TypeId)
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Groups of GraphQL operations, merged to build a client. Group middleware
3
+ * runs outside operation middleware. `@effect/graphql-generator` emits one
4
+ * group per document file.
5
+ *
6
+ * @stability experimental
7
+ * @since 4.0.0
8
+ */
9
+ import { type Pipeable, pipeArguments } from "../Pipeable.ts"
10
+ import { hasProperty } from "../Predicate.ts"
11
+ import type * as GraphQL from "./GraphQL.ts"
12
+ import type * as GraphQLMiddleware from "./GraphQLMiddleware.ts"
13
+
14
+ const TypeId = "~effect/graphql/GraphQLGroup"
15
+
16
+ /**
17
+ * A set of operations plus the middleware attached at the group level.
18
+ *
19
+ * @stability experimental
20
+ * @category models
21
+ * @since 4.0.0
22
+ */
23
+ export interface GraphQLGroup<out Ops extends GraphQL.Any> extends Pipeable {
24
+ readonly [TypeId]: typeof TypeId
25
+ readonly operations: ReadonlyArray<Ops>
26
+ /** Middleware attached to the group, outermost first. */
27
+ readonly middlewares: ReadonlyArray<GraphQLMiddleware.AnyService>
28
+ /**
29
+ * Attaches a middleware to every operation in the group. Group middleware
30
+ * runs outside operation middleware; several group middleware run in the
31
+ * order attached.
32
+ */
33
+ middleware<M extends GraphQLMiddleware.AnyService>(middleware: M): GraphQLGroup<GraphQL.AddMiddleware<Ops, M>>
34
+ }
35
+
36
+ /**
37
+ * Any group, with its operations erased.
38
+ *
39
+ * @stability experimental
40
+ * @category utility types
41
+ * @since 4.0.0
42
+ */
43
+ export type Any = GraphQLGroup<GraphQL.Any>
44
+
45
+ /**
46
+ * Extracts the union of operations in a group.
47
+ *
48
+ * @stability experimental
49
+ * @category utility types
50
+ * @since 4.0.0
51
+ */
52
+ export type Operations<G> = G extends GraphQLGroup<infer Ops> ? Ops : never
53
+
54
+ /**
55
+ * Tests whether a value is a {@link GraphQLGroup}.
56
+ *
57
+ * @stability experimental
58
+ * @category guards
59
+ * @since 4.0.0
60
+ */
61
+ export const isGraphQLGroup = (u: unknown): u is Any => hasProperty(u, TypeId)
62
+
63
+ const Proto = {
64
+ [TypeId]: TypeId,
65
+ pipe() {
66
+ return pipeArguments(this, arguments)
67
+ },
68
+ middleware(this: Any, middleware: GraphQLMiddleware.AnyService) {
69
+ return makeProto(this.operations, [...this.middlewares, middleware])
70
+ }
71
+ }
72
+
73
+ // The operation and middleware types of a group are phantom: at runtime a
74
+ // group holds erased `GraphQL.Any` operations and `AnyService` tags, so the
75
+ // typed constructors below state their group type with one assertion each.
76
+ const makeProto = (
77
+ operations: ReadonlyArray<GraphQL.Any>,
78
+ middlewares: ReadonlyArray<GraphQLMiddleware.AnyService>
79
+ ): Any => {
80
+ const byName = new Map<string, GraphQL.Any>()
81
+ for (const operation of operations) {
82
+ const existing = byName.get(operation.name)
83
+ if (existing !== undefined && existing !== operation) {
84
+ throw new Error(`GraphQLGroup: duplicate operation name "${operation.name}"`)
85
+ }
86
+ byName.set(operation.name, operation)
87
+ }
88
+ return Object.assign(Object.create(Proto), { operations: Array.from(byName.values()), middlewares })
89
+ }
90
+
91
+ /**
92
+ * Builds a group from operations. Operation names must be unique within a
93
+ * group, because they become the client's method names.
94
+ *
95
+ * **Example** (One group per `.graphql` file)
96
+ *
97
+ * ```ts import.meta.vitest
98
+ * import { Schema } from "effect"
99
+ * import { GraphQL, GraphQLGroup } from "effect/graphql"
100
+ *
101
+ * const Viewer = GraphQL.query("Viewer", {
102
+ * document: "query Viewer{viewer{login}}",
103
+ * result: Schema.Struct({ viewer: Schema.Struct({ login: Schema.String }) })
104
+ * })
105
+ *
106
+ * const ViewerGraphQLGroup = GraphQLGroup.make(Viewer)
107
+ *
108
+ * ViewerGraphQLGroup.operations.map((op) => op.name) // => ["Viewer"]
109
+ * ```
110
+ *
111
+ * @stability experimental
112
+ * @category constructors
113
+ * @since 4.0.0
114
+ */
115
+ export const make = <const Ops extends ReadonlyArray<GraphQL.Any>>(...operations: Ops): GraphQLGroup<Ops[number]> =>
116
+ makeProto(operations, []) as GraphQLGroup<Ops[number]>
117
+
118
+ /**
119
+ * Merges groups into one. Middleware attached to a source group stays
120
+ * attached to that group's operations; middleware attached to the merged
121
+ * group afterwards runs outside all of it.
122
+ *
123
+ * **Example** (Auth on one file, logging on everything)
124
+ *
125
+ * ```ts import.meta.vitest
126
+ * import { Schema } from "effect"
127
+ * import { GraphQL, GraphQLGroup, GraphQLMiddleware } from "effect/graphql"
128
+ *
129
+ * class Auth extends GraphQLMiddleware.Service<Auth>()("app/Auth") {}
130
+ * class Logging extends GraphQLMiddleware.Service<Logging>()("app/Logging") {}
131
+ *
132
+ * const Viewer = GraphQL.query("Viewer", {
133
+ * document: "query Viewer{viewer{login}}",
134
+ * result: Schema.Struct({ viewer: Schema.Struct({ login: Schema.String }) })
135
+ * })
136
+ * const Rate = GraphQL.query("Rate", {
137
+ * document: "query Rate{rateLimit{remaining}}",
138
+ * result: Schema.Struct({ rateLimit: Schema.Struct({ remaining: Schema.Int }) })
139
+ * })
140
+ *
141
+ * const all = GraphQLGroup.merge(GraphQLGroup.make(Viewer).middleware(Auth), GraphQLGroup.make(Rate))
142
+ * .middleware(Logging)
143
+ *
144
+ * all.operations.map((op) => op.name) // => ["Viewer", "Rate"]
145
+ * all.middlewares.map((m) => m.key) // => ["app/Logging"]
146
+ * all.operations[0].middlewares.map((m) => m.key) // => ["app/Auth"]
147
+ * ```
148
+ *
149
+ * @stability experimental
150
+ * @category combinators
151
+ * @since 4.0.0
152
+ */
153
+ export const merge = <const Groups extends ReadonlyArray<Any>>(
154
+ ...groups: Groups
155
+ ): GraphQLGroup<Operations<Groups[number]>> =>
156
+ makeProto(
157
+ groups.flatMap((group) =>
158
+ group.middlewares.length === 0
159
+ ? group.operations
160
+ : group.operations.map((operation): GraphQL.Any =>
161
+ Object.assign(Object.create(Object.getPrototypeOf(operation)), operation, {
162
+ middlewares: [...group.middlewares, ...operation.middlewares]
163
+ })
164
+ )
165
+ ),
166
+ []
167
+ ) as GraphQLGroup<Operations<Groups[number]>>