effect 4.0.3 → 4.0.5

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 (449) 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 +117 -0
  5. package/ai-docs/src/52_graphql/20_testing.ts +45 -0
  6. package/ai-docs/src/52_graphql/30_batching.ts +118 -0
  7. package/ai-docs/src/52_graphql/40_aliased-batching.ts +124 -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/Array.d.ts +90 -6
  11. package/dist/Array.d.ts.map +1 -1
  12. package/dist/Array.js +4 -2
  13. package/dist/Array.js.map +1 -1
  14. package/dist/Channel.d.ts +6 -1
  15. package/dist/Channel.d.ts.map +1 -1
  16. package/dist/Channel.js +17 -3
  17. package/dist/Channel.js.map +1 -1
  18. package/dist/ConfigProvider.d.ts.map +1 -1
  19. package/dist/ConfigProvider.js +33 -32
  20. package/dist/ConfigProvider.js.map +1 -1
  21. package/dist/Context.d.ts.map +1 -1
  22. package/dist/Context.js +9 -0
  23. package/dist/Context.js.map +1 -1
  24. package/dist/Duration.d.ts.map +1 -1
  25. package/dist/Duration.js +2 -5
  26. package/dist/Duration.js.map +1 -1
  27. package/dist/Effect.d.ts +61 -19
  28. package/dist/Effect.d.ts.map +1 -1
  29. package/dist/Effect.js +58 -39
  30. package/dist/Effect.js.map +1 -1
  31. package/dist/FileSystem.d.ts +23 -9
  32. package/dist/FileSystem.d.ts.map +1 -1
  33. package/dist/FileSystem.js.map +1 -1
  34. package/dist/Match.d.ts +20 -6
  35. package/dist/Match.d.ts.map +1 -1
  36. package/dist/Match.js +2 -2
  37. package/dist/Match.js.map +1 -1
  38. package/dist/Path.d.ts +37 -5
  39. package/dist/Path.d.ts.map +1 -1
  40. package/dist/Path.js +36 -5
  41. package/dist/Path.js.map +1 -1
  42. package/dist/RequestResolver.d.ts.map +1 -1
  43. package/dist/RequestResolver.js +4 -10
  44. package/dist/RequestResolver.js.map +1 -1
  45. package/dist/Scheduler.d.ts +9 -0
  46. package/dist/Scheduler.d.ts.map +1 -1
  47. package/dist/Scheduler.js +9 -0
  48. package/dist/Scheduler.js.map +1 -1
  49. package/dist/Schema.d.ts +15 -2
  50. package/dist/Schema.d.ts.map +1 -1
  51. package/dist/Schema.js +16 -2
  52. package/dist/Schema.js.map +1 -1
  53. package/dist/SchemaGetter.d.ts +4 -0
  54. package/dist/SchemaGetter.d.ts.map +1 -1
  55. package/dist/SchemaGetter.js +52 -33
  56. package/dist/SchemaGetter.js.map +1 -1
  57. package/dist/Sink.d.ts.map +1 -1
  58. package/dist/Sink.js +38 -17
  59. package/dist/Sink.js.map +1 -1
  60. package/dist/Stream.d.ts +11 -0
  61. package/dist/Stream.d.ts.map +1 -1
  62. package/dist/Stream.js +16 -1
  63. package/dist/Stream.js.map +1 -1
  64. package/dist/String.d.ts +12 -0
  65. package/dist/String.d.ts.map +1 -1
  66. package/dist/String.js +17 -4
  67. package/dist/String.js.map +1 -1
  68. package/dist/TxRef.d.ts.map +1 -1
  69. package/dist/TxRef.js +3 -2
  70. package/dist/TxRef.js.map +1 -1
  71. package/dist/Utils.js +8 -3
  72. package/dist/Utils.js.map +1 -1
  73. package/dist/ai/AiError.d.ts +10 -10
  74. package/dist/ai/AiError.d.ts.map +1 -1
  75. package/dist/ai/AiError.js +1 -1
  76. package/dist/ai/AiError.js.map +1 -1
  77. package/dist/ai/AnthropicStructuredOutput.js +0 -2
  78. package/dist/ai/AnthropicStructuredOutput.js.map +1 -1
  79. package/dist/ai/Chat.d.ts +2 -2
  80. package/dist/ai/Chat.d.ts.map +1 -1
  81. package/dist/ai/Decision.d.ts +70 -19
  82. package/dist/ai/Decision.d.ts.map +1 -1
  83. package/dist/ai/Decision.js +37 -3
  84. package/dist/ai/Decision.js.map +1 -1
  85. package/dist/ai/DecisionModel.js +1 -1
  86. package/dist/ai/DecisionModel.js.map +1 -1
  87. package/dist/ai/LanguageModel.d.ts +38 -7
  88. package/dist/ai/LanguageModel.d.ts.map +1 -1
  89. package/dist/ai/LanguageModel.js +70 -40
  90. package/dist/ai/LanguageModel.js.map +1 -1
  91. package/dist/ai/McpServer.d.ts +61 -52
  92. package/dist/ai/McpServer.d.ts.map +1 -1
  93. package/dist/ai/McpServer.js +134 -50
  94. package/dist/ai/McpServer.js.map +1 -1
  95. package/dist/ai/Prompt.d.ts.map +1 -1
  96. package/dist/ai/Prompt.js +28 -5
  97. package/dist/ai/Prompt.js.map +1 -1
  98. package/dist/ai/Response.d.ts +5 -4
  99. package/dist/ai/Response.d.ts.map +1 -1
  100. package/dist/ai/Response.js.map +1 -1
  101. package/dist/cli/Completions.d.ts +5 -0
  102. package/dist/cli/Completions.d.ts.map +1 -1
  103. package/dist/cli/Completions.js.map +1 -1
  104. package/dist/cli/Flag.d.ts +100 -0
  105. package/dist/cli/Flag.d.ts.map +1 -1
  106. package/dist/cli/Flag.js +33 -0
  107. package/dist/cli/Flag.js.map +1 -1
  108. package/dist/cli/Param.d.ts +87 -0
  109. package/dist/cli/Param.d.ts.map +1 -1
  110. package/dist/cli/Param.js +30 -0
  111. package/dist/cli/Param.js.map +1 -1
  112. package/dist/cli/internal/command.d.ts.map +1 -1
  113. package/dist/cli/internal/command.js +2 -1
  114. package/dist/cli/internal/command.js.map +1 -1
  115. package/dist/cli/internal/completions/bash.js +4 -3
  116. package/dist/cli/internal/completions/bash.js.map +1 -1
  117. package/dist/cli/internal/completions/descriptor.js +8 -1
  118. package/dist/cli/internal/completions/descriptor.js.map +1 -1
  119. package/dist/cli/internal/completions/fish.js +4 -4
  120. package/dist/cli/internal/completions/fish.js.map +1 -1
  121. package/dist/cli/internal/completions/powershell.js +3 -2
  122. package/dist/cli/internal/completions/powershell.js.map +1 -1
  123. package/dist/cli/internal/completions/zsh.js +7 -3
  124. package/dist/cli/internal/completions/zsh.js.map +1 -1
  125. package/dist/cli/internal/parser.js +11 -3
  126. package/dist/cli/internal/parser.js.map +1 -1
  127. package/dist/cli/internal/wizard.d.ts.map +1 -1
  128. package/dist/cli/internal/wizard.js +2 -1
  129. package/dist/cli/internal/wizard.js.map +1 -1
  130. package/dist/cluster/ClusterWorkflowEngine.d.ts +1 -0
  131. package/dist/cluster/ClusterWorkflowEngine.d.ts.map +1 -1
  132. package/dist/cluster/ClusterWorkflowEngine.js +113 -21
  133. package/dist/cluster/ClusterWorkflowEngine.js.map +1 -1
  134. package/dist/cluster/MessageStorage.d.ts.map +1 -1
  135. package/dist/cluster/MessageStorage.js +20 -12
  136. package/dist/cluster/MessageStorage.js.map +1 -1
  137. package/dist/cluster/Runners.d.ts.map +1 -1
  138. package/dist/cluster/Runners.js +54 -34
  139. package/dist/cluster/Runners.js.map +1 -1
  140. package/dist/cluster/Sharding.d.ts.map +1 -1
  141. package/dist/cluster/Sharding.js +6 -4
  142. package/dist/cluster/Sharding.js.map +1 -1
  143. package/dist/cluster/internal/entityManager.js +1 -1
  144. package/dist/cluster/internal/entityManager.js.map +1 -1
  145. package/dist/encoding/Sse.d.ts +6 -0
  146. package/dist/encoding/Sse.d.ts.map +1 -1
  147. package/dist/encoding/Sse.js +63 -71
  148. package/dist/encoding/Sse.js.map +1 -1
  149. package/dist/graphql/GraphQL.d.ts +349 -0
  150. package/dist/graphql/GraphQL.d.ts.map +1 -0
  151. package/dist/graphql/GraphQL.js +207 -0
  152. package/dist/graphql/GraphQL.js.map +1 -0
  153. package/dist/graphql/GraphQLClient.d.ts +562 -0
  154. package/dist/graphql/GraphQLClient.d.ts.map +1 -0
  155. package/dist/graphql/GraphQLClient.js +505 -0
  156. package/dist/graphql/GraphQLClient.js.map +1 -0
  157. package/dist/graphql/GraphQLClientError.d.ts +445 -0
  158. package/dist/graphql/GraphQLClientError.d.ts.map +1 -0
  159. package/dist/graphql/GraphQLClientError.js +479 -0
  160. package/dist/graphql/GraphQLClientError.js.map +1 -0
  161. package/dist/graphql/GraphQLGroup.d.ts +118 -0
  162. package/dist/graphql/GraphQLGroup.d.ts.map +1 -0
  163. package/dist/graphql/GraphQLGroup.js +109 -0
  164. package/dist/graphql/GraphQLGroup.js.map +1 -0
  165. package/dist/graphql/GraphQLMiddleware.d.ts +249 -0
  166. package/dist/graphql/GraphQLMiddleware.d.ts.map +1 -0
  167. package/dist/graphql/GraphQLMiddleware.js +130 -0
  168. package/dist/graphql/GraphQLMiddleware.js.map +1 -0
  169. package/dist/graphql/GraphQLProtocol.d.ts +969 -0
  170. package/dist/graphql/GraphQLProtocol.d.ts.map +1 -0
  171. package/dist/graphql/GraphQLProtocol.js +809 -0
  172. package/dist/graphql/GraphQLProtocol.js.map +1 -0
  173. package/dist/graphql/GraphQLResolver.d.ts +453 -0
  174. package/dist/graphql/GraphQLResolver.d.ts.map +1 -0
  175. package/dist/graphql/GraphQLResolver.js +582 -0
  176. package/dist/graphql/GraphQLResolver.js.map +1 -0
  177. package/dist/graphql/index.d.ts +40 -0
  178. package/dist/graphql/index.d.ts.map +1 -0
  179. package/dist/graphql/index.js +41 -0
  180. package/dist/graphql/index.js.map +1 -0
  181. package/dist/http/FetchHttpClient.d.ts +6 -3
  182. package/dist/http/FetchHttpClient.d.ts.map +1 -1
  183. package/dist/http/FetchHttpClient.js +6 -3
  184. package/dist/http/FetchHttpClient.js.map +1 -1
  185. package/dist/http/FindMyWay/internal/router.js +2 -1
  186. package/dist/http/FindMyWay/internal/router.js.map +1 -1
  187. package/dist/http/Headers.d.ts +3 -3
  188. package/dist/http/Headers.d.ts.map +1 -1
  189. package/dist/http/Headers.js +6 -5
  190. package/dist/http/Headers.js.map +1 -1
  191. package/dist/http/HttpClient.d.ts +27 -0
  192. package/dist/http/HttpClient.d.ts.map +1 -1
  193. package/dist/http/HttpClient.js +38 -11
  194. package/dist/http/HttpClient.js.map +1 -1
  195. package/dist/http/HttpClientError.d.ts.map +1 -1
  196. package/dist/http/HttpClientError.js +8 -6
  197. package/dist/http/HttpClientError.js.map +1 -1
  198. package/dist/http/HttpClientRequest.d.ts +147 -4
  199. package/dist/http/HttpClientRequest.d.ts.map +1 -1
  200. package/dist/http/HttpClientRequest.js +47 -1
  201. package/dist/http/HttpClientRequest.js.map +1 -1
  202. package/dist/http/HttpEffect.d.ts.map +1 -1
  203. package/dist/http/HttpEffect.js +14 -7
  204. package/dist/http/HttpEffect.js.map +1 -1
  205. package/dist/http/HttpMiddleware.d.ts +60 -0
  206. package/dist/http/HttpMiddleware.d.ts.map +1 -1
  207. package/dist/http/HttpMiddleware.js +93 -32
  208. package/dist/http/HttpMiddleware.js.map +1 -1
  209. package/dist/http/HttpRouter.d.ts +35 -0
  210. package/dist/http/HttpRouter.d.ts.map +1 -1
  211. package/dist/http/HttpRouter.js +87 -13
  212. package/dist/http/HttpRouter.js.map +1 -1
  213. package/dist/http/HttpServerError.d.ts.map +1 -1
  214. package/dist/http/HttpServerError.js +6 -4
  215. package/dist/http/HttpServerError.js.map +1 -1
  216. package/dist/http/HttpServerRequest.js +2 -2
  217. package/dist/http/HttpServerRequest.js.map +1 -1
  218. package/dist/http/HttpServerResponse.d.ts +8 -6
  219. package/dist/http/HttpServerResponse.d.ts.map +1 -1
  220. package/dist/http/HttpServerResponse.js +27 -16
  221. package/dist/http/HttpServerResponse.js.map +1 -1
  222. package/dist/http/HttpStaticServer.d.ts.map +1 -1
  223. package/dist/http/HttpStaticServer.js +4 -10
  224. package/dist/http/HttpStaticServer.js.map +1 -1
  225. package/dist/http/HttpTraceContext.js +1 -1
  226. package/dist/http/HttpTraceContext.js.map +1 -1
  227. package/dist/http/Multipart.d.ts.map +1 -1
  228. package/dist/http/Multipart.js +15 -1
  229. package/dist/http/Multipart.js.map +1 -1
  230. package/dist/http/Url.d.ts.map +1 -1
  231. package/dist/http/Url.js +2 -1
  232. package/dist/http/Url.js.map +1 -1
  233. package/dist/http/UrlParams.d.ts +112 -8
  234. package/dist/http/UrlParams.d.ts.map +1 -1
  235. package/dist/http/UrlParams.js +141 -14
  236. package/dist/http/UrlParams.js.map +1 -1
  237. package/dist/http/internal/compression.d.ts +1 -1
  238. package/dist/http/internal/compression.d.ts.map +1 -1
  239. package/dist/http/internal/compression.js +2 -10
  240. package/dist/http/internal/compression.js.map +1 -1
  241. package/dist/http/internal/compressionBody.d.ts +2 -0
  242. package/dist/http/internal/compressionBody.d.ts.map +1 -0
  243. package/dist/http/internal/compressionBody.js +36 -0
  244. package/dist/http/internal/compressionBody.js.map +1 -0
  245. package/dist/http/internal/decompression.d.ts +2 -0
  246. package/dist/http/internal/decompression.d.ts.map +1 -0
  247. package/dist/http/internal/decompression.js +162 -0
  248. package/dist/http/internal/decompression.js.map +1 -0
  249. package/dist/http/internal/tracing.js +14 -4
  250. package/dist/http/internal/tracing.js.map +1 -1
  251. package/dist/http/internal/urlParams.d.ts +2 -0
  252. package/dist/http/internal/urlParams.d.ts.map +1 -0
  253. package/dist/http/internal/urlParams.js +4 -0
  254. package/dist/http/internal/urlParams.js.map +1 -0
  255. package/dist/http-api/HttpApiClient.d.ts.map +1 -1
  256. package/dist/http-api/HttpApiClient.js +2 -1
  257. package/dist/http-api/HttpApiClient.js.map +1 -1
  258. package/dist/http-api/HttpApiScalar.d.ts +5 -0
  259. package/dist/http-api/HttpApiScalar.d.ts.map +1 -1
  260. package/dist/http-api/HttpApiScalar.js.map +1 -1
  261. package/dist/internal/core.js +17 -1
  262. package/dist/internal/core.js.map +1 -1
  263. package/dist/internal/dateTime.js +40 -35
  264. package/dist/internal/dateTime.js.map +1 -1
  265. package/dist/internal/effect.js +13 -6
  266. package/dist/internal/effect.js.map +1 -1
  267. package/dist/internal/graphql.d.ts +2 -0
  268. package/dist/internal/graphql.d.ts.map +1 -0
  269. package/dist/internal/graphql.js +205 -0
  270. package/dist/internal/graphql.js.map +1 -0
  271. package/dist/internal/graphqlHeaders.d.ts +4 -0
  272. package/dist/internal/graphqlHeaders.d.ts.map +1 -0
  273. package/dist/internal/graphqlHeaders.js +11 -0
  274. package/dist/internal/graphqlHeaders.js.map +1 -0
  275. package/dist/internal/request.js +20 -1
  276. package/dist/internal/request.js.map +1 -1
  277. package/dist/internal/schema/toCodeDocument.js +4 -1
  278. package/dist/internal/schema/toCodeDocument.js.map +1 -1
  279. package/dist/internal/schema/toJsonSchemaDocument.js +29 -5
  280. package/dist/internal/schema/toJsonSchemaDocument.js.map +1 -1
  281. package/dist/internal/version.js +1 -1
  282. package/dist/net/Host.d.ts +49 -0
  283. package/dist/net/Host.d.ts.map +1 -1
  284. package/dist/net/Host.js +55 -0
  285. package/dist/net/Host.js.map +1 -1
  286. package/dist/net/NetAddress.d.ts +8 -0
  287. package/dist/net/NetAddress.d.ts.map +1 -1
  288. package/dist/net/NetAddress.js +8 -0
  289. package/dist/net/NetAddress.js.map +1 -1
  290. package/dist/observability/Otlp.d.ts +15 -0
  291. package/dist/observability/Otlp.d.ts.map +1 -1
  292. package/dist/observability/Otlp.js +3 -0
  293. package/dist/observability/Otlp.js.map +1 -1
  294. package/dist/observability/OtlpExporter.d.ts +8 -0
  295. package/dist/observability/OtlpExporter.d.ts.map +1 -1
  296. package/dist/observability/OtlpExporter.js +10 -1
  297. package/dist/observability/OtlpExporter.js.map +1 -1
  298. package/dist/observability/OtlpLogger.d.ts +17 -0
  299. package/dist/observability/OtlpLogger.d.ts.map +1 -1
  300. package/dist/observability/OtlpLogger.js +14 -1
  301. package/dist/observability/OtlpLogger.js.map +1 -1
  302. package/dist/observability/OtlpMetrics.d.ts +17 -0
  303. package/dist/observability/OtlpMetrics.d.ts.map +1 -1
  304. package/dist/observability/OtlpMetrics.js +13 -0
  305. package/dist/observability/OtlpMetrics.js.map +1 -1
  306. package/dist/observability/OtlpTracer.d.ts +17 -0
  307. package/dist/observability/OtlpTracer.d.ts.map +1 -1
  308. package/dist/observability/OtlpTracer.js +15 -2
  309. package/dist/observability/OtlpTracer.js.map +1 -1
  310. package/dist/reactivity/AsyncResult.d.ts +6 -0
  311. package/dist/reactivity/AsyncResult.d.ts.map +1 -1
  312. package/dist/reactivity/AsyncResult.js +27 -0
  313. package/dist/reactivity/AsyncResult.js.map +1 -1
  314. package/dist/reactivity/AtomRegistry.js +3 -1
  315. package/dist/reactivity/AtomRegistry.js.map +1 -1
  316. package/dist/rpc/RpcClient.d.ts.map +1 -1
  317. package/dist/rpc/RpcClient.js +6 -1
  318. package/dist/rpc/RpcClient.js.map +1 -1
  319. package/dist/rpc/RpcServer.d.ts.map +1 -1
  320. package/dist/rpc/RpcServer.js +36 -9
  321. package/dist/rpc/RpcServer.js.map +1 -1
  322. package/dist/socket/Socket.d.ts +6 -0
  323. package/dist/socket/Socket.d.ts.map +1 -1
  324. package/dist/socket/Socket.js.map +1 -1
  325. package/dist/sql/SqlClient.d.ts +8 -7
  326. package/dist/sql/SqlClient.d.ts.map +1 -1
  327. package/dist/sql/SqlClient.js +8 -7
  328. package/dist/sql/SqlClient.js.map +1 -1
  329. package/dist/testing/TestClock.d.ts +16 -3
  330. package/dist/testing/TestClock.d.ts.map +1 -1
  331. package/dist/testing/TestClock.js +112 -22
  332. package/dist/testing/TestClock.js.map +1 -1
  333. package/dist/workflow/Activity.js +1 -0
  334. package/dist/workflow/Activity.js.map +1 -1
  335. package/dist/workflow/DurableDeferred.d.ts.map +1 -1
  336. package/dist/workflow/DurableDeferred.js +32 -3
  337. package/dist/workflow/DurableDeferred.js.map +1 -1
  338. package/dist/workflow/Workflow.d.ts +22 -0
  339. package/dist/workflow/Workflow.d.ts.map +1 -1
  340. package/dist/workflow/Workflow.js +15 -3
  341. package/dist/workflow/Workflow.js.map +1 -1
  342. package/dist/workflow/WorkflowEngine.d.ts +44 -3
  343. package/dist/workflow/WorkflowEngine.d.ts.map +1 -1
  344. package/dist/workflow/WorkflowEngine.js +45 -22
  345. package/dist/workflow/WorkflowEngine.js.map +1 -1
  346. package/package.json +6 -3
  347. package/src/Array.ts +95 -6
  348. package/src/Channel.ts +24 -3
  349. package/src/ConfigProvider.ts +40 -44
  350. package/src/Context.ts +9 -0
  351. package/src/Duration.ts +4 -5
  352. package/src/Effect.ts +140 -53
  353. package/src/FileSystem.ts +23 -9
  354. package/src/Match.ts +65 -12
  355. package/src/Path.ts +52 -7
  356. package/src/RequestResolver.ts +4 -10
  357. package/src/Scheduler.ts +9 -0
  358. package/src/Schema.ts +25 -4
  359. package/src/SchemaGetter.ts +47 -35
  360. package/src/Sink.ts +45 -18
  361. package/src/Stream.ts +33 -4
  362. package/src/String.ts +17 -4
  363. package/src/TxRef.ts +4 -2
  364. package/src/Utils.ts +8 -5
  365. package/src/ai/AiError.ts +3 -1
  366. package/src/ai/AnthropicStructuredOutput.ts +2 -2
  367. package/src/ai/Chat.ts +2 -2
  368. package/src/ai/Decision.ts +70 -21
  369. package/src/ai/DecisionModel.ts +1 -1
  370. package/src/ai/LanguageModel.ts +141 -63
  371. package/src/ai/McpServer.ts +259 -145
  372. package/src/ai/Prompt.ts +24 -10
  373. package/src/ai/Response.ts +6 -3
  374. package/src/cli/Completions.ts +5 -0
  375. package/src/cli/Flag.ts +101 -0
  376. package/src/cli/Param.ts +96 -0
  377. package/src/cli/internal/command.ts +2 -1
  378. package/src/cli/internal/completions/bash.ts +4 -3
  379. package/src/cli/internal/completions/descriptor.ts +10 -1
  380. package/src/cli/internal/completions/fish.ts +4 -4
  381. package/src/cli/internal/completions/powershell.ts +5 -2
  382. package/src/cli/internal/completions/zsh.ts +7 -3
  383. package/src/cli/internal/parser.ts +12 -3
  384. package/src/cli/internal/wizard.ts +2 -1
  385. package/src/cluster/ClusterWorkflowEngine.ts +143 -36
  386. package/src/cluster/MessageStorage.ts +14 -3
  387. package/src/cluster/Runners.ts +88 -66
  388. package/src/cluster/Sharding.ts +12 -9
  389. package/src/cluster/internal/entityManager.ts +1 -1
  390. package/src/encoding/Sse.ts +70 -80
  391. package/src/graphql/GraphQL.ts +494 -0
  392. package/src/graphql/GraphQLClient.ts +924 -0
  393. package/src/graphql/GraphQLClientError.ts +542 -0
  394. package/src/graphql/GraphQLGroup.ts +167 -0
  395. package/src/graphql/GraphQLMiddleware.ts +253 -0
  396. package/src/graphql/GraphQLProtocol.ts +1540 -0
  397. package/src/graphql/GraphQLResolver.ts +1038 -0
  398. package/src/graphql/index.ts +48 -0
  399. package/src/http/FetchHttpClient.ts +6 -3
  400. package/src/http/FindMyWay/internal/router.ts +2 -1
  401. package/src/http/Headers.ts +6 -9
  402. package/src/http/HttpClient.ts +72 -11
  403. package/src/http/HttpClientError.ts +10 -6
  404. package/src/http/HttpClientRequest.ts +173 -7
  405. package/src/http/HttpEffect.ts +11 -5
  406. package/src/http/HttpMiddleware.ts +113 -34
  407. package/src/http/HttpRouter.ts +95 -13
  408. package/src/http/HttpServerError.ts +8 -4
  409. package/src/http/HttpServerRequest.ts +2 -2
  410. package/src/http/HttpServerResponse.ts +28 -16
  411. package/src/http/HttpStaticServer.ts +4 -10
  412. package/src/http/HttpTraceContext.ts +1 -1
  413. package/src/http/Multipart.ts +17 -1
  414. package/src/http/Url.ts +2 -1
  415. package/src/http/UrlParams.ts +225 -26
  416. package/src/http/internal/compression.ts +3 -17
  417. package/src/http/internal/compressionBody.ts +56 -0
  418. package/src/http/internal/decompression.ts +226 -0
  419. package/src/http/internal/tracing.ts +21 -4
  420. package/src/http/internal/urlParams.ts +5 -0
  421. package/src/http-api/HttpApiClient.ts +2 -1
  422. package/src/http-api/HttpApiScalar.ts +5 -0
  423. package/src/internal/core.ts +21 -1
  424. package/src/internal/dateTime.ts +13 -10
  425. package/src/internal/effect.ts +13 -7
  426. package/src/internal/graphql.ts +286 -0
  427. package/src/internal/graphqlHeaders.ts +11 -0
  428. package/src/internal/request.ts +31 -3
  429. package/src/internal/schema/toCodeDocument.ts +7 -1
  430. package/src/internal/schema/toJsonSchemaDocument.ts +34 -2
  431. package/src/internal/version.ts +1 -1
  432. package/src/net/Host.ts +72 -0
  433. package/src/net/NetAddress.ts +9 -0
  434. package/src/observability/Otlp.ts +18 -0
  435. package/src/observability/OtlpExporter.ts +14 -3
  436. package/src/observability/OtlpLogger.ts +31 -2
  437. package/src/observability/OtlpMetrics.ts +30 -1
  438. package/src/observability/OtlpTracer.ts +32 -3
  439. package/src/reactivity/AsyncResult.ts +31 -0
  440. package/src/reactivity/AtomRegistry.ts +3 -1
  441. package/src/rpc/RpcClient.ts +6 -1
  442. package/src/rpc/RpcServer.ts +55 -15
  443. package/src/socket/Socket.ts +6 -0
  444. package/src/sql/SqlClient.ts +9 -7
  445. package/src/testing/TestClock.ts +144 -29
  446. package/src/workflow/Activity.ts +1 -0
  447. package/src/workflow/DurableDeferred.ts +43 -5
  448. package/src/workflow/Workflow.ts +31 -5
  449. package/src/workflow/WorkflowEngine.ts +107 -25
@@ -11,6 +11,7 @@
11
11
  * @stability unstable
12
12
  * @since 4.0.0
13
13
  */
14
+ import * as ByteSize from "../ByteSize.ts"
14
15
  import * as Cause from "../Cause.ts"
15
16
  import { Clock } from "../Clock.ts"
16
17
  import * as Context from "../Context.ts"
@@ -26,6 +27,7 @@ import type { Predicate } from "../Predicate.ts"
26
27
  import type { ReadonlyRecord } from "../Record.ts"
27
28
  import { nativeTracer, ParentSpan, Tracer } from "../Tracer.ts"
28
29
  import * as Headers from "./Headers.ts"
30
+ import { hasBody } from "./HttpMethod.ts"
29
31
  import type { CompressionAlgorithm } from "./HttpPlatform.ts"
30
32
  import { HttpPlatform } from "./HttpPlatform.ts"
31
33
  import { causeResponseStripped, ClientAbort, HttpServerError, ResponseError } from "./HttpServerError.ts"
@@ -35,9 +37,11 @@ import * as Response from "./HttpServerResponse.ts"
35
37
  import type { HttpServerResponse } from "./HttpServerResponse.ts"
36
38
  import * as TraceContext from "./HttpTraceContext.ts"
37
39
  import * as compressionInternal from "./internal/compression.ts"
40
+ import * as decompressionInternal from "./internal/decompression.ts"
38
41
  import * as bodyInternal from "./internal/httpBody.ts"
39
42
  import { appendPreResponseHandlerUnsafe } from "./internal/preResponseHandler.ts"
40
43
  import * as tracing from "./internal/tracing.ts"
44
+ import * as UrlParams from "./UrlParams.ts"
41
45
 
42
46
  /**
43
47
  * Middleware that transforms an HTTP server app effect into another HTTP server app effect.
@@ -268,42 +272,37 @@ export const tracer: <E, R>(
268
272
  }
269
273
  const redactedHeaderNames = fiber.getRef(Headers.CurrentRedactedNames)
270
274
  const headerFilter = fiber.getRef(TracerHeaderFilter)
271
- fiber.currentDispatcher.scheduleTask(() => {
272
- let response: HttpServerResponse
273
- let spanExit: Exit.Exit<HttpServerResponse, unknown>
274
- if (Exit.isFailure(exit)) {
275
- const [failureResponse, cause] = causeResponseStripped(exit.cause)
276
- response = failureResponse
277
- // OpenTelemetry only treats 5xx responses as server span errors; interrupts still fail the span.
278
- spanExit = Option.isSome(cause) && (response.status >= 500 || Cause.hasInterrupts(cause.value))
279
- ? Exit.failCause(cause.value)
280
- : responseSpanExit(request, response)
281
- } else {
282
- response = exit.value
283
- spanExit = responseSpanExit(request, response)
275
+ // Complete the span before the request fiber exits so shutdown can flush it.
276
+ let response: HttpServerResponse
277
+ let spanExit: Exit.Exit<HttpServerResponse, unknown>
278
+ if (Exit.isFailure(exit)) {
279
+ const [failureResponse, cause] = causeResponseStripped(exit.cause)
280
+ response = failureResponse
281
+ // OpenTelemetry only treats 5xx responses as server span errors; interrupts still fail the span.
282
+ spanExit = Option.isSome(cause) && (response.status >= 500 || Cause.hasInterrupts(cause.value))
283
+ ? Exit.failCause(cause.value)
284
+ : responseSpanExit(request, response)
285
+ } else {
286
+ response = exit.value
287
+ spanExit = responseSpanExit(request, response)
288
+ }
289
+ if (span.sampled) {
290
+ span.attribute("http.request.method", request.method)
291
+ const url = Request.toURL(request)
292
+ if (Option.isSome(url)) {
293
+ tracing.addUrlAttributes(span, url.value, fiber.getRef(UrlParams.CurrentRedactedNames))
294
+ }
295
+ if (request.headers["user-agent"] !== undefined) {
296
+ span.attribute("user_agent.original", request.headers["user-agent"])
284
297
  }
285
- if (span.sampled) {
286
- span.attribute("http.request.method", request.method)
287
- const url = Request.toURL(request)
288
- if (Option.isSome(url)) {
289
- if (!request.url.startsWith("/") && (url.value.username !== "" || url.value.password !== "")) {
290
- url.value.username = "REDACTED"
291
- url.value.password = "REDACTED"
292
- }
293
- tracing.addUrlAttributes(span, url.value)
294
- }
295
- if (request.headers["user-agent"] !== undefined) {
296
- span.attribute("user_agent.original", request.headers["user-agent"])
297
- }
298
- tracing.addHeaderAttributes(span, "request", request.headers, headerFilter, redactedHeaderNames)
299
- if (Option.isSome(request.remoteAddress)) {
300
- span.attribute("client.address", request.remoteAddress.value)
301
- }
302
- span.attribute("http.response.status_code", response.status)
303
- tracing.addHeaderAttributes(span, "response", response.headers, headerFilter, redactedHeaderNames)
298
+ tracing.addHeaderAttributes(span, "request", request.headers, headerFilter, redactedHeaderNames)
299
+ if (Option.isSome(request.remoteAddress)) {
300
+ span.attribute("client.address", request.remoteAddress.value)
304
301
  }
305
- span.end(endTime, spanExit)
306
- }, 0)
302
+ span.attribute("http.response.status_code", response.status)
303
+ tracing.addHeaderAttributes(span, "response", response.headers, headerFilter, redactedHeaderNames)
304
+ }
305
+ span.end(endTime, spanExit)
307
306
  return undefined
308
307
  })
309
308
  return httpApp
@@ -627,6 +626,86 @@ export const compression = (
627
626
  })
628
627
  }
629
628
 
629
+ /**
630
+ * Middleware that decompresses request bodies sent with a `Content-Encoding`
631
+ * of `gzip` or `deflate`.
632
+ *
633
+ * **Details**
634
+ *
635
+ * The request service is replaced with a view whose body accessors (`stream`,
636
+ * `text`, `json`, `arrayBuffer`, `urlParamsBody`, `multipart`) yield the
637
+ * decompressed body, and whose headers no longer include `Content-Encoding`
638
+ * or `Content-Length`. Decompression is lazy and only happens when the body
639
+ * is read. Stacked codings such as `deflate, gzip` are undone in reverse
640
+ * order, and `identity` members are ignored.
641
+ *
642
+ * Requests whose `Content-Encoding` includes any other coding are rejected
643
+ * with `415 Unsupported Media Type` and `Accept-Encoding: gzip, deflate`,
644
+ * without running the app. Malformed compressed data fails the body read
645
+ * with a `RequestParseError`, or a `MultipartError` with reason `Parse` when
646
+ * reading multipart data.
647
+ *
648
+ * The decompressed size is limited to `maxDecompressedSize`, falling back to
649
+ * the `MaxBodySize` reference and then to 10 MiB. Exceeding it fails the body
650
+ * read with a `RequestParseError`, or a `MultipartError` with reason
651
+ * `BodyTooLarge` when reading multipart data. The limit applies to every body
652
+ * accessor, including `stream`.
653
+ *
654
+ * Decompression uses the global `DecompressionStream`. `deflate` means the
655
+ * zlib format (RFC 1950); raw deflate data is rejected.
656
+ *
657
+ * **Gotchas**
658
+ *
659
+ * `request.source` is still the original, compressed request.
660
+ * `HttpServerRequest.toWeb` returns that compressed request only when the
661
+ * source is a Web `Request`. On other platforms, such as Node, it builds a new
662
+ * `Request` from the decompressed body and the stripped headers.
663
+ *
664
+ * @stability unstable
665
+ * @category compression
666
+ * @since 4.0.0
667
+ */
668
+ export const decompression = (
669
+ options?: {
670
+ /**
671
+ * Maximum number of decompressed body bytes. Defaults to the
672
+ * `MaxBodySize` reference, or 10 MiB when that is unset.
673
+ */
674
+ readonly maxDecompressedSize?: ByteSize.Input | undefined
675
+ } | undefined
676
+ ): <E, R>(
677
+ httpApp: Effect.Effect<HttpServerResponse, E, R>
678
+ ) => Effect.Effect<HttpServerResponse, E, R | HttpServerRequest> => {
679
+ const maxDecompressedSize = options?.maxDecompressedSize === undefined
680
+ ? undefined
681
+ : ByteSize.fromInputUnsafe(options.maxDecompressedSize)
682
+ return <E, R>(
683
+ httpApp: Effect.Effect<HttpServerResponse, E, R>
684
+ ): Effect.Effect<HttpServerResponse, E, R | HttpServerRequest> =>
685
+ Effect.withFiber((fiber) => {
686
+ const request = Context.getUnsafe(fiber.context, HttpServerRequest)
687
+ const contentEncoding = request.headers["content-encoding"]
688
+ if (contentEncoding === undefined || !hasBody(request.method)) {
689
+ return httpApp
690
+ }
691
+ const codings = decompressionInternal.parseContentEncoding(contentEncoding)
692
+ if (codings === undefined) {
693
+ return Effect.succeed(Response.empty({
694
+ status: 415,
695
+ headers: { "accept-encoding": "gzip, deflate" }
696
+ }))
697
+ }
698
+ if (codings.length === 0) {
699
+ return httpApp
700
+ }
701
+ return Effect.provideService(
702
+ httpApp,
703
+ HttpServerRequest,
704
+ new decompressionInternal.DecompressedRequest(request, codings, maxDecompressedSize)
705
+ )
706
+ })
707
+ }
708
+
630
709
  const withVary = (response: HttpServerResponse): HttpServerResponse => {
631
710
  const vary = compressionInternal.varyWith(response.headers, "Accept-Encoding")
632
711
  return Response.setHeader(response, "vary", vary)
@@ -24,6 +24,7 @@ import * as Scope from "../Scope.ts"
24
24
  import * as Tracer from "../Tracer.ts"
25
25
  import type * as Types from "../Types.ts"
26
26
  import * as FindMyWay from "./FindMyWay.ts"
27
+ import { getClosingParenthensePosition } from "./FindMyWay/internal/router.ts"
27
28
  import * as HttpEffect from "./HttpEffect.ts"
28
29
  import * as HttpIncomingMessage from "./HttpIncomingMessage.ts"
29
30
  import type * as HttpMethod from "./HttpMethod.ts"
@@ -123,7 +124,12 @@ export const HttpRouter: Context.Service<HttpRouter, HttpRouter> = Context.Servi
123
124
  * @since 4.0.0
124
125
  */
125
126
  export const make = Effect.gen(function*() {
126
- const router = FindMyWay.make<Route<any, never>>(yield* RouterConfig)
127
+ const config = yield* RouterConfig
128
+ const ignoreDuplicateSlashes = config.ignoreDuplicateSlashes ?? true
129
+ const router = FindMyWay.make<{
130
+ readonly route: Route<any, never>
131
+ readonly prefixSegments: number | undefined
132
+ }>(config)
127
133
  const middleware = new Set<middleware.Fn>()
128
134
 
129
135
  const addAll = <const Routes extends ReadonlyArray<Route<any, any>>>(
@@ -147,19 +153,25 @@ export const make = Effect.gen(function*() {
147
153
  ...routes[i],
148
154
  handler: applyMiddleware(routes[i].handler as Effect.Effect<HttpServerResponse.HttpServerResponse>)
149
155
  })
156
+ const handler = {
157
+ route: route as Route<any, never>,
158
+ prefixSegments: Option.isSome(route.prefix)
159
+ ? countSegments(route.prefix.value, ignoreDuplicateSlashes)
160
+ : undefined
161
+ }
150
162
  if (route.method === "*") {
151
163
  if (route.path.endsWith("/*")) {
152
- router.all(route.path, route as any)
153
- router.all(route.path.slice(0, -2) as any, route as any)
164
+ router.all(route.path, handler)
165
+ router.all(route.path.slice(0, -2) as any, handler)
154
166
  } else {
155
- router.all(route.path, route as any)
167
+ router.all(route.path, handler)
156
168
  }
157
169
  } else {
158
170
  if (route.path.endsWith("/*")) {
159
- router.on(route.method, route.path, route as any)
160
- router.on(route.method, route.path.slice(0, -2) as any, route as any)
171
+ router.on(route.method, route.path, handler)
172
+ router.on(route.method, route.path.slice(0, -2) as any, handler)
161
173
  } else {
162
- router.on(route.method, route.path, route as any)
174
+ router.on(route.method, route.path, handler)
163
175
  }
164
176
  }
165
177
  }
@@ -211,12 +223,12 @@ export const make = Effect.gen(function*() {
211
223
  })
212
224
  )
213
225
  }
214
- const route = result.handler
215
- if (Option.isSome(route.prefix)) {
226
+ const { route, prefixSegments } = result.handler
227
+ if (prefixSegments !== undefined) {
216
228
  context = Context.add(
217
229
  context,
218
230
  HttpServerRequest.HttpServerRequest,
219
- sliceRequestUrl(request, route.prefix.value)
231
+ sliceRequestUrl(request, prefixSegments, ignoreDuplicateSlashes)
220
232
  )
221
233
  }
222
234
  context = Context.add(context, HttpServerRequest.ParsedSearchParams, result.searchParams)
@@ -251,9 +263,44 @@ export const make = Effect.gen(function*() {
251
263
  })
252
264
  })
253
265
 
254
- function sliceRequestUrl(request: HttpServerRequest.HttpServerRequest, prefix: string) {
255
- const prefexLen = prefix.length
256
- return request.modify({ url: request.url.length <= prefexLen ? "/" : request.url.slice(prefexLen) })
266
+ function sliceRequestUrl(
267
+ request: HttpServerRequest.HttpServerRequest,
268
+ prefixSegments: number,
269
+ ignoreDuplicateSlashes: boolean
270
+ ) {
271
+ const url = request.url
272
+ let index = 0
273
+ for (let segments = prefixSegments; segments > 0; segments--) {
274
+ if (ignoreDuplicateSlashes) {
275
+ while (url[index] === "/") index++
276
+ } else if (url[index] === "/") {
277
+ index++
278
+ }
279
+ while (index < url.length && !"/?;#".includes(url[index])) index++
280
+ }
281
+ return request.modify({ url: index < url.length ? url.slice(index) : "/" })
282
+ }
283
+
284
+ function countSegments(prefix: string, ignoreDuplicateSlashes: boolean) {
285
+ let count = 0
286
+ let inParamName = false
287
+ for (let i = 0; i < prefix.length; i++) {
288
+ const char = prefix[i]
289
+ if (char === "/") {
290
+ if (!ignoreDuplicateSlashes || prefix[i - 1] !== "/") count++
291
+ inParamName = false
292
+ } else if (char === ":") {
293
+ if (prefix[i + 1] === ":") i++
294
+ else inParamName = true
295
+ } else if (char === "-" || char === ".") {
296
+ inParamName = false
297
+ } else if (char === "(" && inParamName) {
298
+ // Slashes inside parameter regexes are not segment boundaries.
299
+ i = getClosingParenthensePosition(prefix, i)
300
+ inParamName = false
301
+ }
302
+ }
303
+ return count
257
304
  }
258
305
 
259
306
  /**
@@ -1410,6 +1457,41 @@ export const serve = <A, E, R, HE, HR = Request.Only<"Requires", R> | Request.On
1410
1457
  * request arrives, in which case that request waits for the build to finish.
1411
1458
  * If the build fails, every request rejects with the build error.
1412
1459
  *
1460
+ * **Gotchas**
1461
+ *
1462
+ * On Cloudflare Workers, build the handler at module scope: fibers started
1463
+ * inside `fetch` capture that request's async context. Layer construction
1464
+ * must not require request-time I/O; bindings are available from
1465
+ * `cloudflare:workers` via its `env` export.
1466
+ * Prefer request-local work or a Durable Object's supported lifecycle APIs,
1467
+ * not indefinite background execution. For shared fibers, provide a sync
1468
+ * scheduler to both layers and requests as below; see `Scheduler.MixedScheduler`
1469
+ * for its limitations. Bound waits for shared work with request-local timeouts.
1470
+ *
1471
+ * **Example** (Opting in to the sync scheduler on Workers)
1472
+ *
1473
+ * ```ts
1474
+ * import { Effect, Layer, Scheduler } from "effect"
1475
+ * import { HttpRouter, HttpServer, HttpServerResponse } from "effect/http"
1476
+ *
1477
+ * const SyncScheduler = Layer.succeed(
1478
+ * Scheduler.Scheduler,
1479
+ * new Scheduler.MixedScheduler("sync")
1480
+ * )
1481
+ * const AppLive = HttpRouter.add(
1482
+ * "GET",
1483
+ * "/ping",
1484
+ * Effect.succeed(HttpServerResponse.text("pong"))
1485
+ * ).pipe(Layer.provide(HttpServer.layerServices))
1486
+ *
1487
+ * // Build once at module scope. The scheduler is also retained for requests.
1488
+ * const app = HttpRouter.toWebHandler(
1489
+ * AppLive.pipe(Layer.provideMerge(SyncScheduler))
1490
+ * )
1491
+ *
1492
+ * export default { fetch: (request: Request) => app.handler(request) }
1493
+ * ```
1494
+ *
1413
1495
  * @stability unstable
1414
1496
  * @category converting
1415
1497
  * @since 4.0.0
@@ -23,6 +23,7 @@ import { hasProperty } from "../Predicate.ts"
23
23
  import type * as Request from "./HttpServerRequest.ts"
24
24
  import * as Respondable from "./HttpServerRespondable.ts"
25
25
  import * as Response from "./HttpServerResponse.ts"
26
+ import * as UrlParams from "./UrlParams.ts"
26
27
 
27
28
  const TypeId = "~effect/http/HttpServerError"
28
29
 
@@ -107,7 +108,7 @@ export class RequestParseError extends Data.TaggedError("RequestParseError")<{
107
108
  }
108
109
 
109
110
  get methodAndUrl() {
110
- return `${this.request.method} ${this.request.url}`
111
+ return formatMethodAndUrl(this.request)
111
112
  }
112
113
 
113
114
  override get message() {
@@ -139,7 +140,7 @@ export class RouteNotFound extends Data.TaggedError("RouteNotFound")<{
139
140
  override readonly [ErrorReporter.ignore] = true
140
141
 
141
142
  get methodAndUrl() {
142
- return `${this.request.method} ${this.request.url}`
143
+ return formatMethodAndUrl(this.request)
143
144
  }
144
145
 
145
146
  override get message() {
@@ -174,7 +175,7 @@ export class InternalError extends Data.TaggedError("InternalError")<{
174
175
  }
175
176
 
176
177
  get methodAndUrl() {
177
- return `${this.request.method} ${this.request.url}`
178
+ return formatMethodAndUrl(this.request)
178
179
  }
179
180
 
180
181
  override get message() {
@@ -214,7 +215,7 @@ export class ResponseError extends Data.TaggedError("ResponseError")<{
214
215
  }
215
216
 
216
217
  get methodAndUrl() {
217
- return `${this.request.method} ${this.request.url}`
218
+ return formatMethodAndUrl(this.request)
218
219
  }
219
220
 
220
221
  override get message() {
@@ -275,6 +276,9 @@ export class ClientAbort extends Context.Service<ClientAbort, true>()("effect/ht
275
276
  )
276
277
  }
277
278
 
279
+ const formatMethodAndUrl = (request: Request.HttpServerRequest) =>
280
+ `${request.method} ${UrlParams.redactUrl(request.url)}`
281
+
278
282
  const formatRequestMessage = (reason: string, description: string | undefined, info: string) => {
279
283
  const prefix = `${reason} (${info})`
280
284
  return description ? `${prefix}: ${description}` : prefix
@@ -512,7 +512,7 @@ class ServerRequestImpl extends Inspectable.Class implements HttpServerRequest {
512
512
  return HttpIncomingMessage.inspect(this, {
513
513
  _id: "HttpServerRequest",
514
514
  method: this.method,
515
- url: this.originalUrl
515
+ url: UrlParams.redactUrl(this.originalUrl)
516
516
  })
517
517
  }
518
518
  modify(
@@ -711,7 +711,7 @@ class ClientRequestImpl extends Inspectable.Class implements HttpServerRequest {
711
711
  return HttpIncomingMessage.inspect(this, {
712
712
  _id: "HttpServerRequest",
713
713
  method: this.method,
714
- url: this.originalUrl
714
+ url: UrlParams.redactUrl(this.originalUrl)
715
715
  })
716
716
  }
717
717
 
@@ -1346,23 +1346,35 @@ export const setStatus: {
1346
1346
  export const omitsBody = (response: HttpServerResponse, withoutBody = false): boolean =>
1347
1347
  withoutBody || response.status === 204 || response.status === 205 || response.status === 304
1348
1348
 
1349
+ const mergeHeaders = (target: globalThis.Headers, headers: globalThis.Headers): globalThis.Headers => {
1350
+ for (const [key, value] of headers as any) {
1351
+ if (key === "set-cookie") {
1352
+ target.append(key, value)
1353
+ } else {
1354
+ target.set(key, value)
1355
+ }
1356
+ }
1357
+ return target
1358
+ }
1359
+
1349
1360
  /**
1350
1361
  * Converts an `HttpServerResponse` to a Web `Response`.
1351
1362
  *
1352
1363
  * **Details**
1353
1364
  *
1354
1365
  * Cookies are appended as `Set-Cookie` headers. Stream bodies are converted using
1355
- * the supplied context.
1366
+ * the supplied context, and are interrupted when `signal` aborts.
1356
1367
  *
1357
1368
  * Bodies are omitted for statuses 204, 205, and 304, or when `withoutBody` is true
1358
1369
  * (for example, for HEAD responses). Omitted raw `ReadableStream` bodies are
1359
1370
  * cancelled without awaiting completion, and cancellation errors are ignored.
1360
1371
  *
1361
- * A raw `Response` body is returned as-is after the outer headers are merged
1362
- * into it: outer headers replace native ones, and cookies are appended. When
1363
- * its body is omitted, the bodyless `Response` keeps those merged headers and
1364
- * uses the outer status for 204, 205 and 304, or the raw `Response` status
1365
- * otherwise.
1372
+ * A raw `Response` is returned as-is when there are no outer headers or cookies
1373
+ * to merge. Otherwise, they are merged into a copy without modifying the raw
1374
+ * response: outer headers replace native ones, and cookies are appended. When
1375
+ * its body is omitted, the bodyless `Response` keeps those merged
1376
+ * headers and uses the outer status for 204, 205 and 304, or the raw `Response`
1377
+ * status otherwise.
1366
1378
  *
1367
1379
  * @stability unstable
1368
1380
  * @category converting
@@ -1373,6 +1385,7 @@ export const toWeb = (
1373
1385
  options?: {
1374
1386
  readonly withoutBody?: boolean | undefined
1375
1387
  readonly context?: Context.Context<never> | undefined
1388
+ readonly signal?: AbortSignal | undefined
1376
1389
  }
1377
1390
  ): Response => {
1378
1391
  const headers = new globalThis.Headers(response.headers)
@@ -1387,15 +1400,13 @@ export const toWeb = (
1387
1400
  const withoutBody = outerOmitsBody || options?.withoutBody === true
1388
1401
  if (body._tag === "Raw" && body.body instanceof Response) {
1389
1402
  const raw = body.body
1390
- for (const [key, value] of headers as any) {
1391
- if (key === "set-cookie") {
1392
- raw.headers.append(key, value)
1393
- } else {
1394
- raw.headers.set(key, value)
1395
- }
1396
- }
1397
1403
  if (!withoutBody) {
1398
- return raw
1404
+ if (headers.keys().next().done) {
1405
+ return raw
1406
+ }
1407
+ const copy = new Response(raw.body, raw)
1408
+ mergeHeaders(copy.headers, headers)
1409
+ return copy
1399
1410
  }
1400
1411
  // A bodyless outer status is what the caller chose to send; otherwise the
1401
1412
  // body is only omitted for HEAD and the raw Response describes the resource.
@@ -1403,7 +1414,7 @@ export const toWeb = (
1403
1414
  return new Response(undefined, {
1404
1415
  status: source.status,
1405
1416
  statusText: source.statusText as string,
1406
- headers: raw.headers
1417
+ headers: mergeHeaders(new globalThis.Headers(raw.headers), headers)
1407
1418
  })
1408
1419
  }
1409
1420
  if (withoutBody) {
@@ -1449,7 +1460,8 @@ export const toWeb = (
1449
1460
  return new Response(
1450
1461
  Stream.toReadableStreamWith(
1451
1462
  body.stream,
1452
- options?.context ?? Context.empty()
1463
+ options?.context ?? Context.empty(),
1464
+ { signal: options?.signal }
1453
1465
  ),
1454
1466
  {
1455
1467
  status: response.status,
@@ -13,6 +13,7 @@
13
13
  import * as Effect from "../Effect.ts"
14
14
  import * as FileSystem from "../FileSystem.ts"
15
15
  import * as Layer from "../Layer.ts"
16
+ import * as Option from "../Option.ts"
16
17
  import * as Path from "../Path.ts"
17
18
  import type { PlatformError } from "../PlatformError.ts"
18
19
  import * as HttpPlatform from "./HttpPlatform.ts"
@@ -398,16 +399,9 @@ const resolveFilePath = (path: Path.Path, root: string, url: string): string | u
398
399
  if (decodedPath.includes("\u0000")) {
399
400
  return undefined
400
401
  }
401
- const normalizedPath = path.normalize(decodedPath.startsWith("/") ? decodedPath.slice(1) : decodedPath)
402
- if (normalizedPath === ".." || normalizedPath.startsWith(`..${path.sep}`)) {
403
- return undefined
404
- }
405
- const resolvedPath = path.join(root, normalizedPath)
406
- const rootPrefix = root.endsWith(path.sep) ? root : `${root}${path.sep}`
407
- if (resolvedPath !== root && !resolvedPath.startsWith(rootPrefix)) {
408
- return undefined
409
- }
410
- return resolvedPath
402
+ // every leading separator is part of the URL, not a filesystem root
403
+ const relativePath = decodedPath.replace(path.sep === "\\" ? /^[/\\]+/ : /^\/+/, "")
404
+ return Option.getOrUndefined(path.resolveWithin(root, relativePath))
411
405
  }
412
406
 
413
407
  const toRouteNotFoundError = (request: HttpServerRequest.HttpServerRequest) =>
@@ -49,7 +49,7 @@ export const toHeaders = (span: Tracer.Span): Headers.Headers =>
49
49
  onSome: (parent) => `-${parent.spanId}`
50
50
  })
51
51
  }`,
52
- traceparent: `00-${span.traceId}-${span.spanId}-${span.sampled ? "01" : "00"}`
52
+ traceparent: `00-${span.traceId.padStart(32, "0")}-${span.spanId}-${span.sampled ? "01" : "00"}`
53
53
  })
54
54
 
55
55
  /**
@@ -689,6 +689,22 @@ const defaultWriteFile = (path: string, file: File) =>
689
689
  (fs) => Effect.mapError(Stream.run(file.content, fs.sink(path)), toMultipartError)
690
690
  )
691
691
 
692
+ const maxPersistedFileNameBytes = 128
693
+ const fileNameEncoder = new TextEncoder()
694
+
695
+ // Keeps the trailing 128 UTF-8 bytes without splitting code points. Empty and dot names fall back to "file".
696
+ const persistedFileName = (baseName: string): string => {
697
+ const codePoints = Array.from(baseName)
698
+ let fileName = ""
699
+ let bytes = 0
700
+ for (let i = codePoints.length - 1; i >= 0; i--) {
701
+ bytes += fileNameEncoder.encode(codePoints[i]).length
702
+ if (bytes > maxPersistedFileNameBytes) break
703
+ fileName = codePoints[i] + fileName
704
+ }
705
+ return fileName === "" || fileName === "." || fileName === ".." ? "file" : fileName
706
+ }
707
+
692
708
  /**
693
709
  * Runs a channel of byte chunks and collects all output into a single
694
710
  * `Uint8Array`.
@@ -748,7 +764,7 @@ export const toPersisted = (
748
764
  return Effect.void
749
765
  }
750
766
  const file = part
751
- const fileName = path_.basename(file.name).slice(-128)
767
+ const fileName = persistedFileName(path_.basename(file.name))
752
768
  let path = path_.join(dir, fileName)
753
769
  while (usedPaths.has(path)) {
754
770
  path = path_.join(dir, `${fileIndex++}-${fileName}`)
package/src/http/Url.ts CHANGED
@@ -14,6 +14,7 @@ import * as Data from "../Data.ts"
14
14
  import { dual } from "../Function.ts"
15
15
  import * as Redacted from "../Redacted.ts"
16
16
  import * as Result from "../Result.ts"
17
+ import * as internal from "./internal/urlParams.ts"
17
18
  import * as UrlParams from "./UrlParams.ts"
18
19
 
19
20
  /**
@@ -49,7 +50,7 @@ export const make = (
49
50
  for (let i = 0; i < params.params.length; i++) {
50
51
  const [key, value] = params.params[i]
51
52
  if (value !== undefined) {
52
- urlInstance.searchParams.append(key, value)
53
+ urlInstance.searchParams.append(key, internal.unwrap(value))
53
54
  }
54
55
  }
55
56
  if (hash !== undefined) {