@effect/platform 0.38.0 → 0.39.0
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/Http/Multipart/package.json +6 -0
- package/dist/cjs/Http/Body.js +6 -1
- package/dist/cjs/Http/Body.js.map +1 -1
- package/dist/cjs/Http/IncomingMessage.js +3 -1
- package/dist/cjs/Http/IncomingMessage.js.map +1 -1
- package/dist/cjs/Http/{FormData.js → Multipart.js} +5 -5
- package/dist/cjs/Http/Multipart.js.map +1 -0
- package/dist/cjs/Http/ServerRequest.js +9 -4
- package/dist/cjs/Http/ServerRequest.js.map +1 -1
- package/dist/cjs/Http/UrlParams.js +32 -5
- package/dist/cjs/Http/UrlParams.js.map +1 -1
- package/dist/cjs/HttpServer.js +3 -3
- package/dist/cjs/HttpServer.js.map +1 -1
- package/dist/cjs/internal/http/body.js +9 -4
- package/dist/cjs/internal/http/body.js.map +1 -1
- package/dist/cjs/internal/http/client.js +4 -5
- package/dist/cjs/internal/http/client.js.map +1 -1
- package/dist/cjs/internal/http/{formData.js → multipart.js} +42 -35
- package/dist/cjs/internal/http/multipart.js.map +1 -0
- package/dist/cjs/internal/http/serverRequest.js +43 -24
- package/dist/cjs/internal/http/serverRequest.js.map +1 -1
- package/dist/cjs/internal/http/serverResponse.js.map +1 -1
- package/dist/cjs/internal/keyValueStore.js +1 -1
- package/dist/cjs/internal/keyValueStore.js.map +1 -1
- package/dist/dts/Http/Body.d.ts +6 -0
- package/dist/dts/Http/Body.d.ts.map +1 -1
- package/dist/dts/Http/{FormData.d.ts → Multipart.d.ts} +11 -11
- package/dist/dts/Http/Multipart.d.ts.map +1 -0
- package/dist/dts/Http/ServerRequest.d.ts +11 -6
- package/dist/dts/Http/ServerRequest.d.ts.map +1 -1
- package/dist/dts/Http/UrlParams.d.ts +35 -0
- package/dist/dts/Http/UrlParams.d.ts.map +1 -1
- package/dist/dts/HttpServer.d.ts +8 -8
- package/dist/dts/HttpServer.d.ts.map +1 -1
- package/dist/dts/internal/http/multipart.d.ts +2 -0
- package/dist/dts/internal/http/multipart.d.ts.map +1 -0
- package/dist/esm/Http/Body.js +5 -0
- package/dist/esm/Http/Body.js.map +1 -1
- package/dist/esm/Http/IncomingMessage.js +3 -1
- package/dist/esm/Http/IncomingMessage.js.map +1 -1
- package/dist/esm/Http/{FormData.js → Multipart.js} +4 -4
- package/dist/esm/Http/Multipart.js.map +1 -0
- package/dist/esm/Http/ServerRequest.js +8 -3
- package/dist/esm/Http/ServerRequest.js.map +1 -1
- package/dist/esm/Http/UrlParams.js +30 -3
- package/dist/esm/Http/UrlParams.js.map +1 -1
- package/dist/esm/HttpServer.js +8 -8
- package/dist/esm/HttpServer.js.map +1 -1
- package/dist/esm/internal/http/body.js +6 -2
- package/dist/esm/internal/http/body.js.map +1 -1
- package/dist/esm/internal/http/client.js +4 -5
- package/dist/esm/internal/http/client.js.map +1 -1
- package/dist/esm/internal/http/{formData.js → multipart.js} +39 -32
- package/dist/esm/internal/http/multipart.js.map +1 -0
- package/dist/esm/internal/http/serverRequest.js +38 -20
- package/dist/esm/internal/http/serverRequest.js.map +1 -1
- package/dist/esm/internal/http/serverResponse.js.map +1 -1
- package/dist/esm/internal/keyValueStore.js +1 -1
- package/dist/esm/internal/keyValueStore.js.map +1 -1
- package/package.json +12 -12
- package/src/Http/Body.ts +7 -0
- package/src/Http/IncomingMessage.ts +1 -1
- package/src/Http/{FormData.ts → Multipart.ts} +17 -18
- package/src/Http/ServerRequest.ts +26 -14
- package/src/Http/UrlParams.ts +79 -0
- package/src/HttpServer.ts +8 -8
- package/src/internal/http/body.ts +9 -3
- package/src/internal/http/client.ts +11 -15
- package/src/internal/http/{formData.ts → multipart.ts} +79 -80
- package/src/internal/http/serverRequest.ts +66 -37
- package/src/internal/http/serverResponse.ts +4 -1
- package/src/internal/keyValueStore.ts +1 -1
- package/Http/FormData/package.json +0 -6
- package/dist/cjs/Http/FormData.js.map +0 -1
- package/dist/cjs/internal/http/formData.js.map +0 -1
- package/dist/dts/Http/FormData.d.ts.map +0 -1
- package/dist/dts/internal/http/formData.d.ts +0 -2
- package/dist/dts/internal/http/formData.d.ts.map +0 -1
- package/dist/esm/Http/FormData.js.map +0 -1
- package/dist/esm/internal/http/formData.js.map +0 -1
|
@@ -10,10 +10,10 @@ import type * as Stream from "effect/Stream"
|
|
|
10
10
|
import type * as FileSystem from "../FileSystem.js"
|
|
11
11
|
import * as internal from "../internal/http/serverRequest.js"
|
|
12
12
|
import type * as Path from "../Path.js"
|
|
13
|
-
import type * as FormData from "./FormData.js"
|
|
14
13
|
import type * as Headers from "./Headers.js"
|
|
15
14
|
import type * as IncomingMessage from "./IncomingMessage.js"
|
|
16
15
|
import type { Method } from "./Method.js"
|
|
16
|
+
import type * as Multipart from "./Multipart.js"
|
|
17
17
|
import type * as Error from "./ServerError.js"
|
|
18
18
|
|
|
19
19
|
export {
|
|
@@ -47,12 +47,12 @@ export interface ServerRequest extends IncomingMessage.IncomingMessage<Error.Req
|
|
|
47
47
|
readonly originalUrl: string
|
|
48
48
|
readonly method: Method
|
|
49
49
|
|
|
50
|
-
readonly
|
|
50
|
+
readonly multipart: Effect.Effect<
|
|
51
51
|
Scope.Scope | FileSystem.FileSystem | Path.Path,
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
Multipart.MultipartError,
|
|
53
|
+
Multipart.Persisted
|
|
54
54
|
>
|
|
55
|
-
readonly
|
|
55
|
+
readonly multipartStream: Stream.Stream<never, Multipart.MultipartError, Multipart.Part>
|
|
56
56
|
|
|
57
57
|
readonly modify: (
|
|
58
58
|
options: {
|
|
@@ -73,11 +73,11 @@ export const ServerRequest: Context.Tag<ServerRequest, ServerRequest> = internal
|
|
|
73
73
|
* @since 1.0.0
|
|
74
74
|
* @category accessors
|
|
75
75
|
*/
|
|
76
|
-
export const
|
|
76
|
+
export const persistedMultipart: Effect.Effect<
|
|
77
77
|
Scope.Scope | FileSystem.FileSystem | Path.Path | ServerRequest,
|
|
78
|
-
|
|
78
|
+
Multipart.MultipartError,
|
|
79
79
|
unknown
|
|
80
|
-
> = internal.
|
|
80
|
+
> = internal.multipartPersisted
|
|
81
81
|
|
|
82
82
|
/**
|
|
83
83
|
* @since 1.0.0
|
|
@@ -95,6 +95,18 @@ export const schemaBodyJson: <I, A>(
|
|
|
95
95
|
schema: Schema.Schema<I, A>
|
|
96
96
|
) => Effect.Effect<ServerRequest, Error.RequestError | ParseResult.ParseError, A> = internal.schemaBodyJson
|
|
97
97
|
|
|
98
|
+
/**
|
|
99
|
+
* @since 1.0.0
|
|
100
|
+
* @category schema
|
|
101
|
+
*/
|
|
102
|
+
export const schemaBodyForm: <I extends Multipart.Persisted, A>(
|
|
103
|
+
schema: Schema.Schema<I, A>
|
|
104
|
+
) => Effect.Effect<
|
|
105
|
+
ServerRequest | Scope.Scope | FileSystem.FileSystem | Path.Path,
|
|
106
|
+
Multipart.MultipartError | Error.RequestError | ParseResult.ParseError,
|
|
107
|
+
A
|
|
108
|
+
> = internal.schemaBodyForm
|
|
109
|
+
|
|
98
110
|
/**
|
|
99
111
|
* @since 1.0.0
|
|
100
112
|
* @category schema
|
|
@@ -107,27 +119,27 @@ export const schemaBodyUrlParams: <I extends Readonly<Record<string, string>>, A
|
|
|
107
119
|
* @since 1.0.0
|
|
108
120
|
* @category schema
|
|
109
121
|
*/
|
|
110
|
-
export const
|
|
122
|
+
export const schemaBodyMultipart: <I extends Multipart.Persisted, A>(
|
|
111
123
|
schema: Schema.Schema<I, A>
|
|
112
124
|
) => Effect.Effect<
|
|
113
125
|
ServerRequest | Scope.Scope | FileSystem.FileSystem | Path.Path,
|
|
114
|
-
|
|
126
|
+
Multipart.MultipartError | ParseResult.ParseError,
|
|
115
127
|
A
|
|
116
|
-
> = internal.
|
|
128
|
+
> = internal.schemaBodyMultipart
|
|
117
129
|
|
|
118
130
|
/**
|
|
119
131
|
* @since 1.0.0
|
|
120
132
|
* @category schema
|
|
121
133
|
*/
|
|
122
|
-
export const
|
|
134
|
+
export const schemaBodyFormJson: <I, A>(
|
|
123
135
|
schema: Schema.Schema<I, A>
|
|
124
136
|
) => (
|
|
125
137
|
field: string
|
|
126
138
|
) => Effect.Effect<
|
|
127
139
|
ServerRequest | Scope.Scope | FileSystem.FileSystem | Path.Path,
|
|
128
|
-
Error.RequestError |
|
|
140
|
+
Error.RequestError | ParseResult.ParseError,
|
|
129
141
|
A
|
|
130
|
-
> = internal.
|
|
142
|
+
> = internal.schemaBodyFormJson
|
|
131
143
|
|
|
132
144
|
/**
|
|
133
145
|
* @since 1.0.0
|
package/src/Http/UrlParams.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @since 1.0.0
|
|
3
3
|
*/
|
|
4
|
+
import type * as ParseResult from "@effect/schema/ParseResult"
|
|
5
|
+
import * as Schema from "@effect/schema/Schema"
|
|
4
6
|
import * as Effect from "effect/Effect"
|
|
5
7
|
import { dual } from "effect/Function"
|
|
8
|
+
import * as Option from "effect/Option"
|
|
6
9
|
import * as ReadonlyArray from "effect/ReadonlyArray"
|
|
7
10
|
|
|
8
11
|
/**
|
|
@@ -34,6 +37,62 @@ export const fromInput = (input: Input): UrlParams => {
|
|
|
34
37
|
*/
|
|
35
38
|
export const empty: UrlParams = []
|
|
36
39
|
|
|
40
|
+
/**
|
|
41
|
+
* @since 1.0.0
|
|
42
|
+
* @category combinators
|
|
43
|
+
*/
|
|
44
|
+
export const getAll: {
|
|
45
|
+
(key: string): (self: UrlParams) => ReadonlyArray<string>
|
|
46
|
+
(self: UrlParams, key: string): ReadonlyArray<string>
|
|
47
|
+
} = dual<
|
|
48
|
+
(key: string) => (self: UrlParams) => ReadonlyArray<string>,
|
|
49
|
+
(self: UrlParams, key: string) => ReadonlyArray<string>
|
|
50
|
+
>(2, (self, key) =>
|
|
51
|
+
ReadonlyArray.reduce(self, [] as Array<string>, (acc, [k, value]) => {
|
|
52
|
+
if (k === key) {
|
|
53
|
+
acc.push(value)
|
|
54
|
+
}
|
|
55
|
+
return acc
|
|
56
|
+
}))
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @since 1.0.0
|
|
60
|
+
* @category combinators
|
|
61
|
+
*/
|
|
62
|
+
export const getFirst: {
|
|
63
|
+
(key: string): (self: UrlParams) => Option.Option<string>
|
|
64
|
+
(self: UrlParams, key: string): Option.Option<string>
|
|
65
|
+
} = dual<
|
|
66
|
+
(key: string) => (self: UrlParams) => Option.Option<string>,
|
|
67
|
+
(self: UrlParams, key: string) => Option.Option<string>
|
|
68
|
+
>(2, (self, key) =>
|
|
69
|
+
Option.map(
|
|
70
|
+
ReadonlyArray.findFirst(
|
|
71
|
+
self,
|
|
72
|
+
([k]) => k === key
|
|
73
|
+
),
|
|
74
|
+
([, value]) => value
|
|
75
|
+
))
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* @since 1.0.0
|
|
79
|
+
* @category combinators
|
|
80
|
+
*/
|
|
81
|
+
export const getLast: {
|
|
82
|
+
(key: string): (self: UrlParams) => Option.Option<string>
|
|
83
|
+
(self: UrlParams, key: string): Option.Option<string>
|
|
84
|
+
} = dual<
|
|
85
|
+
(key: string) => (self: UrlParams) => Option.Option<string>,
|
|
86
|
+
(self: UrlParams, key: string) => Option.Option<string>
|
|
87
|
+
>(2, (self, key) =>
|
|
88
|
+
Option.map(
|
|
89
|
+
ReadonlyArray.findLast(
|
|
90
|
+
self,
|
|
91
|
+
([k]) => k === key
|
|
92
|
+
),
|
|
93
|
+
([, value]) => value
|
|
94
|
+
))
|
|
95
|
+
|
|
37
96
|
/**
|
|
38
97
|
* @since 1.0.0
|
|
39
98
|
* @category combinators
|
|
@@ -143,3 +202,23 @@ const baseUrl = (): string | undefined => {
|
|
|
143
202
|
}
|
|
144
203
|
return undefined
|
|
145
204
|
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* @since 1.0.0
|
|
208
|
+
* @category schema
|
|
209
|
+
*/
|
|
210
|
+
export const schemaJson = <I, A>(schema: Schema.Schema<I, A>): {
|
|
211
|
+
(
|
|
212
|
+
field: string
|
|
213
|
+
): (self: UrlParams) => Effect.Effect<never, ParseResult.ParseError, A>
|
|
214
|
+
(
|
|
215
|
+
self: UrlParams,
|
|
216
|
+
field: string
|
|
217
|
+
): Effect.Effect<never, ParseResult.ParseError, A>
|
|
218
|
+
} => {
|
|
219
|
+
const parse = Schema.parse(Schema.parseJson(schema))
|
|
220
|
+
return dual<
|
|
221
|
+
(field: string) => (self: UrlParams) => Effect.Effect<never, ParseResult.ParseError, A>,
|
|
222
|
+
(self: UrlParams, field: string) => Effect.Effect<never, ParseResult.ParseError, A>
|
|
223
|
+
>(2, (self, field) => parse(Option.getOrElse(getLast(self, field), () => "")))
|
|
224
|
+
}
|
package/src/HttpServer.ts
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import * as app from "./Http/App.js"
|
|
5
5
|
import * as body from "./Http/Body.js"
|
|
6
|
-
import * as formData from "./Http/FormData.js"
|
|
7
6
|
import * as headers from "./Http/Headers.js"
|
|
8
7
|
import * as middleware from "./Http/Middleware.js"
|
|
8
|
+
import * as multipart from "./Http/Multipart.js"
|
|
9
9
|
import * as router from "./Http/Router.js"
|
|
10
10
|
import * as error from "./Http/ServerError.js"
|
|
11
11
|
import * as request from "./Http/ServerRequest.js"
|
|
@@ -34,13 +34,6 @@ export {
|
|
|
34
34
|
* - Module: `@effect/platform/Http/ServerError`
|
|
35
35
|
*/
|
|
36
36
|
error,
|
|
37
|
-
/**
|
|
38
|
-
* @since 1.0.0
|
|
39
|
-
*
|
|
40
|
-
* - Docs: [Http/FormData](https://effect-ts.github.io/platform/platform/Http/FormData.html)
|
|
41
|
-
* - Module: `@effect/platform/Http/FormData`
|
|
42
|
-
*/
|
|
43
|
-
formData,
|
|
44
37
|
/**
|
|
45
38
|
* @since 1.0.0
|
|
46
39
|
*
|
|
@@ -55,6 +48,13 @@ export {
|
|
|
55
48
|
* - Module: `@effect/platform/Http/Middleware`
|
|
56
49
|
*/
|
|
57
50
|
middleware,
|
|
51
|
+
/**
|
|
52
|
+
* @since 1.0.0
|
|
53
|
+
*
|
|
54
|
+
* - Docs: [Http/Multipart](https://effect-ts.github.io/platform/platform/Http/Multipart.html)
|
|
55
|
+
* - Module: `@effect/platform/Http/Multipart`
|
|
56
|
+
*/
|
|
57
|
+
multipart,
|
|
58
58
|
/**
|
|
59
59
|
* @since 1.0.0
|
|
60
60
|
*
|
|
@@ -6,6 +6,7 @@ import * as Stream_ from "effect/Stream"
|
|
|
6
6
|
import type * as PlatformError from "../../Error.js"
|
|
7
7
|
import * as FileSystem from "../../FileSystem.js"
|
|
8
8
|
import type * as Body from "../../Http/Body.js"
|
|
9
|
+
import * as UrlParams from "../../Http/UrlParams.js"
|
|
9
10
|
|
|
10
11
|
/** @internal */
|
|
11
12
|
export const TypeId: Body.TypeId = Symbol.for(
|
|
@@ -68,13 +69,14 @@ class Uint8ArrayImpl implements Body.Uint8Array {
|
|
|
68
69
|
export const uint8Array = (body: Uint8Array, contentType?: string): Body.Uint8Array =>
|
|
69
70
|
new Uint8ArrayImpl(body, contentType ?? "application/octet-stream")
|
|
70
71
|
|
|
72
|
+
const encoder = new TextEncoder()
|
|
73
|
+
|
|
71
74
|
/** @internal */
|
|
72
75
|
export const text = (body: string, contentType?: string): Body.Uint8Array =>
|
|
73
|
-
uint8Array(
|
|
76
|
+
uint8Array(encoder.encode(body), contentType ?? "text/plain")
|
|
74
77
|
|
|
75
78
|
/** @internal */
|
|
76
|
-
export const unsafeJson = (body: unknown): Body.Uint8Array =>
|
|
77
|
-
uint8Array(new TextEncoder().encode(JSON.stringify(body)), "application/json")
|
|
79
|
+
export const unsafeJson = (body: unknown): Body.Uint8Array => text(JSON.stringify(body), "application/json")
|
|
78
80
|
|
|
79
81
|
/** @internal */
|
|
80
82
|
export const json = (body: unknown): Effect.Effect<never, Body.BodyError, Body.Uint8Array> =>
|
|
@@ -83,6 +85,10 @@ export const json = (body: unknown): Effect.Effect<never, Body.BodyError, Body.U
|
|
|
83
85
|
catch: (error) => BodyError({ _tag: "JsonError", error })
|
|
84
86
|
})
|
|
85
87
|
|
|
88
|
+
/** @internal */
|
|
89
|
+
export const urlParams = (urlParams: UrlParams.UrlParams): Body.Uint8Array =>
|
|
90
|
+
text(UrlParams.toString(urlParams), "application/x-www-form-urlencoded")
|
|
91
|
+
|
|
86
92
|
/** @internal */
|
|
87
93
|
export const jsonSchema = <I, A>(schema: Schema.Schema<I, A>) => {
|
|
88
94
|
const encode = Schema.encode(schema)
|
|
@@ -4,7 +4,6 @@ import * as Context from "effect/Context"
|
|
|
4
4
|
import * as Effect from "effect/Effect"
|
|
5
5
|
import { dual } from "effect/Function"
|
|
6
6
|
import * as Layer from "effect/Layer"
|
|
7
|
-
import * as Option from "effect/Option"
|
|
8
7
|
import { pipeArguments } from "effect/Pipeable"
|
|
9
8
|
import type * as Predicate from "effect/Predicate"
|
|
10
9
|
import type * as Schedule from "effect/Schedule"
|
|
@@ -53,20 +52,17 @@ export const make = <R, E, A, R2, E2>(
|
|
|
53
52
|
}
|
|
54
53
|
|
|
55
54
|
const addB3Headers = (req: ClientRequest.ClientRequest) =>
|
|
56
|
-
Effect.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
"
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
)
|
|
68
|
-
})
|
|
69
|
-
)
|
|
55
|
+
Effect.match(Effect.currentSpan, {
|
|
56
|
+
onFailure: () => req,
|
|
57
|
+
onSuccess: (span) =>
|
|
58
|
+
internalRequest.setHeader(
|
|
59
|
+
req,
|
|
60
|
+
"b3",
|
|
61
|
+
`${span.traceId}-${span.spanId}-${span.sampled ? "1" : "0"}${
|
|
62
|
+
span.parent._tag === "Some" ? `-${span.parent.value.spanId}` : ""
|
|
63
|
+
}`
|
|
64
|
+
)
|
|
65
|
+
})
|
|
70
66
|
|
|
71
67
|
/** @internal */
|
|
72
68
|
export const makeDefault = (
|
|
@@ -16,34 +16,34 @@ import type * as AsyncInput from "effect/SingleProducerAsyncInput"
|
|
|
16
16
|
import * as Stream from "effect/Stream"
|
|
17
17
|
import * as MP from "multipasta"
|
|
18
18
|
import * as FileSystem from "../../FileSystem.js"
|
|
19
|
-
import type * as FormData from "../../Http/FormData.js"
|
|
20
19
|
import * as IncomingMessage from "../../Http/IncomingMessage.js"
|
|
20
|
+
import type * as Multipart from "../../Http/Multipart.js"
|
|
21
21
|
import * as Path from "../../Path.js"
|
|
22
22
|
|
|
23
23
|
/** @internal */
|
|
24
|
-
export const TypeId:
|
|
24
|
+
export const TypeId: Multipart.TypeId = Symbol.for("@effect/platform/Http/Multipart") as Multipart.TypeId
|
|
25
25
|
|
|
26
26
|
/** @internal */
|
|
27
|
-
export const ErrorTypeId:
|
|
28
|
-
"@effect/platform/Http/
|
|
29
|
-
) as
|
|
27
|
+
export const ErrorTypeId: Multipart.ErrorTypeId = Symbol.for(
|
|
28
|
+
"@effect/platform/Http/Multipart/MultipartError"
|
|
29
|
+
) as Multipart.ErrorTypeId
|
|
30
30
|
|
|
31
31
|
/** @internal */
|
|
32
|
-
export const
|
|
32
|
+
export const MultipartError = (reason: Multipart.MultipartError["reason"], error: unknown): Multipart.MultipartError =>
|
|
33
33
|
Data.struct({
|
|
34
34
|
[ErrorTypeId]: ErrorTypeId,
|
|
35
|
-
_tag: "
|
|
35
|
+
_tag: "MultipartError",
|
|
36
36
|
reason,
|
|
37
37
|
error
|
|
38
38
|
})
|
|
39
39
|
|
|
40
40
|
/** @internal */
|
|
41
|
-
export const isField = (u: unknown): u is
|
|
41
|
+
export const isField = (u: unknown): u is Multipart.Field =>
|
|
42
42
|
Predicate.hasProperty(u, TypeId) && Predicate.isTagged(u, "Field")
|
|
43
43
|
|
|
44
44
|
/** @internal */
|
|
45
45
|
export const maxParts: FiberRef.FiberRef<Option.Option<number>> = globalValue(
|
|
46
|
-
"@effect/platform/Http/
|
|
46
|
+
"@effect/platform/Http/Multipart/maxParts",
|
|
47
47
|
() => FiberRef.unsafeMake(Option.none<number>())
|
|
48
48
|
)
|
|
49
49
|
|
|
@@ -55,7 +55,7 @@ export const withMaxParts = dual<
|
|
|
55
55
|
|
|
56
56
|
/** @internal */
|
|
57
57
|
export const maxFieldSize: FiberRef.FiberRef<FileSystem.Size> = globalValue(
|
|
58
|
-
"@effect/platform/Http/
|
|
58
|
+
"@effect/platform/Http/Multipart/maxFieldSize",
|
|
59
59
|
() => FiberRef.unsafeMake(FileSystem.Size(10 * 1024 * 1024))
|
|
60
60
|
)
|
|
61
61
|
|
|
@@ -67,7 +67,7 @@ export const withMaxFieldSize = dual<
|
|
|
67
67
|
|
|
68
68
|
/** @internal */
|
|
69
69
|
export const maxFileSize: FiberRef.FiberRef<Option.Option<FileSystem.Size>> = globalValue(
|
|
70
|
-
"@effect/platform/Http/
|
|
70
|
+
"@effect/platform/Http/Multipart/maxFileSize",
|
|
71
71
|
() => FiberRef.unsafeMake(Option.none<FileSystem.Size>())
|
|
72
72
|
)
|
|
73
73
|
|
|
@@ -79,7 +79,7 @@ export const withMaxFileSize = dual<
|
|
|
79
79
|
|
|
80
80
|
/** @internal */
|
|
81
81
|
export const fieldMimeTypes: FiberRef.FiberRef<Chunk.Chunk<string>> = globalValue(
|
|
82
|
-
"@effect/platform/Http/
|
|
82
|
+
"@effect/platform/Http/Multipart/fieldMimeTypes",
|
|
83
83
|
() => FiberRef.unsafeMake<Chunk.Chunk<string>>(Chunk.make("application/json"))
|
|
84
84
|
)
|
|
85
85
|
|
|
@@ -89,59 +89,58 @@ export const withFieldMimeTypes = dual<
|
|
|
89
89
|
<R, E, A>(effect: Effect.Effect<R, E, A>, mimeTypes: ReadonlyArray<string>) => Effect.Effect<R, E, A>
|
|
90
90
|
>(2, (effect, mimeTypes) => Effect.locally(effect, fieldMimeTypes, Chunk.fromIterable(mimeTypes)))
|
|
91
91
|
|
|
92
|
+
const fileSchema: Schema.Schema<Multipart.PersistedFile, Multipart.PersistedFile> = Schema.struct({
|
|
93
|
+
[TypeId]: Schema.uniqueSymbol(TypeId),
|
|
94
|
+
_tag: Schema.literal("PersistedFile"),
|
|
95
|
+
key: Schema.string,
|
|
96
|
+
name: Schema.string,
|
|
97
|
+
contentType: Schema.string,
|
|
98
|
+
path: Schema.string
|
|
99
|
+
})
|
|
100
|
+
|
|
92
101
|
/** @internal */
|
|
93
|
-
export const filesSchema: Schema.Schema<
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
Schema.object,
|
|
98
|
-
Schema.filter(
|
|
99
|
-
(file): file is FormData.PersistedFile => TypeId in file && "_tag" in file && file._tag === "PersistedFile"
|
|
100
|
-
)
|
|
101
|
-
) as any as Schema.Schema<FormData.PersistedFile, FormData.PersistedFile>
|
|
102
|
-
)
|
|
102
|
+
export const filesSchema: Schema.Schema<
|
|
103
|
+
ReadonlyArray<Multipart.PersistedFile>,
|
|
104
|
+
ReadonlyArray<Multipart.PersistedFile>
|
|
105
|
+
> = Schema.array(fileSchema)
|
|
103
106
|
|
|
104
107
|
/** @internal */
|
|
105
|
-
export const schemaPersisted = <I extends
|
|
108
|
+
export const schemaPersisted = <I extends Multipart.Persisted, A>(
|
|
106
109
|
schema: Schema.Schema<I, A>
|
|
107
110
|
) => {
|
|
108
111
|
const parse = Schema.parse(schema)
|
|
109
|
-
return (
|
|
112
|
+
return (persisted: Multipart.Persisted) => parse(persisted)
|
|
110
113
|
}
|
|
111
114
|
|
|
112
115
|
/** @internal */
|
|
113
116
|
export const schemaJson = <I, A>(schema: Schema.Schema<I, A>): {
|
|
114
117
|
(
|
|
115
118
|
field: string
|
|
116
|
-
): (
|
|
119
|
+
): (persisted: Multipart.Persisted) => Effect.Effect<never, ParseResult.ParseError, A>
|
|
117
120
|
(
|
|
118
|
-
|
|
121
|
+
persisted: Multipart.Persisted,
|
|
119
122
|
field: string
|
|
120
|
-
): Effect.Effect<never,
|
|
123
|
+
): Effect.Effect<never, ParseResult.ParseError, A>
|
|
121
124
|
} => {
|
|
122
|
-
const
|
|
125
|
+
const fromJson = Schema.parseJson(schema)
|
|
123
126
|
return dual<
|
|
124
127
|
(
|
|
125
128
|
field: string
|
|
126
129
|
) => (
|
|
127
|
-
|
|
128
|
-
) => Effect.Effect<never,
|
|
130
|
+
persisted: Multipart.Persisted
|
|
131
|
+
) => Effect.Effect<never, ParseResult.ParseError, A>,
|
|
129
132
|
(
|
|
130
|
-
|
|
133
|
+
persisted: Multipart.Persisted,
|
|
131
134
|
field: string
|
|
132
|
-
) => Effect.Effect<never,
|
|
133
|
-
>(2, (
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
),
|
|
140
|
-
|
|
141
|
-
try: (field) => JSON.parse(field.value),
|
|
142
|
-
catch: (error) => FormDataError("Parse", `schemaJson: field was not valid json: ${error}`)
|
|
143
|
-
}),
|
|
144
|
-
Effect.flatMap(parse)
|
|
135
|
+
) => Effect.Effect<never, ParseResult.ParseError, A>
|
|
136
|
+
>(2, (persisted, field) =>
|
|
137
|
+
Effect.map(
|
|
138
|
+
Schema.parse(
|
|
139
|
+
Schema.struct({
|
|
140
|
+
[field]: fromJson
|
|
141
|
+
})
|
|
142
|
+
)(persisted),
|
|
143
|
+
(_) => _[field]
|
|
145
144
|
))
|
|
146
145
|
}
|
|
147
146
|
|
|
@@ -181,8 +180,8 @@ export const makeChannel = <IE>(
|
|
|
181
180
|
IE,
|
|
182
181
|
Chunk.Chunk<Uint8Array>,
|
|
183
182
|
unknown,
|
|
184
|
-
|
|
185
|
-
Chunk.Chunk<
|
|
183
|
+
Multipart.MultipartError | IE,
|
|
184
|
+
Chunk.Chunk<Multipart.Part>,
|
|
186
185
|
unknown
|
|
187
186
|
> =>
|
|
188
187
|
Channel.acquireUseRelease(
|
|
@@ -202,13 +201,13 @@ const makeFromQueue = <IE>(
|
|
|
202
201
|
IE,
|
|
203
202
|
Chunk.Chunk<Uint8Array>,
|
|
204
203
|
unknown,
|
|
205
|
-
IE |
|
|
206
|
-
Chunk.Chunk<
|
|
204
|
+
IE | Multipart.MultipartError,
|
|
205
|
+
Chunk.Chunk<Multipart.Part>,
|
|
207
206
|
unknown
|
|
208
207
|
> =>
|
|
209
208
|
Channel.suspend(() => {
|
|
210
|
-
let error = Option.none<Cause.Cause<IE |
|
|
211
|
-
let partsBuffer: Array<
|
|
209
|
+
let error = Option.none<Cause.Cause<IE | Multipart.MultipartError>>()
|
|
210
|
+
let partsBuffer: Array<Multipart.Part> = []
|
|
212
211
|
let partsFinished = false
|
|
213
212
|
|
|
214
213
|
const input: AsyncInput.AsyncInputProducer<IE, Chunk.Chunk<Uint8Array>, unknown> = {
|
|
@@ -293,8 +292,8 @@ const makeFromQueue = <IE>(
|
|
|
293
292
|
unknown,
|
|
294
293
|
unknown,
|
|
295
294
|
unknown,
|
|
296
|
-
IE |
|
|
297
|
-
Chunk.Chunk<
|
|
295
|
+
IE | Multipart.MultipartError,
|
|
296
|
+
Chunk.Chunk<Multipart.Part>,
|
|
298
297
|
void
|
|
299
298
|
> = Channel.suspend(() => {
|
|
300
299
|
if (error._tag === "Some") {
|
|
@@ -308,32 +307,32 @@ const makeFromQueue = <IE>(
|
|
|
308
307
|
return Channel.embedInput(partsChannel, input)
|
|
309
308
|
})
|
|
310
309
|
|
|
311
|
-
function convertError(error: MP.MultipartError):
|
|
310
|
+
function convertError(error: MP.MultipartError): Multipart.MultipartError {
|
|
312
311
|
switch (error._tag) {
|
|
313
312
|
case "ReachedLimit": {
|
|
314
313
|
switch (error.limit) {
|
|
315
314
|
case "MaxParts": {
|
|
316
|
-
return
|
|
315
|
+
return MultipartError("TooManyParts", error)
|
|
317
316
|
}
|
|
318
317
|
case "MaxFieldSize": {
|
|
319
|
-
return
|
|
318
|
+
return MultipartError("FieldTooLarge", error)
|
|
320
319
|
}
|
|
321
320
|
case "MaxPartSize": {
|
|
322
|
-
return
|
|
321
|
+
return MultipartError("FileTooLarge", error)
|
|
323
322
|
}
|
|
324
323
|
case "MaxTotalSize": {
|
|
325
|
-
return
|
|
324
|
+
return MultipartError("BodyTooLarge", error)
|
|
326
325
|
}
|
|
327
326
|
}
|
|
328
327
|
}
|
|
329
328
|
default: {
|
|
330
|
-
return
|
|
329
|
+
return MultipartError("Parse", error)
|
|
331
330
|
}
|
|
332
331
|
}
|
|
333
332
|
}
|
|
334
333
|
|
|
335
|
-
class FieldImpl implements
|
|
336
|
-
readonly [TypeId]:
|
|
334
|
+
class FieldImpl implements Multipart.Field {
|
|
335
|
+
readonly [TypeId]: Multipart.TypeId
|
|
337
336
|
readonly _tag = "Field"
|
|
338
337
|
|
|
339
338
|
constructor(
|
|
@@ -345,13 +344,13 @@ class FieldImpl implements FormData.Field {
|
|
|
345
344
|
}
|
|
346
345
|
}
|
|
347
346
|
|
|
348
|
-
class FileImpl implements
|
|
347
|
+
class FileImpl implements Multipart.File {
|
|
349
348
|
readonly _tag = "File"
|
|
350
|
-
readonly [TypeId]:
|
|
349
|
+
readonly [TypeId]: Multipart.TypeId
|
|
351
350
|
readonly key: string
|
|
352
351
|
readonly name: string
|
|
353
352
|
readonly contentType: string
|
|
354
|
-
readonly content: Stream.Stream<never,
|
|
353
|
+
readonly content: Stream.Stream<never, Multipart.MultipartError, Uint8Array>
|
|
355
354
|
|
|
356
355
|
constructor(
|
|
357
356
|
info: MP.PartInfo,
|
|
@@ -365,21 +364,21 @@ class FileImpl implements FormData.File {
|
|
|
365
364
|
}
|
|
366
365
|
}
|
|
367
366
|
|
|
368
|
-
const defaultWriteFile = (path: string, file:
|
|
367
|
+
const defaultWriteFile = (path: string, file: Multipart.File) =>
|
|
369
368
|
Effect.flatMap(
|
|
370
369
|
FileSystem.FileSystem,
|
|
371
370
|
(fs) =>
|
|
372
371
|
Effect.mapError(
|
|
373
372
|
Stream.run(file.content, fs.sink(path)),
|
|
374
|
-
(error) =>
|
|
373
|
+
(error) => MultipartError("InternalError", error)
|
|
375
374
|
)
|
|
376
375
|
)
|
|
377
376
|
|
|
378
377
|
/** @internal */
|
|
379
|
-
export const
|
|
380
|
-
stream: Stream.Stream<never,
|
|
378
|
+
export const toPersisted = (
|
|
379
|
+
stream: Stream.Stream<never, Multipart.MultipartError, Multipart.Part>,
|
|
381
380
|
writeFile = defaultWriteFile
|
|
382
|
-
): Effect.Effect<FileSystem.FileSystem | Path.Path | Scope.Scope,
|
|
381
|
+
): Effect.Effect<FileSystem.FileSystem | Path.Path | Scope.Scope, Multipart.MultipartError, Multipart.Persisted> =>
|
|
383
382
|
pipe(
|
|
384
383
|
Effect.Do,
|
|
385
384
|
Effect.bind("fs", () => FileSystem.FileSystem),
|
|
@@ -388,18 +387,18 @@ export const formData = (
|
|
|
388
387
|
Effect.flatMap(({ dir, path: path_ }) =>
|
|
389
388
|
Stream.runFoldEffect(
|
|
390
389
|
stream,
|
|
391
|
-
Object.create(null) as Record<string, Array<
|
|
392
|
-
(
|
|
390
|
+
Object.create(null) as Record<string, Array<Multipart.PersistedFile> | string>,
|
|
391
|
+
(persisted, part) => {
|
|
393
392
|
if (part._tag === "Field") {
|
|
394
|
-
|
|
395
|
-
return Effect.succeed(
|
|
393
|
+
persisted[part.key] = part.value
|
|
394
|
+
return Effect.succeed(persisted)
|
|
396
395
|
}
|
|
397
396
|
const file = part
|
|
398
397
|
const path = path_.join(dir, path_.basename(file.name).slice(-128))
|
|
399
|
-
if (!Array.isArray(
|
|
400
|
-
|
|
398
|
+
if (!Array.isArray(persisted[part.key])) {
|
|
399
|
+
persisted[part.key] = []
|
|
401
400
|
}
|
|
402
|
-
;(
|
|
401
|
+
;(persisted[part.key] as Array<Multipart.PersistedFile>).push(
|
|
403
402
|
new PersistedFileImpl(
|
|
404
403
|
file.key,
|
|
405
404
|
file.name,
|
|
@@ -407,18 +406,18 @@ export const formData = (
|
|
|
407
406
|
path
|
|
408
407
|
)
|
|
409
408
|
)
|
|
410
|
-
return Effect.as(writeFile(path, file),
|
|
409
|
+
return Effect.as(writeFile(path, file), persisted)
|
|
411
410
|
}
|
|
412
411
|
)
|
|
413
412
|
),
|
|
414
413
|
Effect.catchTags({
|
|
415
|
-
SystemError: (err) => Effect.fail(
|
|
416
|
-
BadArgument: (err) => Effect.fail(
|
|
414
|
+
SystemError: (err) => Effect.fail(MultipartError("InternalError", err)),
|
|
415
|
+
BadArgument: (err) => Effect.fail(MultipartError("InternalError", err))
|
|
417
416
|
})
|
|
418
417
|
)
|
|
419
418
|
|
|
420
|
-
class PersistedFileImpl implements
|
|
421
|
-
readonly [TypeId]:
|
|
419
|
+
class PersistedFileImpl implements Multipart.PersistedFile {
|
|
420
|
+
readonly [TypeId]: Multipart.TypeId
|
|
422
421
|
readonly _tag = "PersistedFile"
|
|
423
422
|
|
|
424
423
|
constructor(
|