@effect/platform 0.46.3 → 0.47.1
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/PlatformLogger/package.json +6 -0
- package/Socket/package.json +6 -0
- package/dist/cjs/Error.js +40 -4
- package/dist/cjs/Error.js.map +1 -1
- package/dist/cjs/Http/Body.js +8 -1
- package/dist/cjs/Http/Body.js.map +1 -1
- package/dist/cjs/Http/ClientError.js +21 -2
- package/dist/cjs/Http/ClientError.js.map +1 -1
- package/dist/cjs/Http/Headers.js +22 -1
- package/dist/cjs/Http/Headers.js.map +1 -1
- package/dist/cjs/Http/ServerError.js +30 -7
- package/dist/cjs/Http/ServerError.js.map +1 -1
- package/dist/cjs/Http/ServerRequest.js +11 -1
- package/dist/cjs/Http/ServerRequest.js.map +1 -1
- package/dist/cjs/Http/UrlParams.js +7 -2
- package/dist/cjs/Http/UrlParams.js.map +1 -1
- package/dist/cjs/PlatformLogger.js +57 -0
- package/dist/cjs/PlatformLogger.js.map +1 -0
- package/dist/cjs/Socket.js +212 -0
- package/dist/cjs/Socket.js.map +1 -0
- package/dist/cjs/index.js +5 -1
- package/dist/cjs/internal/http/body.js +1 -1
- package/dist/cjs/internal/http/body.js.map +1 -1
- package/dist/cjs/internal/http/client.js +5 -5
- package/dist/cjs/internal/http/client.js.map +1 -1
- package/dist/cjs/internal/http/clientError.js +1 -36
- package/dist/cjs/internal/http/clientError.js.map +1 -1
- package/dist/cjs/internal/http/clientResponse.js +8 -8
- package/dist/cjs/internal/http/clientResponse.js.map +1 -1
- package/dist/cjs/internal/http/multiplex.js +1 -1
- package/dist/cjs/internal/http/multiplex.js.map +1 -1
- package/dist/cjs/internal/http/router.js +1 -1
- package/dist/cjs/internal/http/router.js.map +1 -1
- package/dist/cjs/internal/http/serverError.js +1 -15
- package/dist/cjs/internal/http/serverError.js.map +1 -1
- package/dist/cjs/internal/http/serverRequest.js +22 -8
- package/dist/cjs/internal/http/serverRequest.js.map +1 -1
- package/dist/cjs/internal/platformLogger.js +46 -0
- package/dist/cjs/internal/platformLogger.js.map +1 -0
- package/dist/cjs/internal/worker.js.map +1 -1
- package/dist/dts/Error.d.ts +22 -0
- package/dist/dts/Error.d.ts.map +1 -1
- package/dist/dts/Http/Body.d.ts +9 -1
- package/dist/dts/Http/Body.d.ts.map +1 -1
- package/dist/dts/Http/ClientError.d.ts +21 -33
- package/dist/dts/Http/ClientError.d.ts.map +1 -1
- package/dist/dts/Http/Headers.d.ts +19 -0
- package/dist/dts/Http/Headers.d.ts.map +1 -1
- package/dist/dts/Http/ServerError.d.ts +33 -47
- package/dist/dts/Http/ServerError.d.ts.map +1 -1
- package/dist/dts/Http/ServerRequest.d.ts +14 -0
- package/dist/dts/Http/ServerRequest.d.ts.map +1 -1
- package/dist/dts/Http/UrlParams.d.ts +5 -0
- package/dist/dts/Http/UrlParams.d.ts.map +1 -1
- package/dist/dts/PlatformLogger.d.ts +41 -0
- package/dist/dts/PlatformLogger.d.ts.map +1 -0
- package/dist/dts/Socket.d.ts +111 -0
- package/dist/dts/Socket.d.ts.map +1 -0
- package/dist/dts/index.d.ts +8 -0
- package/dist/dts/index.d.ts.map +1 -1
- package/dist/dts/internal/platformLogger.d.ts +2 -0
- package/dist/dts/internal/platformLogger.d.ts.map +1 -0
- package/dist/esm/Error.js +38 -3
- package/dist/esm/Error.js.map +1 -1
- package/dist/esm/Http/Body.js +6 -0
- package/dist/esm/Http/Body.js.map +1 -1
- package/dist/esm/Http/ClientError.js +19 -2
- package/dist/esm/Http/ClientError.js.map +1 -1
- package/dist/esm/Http/Headers.js +21 -1
- package/dist/esm/Http/Headers.js.map +1 -1
- package/dist/esm/Http/ServerError.js +26 -7
- package/dist/esm/Http/ServerError.js.map +1 -1
- package/dist/esm/Http/ServerRequest.js +10 -0
- package/dist/esm/Http/ServerRequest.js.map +1 -1
- package/dist/esm/Http/UrlParams.js +5 -0
- package/dist/esm/Http/UrlParams.js.map +1 -1
- package/dist/esm/PlatformLogger.js +26 -0
- package/dist/esm/PlatformLogger.js.map +1 -0
- package/dist/esm/Socket.js +166 -0
- package/dist/esm/Socket.js.map +1 -0
- package/dist/esm/index.js +8 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/internal/http/body.js +1 -1
- package/dist/esm/internal/http/body.js.map +1 -1
- package/dist/esm/internal/http/client.js +5 -5
- package/dist/esm/internal/http/client.js.map +1 -1
- package/dist/esm/internal/http/clientError.js +0 -10
- package/dist/esm/internal/http/clientError.js.map +1 -1
- package/dist/esm/internal/http/clientResponse.js +8 -8
- package/dist/esm/internal/http/clientResponse.js.map +1 -1
- package/dist/esm/internal/http/multiplex.js +1 -1
- package/dist/esm/internal/http/multiplex.js.map +1 -1
- package/dist/esm/internal/http/router.js +1 -1
- package/dist/esm/internal/http/router.js.map +1 -1
- package/dist/esm/internal/http/serverError.js +0 -14
- package/dist/esm/internal/http/serverError.js.map +1 -1
- package/dist/esm/internal/http/serverRequest.js +20 -7
- package/dist/esm/internal/http/serverRequest.js.map +1 -1
- package/dist/esm/internal/platformLogger.js +15 -0
- package/dist/esm/internal/platformLogger.js.map +1 -0
- package/dist/esm/internal/worker.js.map +1 -1
- package/package.json +20 -3
- package/src/Error.ts +61 -0
- package/src/Http/Body.ts +14 -1
- package/src/Http/ClientError.ts +21 -38
- package/src/Http/Headers.ts +29 -1
- package/src/Http/ServerError.ts +25 -56
- package/src/Http/ServerRequest.ts +25 -0
- package/src/Http/UrlParams.ts +10 -0
- package/src/PlatformLogger.ts +59 -0
- package/src/Socket.ts +291 -0
- package/src/index.ts +10 -0
- package/src/internal/http/body.ts +4 -2
- package/src/internal/http/client.ts +5 -6
- package/src/internal/http/clientError.ts +0 -14
- package/src/internal/http/clientResponse.ts +15 -14
- package/src/internal/http/multiplex.ts +1 -1
- package/src/internal/http/router.ts +1 -1
- package/src/internal/http/serverError.ts +0 -20
- package/src/internal/http/serverRequest.ts +31 -11
- package/src/internal/platformLogger.ts +42 -0
- package/src/internal/worker.ts +5 -1
package/src/Socket.ts
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 1.0.0
|
|
3
|
+
*/
|
|
4
|
+
import * as Cause from "effect/Cause"
|
|
5
|
+
import * as Channel from "effect/Channel"
|
|
6
|
+
import * as Chunk from "effect/Chunk"
|
|
7
|
+
import * as Context from "effect/Context"
|
|
8
|
+
import * as Data from "effect/Data"
|
|
9
|
+
import * as Effect from "effect/Effect"
|
|
10
|
+
import * as Exit from "effect/Exit"
|
|
11
|
+
import * as FiberSet from "effect/FiberSet"
|
|
12
|
+
import * as Layer from "effect/Layer"
|
|
13
|
+
import * as Queue from "effect/Queue"
|
|
14
|
+
import * as Scope from "effect/Scope"
|
|
15
|
+
import type * as AsyncProducer from "effect/SingleProducerAsyncInput"
|
|
16
|
+
import IsoWebSocket from "isomorphic-ws"
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @since 1.0.0
|
|
20
|
+
* @category type ids
|
|
21
|
+
*/
|
|
22
|
+
export const SocketTypeId = Symbol.for("@effect/platform/Socket")
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @since 1.0.0
|
|
26
|
+
* @category type ids
|
|
27
|
+
*/
|
|
28
|
+
export type SocketTypeId = typeof SocketTypeId
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @since 1.0.0
|
|
32
|
+
* @category tags
|
|
33
|
+
*/
|
|
34
|
+
export const Socket: Context.Tag<Socket, Socket> = Context.GenericTag<Socket>(
|
|
35
|
+
"@effect/platform/Socket"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @since 1.0.0
|
|
40
|
+
* @category models
|
|
41
|
+
*/
|
|
42
|
+
export interface Socket {
|
|
43
|
+
readonly [SocketTypeId]: SocketTypeId
|
|
44
|
+
readonly run: <R, E, _>(
|
|
45
|
+
handler: (_: Uint8Array) => Effect.Effect<_, E, R>
|
|
46
|
+
) => Effect.Effect<void, SocketError | E, R>
|
|
47
|
+
readonly writer: Effect.Effect<(chunk: Uint8Array) => Effect.Effect<void>, never, Scope.Scope>
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @since 1.0.0
|
|
52
|
+
* @category errors
|
|
53
|
+
*/
|
|
54
|
+
export class SocketError extends Data.TaggedError("SocketError")<{
|
|
55
|
+
readonly reason: "Write" | "Read" | "Open" | "Close"
|
|
56
|
+
readonly error: unknown
|
|
57
|
+
}> {
|
|
58
|
+
/**
|
|
59
|
+
* @since 1.0.0
|
|
60
|
+
*/
|
|
61
|
+
toString(): string {
|
|
62
|
+
return `SocketError: ${this.reason} - ${this.error}`
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @since 1.0.0
|
|
68
|
+
* @category combinators
|
|
69
|
+
*/
|
|
70
|
+
export const toChannel = <IE>(
|
|
71
|
+
self: Socket
|
|
72
|
+
): Channel.Channel<Chunk.Chunk<Uint8Array>, Chunk.Chunk<Uint8Array>, SocketError | IE, IE, void, unknown> =>
|
|
73
|
+
Channel.unwrap(
|
|
74
|
+
Effect.gen(function*(_) {
|
|
75
|
+
const writeScope = yield* _(Scope.make())
|
|
76
|
+
const write = yield* _(Scope.extend(self.writer, writeScope))
|
|
77
|
+
const exitQueue = yield* _(Queue.unbounded<Exit.Exit<Chunk.Chunk<Uint8Array>, SocketError | IE>>())
|
|
78
|
+
|
|
79
|
+
const input: AsyncProducer.AsyncInputProducer<IE, Chunk.Chunk<Uint8Array>, unknown> = {
|
|
80
|
+
awaitRead: () => Effect.unit,
|
|
81
|
+
emit(chunk) {
|
|
82
|
+
return Effect.catchAllCause(
|
|
83
|
+
Effect.forEach(chunk, write, { discard: true }),
|
|
84
|
+
(cause) => Queue.offer(exitQueue, Exit.failCause(cause))
|
|
85
|
+
)
|
|
86
|
+
},
|
|
87
|
+
error(error) {
|
|
88
|
+
return Effect.zipRight(
|
|
89
|
+
Scope.close(writeScope, Exit.unit),
|
|
90
|
+
Queue.offer(exitQueue, Exit.failCause(error))
|
|
91
|
+
)
|
|
92
|
+
},
|
|
93
|
+
done() {
|
|
94
|
+
return Scope.close(writeScope, Exit.unit)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
yield* _(
|
|
99
|
+
self.run((data) => Queue.offer(exitQueue, Exit.succeed(Chunk.of(data)))),
|
|
100
|
+
Effect.zipRight(Effect.failCause(Cause.empty)),
|
|
101
|
+
Effect.exit,
|
|
102
|
+
Effect.tap((exit) => Queue.offer(exitQueue, exit)),
|
|
103
|
+
Effect.fork
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
const loop: Channel.Channel<Chunk.Chunk<Uint8Array>, unknown, SocketError | IE, unknown, void, unknown> = Channel
|
|
107
|
+
.flatMap(
|
|
108
|
+
Queue.take(exitQueue),
|
|
109
|
+
Exit.match({
|
|
110
|
+
onFailure: (cause) => Cause.isEmptyType(cause) ? Channel.unit : Channel.failCause(cause),
|
|
111
|
+
onSuccess: (chunk) => Channel.zipRight(Channel.write(chunk), loop)
|
|
112
|
+
})
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
return Channel.embedInput(loop, input)
|
|
116
|
+
})
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* @since 1.0.0
|
|
121
|
+
* @category combinators
|
|
122
|
+
*/
|
|
123
|
+
export const toChannelWith = <IE = never>() =>
|
|
124
|
+
(
|
|
125
|
+
self: Socket
|
|
126
|
+
): Channel.Channel<Chunk.Chunk<Uint8Array>, Chunk.Chunk<Uint8Array>, SocketError | IE, IE, void, unknown> =>
|
|
127
|
+
toChannel(self)
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* @since 1.0.0
|
|
131
|
+
* @category constructors
|
|
132
|
+
*/
|
|
133
|
+
export const makeChannel = <IE = never>(): Channel.Channel<
|
|
134
|
+
Chunk.Chunk<Uint8Array>,
|
|
135
|
+
Chunk.Chunk<Uint8Array>,
|
|
136
|
+
SocketError | IE,
|
|
137
|
+
IE,
|
|
138
|
+
void,
|
|
139
|
+
unknown,
|
|
140
|
+
Socket
|
|
141
|
+
> => Channel.unwrap(Effect.map(Socket, toChannelWith<IE>()))
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* @since 1.0.0
|
|
145
|
+
*/
|
|
146
|
+
export const defaultCloseCodeIsError = (code: number) => code !== 1000 && code !== 1006
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* @since 1.0.0
|
|
150
|
+
* @category tags
|
|
151
|
+
*/
|
|
152
|
+
export interface WebSocket {
|
|
153
|
+
readonly _: unique symbol
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* @since 1.0.0
|
|
158
|
+
* @category tags
|
|
159
|
+
*/
|
|
160
|
+
export const WebSocket: Context.Tag<WebSocket, globalThis.WebSocket> = Context.GenericTag(
|
|
161
|
+
"@effect/platform/Socket/WebSocket"
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* @since 1.0.0
|
|
166
|
+
* @category constructors
|
|
167
|
+
*/
|
|
168
|
+
export const makeWebSocket = (url: string | Effect.Effect<string>, options?: {
|
|
169
|
+
readonly closeCodeIsError?: (code: number) => boolean
|
|
170
|
+
}): Effect.Effect<Socket> =>
|
|
171
|
+
fromWebSocket(
|
|
172
|
+
Effect.acquireRelease(
|
|
173
|
+
Effect.map(
|
|
174
|
+
typeof url === "string" ? Effect.succeed(url) : url,
|
|
175
|
+
(url) => {
|
|
176
|
+
const WS = "WebSocket" in globalThis ? globalThis.WebSocket : IsoWebSocket
|
|
177
|
+
return new WS(url) as globalThis.WebSocket
|
|
178
|
+
}
|
|
179
|
+
),
|
|
180
|
+
(ws) => Effect.sync(() => ws.close())
|
|
181
|
+
),
|
|
182
|
+
options
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* @since 1.0.0
|
|
187
|
+
* @category constructors
|
|
188
|
+
*/
|
|
189
|
+
export const fromWebSocket = (
|
|
190
|
+
acquire: Effect.Effect<globalThis.WebSocket, SocketError, Scope.Scope>,
|
|
191
|
+
options?: {
|
|
192
|
+
readonly closeCodeIsError?: (code: number) => boolean
|
|
193
|
+
}
|
|
194
|
+
): Effect.Effect<Socket> =>
|
|
195
|
+
Effect.gen(function*(_) {
|
|
196
|
+
const closeCodeIsError = options?.closeCodeIsError ?? defaultCloseCodeIsError
|
|
197
|
+
const sendQueue = yield* _(Queue.unbounded<Uint8Array>())
|
|
198
|
+
|
|
199
|
+
const run = <R, E, _>(handler: (_: Uint8Array) => Effect.Effect<_, E, R>) =>
|
|
200
|
+
Effect.gen(function*(_) {
|
|
201
|
+
const ws = yield* _(acquire)
|
|
202
|
+
const encoder = new TextEncoder()
|
|
203
|
+
const fiberSet = yield* _(FiberSet.make<any, E | SocketError>())
|
|
204
|
+
const run = yield* _(
|
|
205
|
+
FiberSet.runtime(fiberSet)<R>(),
|
|
206
|
+
Effect.provideService(WebSocket, ws)
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
ws.onmessage = (event) => {
|
|
210
|
+
run(
|
|
211
|
+
handler(
|
|
212
|
+
event.data instanceof Uint8Array
|
|
213
|
+
? event.data
|
|
214
|
+
: typeof event.data === "string"
|
|
215
|
+
? encoder.encode(event.data)
|
|
216
|
+
: new Uint8Array(event.data)
|
|
217
|
+
)
|
|
218
|
+
)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (ws.readyState !== IsoWebSocket.OPEN) {
|
|
222
|
+
yield* _(Effect.async<void, SocketError, never>((resume) => {
|
|
223
|
+
ws.onopen = () => {
|
|
224
|
+
resume(Effect.unit)
|
|
225
|
+
}
|
|
226
|
+
ws.onerror = (error_) => {
|
|
227
|
+
resume(Effect.fail(new SocketError({ reason: "Open", error: (error_ as any).message })))
|
|
228
|
+
}
|
|
229
|
+
}))
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
yield* _(
|
|
233
|
+
Queue.take(sendQueue),
|
|
234
|
+
Effect.tap((chunk) =>
|
|
235
|
+
Effect.try({
|
|
236
|
+
try: () => ws.send(chunk),
|
|
237
|
+
catch: (error) => Effect.fail(new SocketError({ reason: "Write", error: (error as any).message }))
|
|
238
|
+
})
|
|
239
|
+
),
|
|
240
|
+
Effect.forever,
|
|
241
|
+
Effect.fork
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
yield* _(
|
|
245
|
+
Effect.async<void, SocketError, never>((resume) => {
|
|
246
|
+
ws.onclose = (event) => {
|
|
247
|
+
if (closeCodeIsError(event.code)) {
|
|
248
|
+
resume(Effect.fail(new SocketError({ reason: "Close", error: event })))
|
|
249
|
+
} else {
|
|
250
|
+
resume(Effect.unit)
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
ws.onerror = (error) => {
|
|
254
|
+
resume(Effect.fail(new SocketError({ reason: "Read", error: (error as any).message })))
|
|
255
|
+
}
|
|
256
|
+
}),
|
|
257
|
+
Effect.raceFirst(FiberSet.join(fiberSet))
|
|
258
|
+
)
|
|
259
|
+
}).pipe(Effect.scoped)
|
|
260
|
+
|
|
261
|
+
const write = (chunk: Uint8Array) => Queue.offer(sendQueue, chunk)
|
|
262
|
+
const writer = Effect.succeed(write)
|
|
263
|
+
|
|
264
|
+
return Socket.of({
|
|
265
|
+
[SocketTypeId]: SocketTypeId,
|
|
266
|
+
run,
|
|
267
|
+
writer
|
|
268
|
+
})
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* @since 1.0.0
|
|
273
|
+
* @category constructors
|
|
274
|
+
*/
|
|
275
|
+
export const makeWebSocketChannel = <IE = never>(
|
|
276
|
+
url: string,
|
|
277
|
+
options?: {
|
|
278
|
+
readonly closeCodeIsError?: (code: number) => boolean
|
|
279
|
+
}
|
|
280
|
+
): Channel.Channel<Chunk.Chunk<Uint8Array>, Chunk.Chunk<Uint8Array>, SocketError | IE, IE, void, unknown> =>
|
|
281
|
+
Channel.unwrapScoped(
|
|
282
|
+
Effect.map(makeWebSocket(url, options), toChannelWith<IE>())
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* @since 1.0.0
|
|
287
|
+
* @category layers
|
|
288
|
+
*/
|
|
289
|
+
export const layerWebSocket = (url: string, options?: {
|
|
290
|
+
readonly closeCodeIsError?: (code: number) => boolean
|
|
291
|
+
}): Layer.Layer<Socket> => Layer.scoped(Socket, makeWebSocket(url, options))
|
package/src/index.ts
CHANGED
|
@@ -43,11 +43,21 @@ export * as KeyValueStore from "./KeyValueStore.js"
|
|
|
43
43
|
*/
|
|
44
44
|
export * as Path from "./Path.js"
|
|
45
45
|
|
|
46
|
+
/**
|
|
47
|
+
* @since 1.0.0
|
|
48
|
+
*/
|
|
49
|
+
export * as PlatformLogger from "./PlatformLogger.js"
|
|
50
|
+
|
|
46
51
|
/**
|
|
47
52
|
* @since 1.0.0
|
|
48
53
|
*/
|
|
49
54
|
export * as Runtime from "./Runtime.js"
|
|
50
55
|
|
|
56
|
+
/**
|
|
57
|
+
* @since 1.0.0
|
|
58
|
+
*/
|
|
59
|
+
export * as Socket from "./Socket.js"
|
|
60
|
+
|
|
51
61
|
/**
|
|
52
62
|
* @since 1.0.0
|
|
53
63
|
*/
|
|
@@ -48,8 +48,10 @@ class RawImpl implements Body.Raw {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
/** @internal */
|
|
51
|
-
export const raw = (body: unknown,
|
|
52
|
-
|
|
51
|
+
export const raw = (body: unknown, options?: {
|
|
52
|
+
readonly contentType?: string
|
|
53
|
+
readonly contentLength?: number
|
|
54
|
+
}): Body.Raw => new RawImpl(body, options?.contentType, options?.contentLength)
|
|
53
55
|
|
|
54
56
|
class Uint8ArrayImpl implements Body.Uint8Array {
|
|
55
57
|
readonly [TypeId]: Body.TypeId
|
|
@@ -11,13 +11,12 @@ import type * as Scope from "effect/Scope"
|
|
|
11
11
|
import * as Stream from "effect/Stream"
|
|
12
12
|
import type * as Body from "../../Http/Body.js"
|
|
13
13
|
import type * as Client from "../../Http/Client.js"
|
|
14
|
-
import
|
|
14
|
+
import * as Error from "../../Http/ClientError.js"
|
|
15
15
|
import type * as ClientRequest from "../../Http/ClientRequest.js"
|
|
16
16
|
import type * as ClientResponse from "../../Http/ClientResponse.js"
|
|
17
17
|
import * as Method from "../../Http/Method.js"
|
|
18
18
|
import * as UrlParams from "../../Http/UrlParams.js"
|
|
19
19
|
import * as internalBody from "./body.js"
|
|
20
|
-
import * as internalError from "./clientError.js"
|
|
21
20
|
import * as internalRequest from "./clientRequest.js"
|
|
22
21
|
import * as internalResponse from "./clientResponse.js"
|
|
23
22
|
|
|
@@ -82,7 +81,7 @@ export const fetch = (options?: RequestInit): Client.Client.Default =>
|
|
|
82
81
|
makeDefault((request) =>
|
|
83
82
|
Effect.flatMap(
|
|
84
83
|
UrlParams.makeUrl(request.url, request.urlParams, (_) =>
|
|
85
|
-
|
|
84
|
+
new Error.RequestError({
|
|
86
85
|
request,
|
|
87
86
|
reason: "InvalidUrl",
|
|
88
87
|
error: _
|
|
@@ -109,7 +108,7 @@ export const fetch = (options?: RequestInit): Client.Client.Default =>
|
|
|
109
108
|
signal: controller.signal
|
|
110
109
|
} as any),
|
|
111
110
|
catch: (_) =>
|
|
112
|
-
|
|
111
|
+
new Error.RequestError({
|
|
113
112
|
request,
|
|
114
113
|
reason: "Transport",
|
|
115
114
|
error: _
|
|
@@ -419,7 +418,7 @@ export const filterStatus = dual<
|
|
|
419
418
|
effect,
|
|
420
419
|
(response) => f(response.status),
|
|
421
420
|
(response) =>
|
|
422
|
-
|
|
421
|
+
new Error.ResponseError({
|
|
423
422
|
request,
|
|
424
423
|
response,
|
|
425
424
|
reason: "StatusCode",
|
|
@@ -571,7 +570,7 @@ export const schemaFunction = dual<
|
|
|
571
570
|
Effect.tryMap(encode(a), {
|
|
572
571
|
try: (body) => new TextEncoder().encode(JSON.stringify(body)),
|
|
573
572
|
catch: (error) =>
|
|
574
|
-
|
|
573
|
+
new Error.RequestError({
|
|
575
574
|
request,
|
|
576
575
|
reason: "Encode",
|
|
577
576
|
error
|
|
@@ -1,20 +1,6 @@
|
|
|
1
|
-
import * as Data from "effect/Data"
|
|
2
1
|
import type * as Error from "../../Http/ClientError.js"
|
|
3
2
|
|
|
4
3
|
/** @internal */
|
|
5
4
|
export const TypeId: Error.TypeId = Symbol.for(
|
|
6
5
|
"@effect/platform/Http/Error"
|
|
7
6
|
) as Error.TypeId
|
|
8
|
-
|
|
9
|
-
const make = <A extends Error.HttpClientError>(tag: A["_tag"]) => (props: Omit<A, Error.HttpError.ProvidedFields>): A =>
|
|
10
|
-
Data.struct({
|
|
11
|
-
[TypeId]: TypeId,
|
|
12
|
-
_tag: tag,
|
|
13
|
-
...props
|
|
14
|
-
} as A)
|
|
15
|
-
|
|
16
|
-
/** @internal */
|
|
17
|
-
export const requestError = make<Error.RequestError>("RequestError")
|
|
18
|
-
|
|
19
|
-
/** @internal */
|
|
20
|
-
export const responseError = make<Error.ResponseError>("ResponseError")
|
|
@@ -3,13 +3,12 @@ import * as Schema from "@effect/schema/Schema"
|
|
|
3
3
|
import * as Effect from "effect/Effect"
|
|
4
4
|
import * as Option from "effect/Option"
|
|
5
5
|
import * as Stream from "effect/Stream"
|
|
6
|
-
import
|
|
6
|
+
import * as Error from "../../Http/ClientError.js"
|
|
7
7
|
import type * as ClientRequest from "../../Http/ClientRequest.js"
|
|
8
8
|
import type * as ClientResponse from "../../Http/ClientResponse.js"
|
|
9
9
|
import * as Headers from "../../Http/Headers.js"
|
|
10
10
|
import * as IncomingMessage from "../../Http/IncomingMessage.js"
|
|
11
11
|
import * as UrlParams from "../../Http/UrlParams.js"
|
|
12
|
-
import * as internalError from "./clientError.js"
|
|
13
12
|
|
|
14
13
|
/** @internal */
|
|
15
14
|
export const TypeId: ClientResponse.TypeId = Symbol.for("@effect/platform/Http/ClientResponse") as ClientResponse.TypeId
|
|
@@ -47,25 +46,27 @@ class ClientResponseImpl implements ClientResponse.ClientResponse {
|
|
|
47
46
|
get stream(): Stream.Stream<Uint8Array, Error.ResponseError> {
|
|
48
47
|
return this.source.body
|
|
49
48
|
? Stream.fromReadableStream(() => this.source.body!, (_) =>
|
|
50
|
-
|
|
49
|
+
new Error.ResponseError({
|
|
51
50
|
request: this.request,
|
|
52
51
|
response: this,
|
|
53
52
|
reason: "Decode",
|
|
54
53
|
error: _
|
|
55
54
|
}))
|
|
56
|
-
: Stream.fail(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
55
|
+
: Stream.fail(
|
|
56
|
+
new Error.ResponseError({
|
|
57
|
+
request: this.request,
|
|
58
|
+
response: this,
|
|
59
|
+
reason: "EmptyBody",
|
|
60
|
+
error: "can not create stream from empty body"
|
|
61
|
+
})
|
|
62
|
+
)
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
get json(): Effect.Effect<unknown, Error.ResponseError> {
|
|
65
66
|
return Effect.tryMap(this.text, {
|
|
66
67
|
try: (text) => text === "" ? null : JSON.parse(text) as unknown,
|
|
67
68
|
catch: (_) =>
|
|
68
|
-
|
|
69
|
+
new Error.ResponseError({
|
|
69
70
|
request: this.request,
|
|
70
71
|
response: this,
|
|
71
72
|
reason: "Decode",
|
|
@@ -79,7 +80,7 @@ class ClientResponseImpl implements ClientResponse.ClientResponse {
|
|
|
79
80
|
return this.textBody ??= Effect.tryPromise({
|
|
80
81
|
try: () => this.source.text(),
|
|
81
82
|
catch: (_) =>
|
|
82
|
-
|
|
83
|
+
new Error.ResponseError({
|
|
83
84
|
request: this.request,
|
|
84
85
|
response: this,
|
|
85
86
|
reason: "Decode",
|
|
@@ -93,7 +94,7 @@ class ClientResponseImpl implements ClientResponse.ClientResponse {
|
|
|
93
94
|
Effect.try({
|
|
94
95
|
try: () => UrlParams.fromInput(new URLSearchParams(_)),
|
|
95
96
|
catch: (_) =>
|
|
96
|
-
|
|
97
|
+
new Error.ResponseError({
|
|
97
98
|
request: this.request,
|
|
98
99
|
response: this,
|
|
99
100
|
reason: "Decode",
|
|
@@ -107,7 +108,7 @@ class ClientResponseImpl implements ClientResponse.ClientResponse {
|
|
|
107
108
|
return this.formDataBody ??= Effect.tryPromise({
|
|
108
109
|
try: () => this.source.formData(),
|
|
109
110
|
catch: (_) =>
|
|
110
|
-
|
|
111
|
+
new Error.ResponseError({
|
|
111
112
|
request: this.request,
|
|
112
113
|
response: this,
|
|
113
114
|
reason: "Decode",
|
|
@@ -121,7 +122,7 @@ class ClientResponseImpl implements ClientResponse.ClientResponse {
|
|
|
121
122
|
return this.arrayBufferBody ??= Effect.tryPromise({
|
|
122
123
|
try: () => this.source.arrayBuffer(),
|
|
123
124
|
catch: (_) =>
|
|
124
|
-
|
|
125
|
+
new Error.ResponseError({
|
|
125
126
|
request: this.request,
|
|
126
127
|
response: this,
|
|
127
128
|
reason: "Decode",
|
|
@@ -29,7 +29,7 @@ class MultiplexImpl<R, E>
|
|
|
29
29
|
this[TypeId] = TypeId
|
|
30
30
|
|
|
31
31
|
let execute: (request: ServerRequest.ServerRequest) => App.Default<R, E | Error.RouteNotFound> = (request) =>
|
|
32
|
-
Effect.fail(Error.RouteNotFound({ request }))
|
|
32
|
+
Effect.fail(new Error.RouteNotFound({ request }))
|
|
33
33
|
|
|
34
34
|
for (let i = apps.length - 1; i >= 0; i--) {
|
|
35
35
|
const [predicate, app] = apps[i]
|
|
@@ -151,7 +151,7 @@ const toHttpApp = <R, E>(
|
|
|
151
151
|
result = router.find("GET", request.url)
|
|
152
152
|
}
|
|
153
153
|
if (result === undefined) {
|
|
154
|
-
return Effect.fail(Error.RouteNotFound({ request }))
|
|
154
|
+
return Effect.fail(new Error.RouteNotFound({ request }))
|
|
155
155
|
}
|
|
156
156
|
const route = result.handler
|
|
157
157
|
if (route.prefix._tag === "Some") {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import * as Cause from "effect/Cause"
|
|
2
|
-
import * as Data from "effect/Data"
|
|
3
2
|
import * as FiberId from "effect/FiberId"
|
|
4
3
|
import { globalValue } from "effect/GlobalValue"
|
|
5
4
|
import * as Option from "effect/Option"
|
|
@@ -11,28 +10,9 @@ export const TypeId: Error.TypeId = Symbol.for(
|
|
|
11
10
|
"@effect/platform/Http/Error"
|
|
12
11
|
) as Error.TypeId
|
|
13
12
|
|
|
14
|
-
const make = <A extends Error.HttpServerError>(tag: A["_tag"]) => (props: Omit<A, Error.HttpError.ProvidedFields>): A =>
|
|
15
|
-
Data.struct({
|
|
16
|
-
[TypeId]: TypeId,
|
|
17
|
-
_tag: tag,
|
|
18
|
-
...props
|
|
19
|
-
} as A)
|
|
20
|
-
|
|
21
13
|
/** @internal */
|
|
22
14
|
export const isServerError = (u: unknown): u is Error.HttpServerError => Predicate.hasProperty(u, TypeId)
|
|
23
15
|
|
|
24
|
-
/** @internal */
|
|
25
|
-
export const requestError = make<Error.RequestError>("RequestError")
|
|
26
|
-
|
|
27
|
-
/** @internal */
|
|
28
|
-
export const responseError = make<Error.ResponseError>("ResponseError")
|
|
29
|
-
|
|
30
|
-
/** @internal */
|
|
31
|
-
export const routeNotFound = make<Error.RouteNotFound>("RouteNotFound")
|
|
32
|
-
|
|
33
|
-
/** @internal */
|
|
34
|
-
export const serveError = make<Error.ServeError>("ServeError")
|
|
35
|
-
|
|
36
16
|
/** @internal */
|
|
37
17
|
export const clientAbortFiberId = globalValue(
|
|
38
18
|
"@effect/platform/Http/ServerError/clientAbortFiberId",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type * as ParseResult from "@effect/schema/ParseResult"
|
|
2
2
|
import type * as Schema from "@effect/schema/Schema"
|
|
3
|
+
import * as Channel from "effect/Channel"
|
|
3
4
|
import * as Context from "effect/Context"
|
|
4
5
|
import * as Effect from "effect/Effect"
|
|
5
6
|
import * as Option from "effect/Option"
|
|
@@ -14,6 +15,7 @@ import * as Error from "../../Http/ServerError.js"
|
|
|
14
15
|
import type * as ServerRequest from "../../Http/ServerRequest.js"
|
|
15
16
|
import * as UrlParams from "../../Http/UrlParams.js"
|
|
16
17
|
import type * as Path from "../../Path.js"
|
|
18
|
+
import * as Socket from "../../Socket.js"
|
|
17
19
|
|
|
18
20
|
/** @internal */
|
|
19
21
|
export const TypeId: ServerRequest.TypeId = Symbol.for("@effect/platform/Http/ServerRequest") as ServerRequest.TypeId
|
|
@@ -21,6 +23,12 @@ export const TypeId: ServerRequest.TypeId = Symbol.for("@effect/platform/Http/Se
|
|
|
21
23
|
/** @internal */
|
|
22
24
|
export const serverRequestTag = Context.GenericTag<ServerRequest.ServerRequest>("@effect/platform/Http/ServerRequest")
|
|
23
25
|
|
|
26
|
+
/** @internal */
|
|
27
|
+
export const upgrade = Effect.flatMap(serverRequestTag, (request) => request.upgrade)
|
|
28
|
+
|
|
29
|
+
/** @internal */
|
|
30
|
+
export const upgradeChannel = <IE = never>() => Channel.unwrap(Effect.map(upgrade, Socket.toChannelWith<IE>()))
|
|
31
|
+
|
|
24
32
|
/** @internal */
|
|
25
33
|
export const multipartPersisted = Effect.flatMap(serverRequestTag, (request) => request.multipart)
|
|
26
34
|
|
|
@@ -90,7 +98,7 @@ export const schemaBodyFormJson = <A, I, R>(schema: Schema.Schema<A, I, R>) => {
|
|
|
90
98
|
if (isMultipart(request)) {
|
|
91
99
|
return Effect.flatMap(
|
|
92
100
|
Effect.mapError(request.multipart, (error) =>
|
|
93
|
-
Error.RequestError({
|
|
101
|
+
new Error.RequestError({
|
|
94
102
|
request,
|
|
95
103
|
reason: "Decode",
|
|
96
104
|
error
|
|
@@ -150,16 +158,18 @@ class ServerRequestImpl implements ServerRequest.ServerRequest {
|
|
|
150
158
|
get stream(): Stream.Stream<Uint8Array, Error.RequestError> {
|
|
151
159
|
return this.source.body
|
|
152
160
|
? Stream.fromReadableStream(() => this.source.body as any, (_) =>
|
|
153
|
-
Error.RequestError({
|
|
161
|
+
new Error.RequestError({
|
|
154
162
|
request: this,
|
|
155
163
|
reason: "Decode",
|
|
156
164
|
error: _
|
|
157
165
|
}))
|
|
158
|
-
: Stream.fail(
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
166
|
+
: Stream.fail(
|
|
167
|
+
new Error.RequestError({
|
|
168
|
+
request: this,
|
|
169
|
+
reason: "Decode",
|
|
170
|
+
error: "can not create stream from empty body"
|
|
171
|
+
})
|
|
172
|
+
)
|
|
163
173
|
}
|
|
164
174
|
|
|
165
175
|
private textEffect: Effect.Effect<string, Error.RequestError> | undefined
|
|
@@ -171,7 +181,7 @@ class ServerRequestImpl implements ServerRequest.ServerRequest {
|
|
|
171
181
|
Effect.tryPromise({
|
|
172
182
|
try: () => this.source.text(),
|
|
173
183
|
catch: (error) =>
|
|
174
|
-
Error.RequestError({
|
|
184
|
+
new Error.RequestError({
|
|
175
185
|
request: this,
|
|
176
186
|
reason: "Decode",
|
|
177
187
|
error
|
|
@@ -185,7 +195,7 @@ class ServerRequestImpl implements ServerRequest.ServerRequest {
|
|
|
185
195
|
return Effect.tryMap(this.text, {
|
|
186
196
|
try: (_) => JSON.parse(_) as unknown,
|
|
187
197
|
catch: (error) =>
|
|
188
|
-
Error.RequestError({
|
|
198
|
+
new Error.RequestError({
|
|
189
199
|
request: this,
|
|
190
200
|
reason: "Decode",
|
|
191
201
|
error
|
|
@@ -198,7 +208,7 @@ class ServerRequestImpl implements ServerRequest.ServerRequest {
|
|
|
198
208
|
Effect.try({
|
|
199
209
|
try: () => UrlParams.fromInput(new URLSearchParams(_)),
|
|
200
210
|
catch: (error) =>
|
|
201
|
-
Error.RequestError({
|
|
211
|
+
new Error.RequestError({
|
|
202
212
|
request: this,
|
|
203
213
|
reason: "Decode",
|
|
204
214
|
error
|
|
@@ -243,7 +253,7 @@ class ServerRequestImpl implements ServerRequest.ServerRequest {
|
|
|
243
253
|
Effect.tryPromise({
|
|
244
254
|
try: () => this.source.arrayBuffer(),
|
|
245
255
|
catch: (error) =>
|
|
246
|
-
Error.RequestError({
|
|
256
|
+
new Error.RequestError({
|
|
247
257
|
request: this,
|
|
248
258
|
reason: "Decode",
|
|
249
259
|
error
|
|
@@ -252,4 +262,14 @@ class ServerRequestImpl implements ServerRequest.ServerRequest {
|
|
|
252
262
|
))
|
|
253
263
|
return this.arrayBufferEffect
|
|
254
264
|
}
|
|
265
|
+
|
|
266
|
+
get upgrade(): Effect.Effect<Socket.Socket, Error.RequestError> {
|
|
267
|
+
return Effect.fail(
|
|
268
|
+
new Error.RequestError({
|
|
269
|
+
request: this,
|
|
270
|
+
reason: "Decode",
|
|
271
|
+
error: "Not an upgradeable ServerRequest"
|
|
272
|
+
})
|
|
273
|
+
)
|
|
274
|
+
}
|
|
255
275
|
}
|