@effect/platform 0.46.2 → 0.47.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/PlatformLogger/package.json +6 -0
- package/Socket/package.json +6 -0
- package/dist/cjs/Http/Router.js.map +1 -1
- package/dist/cjs/Http/ServerRequest.js +11 -1
- package/dist/cjs/Http/ServerRequest.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/router.js +5 -4
- package/dist/cjs/internal/http/router.js.map +1 -1
- package/dist/cjs/internal/http/serverRequest.js +15 -1
- 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/Http/Router.d.ts +39 -37
- package/dist/dts/Http/Router.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/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/http/router.d.ts +1 -2
- package/dist/dts/internal/http/router.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/Http/Router.js.map +1 -1
- package/dist/esm/Http/ServerRequest.js +10 -0
- package/dist/esm/Http/ServerRequest.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/router.js +5 -4
- package/dist/esm/internal/http/router.js.map +1 -1
- package/dist/esm/internal/http/serverRequest.js +13 -0
- 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/Http/Router.ts +40 -147
- package/src/Http/ServerRequest.ts +25 -0
- package/src/PlatformLogger.ts +59 -0
- package/src/Socket.ts +291 -0
- package/src/index.ts +10 -0
- package/src/internal/http/router.ts +86 -38
- package/src/internal/http/serverRequest.ts +16 -0
- 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
|
*/
|
|
@@ -7,6 +7,7 @@ import * as Effectable from "effect/Effectable"
|
|
|
7
7
|
import { dual } from "effect/Function"
|
|
8
8
|
import * as Inspectable from "effect/Inspectable"
|
|
9
9
|
import * as Option from "effect/Option"
|
|
10
|
+
import * as Predicate from "effect/Predicate"
|
|
10
11
|
import * as FindMyWay from "find-my-way-ts"
|
|
11
12
|
import type * as App from "../../Http/App.js"
|
|
12
13
|
import type * as Method from "../../Http/Method.js"
|
|
@@ -63,7 +64,9 @@ class RouterImpl<R, E> extends Effectable.StructuralClass<
|
|
|
63
64
|
readonly [TypeId]: Router.TypeId
|
|
64
65
|
constructor(
|
|
65
66
|
readonly routes: Chunk.Chunk<Router.Route<R, E>>,
|
|
66
|
-
readonly mounts: Chunk.Chunk<
|
|
67
|
+
readonly mounts: Chunk.Chunk<
|
|
68
|
+
readonly [prefix: string, httpApp: App.Default<R, E>, options?: { readonly includePrefix?: boolean } | undefined]
|
|
69
|
+
>
|
|
67
70
|
) {
|
|
68
71
|
super()
|
|
69
72
|
this[TypeId] = TypeId
|
|
@@ -95,9 +98,24 @@ class RouterImpl<R, E> extends Effectable.StructuralClass<
|
|
|
95
98
|
|
|
96
99
|
const toHttpApp = <R, E>(
|
|
97
100
|
self: Router.Router<R, E>
|
|
98
|
-
): App.Default<
|
|
101
|
+
): App.Default<R, E | Error.RouteNotFound> => {
|
|
99
102
|
const router = FindMyWay.make<Router.Route<R, E>>()
|
|
100
|
-
const mounts = Chunk.toReadonlyArray(self.mounts)
|
|
103
|
+
const mounts = Chunk.toReadonlyArray(self.mounts).map(([path, app, options]) =>
|
|
104
|
+
[
|
|
105
|
+
path,
|
|
106
|
+
new RouteContextImpl(
|
|
107
|
+
new RouteImpl(
|
|
108
|
+
"*",
|
|
109
|
+
options?.includePrefix ? `${path}/*` as Router.PathInput : "/*",
|
|
110
|
+
app,
|
|
111
|
+
options?.includePrefix ? Option.none() : Option.some(path)
|
|
112
|
+
),
|
|
113
|
+
{},
|
|
114
|
+
{}
|
|
115
|
+
),
|
|
116
|
+
options
|
|
117
|
+
] as const
|
|
118
|
+
)
|
|
101
119
|
const mountsLen = mounts.length
|
|
102
120
|
Chunk.forEach(self.routes, (route) => {
|
|
103
121
|
if (route.method === "*") {
|
|
@@ -108,16 +126,22 @@ const toHttpApp = <R, E>(
|
|
|
108
126
|
})
|
|
109
127
|
return Effect.flatMap(
|
|
110
128
|
ServerRequest.ServerRequest,
|
|
111
|
-
(request): App.Default<
|
|
129
|
+
(request): App.Default<R, E | Error.RouteNotFound> => {
|
|
112
130
|
if (mountsLen > 0) {
|
|
113
131
|
for (let i = 0; i < mountsLen; i++) {
|
|
114
|
-
const [path,
|
|
132
|
+
const [path, context, options] = mounts[i]
|
|
115
133
|
if (request.url.startsWith(path)) {
|
|
116
134
|
return Effect.provideService(
|
|
117
|
-
|
|
135
|
+
Effect.provideService(
|
|
136
|
+
context.route.handler as App.Default<R, E>,
|
|
137
|
+
RouteContext,
|
|
138
|
+
context
|
|
139
|
+
),
|
|
118
140
|
ServerRequest.ServerRequest,
|
|
119
|
-
|
|
120
|
-
|
|
141
|
+
options?.includePrefix ?
|
|
142
|
+
request :
|
|
143
|
+
sliceRequestUrl(request, path)
|
|
144
|
+
)
|
|
121
145
|
}
|
|
122
146
|
}
|
|
123
147
|
}
|
|
@@ -247,19 +271,32 @@ export const mount = dual<
|
|
|
247
271
|
export const mountApp = dual<
|
|
248
272
|
<R1, E1>(
|
|
249
273
|
path: `/${string}`,
|
|
250
|
-
that: App.Default<R1, E1
|
|
274
|
+
that: App.Default<R1, E1>,
|
|
275
|
+
options?: {
|
|
276
|
+
readonly includePrefix?: boolean
|
|
277
|
+
}
|
|
251
278
|
) => <R, E>(
|
|
252
279
|
self: Router.Router<R, E>
|
|
253
|
-
) => Router.Router<Router.Router.ExcludeProvided<
|
|
280
|
+
) => Router.Router<R | Router.Router.ExcludeProvided<R1>, E | E1>,
|
|
254
281
|
<R, E, R1, E1>(
|
|
255
282
|
self: Router.Router<R, E>,
|
|
256
283
|
path: `/${string}`,
|
|
257
|
-
that: App.Default<R1, E1
|
|
258
|
-
|
|
284
|
+
that: App.Default<R1, E1>,
|
|
285
|
+
options?: {
|
|
286
|
+
readonly includePrefix?: boolean
|
|
287
|
+
}
|
|
288
|
+
) => Router.Router<R | Router.Router.ExcludeProvided<R1>, E | E1>
|
|
259
289
|
>(
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
290
|
+
(args) => Predicate.hasProperty(args[0], TypeId),
|
|
291
|
+
<R, E, R1, E1>(
|
|
292
|
+
self: Router.Router<R, E>,
|
|
293
|
+
path: `/${string}`,
|
|
294
|
+
that: App.Default<R1, E1>,
|
|
295
|
+
options?: {
|
|
296
|
+
readonly includePrefix?: boolean
|
|
297
|
+
}
|
|
298
|
+
): Router.Router<R | Router.Router.ExcludeProvided<R1>, E | E1> =>
|
|
299
|
+
new RouterImpl<any, any>(self.routes, Chunk.append(self.mounts, [removeTrailingSlash(path), that, options])) as any
|
|
263
300
|
)
|
|
264
301
|
|
|
265
302
|
/** @internal */
|
|
@@ -269,12 +306,12 @@ export const route = (method: Method.Method | "*"): {
|
|
|
269
306
|
handler: Router.Route.Handler<R1, E1>
|
|
270
307
|
): <R, E>(
|
|
271
308
|
self: Router.Router<R, E>
|
|
272
|
-
) => Router.Router<Router.Router.ExcludeProvided<
|
|
309
|
+
) => Router.Router<R | Router.Router.ExcludeProvided<R1>, E1 | E>
|
|
273
310
|
<R, E, R1, E1>(
|
|
274
311
|
self: Router.Router<R, E>,
|
|
275
312
|
path: Router.PathInput,
|
|
276
313
|
handler: Router.Route.Handler<R1, E1>
|
|
277
|
-
): Router.Router<Router.Router.ExcludeProvided<
|
|
314
|
+
): Router.Router<R | Router.Router.ExcludeProvided<R1>, E1 | E>
|
|
278
315
|
} =>
|
|
279
316
|
dual<
|
|
280
317
|
<R1, E1>(
|
|
@@ -282,12 +319,12 @@ export const route = (method: Method.Method | "*"): {
|
|
|
282
319
|
handler: Router.Route.Handler<R1, E1>
|
|
283
320
|
) => <R, E>(
|
|
284
321
|
self: Router.Router<R, E>
|
|
285
|
-
) => Router.Router<Router.Router.ExcludeProvided<
|
|
322
|
+
) => Router.Router<R | Router.Router.ExcludeProvided<R1>, E | E1>,
|
|
286
323
|
<R, E, R1, E1>(
|
|
287
324
|
self: Router.Router<R, E>,
|
|
288
325
|
path: Router.PathInput,
|
|
289
326
|
handler: Router.Route.Handler<R1, E1>
|
|
290
|
-
) => Router.Router<Router.Router.ExcludeProvided<
|
|
327
|
+
) => Router.Router<R | Router.Router.ExcludeProvided<R1>, E | E1>
|
|
291
328
|
>(3, (self, path, handler) =>
|
|
292
329
|
new RouterImpl<any, any>(
|
|
293
330
|
Chunk.append(self.routes, new RouteImpl(method, path, handler)),
|
|
@@ -343,22 +380,22 @@ export const use = dual<
|
|
|
343
380
|
export const catchAll = dual<
|
|
344
381
|
<E, R2, E2>(
|
|
345
382
|
f: (e: E) => Router.Route.Handler<R2, E2>
|
|
346
|
-
) => <R>(self: Router.Router<R, E>) => Router.Router<Router.Router.ExcludeProvided<R2
|
|
383
|
+
) => <R>(self: Router.Router<R, E>) => Router.Router<R | Router.Router.ExcludeProvided<R2>, E2>,
|
|
347
384
|
<R, E, R2, E2>(
|
|
348
385
|
self: Router.Router<R, E>,
|
|
349
386
|
f: (e: E) => Router.Route.Handler<R2, E2>
|
|
350
|
-
) => Router.Router<Router.Router.ExcludeProvided<R2
|
|
387
|
+
) => Router.Router<R | Router.Router.ExcludeProvided<R2>, E2>
|
|
351
388
|
>(2, (self, f) => use(self, Effect.catchAll(f)))
|
|
352
389
|
|
|
353
390
|
/** @internal */
|
|
354
391
|
export const catchAllCause = dual<
|
|
355
392
|
<E, R2, E2>(
|
|
356
393
|
f: (e: Cause.Cause<E>) => Router.Route.Handler<R2, E2>
|
|
357
|
-
) => <R>(self: Router.Router<R, E>) => Router.Router<Router.Router.ExcludeProvided<R2
|
|
394
|
+
) => <R>(self: Router.Router<R, E>) => Router.Router<R | Router.Router.ExcludeProvided<R2>, E2>,
|
|
358
395
|
<R, E, R2, E2>(
|
|
359
396
|
self: Router.Router<R, E>,
|
|
360
397
|
f: (e: Cause.Cause<E>) => Router.Route.Handler<R2, E2>
|
|
361
|
-
) => Router.Router<Router.Router.ExcludeProvided<R2
|
|
398
|
+
) => Router.Router<R | Router.Router.ExcludeProvided<R2>, E2>
|
|
362
399
|
>(2, (self, f) => use(self, Effect.catchAllCause(f)))
|
|
363
400
|
|
|
364
401
|
/** @internal */
|
|
@@ -368,12 +405,12 @@ export const catchTag = dual<
|
|
|
368
405
|
f: (e: Extract<E, { _tag: K }>) => Router.Route.Handler<R1, E1>
|
|
369
406
|
) => <R>(
|
|
370
407
|
self: Router.Router<R, E>
|
|
371
|
-
) => Router.Router<Router.Router.ExcludeProvided<
|
|
408
|
+
) => Router.Router<R | Router.Router.ExcludeProvided<R1>, Exclude<E, { _tag: K }> | E1>,
|
|
372
409
|
<R, E, K extends (E extends { _tag: string } ? E["_tag"] : never), R1, E1>(
|
|
373
410
|
self: Router.Router<R, E>,
|
|
374
411
|
k: K,
|
|
375
412
|
f: (e: Extract<E, { _tag: K }>) => Router.Route.Handler<R1, E1>
|
|
376
|
-
) => Router.Router<Router.Router.ExcludeProvided<
|
|
413
|
+
) => Router.Router<R | Router.Router.ExcludeProvided<R1>, Exclude<E, { _tag: K }> | E1>
|
|
377
414
|
>(3, (self, k, f) => use(self, Effect.catchTag(k, f)))
|
|
378
415
|
|
|
379
416
|
/** @internal */
|
|
@@ -387,9 +424,9 @@ export const catchTags: {
|
|
|
387
424
|
>(
|
|
388
425
|
cases: Cases
|
|
389
426
|
): <R>(self: Router.Router<R, E>) => Router.Router<
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
427
|
+
| R
|
|
428
|
+
| Router.Router.ExcludeProvided<
|
|
429
|
+
{
|
|
393
430
|
[K in keyof Cases]: Cases[K] extends ((...args: Array<any>) => Effect.Effect<any, any, infer R>) ? R : never
|
|
394
431
|
}[keyof Cases]
|
|
395
432
|
>,
|
|
@@ -409,9 +446,9 @@ export const catchTags: {
|
|
|
409
446
|
self: Router.Router<R, E>,
|
|
410
447
|
cases: Cases
|
|
411
448
|
): Router.Router<
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
449
|
+
| R
|
|
450
|
+
| Router.Router.ExcludeProvided<
|
|
451
|
+
{
|
|
415
452
|
[K in keyof Cases]: Cases[K] extends ((...args: Array<any>) => Effect.Effect<any, any, infer R>) ? R : never
|
|
416
453
|
}[keyof Cases]
|
|
417
454
|
>,
|
|
@@ -428,18 +465,17 @@ export const provideService = dual<
|
|
|
428
465
|
service: Context.Tag.Service<T>
|
|
429
466
|
) => <R, E>(
|
|
430
467
|
self: Router.Router<R, E>
|
|
431
|
-
) => Router.Router<
|
|
468
|
+
) => Router.Router<Exclude<R, Context.Tag.Identifier<T>>, E>,
|
|
432
469
|
<R, E, T extends Context.Tag<any, any>>(
|
|
433
470
|
self: Router.Router<R, E>,
|
|
434
471
|
tag: T,
|
|
435
472
|
service: Context.Tag.Service<T>
|
|
436
|
-
) => Router.Router<
|
|
473
|
+
) => Router.Router<Exclude<R, Context.Tag.Identifier<T>>, E>
|
|
437
474
|
>(3, <R, E, T extends Context.Tag<any, any>>(
|
|
438
475
|
self: Router.Router<R, E>,
|
|
439
476
|
tag: T,
|
|
440
477
|
service: Context.Tag.Service<T>
|
|
441
|
-
): Router.Router<
|
|
442
|
-
use(self, Effect.provideService(tag, service)))
|
|
478
|
+
): Router.Router<Exclude<R, Context.Tag.Identifier<T>>, E> => use(self, Effect.provideService(tag, service)))
|
|
443
479
|
|
|
444
480
|
/* @internal */
|
|
445
481
|
export const provideServiceEffect = dual<
|
|
@@ -449,7 +485,10 @@ export const provideServiceEffect = dual<
|
|
|
449
485
|
) => <R, E>(
|
|
450
486
|
self: Router.Router<R, E>
|
|
451
487
|
) => Router.Router<
|
|
452
|
-
|
|
488
|
+
Exclude<
|
|
489
|
+
R | Router.Router.ExcludeProvided<R1>,
|
|
490
|
+
Context.Tag.Identifier<T>
|
|
491
|
+
>,
|
|
453
492
|
E | E1
|
|
454
493
|
>,
|
|
455
494
|
<R, E, T extends Context.Tag<any, any>, R1, E1>(
|
|
@@ -457,11 +496,20 @@ export const provideServiceEffect = dual<
|
|
|
457
496
|
tag: T,
|
|
458
497
|
effect: Effect.Effect<Context.Tag.Service<T>, E1, R1>
|
|
459
498
|
) => Router.Router<
|
|
460
|
-
|
|
499
|
+
Exclude<
|
|
500
|
+
R | Router.Router.ExcludeProvided<R1>,
|
|
501
|
+
Context.Tag.Identifier<T>
|
|
502
|
+
>,
|
|
461
503
|
E | E1
|
|
462
504
|
>
|
|
463
505
|
>(3, <R, E, T extends Context.Tag<any, any>, R1, E1>(
|
|
464
506
|
self: Router.Router<R, E>,
|
|
465
507
|
tag: T,
|
|
466
508
|
effect: Effect.Effect<Context.Tag.Service<T>, E1, R1>
|
|
467
|
-
)
|
|
509
|
+
): Router.Router<
|
|
510
|
+
Exclude<
|
|
511
|
+
R | Router.Router.ExcludeProvided<R1>,
|
|
512
|
+
Context.Tag.Identifier<T>
|
|
513
|
+
>,
|
|
514
|
+
E | E1
|
|
515
|
+
> => use(self, Effect.provideServiceEffect(tag, effect)) as any)
|
|
@@ -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
|
|
|
@@ -252,4 +260,12 @@ class ServerRequestImpl implements ServerRequest.ServerRequest {
|
|
|
252
260
|
))
|
|
253
261
|
return this.arrayBufferEffect
|
|
254
262
|
}
|
|
263
|
+
|
|
264
|
+
get upgrade(): Effect.Effect<Socket.Socket, Error.RequestError> {
|
|
265
|
+
return Effect.fail(Error.RequestError({
|
|
266
|
+
request: this,
|
|
267
|
+
reason: "Decode",
|
|
268
|
+
error: "Not an upgradeable ServerRequest"
|
|
269
|
+
}))
|
|
270
|
+
}
|
|
255
271
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 1.0.0
|
|
3
|
+
*/
|
|
4
|
+
import type { DurationInput } from "effect/Duration"
|
|
5
|
+
import * as Effect from "effect/Effect"
|
|
6
|
+
import { dual } from "effect/Function"
|
|
7
|
+
import * as Logger from "effect/Logger"
|
|
8
|
+
import type * as Scope from "effect/Scope"
|
|
9
|
+
import type { PlatformError } from "../Error.js"
|
|
10
|
+
import * as FileSystem from "../FileSystem.js"
|
|
11
|
+
|
|
12
|
+
/** @internal */
|
|
13
|
+
export const toFile = dual<
|
|
14
|
+
(
|
|
15
|
+
path: string,
|
|
16
|
+
options?: FileSystem.OpenFileOptions & {
|
|
17
|
+
readonly batchWindow?: DurationInput
|
|
18
|
+
}
|
|
19
|
+
) => <Message>(
|
|
20
|
+
self: Logger.Logger<Message, string>
|
|
21
|
+
) => Effect.Effect<Logger.Logger<Message, void>, PlatformError, Scope.Scope | FileSystem.FileSystem>,
|
|
22
|
+
<Message>(
|
|
23
|
+
self: Logger.Logger<Message, string>,
|
|
24
|
+
path: string,
|
|
25
|
+
options?: FileSystem.OpenFileOptions & {
|
|
26
|
+
readonly batchWindow?: DurationInput
|
|
27
|
+
}
|
|
28
|
+
) => Effect.Effect<Logger.Logger<Message, void>, PlatformError, Scope.Scope | FileSystem.FileSystem>
|
|
29
|
+
>(
|
|
30
|
+
(args) => Logger.isLogger(args[0]),
|
|
31
|
+
(self, path, options) =>
|
|
32
|
+
Effect.gen(function*(_) {
|
|
33
|
+
const fs = yield* _(FileSystem.FileSystem)
|
|
34
|
+
const logFile = yield* _(fs.open(path, { flag: "a+", ...options }))
|
|
35
|
+
const encoder = new TextEncoder()
|
|
36
|
+
return yield* _(Logger.batched(
|
|
37
|
+
self,
|
|
38
|
+
options?.batchWindow ?? 1000,
|
|
39
|
+
(output) => Effect.ignore(logFile.write(encoder.encode(output.join("\n") + "\n")))
|
|
40
|
+
))
|
|
41
|
+
})
|
|
42
|
+
)
|
package/src/internal/worker.ts
CHANGED
|
@@ -405,7 +405,11 @@ export const makePoolSerialized = <I extends Schema.TaggedRequest.Any>(
|
|
|
405
405
|
makeSerialized<I>(options),
|
|
406
406
|
Effect.tap((worker) => Effect.sync(() => workers.add(worker))),
|
|
407
407
|
Effect.tap((worker) => Effect.addFinalizer(() => Effect.sync(() => workers.delete(worker)))),
|
|
408
|
-
options.onCreate
|
|
408
|
+
options.onCreate
|
|
409
|
+
? Effect.tap(
|
|
410
|
+
options.onCreate as (worker: Worker.SerializedWorker<I>) => Effect.Effect<void, WorkerError>
|
|
411
|
+
)
|
|
412
|
+
: identity,
|
|
409
413
|
Effect.provideService(WorkerManager, manager)
|
|
410
414
|
)
|
|
411
415
|
const backing = yield* _(
|