@effect/platform 0.69.27 → 0.69.29
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/dist/cjs/HttpApi.js +33 -0
- package/dist/cjs/HttpApi.js.map +1 -1
- package/dist/cjs/HttpApiBuilder.js +23 -5
- package/dist/cjs/HttpApiBuilder.js.map +1 -1
- package/dist/cjs/HttpApiClient.js +64 -6
- package/dist/cjs/HttpApiClient.js.map +1 -1
- package/dist/cjs/HttpApiEndpoint.js.map +1 -1
- package/dist/cjs/HttpClient.js +4 -1
- package/dist/cjs/HttpClient.js.map +1 -1
- package/dist/cjs/OpenApi.js +12 -7
- package/dist/cjs/OpenApi.js.map +1 -1
- package/dist/cjs/PlatformLogger.js +2 -0
- package/dist/cjs/PlatformLogger.js.map +1 -1
- package/dist/cjs/UrlParams.js +35 -1
- package/dist/cjs/UrlParams.js.map +1 -1
- package/dist/cjs/internal/httpClient.js +4 -1
- package/dist/cjs/internal/httpClient.js.map +1 -1
- package/dist/dts/HttpApi.d.ts +5 -0
- package/dist/dts/HttpApi.d.ts.map +1 -1
- package/dist/dts/HttpApiBuilder.d.ts +2 -3
- package/dist/dts/HttpApiBuilder.d.ts.map +1 -1
- package/dist/dts/HttpApiClient.d.ts.map +1 -1
- package/dist/dts/HttpApiEndpoint.d.ts +3 -1
- package/dist/dts/HttpApiEndpoint.d.ts.map +1 -1
- package/dist/dts/HttpClient.d.ts +15 -3
- package/dist/dts/HttpClient.d.ts.map +1 -1
- package/dist/dts/OpenApi.d.ts.map +1 -1
- package/dist/dts/PlatformLogger.d.ts +6 -0
- package/dist/dts/PlatformLogger.d.ts.map +1 -1
- package/dist/dts/UrlParams.d.ts +24 -0
- package/dist/dts/UrlParams.d.ts.map +1 -1
- package/dist/dts/internal/httpClient.d.ts.map +1 -1
- package/dist/esm/HttpApi.js +33 -0
- package/dist/esm/HttpApi.js.map +1 -1
- package/dist/esm/HttpApiBuilder.js +23 -5
- package/dist/esm/HttpApiBuilder.js.map +1 -1
- package/dist/esm/HttpApiClient.js +64 -6
- package/dist/esm/HttpApiClient.js.map +1 -1
- package/dist/esm/HttpApiEndpoint.js.map +1 -1
- package/dist/esm/HttpClient.js +4 -1
- package/dist/esm/HttpClient.js.map +1 -1
- package/dist/esm/OpenApi.js +12 -7
- package/dist/esm/OpenApi.js.map +1 -1
- package/dist/esm/PlatformLogger.js +2 -0
- package/dist/esm/PlatformLogger.js.map +1 -1
- package/dist/esm/UrlParams.js +33 -0
- package/dist/esm/UrlParams.js.map +1 -1
- package/dist/esm/internal/httpClient.js +4 -1
- package/dist/esm/internal/httpClient.js.map +1 -1
- package/package.json +2 -2
- package/src/HttpApi.ts +45 -0
- package/src/HttpApiBuilder.ts +28 -17
- package/src/HttpApiClient.ts +74 -13
- package/src/HttpApiEndpoint.ts +2 -1
- package/src/HttpClient.ts +23 -9
- package/src/OpenApi.ts +8 -12
- package/src/PlatformLogger.ts +6 -0
- package/src/UrlParams.ts +34 -0
- package/src/internal/httpClient.ts +16 -4
package/src/HttpApiBuilder.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @since 1.0.0
|
|
3
3
|
*/
|
|
4
|
+
import * as Cause from "effect/Cause"
|
|
4
5
|
import * as Chunk from "effect/Chunk"
|
|
5
6
|
import * as Context from "effect/Context"
|
|
6
7
|
import * as Effect from "effect/Effect"
|
|
@@ -38,6 +39,7 @@ import * as HttpServerRequest from "./HttpServerRequest.js"
|
|
|
38
39
|
import * as HttpServerResponse from "./HttpServerResponse.js"
|
|
39
40
|
import * as OpenApi from "./OpenApi.js"
|
|
40
41
|
import type { Path } from "./Path.js"
|
|
42
|
+
import * as UrlParams from "./UrlParams.js"
|
|
41
43
|
|
|
42
44
|
/**
|
|
43
45
|
* The router that the API endpoints are attached to.
|
|
@@ -111,13 +113,13 @@ export const httpApp: Effect.Effect<
|
|
|
111
113
|
const encodeError = Schema.encodeUnknown(errorSchema)
|
|
112
114
|
return router.pipe(
|
|
113
115
|
apiMiddleware,
|
|
114
|
-
Effect.
|
|
115
|
-
Effect.matchEffect(Effect.provide(encodeError(
|
|
116
|
-
onFailure: () => Effect.
|
|
116
|
+
Effect.catchAllCause((cause) =>
|
|
117
|
+
Effect.matchEffect(Effect.provide(encodeError(Cause.squash(cause)), context), {
|
|
118
|
+
onFailure: () => Effect.failCause(cause),
|
|
117
119
|
onSuccess: Effect.succeed
|
|
118
120
|
})
|
|
119
121
|
)
|
|
120
|
-
)
|
|
122
|
+
) as any
|
|
121
123
|
})
|
|
122
124
|
|
|
123
125
|
/**
|
|
@@ -126,6 +128,7 @@ export const httpApp: Effect.Effect<
|
|
|
126
128
|
* @since 1.0.0
|
|
127
129
|
* @category constructors
|
|
128
130
|
* @example
|
|
131
|
+
* ```ts
|
|
129
132
|
* import { HttpApi, HttpApiBuilder, HttpServer } from "@effect/platform"
|
|
130
133
|
* import { Layer } from "effect"
|
|
131
134
|
*
|
|
@@ -141,6 +144,7 @@ export const httpApp: Effect.Effect<
|
|
|
141
144
|
* HttpServer.layerContext
|
|
142
145
|
* )
|
|
143
146
|
* )
|
|
147
|
+
* ```
|
|
144
148
|
*/
|
|
145
149
|
export const toWebHandler = <LA, LE>(
|
|
146
150
|
layer: Layer.Layer<LA | HttpApi.Api | HttpRouter.HttpRouter.DefaultServices, LE>,
|
|
@@ -506,20 +510,31 @@ export const handler = <
|
|
|
506
510
|
|
|
507
511
|
const requestPayload = (
|
|
508
512
|
request: HttpServerRequest.HttpServerRequest,
|
|
509
|
-
urlParams: ReadonlyRecord<string, string | Array<string
|
|
510
|
-
isMultipart: boolean
|
|
513
|
+
urlParams: ReadonlyRecord<string, string | Array<string>>
|
|
511
514
|
): Effect.Effect<
|
|
512
515
|
unknown,
|
|
513
516
|
never,
|
|
514
517
|
| FileSystem
|
|
515
518
|
| Path
|
|
516
519
|
| Scope
|
|
517
|
-
> =>
|
|
518
|
-
HttpMethod.hasBody(request.method)
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
520
|
+
> => {
|
|
521
|
+
if (!HttpMethod.hasBody(request.method)) {
|
|
522
|
+
return Effect.succeed(urlParams)
|
|
523
|
+
}
|
|
524
|
+
const contentType = request.headers["content-type"]
|
|
525
|
+
? request.headers["content-type"].toLowerCase().trim()
|
|
526
|
+
: "application/json"
|
|
527
|
+
if (contentType.includes("application/json")) {
|
|
528
|
+
return Effect.orDie(request.json)
|
|
529
|
+
} else if (contentType.includes("multipart/form-data")) {
|
|
530
|
+
return Effect.orDie(request.multipart)
|
|
531
|
+
} else if (contentType.includes("x-www-form-urlencoded")) {
|
|
532
|
+
return Effect.map(Effect.orDie(request.urlParamsBody), UrlParams.toRecord)
|
|
533
|
+
} else if (contentType.startsWith("text/")) {
|
|
534
|
+
return Effect.orDie(request.text)
|
|
535
|
+
}
|
|
536
|
+
return Effect.map(Effect.orDie(request.arrayBuffer), (buffer) => new Uint8Array(buffer))
|
|
537
|
+
}
|
|
523
538
|
|
|
524
539
|
type MiddlewareMap = Map<string, {
|
|
525
540
|
readonly tag: HttpApiMiddleware.TagClassAny
|
|
@@ -552,10 +567,6 @@ const handlerToRoute = (
|
|
|
552
567
|
): HttpRouter.Route<any, any> => {
|
|
553
568
|
const endpoint = endpoint_ as HttpApiEndpoint.HttpApiEndpoint.AnyWithProps
|
|
554
569
|
const decodePath = Option.map(endpoint.pathSchema, Schema.decodeUnknown)
|
|
555
|
-
const isMultipart = endpoint.payloadSchema.pipe(
|
|
556
|
-
Option.map((schema) => HttpApiSchema.getMultipart(schema.ast)),
|
|
557
|
-
Option.getOrElse(() => false)
|
|
558
|
-
)
|
|
559
570
|
const decodePayload = Option.map(endpoint.payloadSchema, Schema.decodeUnknown)
|
|
560
571
|
const decodeHeaders = Option.map(endpoint.headersSchema, Schema.decodeUnknown)
|
|
561
572
|
const decodeUrlParams = Option.map(endpoint.urlParamsSchema, Schema.decodeUnknown)
|
|
@@ -577,7 +588,7 @@ const handlerToRoute = (
|
|
|
577
588
|
}
|
|
578
589
|
if (decodePayload._tag === "Some") {
|
|
579
590
|
request.payload = yield* Effect.flatMap(
|
|
580
|
-
requestPayload(httpRequest, urlParams
|
|
591
|
+
requestPayload(httpRequest, urlParams),
|
|
581
592
|
decodePayload.value
|
|
582
593
|
)
|
|
583
594
|
}
|
package/src/HttpApiClient.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import * as Context from "effect/Context"
|
|
5
5
|
import * as Effect from "effect/Effect"
|
|
6
6
|
import { identity } from "effect/Function"
|
|
7
|
+
import { globalValue } from "effect/GlobalValue"
|
|
7
8
|
import * as Option from "effect/Option"
|
|
8
9
|
import * as ParseResult from "effect/ParseResult"
|
|
9
10
|
import type * as Predicate from "effect/Predicate"
|
|
@@ -15,12 +16,14 @@ import * as HttpApi from "./HttpApi.js"
|
|
|
15
16
|
import type { HttpApiEndpoint } from "./HttpApiEndpoint.js"
|
|
16
17
|
import type { HttpApiGroup } from "./HttpApiGroup.js"
|
|
17
18
|
import * as HttpApiSchema from "./HttpApiSchema.js"
|
|
19
|
+
import * as HttpBody from "./HttpBody.js"
|
|
18
20
|
import * as HttpClient from "./HttpClient.js"
|
|
19
21
|
import * as HttpClientError from "./HttpClientError.js"
|
|
20
22
|
import * as HttpClientRequest from "./HttpClientRequest.js"
|
|
21
23
|
import * as HttpClientResponse from "./HttpClientResponse.js"
|
|
22
24
|
import * as HttpMethod from "./HttpMethod.js"
|
|
23
25
|
import type { HttpApiMiddleware } from "./index.js"
|
|
26
|
+
import * as UrlParams from "./UrlParams.js"
|
|
24
27
|
|
|
25
28
|
/**
|
|
26
29
|
* @since 1.0.0
|
|
@@ -160,13 +163,13 @@ const makeClient = <Groups extends HttpApiGroup.Any, ApiError, ApiR>(
|
|
|
160
163
|
successes.forEach((ast, status) => {
|
|
161
164
|
decodeMap[status] = ast._tag === "None" ? responseAsVoid : schemaToResponse(ast.value)
|
|
162
165
|
})
|
|
163
|
-
const
|
|
164
|
-
Option.map((schema) =>
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
166
|
+
const encodePayloadBody = endpoint.payloadSchema.pipe(
|
|
167
|
+
Option.map((schema) => {
|
|
168
|
+
if (HttpMethod.hasBody(endpoint.method)) {
|
|
169
|
+
return Schema.encodeUnknown(payloadSchemaBody(schema as any))
|
|
170
|
+
}
|
|
171
|
+
return Schema.encodeUnknown(schema)
|
|
172
|
+
})
|
|
170
173
|
)
|
|
171
174
|
const encodeHeaders = endpoint.headersSchema.pipe(
|
|
172
175
|
Option.map(Schema.encodeUnknown)
|
|
@@ -185,13 +188,16 @@ const makeClient = <Groups extends HttpApiGroup.Any, ApiError, ApiR>(
|
|
|
185
188
|
let httpRequest = HttpClientRequest.make(endpoint.method)(
|
|
186
189
|
request && request.path ? makeUrl(request.path) : endpoint.path
|
|
187
190
|
)
|
|
188
|
-
if (
|
|
191
|
+
if (request && request.payload instanceof FormData) {
|
|
189
192
|
httpRequest = HttpClientRequest.bodyFormData(httpRequest, request.payload)
|
|
190
|
-
} else if (
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
193
|
+
} else if (encodePayloadBody._tag === "Some") {
|
|
194
|
+
if (HttpMethod.hasBody(endpoint.method)) {
|
|
195
|
+
const body = (yield* encodePayloadBody.value(request.payload)) as HttpBody.HttpBody
|
|
196
|
+
httpRequest = HttpClientRequest.setBody(httpRequest, body)
|
|
197
|
+
} else {
|
|
198
|
+
const urlParams = (yield* encodePayloadBody.value(request.payload)) as Record<string, string>
|
|
199
|
+
httpRequest = HttpClientRequest.setUrlParams(httpRequest, urlParams)
|
|
200
|
+
}
|
|
195
201
|
}
|
|
196
202
|
if (encodeHeaders._tag === "Some") {
|
|
197
203
|
httpRequest = HttpClientRequest.setHeaders(
|
|
@@ -421,3 +427,58 @@ const statusCodeError = (response: HttpClientResponse.HttpClientResponse) =>
|
|
|
421
427
|
)
|
|
422
428
|
|
|
423
429
|
const responseAsVoid = (_response: HttpClientResponse.HttpClientResponse) => Effect.void
|
|
430
|
+
|
|
431
|
+
const HttpBodyFromSelf = Schema.declare(HttpBody.isHttpBody)
|
|
432
|
+
|
|
433
|
+
const payloadSchemaBody = (schema: Schema.Schema.All): Schema.Schema<any, HttpBody.HttpBody> => {
|
|
434
|
+
const members = schema.ast._tag === "Union" ? schema.ast.types : [schema.ast]
|
|
435
|
+
return Schema.Union(...members.map(bodyFromPayload)) as any
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
const bodyFromPayloadCache = globalValue(
|
|
439
|
+
"@effect/platform/HttpApiClient/bodyFromPayloadCache",
|
|
440
|
+
() => new WeakMap<AST.AST, Schema.Schema.Any>()
|
|
441
|
+
)
|
|
442
|
+
|
|
443
|
+
const bodyFromPayload = (ast: AST.AST) => {
|
|
444
|
+
if (bodyFromPayloadCache.has(ast)) {
|
|
445
|
+
return bodyFromPayloadCache.get(ast)!
|
|
446
|
+
}
|
|
447
|
+
const schema = Schema.make(ast)
|
|
448
|
+
const encoding = HttpApiSchema.getEncoding(ast)
|
|
449
|
+
const transform = Schema.transformOrFail(
|
|
450
|
+
HttpBodyFromSelf,
|
|
451
|
+
schema,
|
|
452
|
+
{
|
|
453
|
+
decode(fromA, _, ast) {
|
|
454
|
+
return ParseResult.fail(new ParseResult.Forbidden(ast, fromA, "encode only schema"))
|
|
455
|
+
},
|
|
456
|
+
encode(toI, _, ast) {
|
|
457
|
+
switch (encoding.kind) {
|
|
458
|
+
case "Json": {
|
|
459
|
+
return HttpBody.json(toI).pipe(
|
|
460
|
+
ParseResult.mapError((error) => new ParseResult.Type(ast, toI, `Could not encode as JSON: ${error}`))
|
|
461
|
+
)
|
|
462
|
+
}
|
|
463
|
+
case "Text": {
|
|
464
|
+
if (typeof toI !== "string") {
|
|
465
|
+
return ParseResult.fail(new ParseResult.Type(ast, toI, "Expected a string"))
|
|
466
|
+
}
|
|
467
|
+
return ParseResult.succeed(HttpBody.text(toI))
|
|
468
|
+
}
|
|
469
|
+
case "UrlParams": {
|
|
470
|
+
return ParseResult.succeed(HttpBody.urlParams(UrlParams.fromInput(toI as any)))
|
|
471
|
+
}
|
|
472
|
+
case "Uint8Array": {
|
|
473
|
+
if (!(toI instanceof Uint8Array)) {
|
|
474
|
+
return ParseResult.fail(new ParseResult.Type(ast, toI, "Expected a Uint8Array"))
|
|
475
|
+
}
|
|
476
|
+
return ParseResult.succeed(HttpBody.uint8Array(toI))
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
)
|
|
482
|
+
bodyFromPayloadCache.set(ast, transform)
|
|
483
|
+
return transform
|
|
484
|
+
}
|
package/src/HttpApiEndpoint.ts
CHANGED
|
@@ -406,7 +406,8 @@ export declare namespace HttpApiEndpoint {
|
|
|
406
406
|
& ([UrlParams] extends [never] ? {} : { readonly urlParams: UrlParams })
|
|
407
407
|
& ([Headers] extends [never] ? {} : { readonly headers: Headers })
|
|
408
408
|
& ([Payload] extends [never] ? {}
|
|
409
|
-
:
|
|
409
|
+
: Payload extends infer P ?
|
|
410
|
+
P extends Brand<HttpApiSchema.MultipartTypeId> ? { readonly payload: FormData } : { readonly payload: P }
|
|
410
411
|
: { readonly payload: Payload })
|
|
411
412
|
) extends infer Req ? keyof Req extends never ? (void | { readonly withResponse?: WithResponse }) :
|
|
412
413
|
Req & { readonly withResponse?: WithResponse } :
|
package/src/HttpClient.ts
CHANGED
|
@@ -12,6 +12,7 @@ import type * as Predicate from "effect/Predicate"
|
|
|
12
12
|
import type { Ref } from "effect/Ref"
|
|
13
13
|
import type * as Schedule from "effect/Schedule"
|
|
14
14
|
import type * as Scope from "effect/Scope"
|
|
15
|
+
import type { NoInfer } from "effect/Types"
|
|
15
16
|
import type { Cookies } from "./Cookies.js"
|
|
16
17
|
import type * as Error from "./HttpClientError.js"
|
|
17
18
|
import type * as ClientRequest from "./HttpClientRequest.js"
|
|
@@ -636,34 +637,47 @@ export const retry: {
|
|
|
636
637
|
} = internal.retry
|
|
637
638
|
|
|
638
639
|
/**
|
|
639
|
-
* Retries common transient errors, such as rate limiting or network issues.
|
|
640
|
+
* Retries common transient errors, such as rate limiting, timeouts or network issues.
|
|
641
|
+
*
|
|
642
|
+
* Specifying a `while` predicate allows you to consider other errors as
|
|
643
|
+
* transient.
|
|
640
644
|
*
|
|
641
645
|
* @since 1.0.0
|
|
642
646
|
* @category error handling
|
|
643
647
|
*/
|
|
644
648
|
export const retryTransient: {
|
|
645
649
|
/**
|
|
646
|
-
* Retries common transient errors, such as rate limiting or network issues.
|
|
650
|
+
* Retries common transient errors, such as rate limiting, timeouts or network issues.
|
|
651
|
+
*
|
|
652
|
+
* Specifying a `while` predicate allows you to consider other errors as
|
|
653
|
+
* transient.
|
|
647
654
|
*
|
|
648
655
|
* @since 1.0.0
|
|
649
656
|
* @category error handling
|
|
650
657
|
*/
|
|
651
658
|
<B, E, R1 = never>(
|
|
652
|
-
options:
|
|
653
|
-
|
|
654
|
-
|
|
659
|
+
options: {
|
|
660
|
+
readonly while?: Predicate.Predicate<NoInfer<E>>
|
|
661
|
+
readonly schedule?: Schedule.Schedule<B, NoInfer<E>, R1>
|
|
662
|
+
readonly times?: number
|
|
663
|
+
} | Schedule.Schedule<B, NoInfer<E>, R1>
|
|
655
664
|
): <R>(self: HttpClient<E, R>) => HttpClient<E, R1 | R>
|
|
656
665
|
/**
|
|
657
|
-
* Retries common transient errors, such as rate limiting or network issues.
|
|
666
|
+
* Retries common transient errors, such as rate limiting, timeouts or network issues.
|
|
667
|
+
*
|
|
668
|
+
* Specifying a `while` predicate allows you to consider other errors as
|
|
669
|
+
* transient.
|
|
658
670
|
*
|
|
659
671
|
* @since 1.0.0
|
|
660
672
|
* @category error handling
|
|
661
673
|
*/
|
|
662
674
|
<E, R, B, R1 = never>(
|
|
663
675
|
self: HttpClient<E, R>,
|
|
664
|
-
options:
|
|
665
|
-
|
|
666
|
-
|
|
676
|
+
options: {
|
|
677
|
+
readonly while?: Predicate.Predicate<NoInfer<E>>
|
|
678
|
+
readonly schedule?: Schedule.Schedule<B, NoInfer<E>, R1>
|
|
679
|
+
readonly times?: number
|
|
680
|
+
} | Schedule.Schedule<B, NoInfer<E>, R1>
|
|
667
681
|
): HttpClient<E, R1 | R>
|
|
668
682
|
} = internal.retryTransient
|
|
669
683
|
|
package/src/OpenApi.ts
CHANGED
|
@@ -231,7 +231,7 @@ export const fromApi = <A extends HttpApi.HttpApi.Any>(self: A): OpenAPISpec =>
|
|
|
231
231
|
})
|
|
232
232
|
spec.tags!.push(tag)
|
|
233
233
|
},
|
|
234
|
-
onEndpoint({ endpoint, errors, group, middleware, successes }) {
|
|
234
|
+
onEndpoint({ endpoint, errors, group, middleware, payloads, successes }) {
|
|
235
235
|
const path = endpoint.path.replace(/:(\w+)[^/]*/g, "{$1}")
|
|
236
236
|
const method = endpoint.method.toLowerCase() as OpenAPISpecMethodName
|
|
237
237
|
const op: DeepMutable<OpenAPISpecOperation> = {
|
|
@@ -266,19 +266,15 @@ export const fromApi = <A extends HttpApi.HttpApi.Any>(self: A): OpenAPISpec =>
|
|
|
266
266
|
op.security!.push({ [name]: [] })
|
|
267
267
|
}
|
|
268
268
|
})
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
[HttpApiSchema.getMultipart(schema.ast) ? "multipart/form-data" : "application/json"]: {
|
|
275
|
-
schema: makeJsonSchemaOrRef(schema)
|
|
276
|
-
}
|
|
277
|
-
},
|
|
278
|
-
required: true
|
|
269
|
+
if (payloads.size > 0) {
|
|
270
|
+
const content: Mutable<OpenApiSpecContent> = {}
|
|
271
|
+
payloads.forEach(({ ast }, contentType) => {
|
|
272
|
+
content[contentType as OpenApiSpecContentType] = {
|
|
273
|
+
schema: makeJsonSchemaOrRef(Schema.make(ast))
|
|
279
274
|
}
|
|
280
275
|
})
|
|
281
|
-
|
|
276
|
+
op.requestBody = { content, required: true }
|
|
277
|
+
}
|
|
282
278
|
for (const [status, ast] of successes) {
|
|
283
279
|
if (op.responses![status]) continue
|
|
284
280
|
op.responses![status] = {
|
package/src/PlatformLogger.ts
CHANGED
|
@@ -14,6 +14,7 @@ import * as internal from "./internal/platformLogger.js"
|
|
|
14
14
|
*
|
|
15
15
|
* @since 1.0.0
|
|
16
16
|
* @example
|
|
17
|
+
* ```ts
|
|
17
18
|
* import { PlatformLogger } from "@effect/platform"
|
|
18
19
|
* import { NodeFileSystem, NodeRuntime } from "@effect/platform-node"
|
|
19
20
|
* import { Effect, Layer, Logger } from "effect"
|
|
@@ -31,6 +32,7 @@ import * as internal from "./internal/platformLogger.js"
|
|
|
31
32
|
* Effect.provide(LoggerLive),
|
|
32
33
|
* NodeRuntime.runMain
|
|
33
34
|
* )
|
|
35
|
+
* ```
|
|
34
36
|
*/
|
|
35
37
|
export const toFile: {
|
|
36
38
|
/**
|
|
@@ -38,6 +40,7 @@ export const toFile: {
|
|
|
38
40
|
*
|
|
39
41
|
* @since 1.0.0
|
|
40
42
|
* @example
|
|
43
|
+
* ```ts
|
|
41
44
|
* import { PlatformLogger } from "@effect/platform"
|
|
42
45
|
* import { NodeFileSystem, NodeRuntime } from "@effect/platform-node"
|
|
43
46
|
* import { Effect, Layer, Logger } from "effect"
|
|
@@ -55,6 +58,7 @@ export const toFile: {
|
|
|
55
58
|
* Effect.provide(LoggerLive),
|
|
56
59
|
* NodeRuntime.runMain
|
|
57
60
|
* )
|
|
61
|
+
* ```
|
|
58
62
|
*/
|
|
59
63
|
(
|
|
60
64
|
path: string,
|
|
@@ -73,6 +77,7 @@ export const toFile: {
|
|
|
73
77
|
*
|
|
74
78
|
* @since 1.0.0
|
|
75
79
|
* @example
|
|
80
|
+
* ```ts
|
|
76
81
|
* import { PlatformLogger } from "@effect/platform"
|
|
77
82
|
* import { NodeFileSystem, NodeRuntime } from "@effect/platform-node"
|
|
78
83
|
* import { Effect, Layer, Logger } from "effect"
|
|
@@ -90,6 +95,7 @@ export const toFile: {
|
|
|
90
95
|
* Effect.provide(LoggerLive),
|
|
91
96
|
* NodeRuntime.runMain
|
|
92
97
|
* )
|
|
98
|
+
* ```
|
|
93
99
|
*/
|
|
94
100
|
<Message>(
|
|
95
101
|
self: Logger.Logger<Message, string>,
|
package/src/UrlParams.ts
CHANGED
|
@@ -274,6 +274,40 @@ const baseUrl = (): string | undefined => {
|
|
|
274
274
|
return undefined
|
|
275
275
|
}
|
|
276
276
|
|
|
277
|
+
/**
|
|
278
|
+
* Builds a `Record` containing all the key-value pairs in the given `UrlParams`
|
|
279
|
+
* as `string` (if only one value for a key) or a `NonEmptyArray<string>`
|
|
280
|
+
* (when more than one value for a key)
|
|
281
|
+
*
|
|
282
|
+
* @example
|
|
283
|
+
* import { UrlParams } from "@effect/platform"
|
|
284
|
+
*
|
|
285
|
+
* const urlParams = UrlParams.fromInput({ a: 1, b: true, c: "string", e: [1, 2, 3] })
|
|
286
|
+
* const result = UrlParams.toRecord(urlParams)
|
|
287
|
+
*
|
|
288
|
+
* assert.deepStrictEqual(
|
|
289
|
+
* result,
|
|
290
|
+
* { "a": "1", "b": "true", "c": "string", "e": ["1", "2", "3"] }
|
|
291
|
+
* )
|
|
292
|
+
*
|
|
293
|
+
* @since 1.0.0
|
|
294
|
+
* @category conversions
|
|
295
|
+
*/
|
|
296
|
+
export const toRecord = (self: UrlParams): Record<string, string | Arr.NonEmptyArray<string>> => {
|
|
297
|
+
const out: Record<string, string | Arr.NonEmptyArray<string>> = {}
|
|
298
|
+
for (const [k, value] of self) {
|
|
299
|
+
const curr = out[k]
|
|
300
|
+
if (curr === undefined) {
|
|
301
|
+
out[k] = value
|
|
302
|
+
} else if (typeof curr === "string") {
|
|
303
|
+
out[k] = [curr, value]
|
|
304
|
+
} else {
|
|
305
|
+
curr.push(value)
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return out
|
|
309
|
+
}
|
|
310
|
+
|
|
277
311
|
/**
|
|
278
312
|
* @since 1.0.0
|
|
279
313
|
* @category schema
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as Cause from "effect/Cause"
|
|
1
2
|
import * as Context from "effect/Context"
|
|
2
3
|
import * as Effect from "effect/Effect"
|
|
3
4
|
import type * as Fiber from "effect/Fiber"
|
|
@@ -11,6 +12,7 @@ import * as Predicate from "effect/Predicate"
|
|
|
11
12
|
import * as Ref from "effect/Ref"
|
|
12
13
|
import * as Schedule from "effect/Schedule"
|
|
13
14
|
import * as Scope from "effect/Scope"
|
|
15
|
+
import type { NoInfer } from "effect/Types"
|
|
14
16
|
import * as Cookies from "../Cookies.js"
|
|
15
17
|
import * as Headers from "../Headers.js"
|
|
16
18
|
import type * as Client from "../HttpClient.js"
|
|
@@ -565,6 +567,7 @@ export const retry: {
|
|
|
565
567
|
export const retryTransient: {
|
|
566
568
|
<B, E, R1 = never>(
|
|
567
569
|
options: {
|
|
570
|
+
readonly while?: Predicate.Predicate<NoInfer<E>>
|
|
568
571
|
readonly schedule?: Schedule.Schedule<B, NoInfer<E>, R1>
|
|
569
572
|
readonly times?: number
|
|
570
573
|
} | Schedule.Schedule<B, NoInfer<E>, R1>
|
|
@@ -572,6 +575,7 @@ export const retryTransient: {
|
|
|
572
575
|
<E, R, B, R1 = never>(
|
|
573
576
|
self: Client.HttpClient<E, R>,
|
|
574
577
|
options: {
|
|
578
|
+
readonly while?: Predicate.Predicate<NoInfer<E>>
|
|
575
579
|
readonly schedule?: Schedule.Schedule<B, NoInfer<E>, R1>
|
|
576
580
|
readonly times?: number
|
|
577
581
|
} | Schedule.Schedule<B, NoInfer<E>, R1>
|
|
@@ -581,6 +585,7 @@ export const retryTransient: {
|
|
|
581
585
|
<E extends E0, E0, R, B, R1 = never>(
|
|
582
586
|
self: Client.HttpClient<E, R>,
|
|
583
587
|
options: {
|
|
588
|
+
readonly while?: Predicate.Predicate<NoInfer<E>>
|
|
584
589
|
readonly schedule?: Schedule.Schedule<B, NoInfer<E>, R1>
|
|
585
590
|
readonly times?: number
|
|
586
591
|
} | Schedule.Schedule<B, NoInfer<E>, R1>
|
|
@@ -588,16 +593,23 @@ export const retryTransient: {
|
|
|
588
593
|
transformResponse(
|
|
589
594
|
self,
|
|
590
595
|
Effect.retry({
|
|
591
|
-
while:
|
|
592
|
-
|
|
593
|
-
(
|
|
594
|
-
(error._tag === "ResponseError" && error.response.status >= 429)),
|
|
596
|
+
while: Schedule.ScheduleTypeId in options || options.while === undefined
|
|
597
|
+
? isTransientError
|
|
598
|
+
: Predicate.or(isTransientError, options.while),
|
|
595
599
|
schedule: Schedule.ScheduleTypeId in options ? options : options.schedule,
|
|
596
600
|
times: Schedule.ScheduleTypeId in options ? undefined : options.times
|
|
597
601
|
})
|
|
598
602
|
)
|
|
599
603
|
)
|
|
600
604
|
|
|
605
|
+
const isTransientError = (error: unknown) =>
|
|
606
|
+
Predicate.hasProperty(error, Cause.TimeoutExceptionTypeId) || isTransientHttpError(error)
|
|
607
|
+
|
|
608
|
+
const isTransientHttpError = (error: unknown) =>
|
|
609
|
+
Error.isHttpClientError(error) &&
|
|
610
|
+
((error._tag === "RequestError" && error.reason === "Transport") ||
|
|
611
|
+
(error._tag === "ResponseError" && error.response.status >= 429))
|
|
612
|
+
|
|
601
613
|
/** @internal */
|
|
602
614
|
export const tap = dual<
|
|
603
615
|
<_, E2, R2>(
|