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.
- package/ai-docs/src/52_graphql/10_basics.ts +19 -13
- package/ai-docs/src/52_graphql/30_batching.ts +6 -4
- package/ai-docs/src/52_graphql/40_aliased-batching.ts +6 -4
- package/dist/Array.d.ts +90 -6
- package/dist/Array.d.ts.map +1 -1
- package/dist/Array.js +4 -2
- package/dist/Array.js.map +1 -1
- package/dist/Channel.d.ts +6 -1
- package/dist/Channel.d.ts.map +1 -1
- package/dist/Channel.js +17 -3
- package/dist/Channel.js.map +1 -1
- package/dist/ConfigProvider.d.ts.map +1 -1
- package/dist/ConfigProvider.js +33 -32
- package/dist/ConfigProvider.js.map +1 -1
- package/dist/Duration.d.ts.map +1 -1
- package/dist/Duration.js +2 -5
- package/dist/Duration.js.map +1 -1
- package/dist/Effect.d.ts +61 -19
- package/dist/Effect.d.ts.map +1 -1
- package/dist/Effect.js +58 -39
- package/dist/Effect.js.map +1 -1
- package/dist/FileSystem.d.ts +23 -9
- package/dist/FileSystem.d.ts.map +1 -1
- package/dist/FileSystem.js.map +1 -1
- package/dist/Match.d.ts +20 -6
- package/dist/Match.d.ts.map +1 -1
- package/dist/Match.js +2 -2
- package/dist/Match.js.map +1 -1
- package/dist/Path.d.ts +37 -5
- package/dist/Path.d.ts.map +1 -1
- package/dist/Path.js +36 -5
- package/dist/Path.js.map +1 -1
- package/dist/Schema.d.ts +7 -2
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +9 -2
- package/dist/Schema.js.map +1 -1
- package/dist/String.d.ts +12 -0
- package/dist/String.d.ts.map +1 -1
- package/dist/String.js +17 -4
- package/dist/String.js.map +1 -1
- package/dist/TxRef.d.ts.map +1 -1
- package/dist/TxRef.js +3 -2
- package/dist/TxRef.js.map +1 -1
- package/dist/ai/AiError.d.ts +10 -10
- package/dist/ai/AiError.d.ts.map +1 -1
- package/dist/ai/AiError.js +1 -1
- package/dist/ai/AiError.js.map +1 -1
- package/dist/ai/Decision.d.ts +70 -19
- package/dist/ai/Decision.d.ts.map +1 -1
- package/dist/ai/Decision.js +37 -3
- package/dist/ai/Decision.js.map +1 -1
- package/dist/ai/DecisionModel.js +1 -1
- package/dist/ai/DecisionModel.js.map +1 -1
- package/dist/ai/LanguageModel.d.ts.map +1 -1
- package/dist/ai/LanguageModel.js +47 -35
- package/dist/ai/LanguageModel.js.map +1 -1
- package/dist/ai/McpServer.d.ts +6 -52
- package/dist/ai/McpServer.d.ts.map +1 -1
- package/dist/ai/McpServer.js +2 -28
- package/dist/ai/McpServer.js.map +1 -1
- package/dist/ai/Response.d.ts +1 -1
- package/dist/cli/Completions.d.ts +5 -0
- package/dist/cli/Completions.d.ts.map +1 -1
- package/dist/cli/Completions.js.map +1 -1
- package/dist/cli/Flag.d.ts +100 -0
- package/dist/cli/Flag.d.ts.map +1 -1
- package/dist/cli/Flag.js +33 -0
- package/dist/cli/Flag.js.map +1 -1
- package/dist/cli/Param.d.ts +87 -0
- package/dist/cli/Param.d.ts.map +1 -1
- package/dist/cli/Param.js +30 -0
- package/dist/cli/Param.js.map +1 -1
- package/dist/cli/internal/command.d.ts.map +1 -1
- package/dist/cli/internal/command.js +2 -1
- package/dist/cli/internal/command.js.map +1 -1
- package/dist/cli/internal/completions/bash.js +4 -3
- package/dist/cli/internal/completions/bash.js.map +1 -1
- package/dist/cli/internal/completions/descriptor.js +8 -1
- package/dist/cli/internal/completions/descriptor.js.map +1 -1
- package/dist/cli/internal/completions/fish.js +4 -4
- package/dist/cli/internal/completions/fish.js.map +1 -1
- package/dist/cli/internal/completions/powershell.js +3 -2
- package/dist/cli/internal/completions/powershell.js.map +1 -1
- package/dist/cli/internal/completions/zsh.js +7 -3
- package/dist/cli/internal/completions/zsh.js.map +1 -1
- package/dist/cli/internal/parser.js +11 -3
- package/dist/cli/internal/parser.js.map +1 -1
- package/dist/cli/internal/wizard.d.ts.map +1 -1
- package/dist/cli/internal/wizard.js +2 -1
- package/dist/cli/internal/wizard.js.map +1 -1
- package/dist/cluster/ClusterWorkflowEngine.d.ts +1 -0
- package/dist/cluster/ClusterWorkflowEngine.d.ts.map +1 -1
- package/dist/cluster/ClusterWorkflowEngine.js +67 -13
- package/dist/cluster/ClusterWorkflowEngine.js.map +1 -1
- package/dist/cluster/MessageStorage.d.ts.map +1 -1
- package/dist/cluster/MessageStorage.js +20 -12
- package/dist/cluster/MessageStorage.js.map +1 -1
- package/dist/cluster/Runners.d.ts.map +1 -1
- package/dist/cluster/Runners.js +54 -34
- package/dist/cluster/Runners.js.map +1 -1
- package/dist/cluster/internal/entityManager.js +1 -1
- package/dist/cluster/internal/entityManager.js.map +1 -1
- package/dist/encoding/Sse.js +1 -1
- package/dist/encoding/Sse.js.map +1 -1
- package/dist/graphql/GraphQLClient.d.ts.map +1 -1
- package/dist/graphql/GraphQLClient.js +12 -5
- package/dist/graphql/GraphQLClient.js.map +1 -1
- package/dist/graphql/GraphQLProtocol.d.ts +249 -11
- package/dist/graphql/GraphQLProtocol.d.ts.map +1 -1
- package/dist/graphql/GraphQLProtocol.js +98 -8
- package/dist/graphql/GraphQLProtocol.js.map +1 -1
- package/dist/graphql/GraphQLResolver.d.ts +1 -1
- package/dist/graphql/GraphQLResolver.d.ts.map +1 -1
- package/dist/graphql/GraphQLResolver.js +46 -16
- package/dist/graphql/GraphQLResolver.js.map +1 -1
- package/dist/http/FetchHttpClient.d.ts +6 -3
- package/dist/http/FetchHttpClient.d.ts.map +1 -1
- package/dist/http/FetchHttpClient.js +6 -3
- package/dist/http/FetchHttpClient.js.map +1 -1
- package/dist/http/FindMyWay/internal/router.js +2 -1
- package/dist/http/FindMyWay/internal/router.js.map +1 -1
- package/dist/http/Headers.d.ts +3 -3
- package/dist/http/Headers.d.ts.map +1 -1
- package/dist/http/Headers.js +6 -5
- package/dist/http/Headers.js.map +1 -1
- package/dist/http/HttpClient.d.ts +12 -0
- package/dist/http/HttpClient.d.ts.map +1 -1
- package/dist/http/HttpClient.js +14 -1
- package/dist/http/HttpClient.js.map +1 -1
- package/dist/http/HttpClientError.d.ts.map +1 -1
- package/dist/http/HttpClientError.js +8 -6
- package/dist/http/HttpClientError.js.map +1 -1
- package/dist/http/HttpClientRequest.d.ts +147 -4
- package/dist/http/HttpClientRequest.d.ts.map +1 -1
- package/dist/http/HttpClientRequest.js +47 -1
- package/dist/http/HttpClientRequest.js.map +1 -1
- package/dist/http/HttpMiddleware.d.ts +60 -0
- package/dist/http/HttpMiddleware.d.ts.map +1 -1
- package/dist/http/HttpMiddleware.js +67 -5
- package/dist/http/HttpMiddleware.js.map +1 -1
- package/dist/http/HttpRouter.d.ts.map +1 -1
- package/dist/http/HttpRouter.js +52 -13
- package/dist/http/HttpRouter.js.map +1 -1
- package/dist/http/HttpServerError.d.ts.map +1 -1
- package/dist/http/HttpServerError.js +6 -4
- package/dist/http/HttpServerError.js.map +1 -1
- package/dist/http/HttpServerRequest.js +2 -2
- package/dist/http/HttpServerRequest.js.map +1 -1
- package/dist/http/HttpStaticServer.d.ts.map +1 -1
- package/dist/http/HttpStaticServer.js +4 -10
- package/dist/http/HttpStaticServer.js.map +1 -1
- package/dist/http/HttpTraceContext.js +1 -1
- package/dist/http/HttpTraceContext.js.map +1 -1
- package/dist/http/Url.d.ts.map +1 -1
- package/dist/http/Url.js +2 -1
- package/dist/http/Url.js.map +1 -1
- package/dist/http/UrlParams.d.ts +112 -8
- package/dist/http/UrlParams.d.ts.map +1 -1
- package/dist/http/UrlParams.js +141 -14
- package/dist/http/UrlParams.js.map +1 -1
- package/dist/http/internal/compression.d.ts +1 -1
- package/dist/http/internal/compression.d.ts.map +1 -1
- package/dist/http/internal/compression.js +2 -10
- package/dist/http/internal/compression.js.map +1 -1
- package/dist/http/internal/compressionBody.d.ts +2 -0
- package/dist/http/internal/compressionBody.d.ts.map +1 -0
- package/dist/http/internal/compressionBody.js +36 -0
- package/dist/http/internal/compressionBody.js.map +1 -0
- package/dist/http/internal/decompression.d.ts +2 -0
- package/dist/http/internal/decompression.d.ts.map +1 -0
- package/dist/http/internal/decompression.js +162 -0
- package/dist/http/internal/decompression.js.map +1 -0
- package/dist/http/internal/tracing.js +14 -4
- package/dist/http/internal/tracing.js.map +1 -1
- package/dist/http/internal/urlParams.d.ts +2 -0
- package/dist/http/internal/urlParams.d.ts.map +1 -0
- package/dist/http/internal/urlParams.js +4 -0
- package/dist/http/internal/urlParams.js.map +1 -0
- package/dist/http-api/HttpApiClient.d.ts.map +1 -1
- package/dist/http-api/HttpApiClient.js +2 -1
- package/dist/http-api/HttpApiClient.js.map +1 -1
- package/dist/internal/core.js +17 -1
- package/dist/internal/core.js.map +1 -1
- package/dist/internal/effect.js +5 -3
- package/dist/internal/effect.js.map +1 -1
- package/dist/internal/graphql.js +12 -2
- package/dist/internal/graphql.js.map +1 -1
- package/dist/internal/version.js +1 -1
- package/dist/observability/Otlp.d.ts +15 -0
- package/dist/observability/Otlp.d.ts.map +1 -1
- package/dist/observability/Otlp.js +3 -0
- package/dist/observability/Otlp.js.map +1 -1
- package/dist/observability/OtlpExporter.d.ts +8 -0
- package/dist/observability/OtlpExporter.d.ts.map +1 -1
- package/dist/observability/OtlpExporter.js +10 -1
- package/dist/observability/OtlpExporter.js.map +1 -1
- package/dist/observability/OtlpLogger.d.ts +17 -0
- package/dist/observability/OtlpLogger.d.ts.map +1 -1
- package/dist/observability/OtlpLogger.js +14 -1
- package/dist/observability/OtlpLogger.js.map +1 -1
- package/dist/observability/OtlpMetrics.d.ts +17 -0
- package/dist/observability/OtlpMetrics.d.ts.map +1 -1
- package/dist/observability/OtlpMetrics.js +13 -0
- package/dist/observability/OtlpMetrics.js.map +1 -1
- package/dist/observability/OtlpTracer.d.ts +17 -0
- package/dist/observability/OtlpTracer.d.ts.map +1 -1
- package/dist/observability/OtlpTracer.js +15 -2
- package/dist/observability/OtlpTracer.js.map +1 -1
- package/dist/reactivity/AsyncResult.d.ts +6 -0
- package/dist/reactivity/AsyncResult.d.ts.map +1 -1
- package/dist/reactivity/AsyncResult.js +27 -0
- package/dist/reactivity/AsyncResult.js.map +1 -1
- package/dist/reactivity/AtomRegistry.js +3 -1
- package/dist/reactivity/AtomRegistry.js.map +1 -1
- package/dist/rpc/RpcClient.d.ts.map +1 -1
- package/dist/rpc/RpcClient.js +6 -1
- package/dist/rpc/RpcClient.js.map +1 -1
- package/dist/sql/SqlClient.d.ts +8 -7
- package/dist/sql/SqlClient.d.ts.map +1 -1
- package/dist/sql/SqlClient.js +8 -7
- package/dist/sql/SqlClient.js.map +1 -1
- package/dist/testing/TestClock.d.ts +16 -3
- package/dist/testing/TestClock.d.ts.map +1 -1
- package/dist/testing/TestClock.js +112 -22
- package/dist/testing/TestClock.js.map +1 -1
- package/dist/workflow/Activity.js +1 -0
- package/dist/workflow/Activity.js.map +1 -1
- package/dist/workflow/Workflow.d.ts +22 -0
- package/dist/workflow/Workflow.d.ts.map +1 -1
- package/dist/workflow/Workflow.js +15 -3
- package/dist/workflow/Workflow.js.map +1 -1
- package/dist/workflow/WorkflowEngine.d.ts +20 -1
- package/dist/workflow/WorkflowEngine.d.ts.map +1 -1
- package/dist/workflow/WorkflowEngine.js +35 -21
- package/dist/workflow/WorkflowEngine.js.map +1 -1
- package/package.json +1 -1
- package/src/Array.ts +95 -6
- package/src/Channel.ts +24 -3
- package/src/ConfigProvider.ts +40 -44
- package/src/Duration.ts +4 -5
- package/src/Effect.ts +140 -53
- package/src/FileSystem.ts +23 -9
- package/src/Match.ts +65 -12
- package/src/Path.ts +52 -7
- package/src/Schema.ts +17 -4
- package/src/String.ts +17 -4
- package/src/TxRef.ts +4 -2
- package/src/ai/AiError.ts +3 -1
- package/src/ai/Decision.ts +70 -21
- package/src/ai/DecisionModel.ts +1 -1
- package/src/ai/LanguageModel.ts +57 -51
- package/src/ai/McpServer.ts +15 -55
- package/src/cli/Completions.ts +5 -0
- package/src/cli/Flag.ts +101 -0
- package/src/cli/Param.ts +96 -0
- package/src/cli/internal/command.ts +2 -1
- package/src/cli/internal/completions/bash.ts +4 -3
- package/src/cli/internal/completions/descriptor.ts +10 -1
- package/src/cli/internal/completions/fish.ts +4 -4
- package/src/cli/internal/completions/powershell.ts +5 -2
- package/src/cli/internal/completions/zsh.ts +7 -3
- package/src/cli/internal/parser.ts +12 -3
- package/src/cli/internal/wizard.ts +2 -1
- package/src/cluster/ClusterWorkflowEngine.ts +92 -24
- package/src/cluster/MessageStorage.ts +14 -3
- package/src/cluster/Runners.ts +88 -66
- package/src/cluster/internal/entityManager.ts +1 -1
- package/src/encoding/Sse.ts +1 -1
- package/src/graphql/GraphQLClient.ts +17 -5
- package/src/graphql/GraphQLProtocol.ts +274 -20
- package/src/graphql/GraphQLResolver.ts +45 -17
- package/src/http/FetchHttpClient.ts +6 -3
- package/src/http/FindMyWay/internal/router.ts +2 -1
- package/src/http/Headers.ts +6 -9
- package/src/http/HttpClient.ts +18 -1
- package/src/http/HttpClientError.ts +10 -6
- package/src/http/HttpClientRequest.ts +173 -7
- package/src/http/HttpMiddleware.ts +85 -5
- package/src/http/HttpRouter.ts +60 -13
- package/src/http/HttpServerError.ts +8 -4
- package/src/http/HttpServerRequest.ts +2 -2
- package/src/http/HttpStaticServer.ts +4 -10
- package/src/http/HttpTraceContext.ts +1 -1
- package/src/http/Url.ts +2 -1
- package/src/http/UrlParams.ts +225 -26
- package/src/http/internal/compression.ts +3 -17
- package/src/http/internal/compressionBody.ts +56 -0
- package/src/http/internal/decompression.ts +226 -0
- package/src/http/internal/tracing.ts +21 -4
- package/src/http/internal/urlParams.ts +5 -0
- package/src/http-api/HttpApiClient.ts +2 -1
- package/src/internal/core.ts +21 -1
- package/src/internal/effect.ts +5 -4
- package/src/internal/graphql.ts +30 -7
- package/src/internal/version.ts +1 -1
- package/src/observability/Otlp.ts +18 -0
- package/src/observability/OtlpExporter.ts +14 -3
- package/src/observability/OtlpLogger.ts +31 -2
- package/src/observability/OtlpMetrics.ts +30 -1
- package/src/observability/OtlpTracer.ts +32 -3
- package/src/reactivity/AsyncResult.ts +31 -0
- package/src/reactivity/AtomRegistry.ts +3 -1
- package/src/rpc/RpcClient.ts +6 -1
- package/src/sql/SqlClient.ts +9 -7
- package/src/testing/TestClock.ts +144 -29
- package/src/workflow/Activity.ts +1 -0
- package/src/workflow/Workflow.ts +31 -5
- package/src/workflow/WorkflowEngine.ts +60 -22
package/src/http/Headers.ts
CHANGED
|
@@ -731,10 +731,11 @@ export const isRedactedName = (
|
|
|
731
731
|
name: string,
|
|
732
732
|
patterns: ReadonlyArray<string | RegExp>
|
|
733
733
|
): boolean => {
|
|
734
|
+
const lowerName = name.toLowerCase()
|
|
734
735
|
for (let i = 0; i < patterns.length; i++) {
|
|
735
736
|
const pattern = patterns[i]
|
|
736
737
|
if (typeof pattern === "string") {
|
|
737
|
-
if (pattern.toLowerCase() ===
|
|
738
|
+
if (pattern.toLowerCase() === lowerName) {
|
|
738
739
|
return true
|
|
739
740
|
}
|
|
740
741
|
} else if (name.search(pattern) !== -1) {
|
|
@@ -749,9 +750,9 @@ export const isRedactedName = (
|
|
|
749
750
|
*
|
|
750
751
|
* **Details**
|
|
751
752
|
*
|
|
752
|
-
* Defaults include `authorization`, `cookie`, `set-cookie
|
|
753
|
-
*
|
|
754
|
-
*
|
|
753
|
+
* Defaults include `authorization`, `cookie`, `set-cookie` and `x-api-key`.
|
|
754
|
+
* A custom list replaces the defaults. Matching uses `isRedactedName`.
|
|
755
|
+
* Use `UrlParams.CurrentRedactedNames` for query parameters.
|
|
755
756
|
*
|
|
756
757
|
* @stability unstable
|
|
757
758
|
* @category services
|
|
@@ -764,10 +765,6 @@ export const CurrentRedactedNames = Context.Reference<
|
|
|
764
765
|
"authorization",
|
|
765
766
|
"cookie",
|
|
766
767
|
"set-cookie",
|
|
767
|
-
"x-api-key"
|
|
768
|
-
"AWSAccessKeyId",
|
|
769
|
-
"Signature",
|
|
770
|
-
"sig",
|
|
771
|
-
"X-Goog-Signature"
|
|
768
|
+
"x-api-key"
|
|
772
769
|
]
|
|
773
770
|
})
|
package/src/http/HttpClient.ts
CHANGED
|
@@ -47,6 +47,7 @@ import * as HttpMethod from "./HttpMethod.ts"
|
|
|
47
47
|
import * as TraceContext from "./HttpTraceContext.ts"
|
|
48
48
|
import * as tracing from "./internal/tracing.ts"
|
|
49
49
|
import * as Url from "./Url.ts"
|
|
50
|
+
import * as UrlParams from "./UrlParams.ts"
|
|
50
51
|
|
|
51
52
|
const TypeId = "~effect/http/HttpClient"
|
|
52
53
|
|
|
@@ -905,7 +906,11 @@ export const make = (
|
|
|
905
906
|
(span) => {
|
|
906
907
|
span.attribute("http.request.method", request.method)
|
|
907
908
|
const redactedHeaderNames = fiber.getRef(Headers.CurrentRedactedNames)
|
|
908
|
-
tracing.addUrlAttributes(
|
|
909
|
+
tracing.addUrlAttributes(
|
|
910
|
+
span,
|
|
911
|
+
url,
|
|
912
|
+
tracing.redactedNames(request.urlParams, fiber.getRef(UrlParams.CurrentRedactedNames))
|
|
913
|
+
)
|
|
909
914
|
const headerFilter = fiber.getRef(TracerHeaderFilter)
|
|
910
915
|
tracing.addHeaderAttributes(span, "request", request.headers, headerFilter, redactedHeaderNames)
|
|
911
916
|
request = fiber.getRef(TracerPropagationEnabled)
|
|
@@ -2255,6 +2260,18 @@ export const TracerHeaderFilter = Context.Reference<
|
|
|
2255
2260
|
/**
|
|
2256
2261
|
* Context reference that controls whether outgoing client spans are propagated to request headers.
|
|
2257
2262
|
*
|
|
2263
|
+
* **Details**
|
|
2264
|
+
*
|
|
2265
|
+
* When this is `true`, the client writes the compact B3 `b3` header and the
|
|
2266
|
+
* W3C `traceparent` header. The default is on. The client span is still
|
|
2267
|
+
* created when this is `false`.
|
|
2268
|
+
*
|
|
2269
|
+
* **Gotchas**
|
|
2270
|
+
*
|
|
2271
|
+
* A browser preflight fails when the target does not list `b3` and
|
|
2272
|
+
* `traceparent` in `Access-Control-Allow-Headers`. Set this to `false` for
|
|
2273
|
+
* those requests.
|
|
2274
|
+
*
|
|
2258
2275
|
* @stability unstable
|
|
2259
2276
|
* @category services
|
|
2260
2277
|
* @since 4.0.0
|
|
@@ -15,6 +15,7 @@ import { hasProperty } from "../Predicate.ts"
|
|
|
15
15
|
import * as Schema from "../Schema.ts"
|
|
16
16
|
import type * as HttpClientRequest from "./HttpClientRequest.ts"
|
|
17
17
|
import type * as ClientResponse from "./HttpClientResponse.ts"
|
|
18
|
+
import * as UrlParams from "./UrlParams.ts"
|
|
18
19
|
|
|
19
20
|
const TypeId = "~effect/http/HttpClientError"
|
|
20
21
|
|
|
@@ -88,6 +89,9 @@ const formatReason = (tag: string) => tag.endsWith("Error") ? tag.slice(0, -5) :
|
|
|
88
89
|
const formatMessage = (reason: string, description: string | undefined, info: string) =>
|
|
89
90
|
description ? `${reason}: ${description} (${info})` : `${reason} error (${info})`
|
|
90
91
|
|
|
92
|
+
const formatMethodAndUrl = (request: HttpClientRequest.HttpClientRequest) =>
|
|
93
|
+
`${request.method} ${UrlParams.redactUrl(request.url)}`
|
|
94
|
+
|
|
91
95
|
/**
|
|
92
96
|
* Error describing transport-level failures that occur while sending an HTTP request.
|
|
93
97
|
*
|
|
@@ -107,7 +111,7 @@ export class TransportError extends Data.TaggedError("TransportError")<{
|
|
|
107
111
|
* @since 4.0.0
|
|
108
112
|
*/
|
|
109
113
|
get methodAndUrl() {
|
|
110
|
-
return
|
|
114
|
+
return formatMethodAndUrl(this.request)
|
|
111
115
|
}
|
|
112
116
|
|
|
113
117
|
/**
|
|
@@ -140,7 +144,7 @@ export class EncodeError extends Data.TaggedError("EncodeError")<{
|
|
|
140
144
|
* @since 4.0.0
|
|
141
145
|
*/
|
|
142
146
|
get methodAndUrl() {
|
|
143
|
-
return
|
|
147
|
+
return formatMethodAndUrl(this.request)
|
|
144
148
|
}
|
|
145
149
|
|
|
146
150
|
/**
|
|
@@ -173,7 +177,7 @@ export class InvalidUrlError extends Data.TaggedError("InvalidUrlError")<{
|
|
|
173
177
|
* @since 4.0.0
|
|
174
178
|
*/
|
|
175
179
|
get methodAndUrl() {
|
|
176
|
-
return
|
|
180
|
+
return formatMethodAndUrl(this.request)
|
|
177
181
|
}
|
|
178
182
|
|
|
179
183
|
/**
|
|
@@ -207,7 +211,7 @@ export class StatusCodeError extends Data.TaggedError("StatusCodeError")<{
|
|
|
207
211
|
* @since 4.0.0
|
|
208
212
|
*/
|
|
209
213
|
get methodAndUrl() {
|
|
210
|
-
return
|
|
214
|
+
return formatMethodAndUrl(this.request)
|
|
211
215
|
}
|
|
212
216
|
|
|
213
217
|
/**
|
|
@@ -242,7 +246,7 @@ export class DecodeError extends Data.TaggedError("DecodeError")<{
|
|
|
242
246
|
* @since 4.0.0
|
|
243
247
|
*/
|
|
244
248
|
get methodAndUrl() {
|
|
245
|
-
return
|
|
249
|
+
return formatMethodAndUrl(this.request)
|
|
246
250
|
}
|
|
247
251
|
|
|
248
252
|
/**
|
|
@@ -277,7 +281,7 @@ export class EmptyBodyError extends Data.TaggedError("EmptyBodyError")<{
|
|
|
277
281
|
* @since 4.0.0
|
|
278
282
|
*/
|
|
279
283
|
get methodAndUrl() {
|
|
280
|
-
return
|
|
284
|
+
return formatMethodAndUrl(this.request)
|
|
281
285
|
}
|
|
282
286
|
|
|
283
287
|
/**
|
|
@@ -30,6 +30,7 @@ import * as Stream from "../Stream.ts"
|
|
|
30
30
|
import * as Headers from "./Headers.ts"
|
|
31
31
|
import * as HttpBody from "./HttpBody.ts"
|
|
32
32
|
import { hasBody, type HttpMethod } from "./HttpMethod.ts"
|
|
33
|
+
import * as compression from "./internal/compressionBody.ts"
|
|
33
34
|
import * as bodyInternal from "./internal/httpBody.ts"
|
|
34
35
|
import * as Url from "./Url.ts"
|
|
35
36
|
import * as UrlParams from "./UrlParams.ts"
|
|
@@ -104,7 +105,7 @@ const Proto = {
|
|
|
104
105
|
return {
|
|
105
106
|
_id: "HttpClientRequest",
|
|
106
107
|
method: this.method,
|
|
107
|
-
url: this.url,
|
|
108
|
+
url: UrlParams.redactUrl(this.url),
|
|
108
109
|
urlParams: this.urlParams,
|
|
109
110
|
hash: this.hash,
|
|
110
111
|
headers: redact(this.headers),
|
|
@@ -755,6 +756,10 @@ export const updateUrl: {
|
|
|
755
756
|
/**
|
|
756
757
|
* Sets one query parameter, replacing existing values for that parameter name.
|
|
757
758
|
*
|
|
759
|
+
* **Details**
|
|
760
|
+
*
|
|
761
|
+
* A `Redacted` value is sent unwrapped and stays redacted in inspection, errors and spans.
|
|
762
|
+
*
|
|
758
763
|
* @stability unstable
|
|
759
764
|
* @category combinators
|
|
760
765
|
* @since 4.0.0
|
|
@@ -763,20 +768,28 @@ export const setUrlParam: {
|
|
|
763
768
|
/**
|
|
764
769
|
* Sets one query parameter, replacing existing values for that parameter name.
|
|
765
770
|
*
|
|
771
|
+
* **Details**
|
|
772
|
+
*
|
|
773
|
+
* A `Redacted` value is sent unwrapped and stays redacted in inspection, errors and spans.
|
|
774
|
+
*
|
|
766
775
|
* @stability unstable
|
|
767
776
|
* @category combinators
|
|
768
777
|
* @since 4.0.0
|
|
769
778
|
*/
|
|
770
|
-
(key: string, value: string): (self: HttpClientRequest) => HttpClientRequest
|
|
779
|
+
(key: string, value: string | Redacted.Redacted): (self: HttpClientRequest) => HttpClientRequest
|
|
771
780
|
/**
|
|
772
781
|
* Sets one query parameter, replacing existing values for that parameter name.
|
|
773
782
|
*
|
|
783
|
+
* **Details**
|
|
784
|
+
*
|
|
785
|
+
* A `Redacted` value is sent unwrapped and stays redacted in inspection, errors and spans.
|
|
786
|
+
*
|
|
774
787
|
* @stability unstable
|
|
775
788
|
* @category combinators
|
|
776
789
|
* @since 4.0.0
|
|
777
790
|
*/
|
|
778
|
-
(self: HttpClientRequest, key: string, value: string): HttpClientRequest
|
|
779
|
-
} = dual(3, (self: HttpClientRequest, key: string, value: string): HttpClientRequest =>
|
|
791
|
+
(self: HttpClientRequest, key: string, value: string | Redacted.Redacted): HttpClientRequest
|
|
792
|
+
} = dual(3, (self: HttpClientRequest, key: string, value: string | Redacted.Redacted): HttpClientRequest =>
|
|
780
793
|
makeWith(
|
|
781
794
|
self.method,
|
|
782
795
|
self.url,
|
|
@@ -823,6 +836,10 @@ export const setUrlParams: {
|
|
|
823
836
|
/**
|
|
824
837
|
* Appends one query parameter value without removing existing values for the same name.
|
|
825
838
|
*
|
|
839
|
+
* **Details**
|
|
840
|
+
*
|
|
841
|
+
* A `Redacted` value is sent unwrapped and stays redacted in inspection, errors and spans.
|
|
842
|
+
*
|
|
826
843
|
* @stability unstable
|
|
827
844
|
* @category combinators
|
|
828
845
|
* @since 4.0.0
|
|
@@ -831,20 +848,28 @@ export const appendUrlParam: {
|
|
|
831
848
|
/**
|
|
832
849
|
* Appends one query parameter value without removing existing values for the same name.
|
|
833
850
|
*
|
|
851
|
+
* **Details**
|
|
852
|
+
*
|
|
853
|
+
* A `Redacted` value is sent unwrapped and stays redacted in inspection, errors and spans.
|
|
854
|
+
*
|
|
834
855
|
* @stability unstable
|
|
835
856
|
* @category combinators
|
|
836
857
|
* @since 4.0.0
|
|
837
858
|
*/
|
|
838
|
-
(key: string, value: string): (self: HttpClientRequest) => HttpClientRequest
|
|
859
|
+
(key: string, value: string | Redacted.Redacted): (self: HttpClientRequest) => HttpClientRequest
|
|
839
860
|
/**
|
|
840
861
|
* Appends one query parameter value without removing existing values for the same name.
|
|
841
862
|
*
|
|
863
|
+
* **Details**
|
|
864
|
+
*
|
|
865
|
+
* A `Redacted` value is sent unwrapped and stays redacted in inspection, errors and spans.
|
|
866
|
+
*
|
|
842
867
|
* @stability unstable
|
|
843
868
|
* @category combinators
|
|
844
869
|
* @since 4.0.0
|
|
845
870
|
*/
|
|
846
|
-
(self: HttpClientRequest, key: string, value: string): HttpClientRequest
|
|
847
|
-
} = dual(3, (self: HttpClientRequest, key: string, value: string): HttpClientRequest =>
|
|
871
|
+
(self: HttpClientRequest, key: string, value: string | Redacted.Redacted): HttpClientRequest
|
|
872
|
+
} = dual(3, (self: HttpClientRequest, key: string, value: string | Redacted.Redacted): HttpClientRequest =>
|
|
848
873
|
makeWith(
|
|
849
874
|
self.method,
|
|
850
875
|
self.url,
|
|
@@ -974,6 +999,147 @@ export const setBody: {
|
|
|
974
999
|
)
|
|
975
1000
|
})
|
|
976
1001
|
|
|
1002
|
+
/**
|
|
1003
|
+
* Algorithms supported by Web `CompressionStream` for request body compression.
|
|
1004
|
+
*
|
|
1005
|
+
* @stability unstable
|
|
1006
|
+
* @category models
|
|
1007
|
+
* @since 4.0.0
|
|
1008
|
+
*/
|
|
1009
|
+
export type CompressionAlgorithm = "gzip" | "deflate"
|
|
1010
|
+
|
|
1011
|
+
/**
|
|
1012
|
+
* Options for request body compression.
|
|
1013
|
+
*
|
|
1014
|
+
* @stability unstable
|
|
1015
|
+
* @category options
|
|
1016
|
+
* @since 4.0.0
|
|
1017
|
+
*/
|
|
1018
|
+
export interface CompressionOptions {
|
|
1019
|
+
/**
|
|
1020
|
+
* Minimum known body length in bytes to compress. Defaults to `1024`.
|
|
1021
|
+
* Bodies with unknown length are always compressed.
|
|
1022
|
+
*/
|
|
1023
|
+
readonly minSize?: number | undefined
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
/**
|
|
1027
|
+
* Compresses a request body using Web `CompressionStream` and sets `Content-Encoding`.
|
|
1028
|
+
*
|
|
1029
|
+
* **Details**
|
|
1030
|
+
*
|
|
1031
|
+
* Byte-array bodies (including text, JSON, and URL-encoded bodies) are compressed
|
|
1032
|
+
* into a new `Uint8Array` body, preserving the content type and updating
|
|
1033
|
+
* `Content-Length`. The compressed body does not retain the original lazy text.
|
|
1034
|
+
* Stream bodies are compressed lazily and have their content length removed.
|
|
1035
|
+
* They still require a client runtime that supports streaming request bodies
|
|
1036
|
+
* (Fetch requires `duplex: "half"`, which Safari and Firefox do not support).
|
|
1037
|
+
*
|
|
1038
|
+
* Returns the original request unchanged for empty, FormData, and raw bodies,
|
|
1039
|
+
* bodies with a known length below `options.minSize`, or requests with any
|
|
1040
|
+
* existing `Content-Encoding` header, including `identity`. Unknown-length
|
|
1041
|
+
* streams are always compressed. The default minimum is 1024 bytes because
|
|
1042
|
+
* gzip can make very small bodies larger.
|
|
1043
|
+
*
|
|
1044
|
+
* Requires `CompressionStream` (available in browsers, Node 18+, Bun, and Deno).
|
|
1045
|
+
* There is no typed error channel: byte compression failures are defects;
|
|
1046
|
+
* stream failures surface when the body is sent.
|
|
1047
|
+
*
|
|
1048
|
+
* To compress every request, use
|
|
1049
|
+
* `HttpClient.mapRequestEffect(HttpClientRequest.compressBody("gzip"))`.
|
|
1050
|
+
* `HttpClient.retryTransient` re-runs preprocessing, so this recompresses on
|
|
1051
|
+
* every retry. For large bodies, compress once before calling `client.execute`.
|
|
1052
|
+
*
|
|
1053
|
+
* @stability unstable
|
|
1054
|
+
* @category combinators
|
|
1055
|
+
* @since 4.0.0
|
|
1056
|
+
*/
|
|
1057
|
+
export const compressBody: {
|
|
1058
|
+
/**
|
|
1059
|
+
* Compresses a request body using Web `CompressionStream` and sets `Content-Encoding`.
|
|
1060
|
+
*
|
|
1061
|
+
* **Details**
|
|
1062
|
+
*
|
|
1063
|
+
* Byte-array bodies (including text, JSON, and URL-encoded bodies) are compressed
|
|
1064
|
+
* into a new `Uint8Array` body, preserving the content type and updating
|
|
1065
|
+
* `Content-Length`. The compressed body does not retain the original lazy text.
|
|
1066
|
+
* Stream bodies are compressed lazily and have their content length removed.
|
|
1067
|
+
* They still require a client runtime that supports streaming request bodies
|
|
1068
|
+
* (Fetch requires `duplex: "half"`, which Safari and Firefox do not support).
|
|
1069
|
+
*
|
|
1070
|
+
* Returns the original request unchanged for empty, FormData, and raw bodies,
|
|
1071
|
+
* bodies with a known length below `options.minSize`, or requests with any
|
|
1072
|
+
* existing `Content-Encoding` header, including `identity`. Unknown-length
|
|
1073
|
+
* streams are always compressed. The default minimum is 1024 bytes because
|
|
1074
|
+
* gzip can make very small bodies larger.
|
|
1075
|
+
*
|
|
1076
|
+
* Requires `CompressionStream` (available in browsers, Node 18+, Bun, and Deno).
|
|
1077
|
+
* There is no typed error channel: byte compression failures are defects;
|
|
1078
|
+
* stream failures surface when the body is sent.
|
|
1079
|
+
*
|
|
1080
|
+
* To compress every request, use
|
|
1081
|
+
* `HttpClient.mapRequestEffect(HttpClientRequest.compressBody("gzip"))`.
|
|
1082
|
+
* `HttpClient.retryTransient` re-runs preprocessing, so this recompresses on
|
|
1083
|
+
* every retry. For large bodies, compress once before calling `client.execute`.
|
|
1084
|
+
*
|
|
1085
|
+
* @stability unstable
|
|
1086
|
+
* @category combinators
|
|
1087
|
+
* @since 4.0.0
|
|
1088
|
+
*/
|
|
1089
|
+
(algorithm: CompressionAlgorithm, options?: CompressionOptions | undefined): (self: HttpClientRequest) => Effect.Effect<HttpClientRequest>
|
|
1090
|
+
/**
|
|
1091
|
+
* Compresses a request body using Web `CompressionStream` and sets `Content-Encoding`.
|
|
1092
|
+
*
|
|
1093
|
+
* **Details**
|
|
1094
|
+
*
|
|
1095
|
+
* Byte-array bodies (including text, JSON, and URL-encoded bodies) are compressed
|
|
1096
|
+
* into a new `Uint8Array` body, preserving the content type and updating
|
|
1097
|
+
* `Content-Length`. The compressed body does not retain the original lazy text.
|
|
1098
|
+
* Stream bodies are compressed lazily and have their content length removed.
|
|
1099
|
+
* They still require a client runtime that supports streaming request bodies
|
|
1100
|
+
* (Fetch requires `duplex: "half"`, which Safari and Firefox do not support).
|
|
1101
|
+
*
|
|
1102
|
+
* Returns the original request unchanged for empty, FormData, and raw bodies,
|
|
1103
|
+
* bodies with a known length below `options.minSize`, or requests with any
|
|
1104
|
+
* existing `Content-Encoding` header, including `identity`. Unknown-length
|
|
1105
|
+
* streams are always compressed. The default minimum is 1024 bytes because
|
|
1106
|
+
* gzip can make very small bodies larger.
|
|
1107
|
+
*
|
|
1108
|
+
* Requires `CompressionStream` (available in browsers, Node 18+, Bun, and Deno).
|
|
1109
|
+
* There is no typed error channel: byte compression failures are defects;
|
|
1110
|
+
* stream failures surface when the body is sent.
|
|
1111
|
+
*
|
|
1112
|
+
* To compress every request, use
|
|
1113
|
+
* `HttpClient.mapRequestEffect(HttpClientRequest.compressBody("gzip"))`.
|
|
1114
|
+
* `HttpClient.retryTransient` re-runs preprocessing, so this recompresses on
|
|
1115
|
+
* every retry. For large bodies, compress once before calling `client.execute`.
|
|
1116
|
+
*
|
|
1117
|
+
* @stability unstable
|
|
1118
|
+
* @category combinators
|
|
1119
|
+
* @since 4.0.0
|
|
1120
|
+
*/
|
|
1121
|
+
(
|
|
1122
|
+
self: HttpClientRequest,
|
|
1123
|
+
algorithm: CompressionAlgorithm,
|
|
1124
|
+
options?: CompressionOptions | undefined
|
|
1125
|
+
): Effect.Effect<HttpClientRequest>
|
|
1126
|
+
} = dual(
|
|
1127
|
+
(args) => isHttpClientRequest(args[0]),
|
|
1128
|
+
(
|
|
1129
|
+
self: HttpClientRequest,
|
|
1130
|
+
algorithm: CompressionAlgorithm,
|
|
1131
|
+
options?: CompressionOptions | undefined
|
|
1132
|
+
): Effect.Effect<HttpClientRequest> => {
|
|
1133
|
+
if (self.headers["content-encoding"] !== undefined) {
|
|
1134
|
+
return Effect.succeed(self)
|
|
1135
|
+
}
|
|
1136
|
+
return Effect.map(
|
|
1137
|
+
compression.compressBody(self.body, algorithm, options),
|
|
1138
|
+
(body) => body === self.body ? self : setHeader(setBody(self, body), "content-encoding", algorithm)
|
|
1139
|
+
)
|
|
1140
|
+
}
|
|
1141
|
+
)
|
|
1142
|
+
|
|
977
1143
|
/**
|
|
978
1144
|
* Sets a `Uint8Array` request body with an optional content type.
|
|
979
1145
|
*
|
|
@@ -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.
|
|
@@ -286,11 +290,7 @@ export const tracer: <E, R>(
|
|
|
286
290
|
span.attribute("http.request.method", request.method)
|
|
287
291
|
const url = Request.toURL(request)
|
|
288
292
|
if (Option.isSome(url)) {
|
|
289
|
-
|
|
290
|
-
url.value.username = "REDACTED"
|
|
291
|
-
url.value.password = "REDACTED"
|
|
292
|
-
}
|
|
293
|
-
tracing.addUrlAttributes(span, url.value)
|
|
293
|
+
tracing.addUrlAttributes(span, url.value, fiber.getRef(UrlParams.CurrentRedactedNames))
|
|
294
294
|
}
|
|
295
295
|
if (request.headers["user-agent"] !== undefined) {
|
|
296
296
|
span.attribute("user_agent.original", request.headers["user-agent"])
|
|
@@ -626,6 +626,86 @@ export const compression = (
|
|
|
626
626
|
})
|
|
627
627
|
}
|
|
628
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
|
+
|
|
629
709
|
const withVary = (response: HttpServerResponse): HttpServerResponse => {
|
|
630
710
|
const vary = compressionInternal.varyWith(response.headers, "Accept-Encoding")
|
|
631
711
|
return Response.setHeader(response, "vary", vary)
|
package/src/http/HttpRouter.ts
CHANGED
|
@@ -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
|
|
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,
|
|
153
|
-
router.all(route.path.slice(0, -2) 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,
|
|
167
|
+
router.all(route.path, handler)
|
|
156
168
|
}
|
|
157
169
|
} else {
|
|
158
170
|
if (route.path.endsWith("/*")) {
|
|
159
|
-
router.on(route.method, route.path,
|
|
160
|
-
router.on(route.method, route.path.slice(0, -2) 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,
|
|
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 (
|
|
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,
|
|
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(
|
|
255
|
-
|
|
256
|
-
|
|
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
|
/**
|