effect 4.0.4 → 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 (308) hide show
  1. package/ai-docs/src/52_graphql/10_basics.ts +19 -13
  2. package/ai-docs/src/52_graphql/30_batching.ts +6 -4
  3. package/ai-docs/src/52_graphql/40_aliased-batching.ts +6 -4
  4. package/dist/Array.d.ts +90 -6
  5. package/dist/Array.d.ts.map +1 -1
  6. package/dist/Array.js +4 -2
  7. package/dist/Array.js.map +1 -1
  8. package/dist/Channel.d.ts +6 -1
  9. package/dist/Channel.d.ts.map +1 -1
  10. package/dist/Channel.js +17 -3
  11. package/dist/Channel.js.map +1 -1
  12. package/dist/ConfigProvider.d.ts.map +1 -1
  13. package/dist/ConfigProvider.js +33 -32
  14. package/dist/ConfigProvider.js.map +1 -1
  15. package/dist/Duration.d.ts.map +1 -1
  16. package/dist/Duration.js +2 -5
  17. package/dist/Duration.js.map +1 -1
  18. package/dist/Effect.d.ts +61 -19
  19. package/dist/Effect.d.ts.map +1 -1
  20. package/dist/Effect.js +58 -39
  21. package/dist/Effect.js.map +1 -1
  22. package/dist/FileSystem.d.ts +23 -9
  23. package/dist/FileSystem.d.ts.map +1 -1
  24. package/dist/FileSystem.js.map +1 -1
  25. package/dist/Match.d.ts +20 -6
  26. package/dist/Match.d.ts.map +1 -1
  27. package/dist/Match.js +2 -2
  28. package/dist/Match.js.map +1 -1
  29. package/dist/Path.d.ts +37 -5
  30. package/dist/Path.d.ts.map +1 -1
  31. package/dist/Path.js +36 -5
  32. package/dist/Path.js.map +1 -1
  33. package/dist/Schema.d.ts +7 -2
  34. package/dist/Schema.d.ts.map +1 -1
  35. package/dist/Schema.js +9 -2
  36. package/dist/Schema.js.map +1 -1
  37. package/dist/String.d.ts +12 -0
  38. package/dist/String.d.ts.map +1 -1
  39. package/dist/String.js +17 -4
  40. package/dist/String.js.map +1 -1
  41. package/dist/TxRef.d.ts.map +1 -1
  42. package/dist/TxRef.js +3 -2
  43. package/dist/TxRef.js.map +1 -1
  44. package/dist/ai/AiError.d.ts +10 -10
  45. package/dist/ai/AiError.d.ts.map +1 -1
  46. package/dist/ai/AiError.js +1 -1
  47. package/dist/ai/AiError.js.map +1 -1
  48. package/dist/ai/Decision.d.ts +70 -19
  49. package/dist/ai/Decision.d.ts.map +1 -1
  50. package/dist/ai/Decision.js +37 -3
  51. package/dist/ai/Decision.js.map +1 -1
  52. package/dist/ai/DecisionModel.js +1 -1
  53. package/dist/ai/DecisionModel.js.map +1 -1
  54. package/dist/ai/LanguageModel.d.ts.map +1 -1
  55. package/dist/ai/LanguageModel.js +47 -35
  56. package/dist/ai/LanguageModel.js.map +1 -1
  57. package/dist/ai/McpServer.d.ts +6 -52
  58. package/dist/ai/McpServer.d.ts.map +1 -1
  59. package/dist/ai/McpServer.js +2 -28
  60. package/dist/ai/McpServer.js.map +1 -1
  61. package/dist/ai/Response.d.ts +1 -1
  62. package/dist/cli/Completions.d.ts +5 -0
  63. package/dist/cli/Completions.d.ts.map +1 -1
  64. package/dist/cli/Completions.js.map +1 -1
  65. package/dist/cli/Flag.d.ts +100 -0
  66. package/dist/cli/Flag.d.ts.map +1 -1
  67. package/dist/cli/Flag.js +33 -0
  68. package/dist/cli/Flag.js.map +1 -1
  69. package/dist/cli/Param.d.ts +87 -0
  70. package/dist/cli/Param.d.ts.map +1 -1
  71. package/dist/cli/Param.js +30 -0
  72. package/dist/cli/Param.js.map +1 -1
  73. package/dist/cli/internal/command.d.ts.map +1 -1
  74. package/dist/cli/internal/command.js +2 -1
  75. package/dist/cli/internal/command.js.map +1 -1
  76. package/dist/cli/internal/completions/bash.js +4 -3
  77. package/dist/cli/internal/completions/bash.js.map +1 -1
  78. package/dist/cli/internal/completions/descriptor.js +8 -1
  79. package/dist/cli/internal/completions/descriptor.js.map +1 -1
  80. package/dist/cli/internal/completions/fish.js +4 -4
  81. package/dist/cli/internal/completions/fish.js.map +1 -1
  82. package/dist/cli/internal/completions/powershell.js +3 -2
  83. package/dist/cli/internal/completions/powershell.js.map +1 -1
  84. package/dist/cli/internal/completions/zsh.js +7 -3
  85. package/dist/cli/internal/completions/zsh.js.map +1 -1
  86. package/dist/cli/internal/parser.js +11 -3
  87. package/dist/cli/internal/parser.js.map +1 -1
  88. package/dist/cli/internal/wizard.d.ts.map +1 -1
  89. package/dist/cli/internal/wizard.js +2 -1
  90. package/dist/cli/internal/wizard.js.map +1 -1
  91. package/dist/cluster/ClusterWorkflowEngine.d.ts +1 -0
  92. package/dist/cluster/ClusterWorkflowEngine.d.ts.map +1 -1
  93. package/dist/cluster/ClusterWorkflowEngine.js +67 -13
  94. package/dist/cluster/ClusterWorkflowEngine.js.map +1 -1
  95. package/dist/cluster/MessageStorage.d.ts.map +1 -1
  96. package/dist/cluster/MessageStorage.js +20 -12
  97. package/dist/cluster/MessageStorage.js.map +1 -1
  98. package/dist/cluster/Runners.d.ts.map +1 -1
  99. package/dist/cluster/Runners.js +54 -34
  100. package/dist/cluster/Runners.js.map +1 -1
  101. package/dist/cluster/internal/entityManager.js +1 -1
  102. package/dist/cluster/internal/entityManager.js.map +1 -1
  103. package/dist/encoding/Sse.js +1 -1
  104. package/dist/encoding/Sse.js.map +1 -1
  105. package/dist/graphql/GraphQLClient.d.ts.map +1 -1
  106. package/dist/graphql/GraphQLClient.js +12 -5
  107. package/dist/graphql/GraphQLClient.js.map +1 -1
  108. package/dist/graphql/GraphQLProtocol.d.ts +249 -11
  109. package/dist/graphql/GraphQLProtocol.d.ts.map +1 -1
  110. package/dist/graphql/GraphQLProtocol.js +98 -8
  111. package/dist/graphql/GraphQLProtocol.js.map +1 -1
  112. package/dist/graphql/GraphQLResolver.d.ts +1 -1
  113. package/dist/graphql/GraphQLResolver.d.ts.map +1 -1
  114. package/dist/graphql/GraphQLResolver.js +46 -16
  115. package/dist/graphql/GraphQLResolver.js.map +1 -1
  116. package/dist/http/FetchHttpClient.d.ts +6 -3
  117. package/dist/http/FetchHttpClient.d.ts.map +1 -1
  118. package/dist/http/FetchHttpClient.js +6 -3
  119. package/dist/http/FetchHttpClient.js.map +1 -1
  120. package/dist/http/FindMyWay/internal/router.js +2 -1
  121. package/dist/http/FindMyWay/internal/router.js.map +1 -1
  122. package/dist/http/Headers.d.ts +3 -3
  123. package/dist/http/Headers.d.ts.map +1 -1
  124. package/dist/http/Headers.js +6 -5
  125. package/dist/http/Headers.js.map +1 -1
  126. package/dist/http/HttpClient.d.ts +12 -0
  127. package/dist/http/HttpClient.d.ts.map +1 -1
  128. package/dist/http/HttpClient.js +14 -1
  129. package/dist/http/HttpClient.js.map +1 -1
  130. package/dist/http/HttpClientError.d.ts.map +1 -1
  131. package/dist/http/HttpClientError.js +8 -6
  132. package/dist/http/HttpClientError.js.map +1 -1
  133. package/dist/http/HttpClientRequest.d.ts +147 -4
  134. package/dist/http/HttpClientRequest.d.ts.map +1 -1
  135. package/dist/http/HttpClientRequest.js +47 -1
  136. package/dist/http/HttpClientRequest.js.map +1 -1
  137. package/dist/http/HttpMiddleware.d.ts +60 -0
  138. package/dist/http/HttpMiddleware.d.ts.map +1 -1
  139. package/dist/http/HttpMiddleware.js +67 -5
  140. package/dist/http/HttpMiddleware.js.map +1 -1
  141. package/dist/http/HttpRouter.d.ts.map +1 -1
  142. package/dist/http/HttpRouter.js +52 -13
  143. package/dist/http/HttpRouter.js.map +1 -1
  144. package/dist/http/HttpServerError.d.ts.map +1 -1
  145. package/dist/http/HttpServerError.js +6 -4
  146. package/dist/http/HttpServerError.js.map +1 -1
  147. package/dist/http/HttpServerRequest.js +2 -2
  148. package/dist/http/HttpServerRequest.js.map +1 -1
  149. package/dist/http/HttpStaticServer.d.ts.map +1 -1
  150. package/dist/http/HttpStaticServer.js +4 -10
  151. package/dist/http/HttpStaticServer.js.map +1 -1
  152. package/dist/http/HttpTraceContext.js +1 -1
  153. package/dist/http/HttpTraceContext.js.map +1 -1
  154. package/dist/http/Url.d.ts.map +1 -1
  155. package/dist/http/Url.js +2 -1
  156. package/dist/http/Url.js.map +1 -1
  157. package/dist/http/UrlParams.d.ts +112 -8
  158. package/dist/http/UrlParams.d.ts.map +1 -1
  159. package/dist/http/UrlParams.js +141 -14
  160. package/dist/http/UrlParams.js.map +1 -1
  161. package/dist/http/internal/compression.d.ts +1 -1
  162. package/dist/http/internal/compression.d.ts.map +1 -1
  163. package/dist/http/internal/compression.js +2 -10
  164. package/dist/http/internal/compression.js.map +1 -1
  165. package/dist/http/internal/compressionBody.d.ts +2 -0
  166. package/dist/http/internal/compressionBody.d.ts.map +1 -0
  167. package/dist/http/internal/compressionBody.js +36 -0
  168. package/dist/http/internal/compressionBody.js.map +1 -0
  169. package/dist/http/internal/decompression.d.ts +2 -0
  170. package/dist/http/internal/decompression.d.ts.map +1 -0
  171. package/dist/http/internal/decompression.js +162 -0
  172. package/dist/http/internal/decompression.js.map +1 -0
  173. package/dist/http/internal/tracing.js +14 -4
  174. package/dist/http/internal/tracing.js.map +1 -1
  175. package/dist/http/internal/urlParams.d.ts +2 -0
  176. package/dist/http/internal/urlParams.d.ts.map +1 -0
  177. package/dist/http/internal/urlParams.js +4 -0
  178. package/dist/http/internal/urlParams.js.map +1 -0
  179. package/dist/http-api/HttpApiClient.d.ts.map +1 -1
  180. package/dist/http-api/HttpApiClient.js +2 -1
  181. package/dist/http-api/HttpApiClient.js.map +1 -1
  182. package/dist/internal/core.js +17 -1
  183. package/dist/internal/core.js.map +1 -1
  184. package/dist/internal/effect.js +5 -3
  185. package/dist/internal/effect.js.map +1 -1
  186. package/dist/internal/graphql.js +12 -2
  187. package/dist/internal/graphql.js.map +1 -1
  188. package/dist/internal/version.js +1 -1
  189. package/dist/observability/Otlp.d.ts +15 -0
  190. package/dist/observability/Otlp.d.ts.map +1 -1
  191. package/dist/observability/Otlp.js +3 -0
  192. package/dist/observability/Otlp.js.map +1 -1
  193. package/dist/observability/OtlpExporter.d.ts +8 -0
  194. package/dist/observability/OtlpExporter.d.ts.map +1 -1
  195. package/dist/observability/OtlpExporter.js +10 -1
  196. package/dist/observability/OtlpExporter.js.map +1 -1
  197. package/dist/observability/OtlpLogger.d.ts +17 -0
  198. package/dist/observability/OtlpLogger.d.ts.map +1 -1
  199. package/dist/observability/OtlpLogger.js +14 -1
  200. package/dist/observability/OtlpLogger.js.map +1 -1
  201. package/dist/observability/OtlpMetrics.d.ts +17 -0
  202. package/dist/observability/OtlpMetrics.d.ts.map +1 -1
  203. package/dist/observability/OtlpMetrics.js +13 -0
  204. package/dist/observability/OtlpMetrics.js.map +1 -1
  205. package/dist/observability/OtlpTracer.d.ts +17 -0
  206. package/dist/observability/OtlpTracer.d.ts.map +1 -1
  207. package/dist/observability/OtlpTracer.js +15 -2
  208. package/dist/observability/OtlpTracer.js.map +1 -1
  209. package/dist/reactivity/AsyncResult.d.ts +6 -0
  210. package/dist/reactivity/AsyncResult.d.ts.map +1 -1
  211. package/dist/reactivity/AsyncResult.js +27 -0
  212. package/dist/reactivity/AsyncResult.js.map +1 -1
  213. package/dist/reactivity/AtomRegistry.js +3 -1
  214. package/dist/reactivity/AtomRegistry.js.map +1 -1
  215. package/dist/rpc/RpcClient.d.ts.map +1 -1
  216. package/dist/rpc/RpcClient.js +6 -1
  217. package/dist/rpc/RpcClient.js.map +1 -1
  218. package/dist/sql/SqlClient.d.ts +8 -7
  219. package/dist/sql/SqlClient.d.ts.map +1 -1
  220. package/dist/sql/SqlClient.js +8 -7
  221. package/dist/sql/SqlClient.js.map +1 -1
  222. package/dist/testing/TestClock.d.ts +16 -3
  223. package/dist/testing/TestClock.d.ts.map +1 -1
  224. package/dist/testing/TestClock.js +112 -22
  225. package/dist/testing/TestClock.js.map +1 -1
  226. package/dist/workflow/Activity.js +1 -0
  227. package/dist/workflow/Activity.js.map +1 -1
  228. package/dist/workflow/Workflow.d.ts +22 -0
  229. package/dist/workflow/Workflow.d.ts.map +1 -1
  230. package/dist/workflow/Workflow.js +15 -3
  231. package/dist/workflow/Workflow.js.map +1 -1
  232. package/dist/workflow/WorkflowEngine.d.ts +20 -1
  233. package/dist/workflow/WorkflowEngine.d.ts.map +1 -1
  234. package/dist/workflow/WorkflowEngine.js +35 -21
  235. package/dist/workflow/WorkflowEngine.js.map +1 -1
  236. package/package.json +1 -1
  237. package/src/Array.ts +95 -6
  238. package/src/Channel.ts +24 -3
  239. package/src/ConfigProvider.ts +40 -44
  240. package/src/Duration.ts +4 -5
  241. package/src/Effect.ts +140 -53
  242. package/src/FileSystem.ts +23 -9
  243. package/src/Match.ts +65 -12
  244. package/src/Path.ts +52 -7
  245. package/src/Schema.ts +17 -4
  246. package/src/String.ts +17 -4
  247. package/src/TxRef.ts +4 -2
  248. package/src/ai/AiError.ts +3 -1
  249. package/src/ai/Decision.ts +70 -21
  250. package/src/ai/DecisionModel.ts +1 -1
  251. package/src/ai/LanguageModel.ts +57 -51
  252. package/src/ai/McpServer.ts +15 -55
  253. package/src/cli/Completions.ts +5 -0
  254. package/src/cli/Flag.ts +101 -0
  255. package/src/cli/Param.ts +96 -0
  256. package/src/cli/internal/command.ts +2 -1
  257. package/src/cli/internal/completions/bash.ts +4 -3
  258. package/src/cli/internal/completions/descriptor.ts +10 -1
  259. package/src/cli/internal/completions/fish.ts +4 -4
  260. package/src/cli/internal/completions/powershell.ts +5 -2
  261. package/src/cli/internal/completions/zsh.ts +7 -3
  262. package/src/cli/internal/parser.ts +12 -3
  263. package/src/cli/internal/wizard.ts +2 -1
  264. package/src/cluster/ClusterWorkflowEngine.ts +92 -24
  265. package/src/cluster/MessageStorage.ts +14 -3
  266. package/src/cluster/Runners.ts +88 -66
  267. package/src/cluster/internal/entityManager.ts +1 -1
  268. package/src/encoding/Sse.ts +1 -1
  269. package/src/graphql/GraphQLClient.ts +17 -5
  270. package/src/graphql/GraphQLProtocol.ts +274 -20
  271. package/src/graphql/GraphQLResolver.ts +45 -17
  272. package/src/http/FetchHttpClient.ts +6 -3
  273. package/src/http/FindMyWay/internal/router.ts +2 -1
  274. package/src/http/Headers.ts +6 -9
  275. package/src/http/HttpClient.ts +18 -1
  276. package/src/http/HttpClientError.ts +10 -6
  277. package/src/http/HttpClientRequest.ts +173 -7
  278. package/src/http/HttpMiddleware.ts +85 -5
  279. package/src/http/HttpRouter.ts +60 -13
  280. package/src/http/HttpServerError.ts +8 -4
  281. package/src/http/HttpServerRequest.ts +2 -2
  282. package/src/http/HttpStaticServer.ts +4 -10
  283. package/src/http/HttpTraceContext.ts +1 -1
  284. package/src/http/Url.ts +2 -1
  285. package/src/http/UrlParams.ts +225 -26
  286. package/src/http/internal/compression.ts +3 -17
  287. package/src/http/internal/compressionBody.ts +56 -0
  288. package/src/http/internal/decompression.ts +226 -0
  289. package/src/http/internal/tracing.ts +21 -4
  290. package/src/http/internal/urlParams.ts +5 -0
  291. package/src/http-api/HttpApiClient.ts +2 -1
  292. package/src/internal/core.ts +21 -1
  293. package/src/internal/effect.ts +5 -4
  294. package/src/internal/graphql.ts +30 -7
  295. package/src/internal/version.ts +1 -1
  296. package/src/observability/Otlp.ts +18 -0
  297. package/src/observability/OtlpExporter.ts +14 -3
  298. package/src/observability/OtlpLogger.ts +31 -2
  299. package/src/observability/OtlpMetrics.ts +30 -1
  300. package/src/observability/OtlpTracer.ts +32 -3
  301. package/src/reactivity/AsyncResult.ts +31 -0
  302. package/src/reactivity/AtomRegistry.ts +3 -1
  303. package/src/rpc/RpcClient.ts +6 -1
  304. package/src/sql/SqlClient.ts +9 -7
  305. package/src/testing/TestClock.ts +144 -29
  306. package/src/workflow/Activity.ts +1 -0
  307. package/src/workflow/Workflow.ts +31 -5
  308. package/src/workflow/WorkflowEngine.ts +60 -22
@@ -0,0 +1,226 @@
1
+ import * as ByteSize from "../../ByteSize.ts"
2
+ import * as Effect from "../../Effect.ts"
3
+ import * as Inspectable from "../../Inspectable.ts"
4
+ import type * as Option from "../../Option.ts"
5
+ import type { ReadonlyRecord } from "../../Record.ts"
6
+ import type * as Schema from "../../Schema.ts"
7
+ import type * as Socket from "../../socket/Socket.ts"
8
+ import * as Stream from "../../Stream.ts"
9
+ import * as Headers from "../Headers.ts"
10
+ import * as HttpIncomingMessage from "../HttpIncomingMessage.ts"
11
+ import type { HttpMethod } from "../HttpMethod.ts"
12
+ import { HttpServerError, RequestParseError } from "../HttpServerError.ts"
13
+ import * as HttpServerRequest from "../HttpServerRequest.ts"
14
+ import * as Multipart from "../Multipart.ts"
15
+ import * as UrlParams from "../UrlParams.ts"
16
+
17
+ /**
18
+ * @internal
19
+ */
20
+ export type Coding = "gzip" | "deflate"
21
+
22
+ const defaultMaxDecompressedSize = ByteSize.mebibytes(10)
23
+
24
+ /**
25
+ * Parses a `Content-Encoding` header into the codings to undo, in the order
26
+ * they were applied. `identity` members are dropped. Returns `undefined` if
27
+ * any member is unsupported.
28
+ *
29
+ * @internal
30
+ */
31
+ export const parseContentEncoding = (header: string): ReadonlyArray<Coding> | undefined => {
32
+ const codings: Array<Coding> = []
33
+ for (const member of header.split(",")) {
34
+ const coding = member.trim().toLowerCase()
35
+ if (coding === "" || coding === "identity") continue
36
+ if (coding !== "gzip" && coding !== "deflate") return undefined
37
+ codings.push(coding)
38
+ }
39
+ return codings
40
+ }
41
+
42
+ /**
43
+ * Wraps a request so that its body accessors yield the decompressed body.
44
+ * Decompression happens lazily, when a body accessor is read.
45
+ *
46
+ * @internal
47
+ */
48
+ export class DecompressedRequest extends Inspectable.Class implements HttpServerRequest.HttpServerRequest {
49
+ readonly [HttpServerRequest.TypeId]: typeof HttpServerRequest.TypeId
50
+ readonly [HttpIncomingMessage.TypeId]: typeof HttpIncomingMessage.TypeId
51
+ readonly inner: HttpServerRequest.HttpServerRequest
52
+ readonly codings: ReadonlyArray<Coding>
53
+ readonly maxDecompressedSize: ByteSize.ByteSize | undefined
54
+ readonly headers: Headers.Headers
55
+
56
+ constructor(
57
+ inner: HttpServerRequest.HttpServerRequest,
58
+ codings: ReadonlyArray<Coding>,
59
+ maxDecompressedSize: ByteSize.ByteSize | undefined
60
+ ) {
61
+ super()
62
+ this[HttpServerRequest.TypeId] = HttpServerRequest.TypeId
63
+ this[HttpIncomingMessage.TypeId] = HttpIncomingMessage.TypeId
64
+ this.inner = inner
65
+ this.codings = codings
66
+ this.maxDecompressedSize = maxDecompressedSize
67
+ this.headers = Headers.removeMany(inner.headers, ["content-encoding", "content-length"])
68
+ }
69
+
70
+ toJSON(): unknown {
71
+ return HttpIncomingMessage.inspect(this, {
72
+ _id: "HttpServerRequest",
73
+ method: this.method,
74
+ url: this.originalUrl
75
+ })
76
+ }
77
+
78
+ modify(
79
+ options: {
80
+ readonly url?: string
81
+ readonly headers?: Headers.Headers
82
+ readonly remoteAddress?: Option.Option<string>
83
+ }
84
+ ): HttpServerRequest.HttpServerRequest {
85
+ return new DecompressedRequest(this.inner.modify(options), this.codings, this.maxDecompressedSize)
86
+ }
87
+
88
+ get source(): object {
89
+ return this.inner.source
90
+ }
91
+
92
+ get url(): string {
93
+ return this.inner.url
94
+ }
95
+
96
+ get originalUrl(): string {
97
+ return this.inner.originalUrl
98
+ }
99
+
100
+ get method(): HttpMethod {
101
+ return this.inner.method
102
+ }
103
+
104
+ get cookies(): ReadonlyRecord<string, string> {
105
+ return this.inner.cookies
106
+ }
107
+
108
+ get remoteAddress(): Option.Option<string> {
109
+ return this.inner.remoteAddress
110
+ }
111
+
112
+ get upgrade(): Effect.Effect<Socket.Socket, HttpServerError> {
113
+ return this.inner.upgrade
114
+ }
115
+
116
+ // Platform accessors such as `NodeHttpServerRequest.toServerResponse` read
117
+ // the response handle from the request.
118
+ get response(): unknown {
119
+ return (this.inner as any).response
120
+ }
121
+
122
+ get stream(): Stream.Stream<Uint8Array, HttpServerError> {
123
+ return Stream.unwrap(Effect.withFiber((fiber) => {
124
+ const limit = this.maxDecompressedSize ?? fiber.getRef(HttpIncomingMessage.MaxBodySize) ??
125
+ defaultMaxDecompressedSize
126
+ return Effect.succeed(Stream.fromReadableStream({
127
+ evaluate: () => {
128
+ let body = Stream.toReadableStreamWith(this.inner.stream, fiber.context)
129
+ for (let i = this.codings.length - 1; i >= 0; i--) {
130
+ body = body.pipeThrough(
131
+ new DecompressionStream(this.codings[i]) as unknown as ReadableWritablePair<Uint8Array, Uint8Array>
132
+ )
133
+ }
134
+ let size = 0
135
+ return body.pipeThrough(
136
+ new TransformStream<Uint8Array, Uint8Array>({
137
+ transform: (chunk, controller) => {
138
+ size += chunk.byteLength
139
+ if (size > limit) {
140
+ controller.error(
141
+ this.parseError(undefined, `Decompressed request body exceeds ${limit} bytes`, "BodyTooLarge")
142
+ )
143
+ } else {
144
+ controller.enqueue(chunk)
145
+ }
146
+ }
147
+ })
148
+ )
149
+ },
150
+ onError: (cause) => cause instanceof HttpServerError ? cause : this.parseError(cause)
151
+ }))
152
+ }))
153
+ }
154
+
155
+ private bytesEffect: Effect.Effect<Uint8Array, HttpServerError> | undefined
156
+ private get bytes(): Effect.Effect<Uint8Array, HttpServerError> {
157
+ return this.bytesEffect ??= Effect.runSync(Effect.cached(Stream.mkUint8Array(this.stream)))
158
+ }
159
+
160
+ get text(): Effect.Effect<string, HttpServerError> {
161
+ return Effect.map(this.bytes, (bytes) => textDecoder.decode(bytes))
162
+ }
163
+
164
+ get json(): Effect.Effect<Schema.Json, HttpServerError> {
165
+ return Effect.flatMap(this.text, (text) =>
166
+ Effect.try({
167
+ try: () => text === "" ? null : JSON.parse(text) as Schema.Json,
168
+ catch: (cause) => this.parseError(cause)
169
+ }))
170
+ }
171
+
172
+ get urlParamsBody(): Effect.Effect<UrlParams.UrlParams, HttpServerError> {
173
+ return Effect.flatMap(this.text, (text) =>
174
+ Effect.try({
175
+ try: () => UrlParams.fromInput(new URLSearchParams(text)),
176
+ catch: (cause) => this.parseError(cause)
177
+ }))
178
+ }
179
+
180
+ get arrayBuffer(): Effect.Effect<ArrayBuffer, HttpServerError> {
181
+ return Effect.map(
182
+ this.bytes,
183
+ (bytes) => bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) as ArrayBuffer
184
+ )
185
+ }
186
+
187
+ get multipartStream(): Stream.Stream<Multipart.Part, Multipart.MultipartError> {
188
+ return Stream.pipeThroughChannel(
189
+ Stream.mapError(
190
+ this.stream,
191
+ (cause) => Multipart.MultipartError.fromReason(multipartReasons.get(cause) ?? "InternalError", cause)
192
+ ),
193
+ Multipart.makeChannel(this.headers)
194
+ )
195
+ }
196
+
197
+ private multipartEffect: HttpServerRequest.HttpServerRequest["multipart"] | undefined
198
+ get multipart(): HttpServerRequest.HttpServerRequest["multipart"] {
199
+ return this.multipartEffect ??= Effect.runSync(Effect.cached(Multipart.toPersisted(this.multipartStream)))
200
+ }
201
+
202
+ private parseError(
203
+ cause: unknown,
204
+ description?: string,
205
+ multipartReason: MultipartReason = "Parse"
206
+ ): HttpServerError {
207
+ const error = new HttpServerError({
208
+ reason: new RequestParseError({
209
+ request: this,
210
+ ...(description === undefined ? undefined : { description }),
211
+ ...(cause === undefined ? undefined : { cause })
212
+ })
213
+ })
214
+ multipartReasons.set(error, multipartReason)
215
+ return error
216
+ }
217
+ }
218
+
219
+ const textDecoder = new TextDecoder()
220
+
221
+ type MultipartReason = "Parse" | "BodyTooLarge"
222
+
223
+ // Failures caused by the client's data, keyed to the multipart reason they map
224
+ // to. Transport errors from the inner request are absent and map to
225
+ // `InternalError`.
226
+ const multipartReasons = new WeakMap<HttpServerError, MultipartReason>()
@@ -1,5 +1,6 @@
1
1
  import type * as Tracer from "../../Tracer.ts"
2
2
  import * as Headers from "../Headers.ts"
3
+ import * as UrlParams from "../UrlParams.ts"
3
4
 
4
5
  const defaultPorts: Record<string, number> = {
5
6
  "http:": 80,
@@ -9,16 +10,17 @@ const defaultPorts: Record<string, number> = {
9
10
  }
10
11
 
11
12
  /**
12
- * Records the `url.*` and `server.*` attributes for a request URL.
13
+ * Records `url.*` and `server.*` attributes with URL redaction.
13
14
  *
14
15
  * @internal
15
16
  */
16
17
  export const addUrlAttributes = (
17
18
  span: Tracer.Span,
18
- url: URL
19
+ url: URL,
20
+ redactedNames: ReadonlyArray<string | RegExp>
19
21
  ): void => {
20
- const query = url.search.slice(1)
21
- span.attribute("url.full", url.toString())
22
+ const query = UrlParams.redactUrl(url.search, redactedNames).slice(1)
23
+ span.attribute("url.full", UrlParams.redactUrl(url.href, redactedNames))
22
24
  span.attribute("url.path", url.pathname)
23
25
  if (query !== "") {
24
26
  span.attribute("url.query", query)
@@ -34,6 +36,21 @@ export const addUrlAttributes = (
34
36
  }
35
37
  }
36
38
 
39
+ /**
40
+ * Adds the names of `Redacted` parameters to the redacted names.
41
+ *
42
+ * @internal
43
+ */
44
+ export const redactedNames = (
45
+ urlParams: UrlParams.UrlParams,
46
+ redactedNames: ReadonlyArray<string | RegExp>
47
+ ): ReadonlyArray<string | RegExp> => {
48
+ const keys = urlParams.params.filter(([, value]) => typeof value !== "string").map(([key]) => key)
49
+ return keys.length === 0
50
+ ? redactedNames
51
+ : [...redactedNames, ...new URLSearchParams(keys.map((key) => [key, ""])).keys()]
52
+ }
53
+
37
54
  /** @internal */
38
55
  export const addHeaderAttributes = (
39
56
  span: Tracer.Span,
@@ -0,0 +1,5 @@
1
+ import * as Redacted from "../../Redacted.ts"
2
+
3
+ /** @internal */
4
+ export const unwrap = (value: string | Redacted.Redacted): string =>
5
+ typeof value === "string" ? value : Redacted.value(value)
@@ -23,6 +23,7 @@ import * as HttpClientError from "../http/HttpClientError.ts"
23
23
  import * as HttpClientRequest from "../http/HttpClientRequest.ts"
24
24
  import type * as HttpClientResponse from "../http/HttpClientResponse.ts"
25
25
  import * as HttpMethod from "../http/HttpMethod.ts"
26
+ import * as internalUrlParams from "../http/internal/urlParams.ts"
26
27
  import * as UrlParams from "../http/UrlParams.ts"
27
28
  import * as InternalRecord from "../internal/record.ts"
28
29
  import * as Predicate from "../Predicate.ts"
@@ -746,7 +747,7 @@ export const urlBuilder = <Api extends HttpApi.Constraint>(api: Api, options?: {
746
747
  )
747
748
  for (const [key, value] of urlParams.params) {
748
749
  if (value !== undefined) {
749
- url.searchParams.append(key, value)
750
+ url.searchParams.append(key, internalUrlParams.unwrap(value))
750
751
  }
751
752
  }
752
753
  return url.toString()
@@ -321,6 +321,26 @@ export const causeCombine: {
321
321
  }
322
322
  )
323
323
 
324
+ /**
325
+ * Appends the interrupts from `interrupts` that `self` does not contain yet.
326
+ * Unlike `causeCombine`, it never calls hash or equality methods, which can be
327
+ * user-defined, random, or throwing.
328
+ *
329
+ * @internal
330
+ */
331
+ export const causeAddInterrupts = <E>(self: Cause.Cause<E>, interrupts: Cause.Cause<never>): Cause.Cause<E> => {
332
+ const reasons: Array<Cause.Reason<E>> = self.reasons.slice()
333
+ for (const reason of interrupts.reasons) {
334
+ if (
335
+ isInterruptReason(reason) &&
336
+ !reasons.some((r) => isInterruptReason(r) && r.fiberId === reason.fiberId && r.annotations === reason.annotations)
337
+ ) {
338
+ reasons.push(reason)
339
+ }
340
+ }
341
+ return reasons.length === self.reasons.length ? self : new CauseImpl(reasons)
342
+ }
343
+
324
344
  /** @internal */
325
345
  export const causeEmpty: Cause.Cause<never> = new CauseImpl([])
326
346
 
@@ -600,7 +620,7 @@ export const exitFailCause: <E>(cause: Cause.Cause<E>) => Exit.Exit<never, E> =
600
620
  if (skippedHandler) {
601
621
  cause = causeFromReasons(cause.reasons.filter((reason) => reason._tag !== "Fail"))
602
622
  }
603
- cause = causeCombine(cause, interruptedCause)
623
+ cause = causeAddInterrupts(cause, interruptedCause)
604
624
  annotated = true
605
625
  }
606
626
  return cont
@@ -49,6 +49,7 @@ import { internalCall } from "../Utils.ts"
49
49
  import type { Primitive } from "./core.ts"
50
50
  import {
51
51
  args,
52
+ causeAddInterrupts,
52
53
  causeAnnotate,
53
54
  causeCombine,
54
55
  causeEmpty,
@@ -130,9 +131,9 @@ export class Interrupt extends ReasonBase<"Interrupt"> implements Cause.Interrup
130
131
  )
131
132
  }
132
133
  [Hash.symbol](): number {
133
- return Hash.combine(Hash.string(`${this._tag}:${this.fiberId}`))(
134
- Hash.random(this.annotations)
135
- )
134
+ // Equality compares annotations by reference, so leave them out instead of
135
+ // hashing them randomly.
136
+ return Hash.string(`${this._tag}:${this.fiberId}`)
136
137
  }
137
138
  }
138
139
 
@@ -566,7 +567,7 @@ export class FiberImpl<A = any, E = any> implements Fiber.Fiber<A, E> {
566
567
  cause = causeAnnotate(cause, annotations)
567
568
  }
568
569
  this._interruptedCause = this._interruptedCause
569
- ? causeCombine(this._interruptedCause, cause)
570
+ ? causeAddInterrupts(this._interruptedCause, cause)
570
571
  : cause
571
572
  if (this.interruptible) {
572
573
  if (this._running) {
@@ -12,7 +12,7 @@ import {
12
12
  type Reason,
13
13
  ResponseError
14
14
  } from "../graphql/GraphQLClientError.ts"
15
- import type { ExecutionResult, GraphQLRequest } from "../graphql/GraphQLProtocol.ts"
15
+ import type { ExecutionResult, GraphQLProtocol, GraphQLRequest } from "../graphql/GraphQLProtocol.ts"
16
16
  import * as Schema from "../Schema.ts"
17
17
 
18
18
  /**
@@ -153,6 +153,29 @@ export const renderSlices = (
153
153
  */
154
154
  export const ForcePost = Context.Reference<boolean>("effect/graphql/ForcePost", { defaultValue: () => false })
155
155
 
156
+ /**
157
+ * The URL a resolver batch was grouped by, and the protocol `url` that
158
+ * resolved it, so that transport sends the batch there without resolving it
159
+ * again.
160
+ *
161
+ * @internal
162
+ */
163
+ export type ResolvedUrl = {
164
+ readonly resolve: GraphQLProtocol["Service"]["url"]
165
+ readonly url: string
166
+ } | undefined
167
+
168
+ /**
169
+ * Carries a {@link ResolvedUrl} from a resolver batch to the transport. The
170
+ * client clears it while middleware run, so calls they make resolve their
171
+ * own URL.
172
+ *
173
+ * @internal
174
+ */
175
+ export const ResolvedUrl = Context.Reference<ResolvedUrl>("effect/graphql/ResolvedUrl", {
176
+ defaultValue: () => undefined
177
+ })
178
+
156
179
  /**
157
180
  * JSON with object keys sorted, so equal JSON values give equal strings.
158
181
  *
@@ -219,15 +242,15 @@ export const makeCodec = <Op extends GraphQL.Any>(operation: Op) => {
219
242
  }
220
243
 
221
244
  /**
222
- * Each real client method's runner for batches: its middleware and protocol,
223
- * sending as POST. Mock and custom methods have none.
245
+ * Client batch runners (middleware and protocol, using POST) and URLs for
246
+ * resolver grouping. Mock and custom methods are not registered.
224
247
  *
225
248
  * @internal
226
249
  */
227
- export const rawExecutors = new WeakMap<
228
- object,
229
- (request: GraphQLRequest) => Effect.Effect<ExecutionResult, unknown, unknown>
230
- >()
250
+ export const rawExecutors = new WeakMap<object, {
251
+ readonly execute: (request: GraphQLRequest) => Effect.Effect<ExecutionResult, unknown, unknown>
252
+ readonly url: GraphQLProtocol["Service"]["url"]
253
+ }>()
231
254
 
232
255
  /**
233
256
  * Splits a batched response, removing prefixes from root keys and error paths.
@@ -1,2 +1,2 @@
1
1
  /** @internal */
2
- export const version = "4.0.4"
2
+ export const version = "4.0.5"
@@ -42,6 +42,11 @@ export const layer = (options: {
42
42
  readonly attributes?: Record<string, unknown>
43
43
  } | undefined
44
44
  readonly headers?: Headers.Input | undefined
45
+ /**
46
+ * Request compression. Defaults to "none". Gzip uses
47
+ * HttpClientRequest's default minimum body size of 1KB.
48
+ */
49
+ readonly compression?: "gzip" | "none" | undefined
45
50
  readonly maxBatchSize?: number | undefined
46
51
  readonly tracerContext?: (<X>(primitive: Tracer.EffectPrimitive<X>, span: Tracer.AnySpan) => X) | undefined
47
52
  readonly loggerExportInterval?: Duration.Input | undefined
@@ -59,6 +64,7 @@ export const layer = (options: {
59
64
  url: url("/v1/logs"),
60
65
  resource: options.resource,
61
66
  headers: options.headers,
67
+ compression: options.compression,
62
68
  exportInterval: options.loggerExportInterval,
63
69
  maxBatchSize: options.maxBatchSize,
64
70
  shutdownTimeout: options.shutdownTimeout,
@@ -69,6 +75,7 @@ export const layer = (options: {
69
75
  url: url("/v1/metrics"),
70
76
  resource: options.resource,
71
77
  headers: options.headers,
78
+ compression: options.compression,
72
79
  exportInterval: options.metricsExportInterval,
73
80
  shutdownTimeout: options.shutdownTimeout,
74
81
  temporality: options.metricsTemporality
@@ -77,6 +84,7 @@ export const layer = (options: {
77
84
  url: url("/v1/traces"),
78
85
  resource: options.resource,
79
86
  headers: options.headers,
87
+ compression: options.compression,
80
88
  exportInterval: options.tracerExportInterval,
81
89
  maxBatchSize: options.maxBatchSize,
82
90
  context: options.tracerContext,
@@ -138,6 +146,11 @@ export const layerJson: (options: {
138
146
  readonly attributes?: Record<string, unknown>
139
147
  } | undefined
140
148
  readonly headers?: Headers.Input | undefined
149
+ /**
150
+ * Request compression. Defaults to "none". Gzip uses
151
+ * HttpClientRequest's default minimum body size of 1KB.
152
+ */
153
+ readonly compression?: "gzip" | "none" | undefined
141
154
  readonly maxBatchSize?: number | undefined
142
155
  readonly tracerContext?: (<X>(primitive: Tracer.EffectPrimitive<X>, span: Tracer.AnySpan) => X) | undefined
143
156
  readonly loggerExportInterval?: Duration.Input | undefined
@@ -165,6 +178,11 @@ export const layerProtobuf: (options: {
165
178
  readonly attributes?: Record<string, unknown>
166
179
  } | undefined
167
180
  readonly headers?: Headers.Input | undefined
181
+ /**
182
+ * Request compression. Defaults to "none". Gzip uses
183
+ * HttpClientRequest's default minimum body size of 1KB.
184
+ */
185
+ readonly compression?: "gzip" | "none" | undefined
168
186
  readonly maxBatchSize?: number | undefined
169
187
  readonly tracerContext?: (<X>(primitive: Tracer.EffectPrimitive<X>, span: Tracer.AnySpan) => X) | undefined
170
188
  readonly loggerExportInterval?: Duration.Input | undefined
@@ -161,6 +161,12 @@ export const layerFlusher: Layer.Layer<Flusher> = Layer.sync(Flusher, () => {
161
161
  * after unhandled failures, and flushes during scope finalization up to
162
162
  * `shutdownTimeout`.
163
163
  *
164
+ * Optional gzip compression uses `HttpClientRequest.compressBody` before the
165
+ * retry loop, so retries reuse the compressed bytes. Bodies smaller than 1KB
166
+ * remain uncompressed. Compression requires runtime `CompressionStream` support;
167
+ * compression failures follow the same temporary-disable behavior as transport
168
+ * failures and do not fail flushing or shutdown.
169
+ *
164
170
  * @stability unstable
165
171
  * @category constructors
166
172
  * @since 4.0.0
@@ -169,6 +175,8 @@ export const make: (
169
175
  options: {
170
176
  readonly url: string
171
177
  readonly headers: Headers.Input | undefined
178
+ /** Gzip uses the default 1KB minimum body size; smaller batches remain uncompressed. */
179
+ readonly compression?: "gzip" | "none" | undefined
172
180
  readonly label: string
173
181
  readonly exportInterval: Duration.Input
174
182
  readonly maxBatchSize: number | "disabled"
@@ -218,9 +226,12 @@ export const make: (
218
226
  const items = buffer
219
227
  buffer = []
220
228
  const [body, onSuccess] = options.body(items)
221
- return client.execute(
222
- HttpClientRequest.setBody(request, body)
223
- ).pipe(
229
+ const batchRequest = HttpClientRequest.setBody(request, body)
230
+ // Compress before client.execute so transient retries reuse the encoded bytes.
231
+ const response = options.compression === "gzip"
232
+ ? HttpClientRequest.compressBody(batchRequest, "gzip").pipe(Effect.flatMap(client.execute))
233
+ : client.execute(batchRequest)
234
+ return response.pipe(
224
235
  Effect.andThen(onSuccess),
225
236
  Effect.asVoid,
226
237
  Effect.withTracerEnabled(false)
@@ -52,6 +52,11 @@ export const make: (
52
52
  readonly attributes?: Record<string, unknown>
53
53
  } | undefined
54
54
  readonly headers?: Headers.Input | undefined
55
+ /**
56
+ * Request compression. Defaults to "none". Gzip uses
57
+ * HttpClientRequest's default minimum body size of 1KB.
58
+ */
59
+ readonly compression?: "gzip" | "none" | undefined
55
60
  readonly exportInterval?: Duration.Input | undefined
56
61
  readonly maxBatchSize?: number | undefined
57
62
  readonly shutdownTimeout?: Duration.Input | undefined
@@ -73,6 +78,7 @@ export const make: (
73
78
  label: "OtlpLogger",
74
79
  url: options.url,
75
80
  headers: options.headers,
81
+ compression: options.compression,
76
82
  maxBatchSize: options.maxBatchSize ?? 1000,
77
83
  exportInterval: options.exportInterval ?? Duration.seconds(1),
78
84
  body: (data) => [
@@ -118,6 +124,11 @@ export const layer = (options: {
118
124
  readonly attributes?: Record<string, unknown>
119
125
  } | undefined
120
126
  readonly headers?: Headers.Input | undefined
127
+ /**
128
+ * Request compression. Defaults to "none". Gzip uses
129
+ * HttpClientRequest's default minimum body size of 1KB.
130
+ */
131
+ readonly compression?: "gzip" | "none" | undefined
121
132
  readonly exportInterval?: Duration.Input | undefined
122
133
  readonly maxBatchSize?: number | undefined
123
134
  readonly shutdownTimeout?: Duration.Input | undefined
@@ -131,6 +142,13 @@ export const layer = (options: {
131
142
  /**
132
143
  * Creates an OTLP logs layer from OpenTelemetry configuration.
133
144
  *
145
+ * **Details**
146
+ *
147
+ * Compression accepts "gzip" or "none".
148
+ * `OTEL_EXPORTER_OTLP_LOGS_COMPRESSION` takes precedence over
149
+ * `OTEL_EXPORTER_OTLP_COMPRESSION`, defaulting to "none". Gzip uses
150
+ * HttpClientRequest's default minimum body size of 1KB.
151
+ *
134
152
  * @stability unstable
135
153
  * @category layers
136
154
  * @since 4.0.0
@@ -156,7 +174,17 @@ export const layerFromConfig = (options?: {
156
174
  return Exporter.layerFlusher
157
175
  }
158
176
 
159
- const { baseTimeout, logsTimeout, exportTimeout, scheduleDelay, maxBatchSize } = yield* Config.all({
177
+ const {
178
+ baseCompression,
179
+ compression,
180
+ baseTimeout,
181
+ logsTimeout,
182
+ exportTimeout,
183
+ scheduleDelay,
184
+ maxBatchSize
185
+ } = yield* Config.all({
186
+ baseCompression: Config.option(Config.Literals(["gzip", "none"], "OTEL_EXPORTER_OTLP_COMPRESSION")),
187
+ compression: Config.option(Config.Literals(["gzip", "none"], "OTEL_EXPORTER_OTLP_LOGS_COMPRESSION")),
160
188
  baseTimeout: Config.option(Config.Int("OTEL_EXPORTER_OTLP_TIMEOUT")),
161
189
  logsTimeout: Config.option(Config.Int("OTEL_EXPORTER_OTLP_LOGS_TIMEOUT")),
162
190
  exportTimeout: Config.option(Config.Int("OTEL_BLRP_EXPORT_TIMEOUT")),
@@ -173,6 +201,7 @@ export const layerFromConfig = (options?: {
173
201
  url: endpoint.toString(),
174
202
  resource: options?.resource,
175
203
  headers: options?.headers ?? (yield* OtlpEnv.headers("LOGS")),
204
+ compression: Option.getOrElse(Option.firstSomeOf([compression, baseCompression]), () => "none" as const),
176
205
  exportInterval: Option.getOrUndefined(exportInterval),
177
206
  maxBatchSize: Option.getOrUndefined(maxBatchSize),
178
207
  shutdownTimeout: Option.getOrUndefined(shutdownTimeout),
@@ -232,7 +261,7 @@ const makeLogRecord = (options: Logger.Options<unknown>, opts: {
232
261
  }
233
262
 
234
263
  if (options.fiber.cache.span) {
235
- logRecord.traceId = options.fiber.cache.span.traceId
264
+ logRecord.traceId = options.fiber.cache.span.traceId.padStart(32, "0")
236
265
  logRecord.spanId = options.fiber.cache.span.spanId
237
266
  }
238
267
 
@@ -89,6 +89,11 @@ export const make: (options: {
89
89
  readonly attributes?: Record<string, unknown>
90
90
  } | undefined
91
91
  readonly headers?: Headers.Input | undefined
92
+ /**
93
+ * Request compression. Defaults to "none". Gzip uses
94
+ * HttpClientRequest's default minimum body size of 1KB.
95
+ */
96
+ readonly compression?: "gzip" | "none" | undefined
92
97
  readonly exportInterval?: Duration.Input | undefined
93
98
  readonly shutdownTimeout?: Duration.Input | undefined
94
99
  readonly temporality?: AggregationTemporality | undefined
@@ -387,6 +392,7 @@ export const make: (options: {
387
392
  label: "OtlpMetrics",
388
393
  url: options.url,
389
394
  headers: options.headers,
395
+ compression: options.compression,
390
396
  maxBatchSize: "disabled",
391
397
  exportEmpty: true,
392
398
  exportInterval: options.exportInterval ?? Duration.seconds(10),
@@ -410,6 +416,11 @@ export const layer = (options: {
410
416
  readonly attributes?: Record<string, unknown>
411
417
  } | undefined
412
418
  readonly headers?: Headers.Input | undefined
419
+ /**
420
+ * Request compression. Defaults to "none". Gzip uses
421
+ * HttpClientRequest's default minimum body size of 1KB.
422
+ */
423
+ readonly compression?: "gzip" | "none" | undefined
413
424
  readonly exportInterval?: Duration.Input | undefined
414
425
  readonly shutdownTimeout?: Duration.Input | undefined
415
426
  readonly temporality?: AggregationTemporality | undefined
@@ -419,6 +430,13 @@ export const layer = (options: {
419
430
  /**
420
431
  * Creates an OTLP metrics layer from OpenTelemetry configuration.
421
432
  *
433
+ * **Details**
434
+ *
435
+ * Compression accepts "gzip" or "none".
436
+ * `OTEL_EXPORTER_OTLP_METRICS_COMPRESSION` takes precedence over
437
+ * `OTEL_EXPORTER_OTLP_COMPRESSION`, defaulting to "none". Gzip uses
438
+ * HttpClientRequest's default minimum body size of 1KB.
439
+ *
422
440
  * @stability unstable
423
441
  * @category layers
424
442
  * @since 4.0.0
@@ -441,7 +459,17 @@ export const layerFromConfig = (options?: {
441
459
  return Exporter.layerFlusher
442
460
  }
443
461
 
444
- const { baseTimeout, metricsTimeout, exportTimeout, exportInterval, temporalityPreference } = yield* Config.all({
462
+ const {
463
+ baseCompression,
464
+ compression,
465
+ baseTimeout,
466
+ metricsTimeout,
467
+ exportTimeout,
468
+ exportInterval,
469
+ temporalityPreference
470
+ } = yield* Config.all({
471
+ baseCompression: Config.option(Config.Literals(["gzip", "none"], "OTEL_EXPORTER_OTLP_COMPRESSION")),
472
+ compression: Config.option(Config.Literals(["gzip", "none"], "OTEL_EXPORTER_OTLP_METRICS_COMPRESSION")),
445
473
  baseTimeout: Config.option(Config.Int("OTEL_EXPORTER_OTLP_TIMEOUT")),
446
474
  metricsTimeout: Config.option(Config.Int("OTEL_EXPORTER_OTLP_METRICS_TIMEOUT")),
447
475
  exportTimeout: Config.option(Config.Int("OTEL_METRIC_EXPORT_TIMEOUT")),
@@ -463,6 +491,7 @@ export const layerFromConfig = (options?: {
463
491
  url: endpoint.toString(),
464
492
  resource: options?.resource,
465
493
  headers: options?.headers ?? (yield* OtlpEnv.headers("METRICS")),
494
+ compression: Option.getOrElse(Option.firstSomeOf([compression, baseCompression]), () => "none" as const),
466
495
  exportInterval: Option.getOrUndefined(exportInterval),
467
496
  shutdownTimeout: Option.getOrUndefined(shutdownTimeout),
468
497
  temporality: Option.getOrUndefined(temporalityPreference)