@effect/platform 0.64.1 → 0.65.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.
Files changed (91) hide show
  1. package/FetchHttpClient/package.json +6 -0
  2. package/README.md +173 -195
  3. package/dist/cjs/FetchHttpClient.js +32 -0
  4. package/dist/cjs/FetchHttpClient.js.map +1 -0
  5. package/dist/cjs/HttpApiClient.js +1 -1
  6. package/dist/cjs/HttpApiClient.js.map +1 -1
  7. package/dist/cjs/HttpClient.js +5 -36
  8. package/dist/cjs/HttpClient.js.map +1 -1
  9. package/dist/cjs/HttpClientRequest.js +11 -11
  10. package/dist/cjs/HttpClientRequest.js.map +1 -1
  11. package/dist/cjs/HttpClientResponse.js +2 -61
  12. package/dist/cjs/HttpClientResponse.js.map +1 -1
  13. package/dist/cjs/HttpIncomingMessage.js +2 -29
  14. package/dist/cjs/HttpIncomingMessage.js.map +1 -1
  15. package/dist/cjs/Runtime.js +1 -1
  16. package/dist/cjs/Runtime.js.map +1 -1
  17. package/dist/cjs/Socket.js +42 -53
  18. package/dist/cjs/Socket.js.map +1 -1
  19. package/dist/cjs/index.js +3 -1
  20. package/dist/cjs/internal/fetchHttpClient.js +52 -0
  21. package/dist/cjs/internal/fetchHttpClient.js.map +1 -0
  22. package/dist/cjs/internal/httpClient.js +82 -71
  23. package/dist/cjs/internal/httpClient.js.map +1 -1
  24. package/dist/cjs/internal/httpClientRequest.js +17 -15
  25. package/dist/cjs/internal/httpClientRequest.js.map +1 -1
  26. package/dist/cjs/internal/httpClientResponse.js +1 -33
  27. package/dist/cjs/internal/httpClientResponse.js.map +1 -1
  28. package/dist/cjs/internal/worker.js +21 -29
  29. package/dist/cjs/internal/worker.js.map +1 -1
  30. package/dist/dts/FetchHttpClient.d.ts +27 -0
  31. package/dist/dts/FetchHttpClient.d.ts.map +1 -0
  32. package/dist/dts/HttpApiClient.d.ts +2 -2
  33. package/dist/dts/HttpClient.d.ts +53 -61
  34. package/dist/dts/HttpClient.d.ts.map +1 -1
  35. package/dist/dts/HttpClientRequest.d.ts +16 -15
  36. package/dist/dts/HttpClientRequest.d.ts.map +1 -1
  37. package/dist/dts/HttpClientResponse.d.ts +4 -90
  38. package/dist/dts/HttpClientResponse.d.ts.map +1 -1
  39. package/dist/dts/HttpIncomingMessage.d.ts +0 -16
  40. package/dist/dts/HttpIncomingMessage.d.ts.map +1 -1
  41. package/dist/dts/HttpServer.d.ts +1 -1
  42. package/dist/dts/Socket.d.ts +3 -3
  43. package/dist/dts/Socket.d.ts.map +1 -1
  44. package/dist/dts/index.d.ts +4 -0
  45. package/dist/dts/index.d.ts.map +1 -1
  46. package/dist/dts/internal/fetchHttpClient.d.ts +2 -0
  47. package/dist/dts/internal/fetchHttpClient.d.ts.map +1 -0
  48. package/dist/esm/FetchHttpClient.js +21 -0
  49. package/dist/esm/FetchHttpClient.js.map +1 -0
  50. package/dist/esm/HttpApiClient.js +1 -1
  51. package/dist/esm/HttpApiClient.js.map +1 -1
  52. package/dist/esm/HttpClient.js +4 -35
  53. package/dist/esm/HttpClient.js.map +1 -1
  54. package/dist/esm/HttpClientRequest.js +10 -10
  55. package/dist/esm/HttpClientRequest.js.map +1 -1
  56. package/dist/esm/HttpClientResponse.js +1 -63
  57. package/dist/esm/HttpClientResponse.js.map +1 -1
  58. package/dist/esm/HttpIncomingMessage.js +0 -24
  59. package/dist/esm/HttpIncomingMessage.js.map +1 -1
  60. package/dist/esm/Runtime.js +1 -1
  61. package/dist/esm/Runtime.js.map +1 -1
  62. package/dist/esm/Socket.js +42 -53
  63. package/dist/esm/Socket.js.map +1 -1
  64. package/dist/esm/index.js +4 -0
  65. package/dist/esm/index.js.map +1 -1
  66. package/dist/esm/internal/fetchHttpClient.js +44 -0
  67. package/dist/esm/internal/fetchHttpClient.js.map +1 -0
  68. package/dist/esm/internal/httpClient.js +79 -69
  69. package/dist/esm/internal/httpClient.js.map +1 -1
  70. package/dist/esm/internal/httpClientRequest.js +15 -13
  71. package/dist/esm/internal/httpClientRequest.js.map +1 -1
  72. package/dist/esm/internal/httpClientResponse.js +0 -24
  73. package/dist/esm/internal/httpClientResponse.js.map +1 -1
  74. package/dist/esm/internal/worker.js +21 -29
  75. package/dist/esm/internal/worker.js.map +1 -1
  76. package/package.json +11 -3
  77. package/src/FetchHttpClient.ts +25 -0
  78. package/src/HttpApiClient.ts +5 -5
  79. package/src/HttpClient.ts +79 -85
  80. package/src/HttpClientRequest.ts +26 -25
  81. package/src/HttpClientResponse.ts +4 -162
  82. package/src/HttpIncomingMessage.ts +0 -43
  83. package/src/HttpServer.ts +1 -1
  84. package/src/Runtime.ts +1 -1
  85. package/src/Socket.ts +42 -58
  86. package/src/index.ts +5 -0
  87. package/src/internal/fetchHttpClient.ts +53 -0
  88. package/src/internal/httpClient.ts +149 -125
  89. package/src/internal/httpClientRequest.ts +33 -18
  90. package/src/internal/httpClientResponse.ts +6 -96
  91. package/src/internal/worker.ts +40 -55
@@ -10,7 +10,6 @@ import { dual } from "effect/Function"
10
10
  import * as Global from "effect/GlobalValue"
11
11
  import type { Inspectable } from "effect/Inspectable"
12
12
  import * as Option from "effect/Option"
13
- import type * as Scope from "effect/Scope"
14
13
  import type * as Stream from "effect/Stream"
15
14
  import * as FileSystem from "./FileSystem.js"
16
15
  import type * as Headers from "./Headers.js"
@@ -53,18 +52,6 @@ export const schemaBodyJson = <A, I, R>(schema: Schema.Schema<A, I, R>, options?
53
52
  Effect.flatMap(self.json, parse)
54
53
  }
55
54
 
56
- /**
57
- * @since 1.0.0
58
- * @category schema
59
- */
60
- export const schemaBodyJsonScoped = <A, I, R>(schema: Schema.Schema<A, I, R>, options?: ParseOptions | undefined) => {
61
- const decode = schemaBodyJson(schema, options)
62
- return <E, E2, R2>(
63
- effect: Effect.Effect<HttpIncomingMessage<E>, E2, R2>
64
- ): Effect.Effect<A, ParseResult.ParseError | E | E2, Exclude<R, Scope.Scope> | Exclude<R2, Scope.Scope>> =>
65
- Effect.scoped(Effect.flatMap(effect, decode))
66
- }
67
-
68
55
  /**
69
56
  * @since 1.0.0
70
57
  * @category schema
@@ -78,21 +65,6 @@ export const schemaBodyUrlParams = <A, I extends Readonly<Record<string, string
78
65
  Effect.flatMap(self.urlParamsBody, (_) => parse(Object.fromEntries(_)))
79
66
  }
80
67
 
81
- /**
82
- * @since 1.0.0
83
- * @category schema
84
- */
85
- export const schemaBodyUrlParamsScoped = <A, I extends Readonly<Record<string, string | undefined>>, R>(
86
- schema: Schema.Schema<A, I, R>,
87
- options?: ParseOptions | undefined
88
- ) => {
89
- const decode = schemaBodyUrlParams(schema, options)
90
- return <E, E2, R2>(
91
- effect: Effect.Effect<HttpIncomingMessage<E>, E2, R2>
92
- ): Effect.Effect<A, ParseResult.ParseError | E | E2, Exclude<R, Scope.Scope> | Exclude<R2, Scope.Scope>> =>
93
- Effect.scoped(Effect.flatMap(effect, decode))
94
- }
95
-
96
68
  /**
97
69
  * @since 1.0.0
98
70
  * @category schema
@@ -105,21 +77,6 @@ export const schemaHeaders = <A, I extends Readonly<Record<string, string | unde
105
77
  return <E>(self: HttpIncomingMessage<E>): Effect.Effect<A, ParseResult.ParseError, R> => parse(self.headers)
106
78
  }
107
79
 
108
- /**
109
- * @since 1.0.0
110
- * @category schema
111
- */
112
- export const schemaHeadersScoped = <A, I extends Readonly<Record<string, string | undefined>>, R>(
113
- schema: Schema.Schema<A, I, R>,
114
- options?: ParseOptions | undefined
115
- ) => {
116
- const decode = schemaHeaders(schema, options)
117
- return <E, E2, R2>(
118
- effect: Effect.Effect<HttpIncomingMessage<E>, E2, R2>
119
- ): Effect.Effect<A, ParseResult.ParseError | E2, Exclude<R, Scope.Scope> | Exclude<R2, Scope.Scope>> =>
120
- Effect.scoped(Effect.flatMap(effect, decode))
121
- }
122
-
123
80
  /**
124
81
  * @since 1.0.0
125
82
  * @category fiber refs
package/src/HttpServer.ts CHANGED
@@ -201,5 +201,5 @@ export const withLogAddress: <A, E, R>(layer: Layer.Layer<A, E, R>) => Layer.Lay
201
201
  * @since 1.0.0
202
202
  * @category layers
203
203
  */
204
- export const layerTestClient: Layer.Layer<Client.HttpClient.Default, never, Client.HttpClient.Default | HttpServer> =
204
+ export const layerTestClient: Layer.Layer<Client.HttpClient.Service, never, Client.HttpClient.Service | HttpServer> =
205
205
  internal.layerTestClient
package/src/Runtime.ts CHANGED
@@ -63,7 +63,7 @@ const addPrettyLogger = (refs: FiberRefs.FiberRefs, fiberId: FiberId.Runtime) =>
63
63
  fiberRef: FiberRef.currentLoggers,
64
64
  value: loggers.pipe(
65
65
  HashSet.remove(Logger.defaultLogger),
66
- HashSet.add(Logger.prettyLogger())
66
+ HashSet.add(Logger.prettyLoggerDefault)
67
67
  )
68
68
  })
69
69
  }
package/src/Socket.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  * @since 1.0.0
3
3
  */
4
4
  import * as Channel from "effect/Channel"
5
- import * as Chunk from "effect/Chunk"
5
+ import type * as Chunk from "effect/Chunk"
6
6
  import * as Context from "effect/Context"
7
7
  import * as Deferred from "effect/Deferred"
8
8
  import type { DurationInput } from "effect/Duration"
@@ -14,6 +14,7 @@ import * as FiberSet from "effect/FiberSet"
14
14
  import { dual } from "effect/Function"
15
15
  import { globalValue } from "effect/GlobalValue"
16
16
  import * as Layer from "effect/Layer"
17
+ import * as Mailbox from "effect/Mailbox"
17
18
  import * as Predicate from "effect/Predicate"
18
19
  import * as Queue from "effect/Queue"
19
20
  import * as Scope from "effect/Scope"
@@ -52,11 +53,11 @@ export const Socket: Context.Tag<Socket, Socket> = Context.GenericTag<Socket>(
52
53
  */
53
54
  export interface Socket {
54
55
  readonly [TypeId]: TypeId
55
- readonly run: <_, E, R>(
56
- handler: (_: Uint8Array) => Effect.Effect<_, E, R>
56
+ readonly run: <_, E = never, R = never>(
57
+ handler: (_: Uint8Array) => Effect.Effect<_, E, R> | void
57
58
  ) => Effect.Effect<void, SocketError | E, R>
58
- readonly runRaw: <_, E, R>(
59
- handler: (_: string | Uint8Array) => Effect.Effect<_, E, R>
59
+ readonly runRaw: <_, E = never, R = never>(
60
+ handler: (_: string | Uint8Array) => Effect.Effect<_, E, R> | void
60
61
  ) => Effect.Effect<void, SocketError | E, R>
61
62
  readonly writer: Effect.Effect<
62
63
  (chunk: Uint8Array | string | CloseEvent) => Effect.Effect<boolean>,
@@ -190,27 +191,25 @@ export const toChannelMap = <IE, A>(
190
191
  > =>
191
192
  Effect.scope.pipe(
192
193
  Effect.bindTo("scope"),
193
- Effect.let("state", () => ({ finished: false, buffer: [] as Array<A> })),
194
- Effect.bind("semaphore", () => Effect.makeSemaphore(0)),
194
+ Effect.bind("mailbox", () => Mailbox.make<A, SocketError | IE>()),
195
195
  Effect.bind("writeScope", ({ scope }) => Scope.fork(scope, ExecutionStrategy.sequential)),
196
196
  Effect.bind("write", ({ writeScope }) => Scope.extend(self.writer, writeScope)),
197
- Effect.bind("deferred", () => Deferred.make<void, SocketError | IE>()),
198
197
  Effect.let(
199
198
  "input",
200
199
  (
201
- { deferred, write, writeScope }
200
+ { mailbox, write, writeScope }
202
201
  ): AsyncProducer.AsyncInputProducer<IE, Chunk.Chunk<Uint8Array | string | CloseEvent>, unknown> => ({
203
202
  awaitRead: () => Effect.void,
204
203
  emit(chunk) {
205
204
  return Effect.catchAllCause(
206
205
  Effect.forEach(chunk, write, { discard: true }),
207
- (cause) => Deferred.failCause(deferred, cause)
206
+ (cause) => mailbox.failCause(cause)
208
207
  )
209
208
  },
210
209
  error(error) {
211
210
  return Effect.zipRight(
212
211
  Scope.close(writeScope, Exit.void),
213
- Deferred.failCause(deferred, error)
212
+ mailbox.failCause(error)
214
213
  )
215
214
  },
216
215
  done() {
@@ -218,35 +217,16 @@ export const toChannelMap = <IE, A>(
218
217
  }
219
218
  })
220
219
  ),
221
- Effect.tap(({ deferred, scope, semaphore, state }) =>
220
+ Effect.tap(({ mailbox, scope }) =>
222
221
  self.runRaw((data) => {
223
- state.buffer.push(f(data))
224
- return semaphore.release(1)
222
+ mailbox.unsafeOffer(f(data))
225
223
  }).pipe(
226
- Effect.intoDeferred(deferred),
227
- Effect.raceFirst(Deferred.await(deferred)),
228
- Effect.ensuring(Effect.suspend(() => {
229
- state.finished = true
230
- return semaphore.release(1)
231
- })),
224
+ Mailbox.into(mailbox),
232
225
  Effect.forkIn(scope),
233
226
  Effect.interruptible
234
227
  )
235
228
  ),
236
- Effect.map(({ deferred, input, semaphore, state }) => {
237
- const loop: Channel.Channel<Chunk.Chunk<A>, unknown, SocketError | IE, unknown, void, unknown> = Channel.flatMap(
238
- semaphore.take(1),
239
- (_) => {
240
- if (state.buffer.length === 0) {
241
- return state.finished ? Deferred.await(deferred) : loop
242
- }
243
- const chunk = Chunk.unsafeFromArray(state.buffer)
244
- state.buffer = []
245
- return Channel.zipRight(Channel.write(chunk), state.finished ? Deferred.await(deferred) : loop)
246
- }
247
- )
248
- return Channel.embedInput(loop, input)
249
- }),
229
+ Effect.map(({ input, mailbox }) => Channel.embedInput(Mailbox.toChannel(mailbox), input)),
250
230
  Channel.unwrapScoped
251
231
  )
252
232
 
@@ -395,14 +375,7 @@ export const makeWebSocket = (url: string | Effect.Effect<string>, options?: {
395
375
  (typeof url === "string" ? Effect.succeed(url) : url).pipe(
396
376
  Effect.flatMap((url) => Effect.map(WebSocketConstructor, (f) => f(url)))
397
377
  ),
398
- (ws) =>
399
- Effect.sync(() => {
400
- ws.onclose = null
401
- ws.onerror = null
402
- ws.onmessage = null
403
- ws.onopen = null
404
- return ws.close()
405
- })
378
+ (ws) => Effect.sync(() => ws.close())
406
379
  ),
407
380
  options
408
381
  )
@@ -424,7 +397,7 @@ export const fromWebSocket = <R>(
424
397
  (sendQueue) => {
425
398
  const acquireContext = fiber.getFiberRef(FiberRef.currentContext) as Context.Context<R>
426
399
  const closeCodeIsError = options?.closeCodeIsError ?? defaultCloseCodeIsError
427
- const runRaw = <_, E, R>(handler: (_: string | Uint8Array) => Effect.Effect<_, E, R>) =>
400
+ const runRaw = <_, E, R>(handler: (_: string | Uint8Array) => Effect.Effect<_, E, R> | void) =>
428
401
  acquire.pipe(
429
402
  Effect.bindTo("ws"),
430
403
  Effect.bind("fiberSet", () => FiberSet.make<any, E | SocketError>()),
@@ -433,16 +406,29 @@ export const fromWebSocket = <R>(
433
406
  Effect.tap(({ fiberSet, run, ws }) => {
434
407
  let open = false
435
408
 
436
- ws.onmessage = (event) => {
437
- run(handler(
409
+ function onMessage(event: MessageEvent) {
410
+ const result = handler(
438
411
  typeof event.data === "string"
439
412
  ? event.data
440
413
  : event.data instanceof Uint8Array
441
414
  ? event.data
442
415
  : new Uint8Array(event.data)
443
- ))
416
+ )
417
+ if (Effect.isEffect(result)) {
418
+ run(result)
419
+ }
444
420
  }
445
- ws.onclose = (event) => {
421
+ function onError(cause: Event) {
422
+ ws.removeEventListener("message", onMessage)
423
+ ws.removeEventListener("close", onClose)
424
+ Deferred.unsafeDone(
425
+ fiberSet.deferred,
426
+ Effect.fail(new SocketGenericError({ reason: open ? "Read" : "Open", cause }))
427
+ )
428
+ }
429
+ function onClose(event: globalThis.CloseEvent) {
430
+ ws.removeEventListener("message", onMessage)
431
+ ws.removeEventListener("error", onError)
446
432
  Deferred.unsafeDone(
447
433
  fiberSet.deferred,
448
434
  Effect.fail(
@@ -454,19 +440,17 @@ export const fromWebSocket = <R>(
454
440
  )
455
441
  )
456
442
  }
457
- ws.onerror = (cause) => {
458
- Deferred.unsafeDone(
459
- fiberSet.deferred,
460
- Effect.fail(new SocketGenericError({ reason: open ? "Read" : "Open", cause }))
461
- )
462
- }
443
+
444
+ ws.addEventListener("close", onClose, { once: true })
445
+ ws.addEventListener("error", onError, { once: true })
446
+ ws.addEventListener("message", onMessage)
463
447
 
464
448
  if (ws.readyState !== 1) {
465
449
  const openDeferred = Deferred.unsafeMake<void>(fiber.id())
466
- ws.onopen = () => {
450
+ ws.addEventListener("open", () => {
467
451
  open = true
468
452
  Deferred.unsafeDone(openDeferred, Effect.void)
469
- }
453
+ }, { once: true })
470
454
  return Deferred.await(openDeferred).pipe(
471
455
  Effect.timeoutFail({
472
456
  duration: options?.openTimeout ?? 10000,
@@ -514,7 +498,7 @@ export const fromWebSocket = <R>(
514
498
  )
515
499
 
516
500
  const encoder = new TextEncoder()
517
- const run = <_, E, R>(handler: (_: Uint8Array) => Effect.Effect<_, E, R>) =>
501
+ const run = <_, E, R>(handler: (_: Uint8Array) => Effect.Effect<_, E, R> | void) =>
518
502
  runRaw((data) =>
519
503
  typeof data === "string"
520
504
  ? handler(encoder.encode(data))
@@ -600,7 +584,7 @@ export const fromTransformStream = <R>(acquire: Effect.Effect<InputTransformStre
600
584
  (sendQueue) => {
601
585
  const acquireContext = fiber.getFiberRef(FiberRef.currentContext) as Context.Context<R>
602
586
  const closeCodeIsError = options?.closeCodeIsError ?? defaultCloseCodeIsError
603
- const runRaw = <_, E, R>(handler: (_: string | Uint8Array) => Effect.Effect<_, E, R>) =>
587
+ const runRaw = <_, E, R>(handler: (_: string | Uint8Array) => Effect.Effect<_, E, R> | void) =>
604
588
  acquire.pipe(
605
589
  Effect.bindTo("stream"),
606
590
  Effect.bind("reader", ({ stream }) =>
@@ -681,7 +665,7 @@ export const fromTransformStream = <R>(acquire: Effect.Effect<InputTransformStre
681
665
  )
682
666
 
683
667
  const encoder = new TextEncoder()
684
- const run = <_, E, R>(handler: (_: Uint8Array) => Effect.Effect<_, E, R>) =>
668
+ const run = <_, E, R>(handler: (_: Uint8Array) => Effect.Effect<_, E, R> | void) =>
685
669
  runRaw((data) =>
686
670
  typeof data === "string"
687
671
  ? handler(encoder.encode(data))
package/src/index.ts CHANGED
@@ -28,6 +28,11 @@ export * as Error from "./Error.js"
28
28
  */
29
29
  export * as Etag from "./Etag.js"
30
30
 
31
+ /**
32
+ * @since 1.0.0
33
+ */
34
+ export * as FetchHttpClient from "./FetchHttpClient.js"
35
+
31
36
  /**
32
37
  * @since 1.0.0
33
38
  */
@@ -0,0 +1,53 @@
1
+ import * as Effect from "effect/Effect"
2
+ import * as FiberRef from "effect/FiberRef"
3
+ import * as Stream from "effect/Stream"
4
+ import type * as Client from "../HttpClient.js"
5
+ import * as Error from "../HttpClientError.js"
6
+ import * as client from "./httpClient.js"
7
+ import * as internalResponse from "./httpClientResponse.js"
8
+
9
+ /** @internal */
10
+ export const fetchTagKey = "@effect/platform/FetchHttpClient/Fetch"
11
+ /** @internal */
12
+ export const requestInitTagKey = "@effect/platform/FetchHttpClient/FetchOptions"
13
+
14
+ const fetch: Client.HttpClient.Service = client.makeService((request, url, signal, fiber) => {
15
+ const context = fiber.getFiberRef(FiberRef.currentContext)
16
+ const fetch: typeof globalThis.fetch = context.unsafeMap.get(fetchTagKey) ?? globalThis.fetch
17
+ const options: RequestInit = context.unsafeMap.get(requestInitTagKey) ?? {}
18
+ const headers = new globalThis.Headers(request.headers)
19
+ const send = (body: BodyInit | undefined) =>
20
+ Effect.map(
21
+ Effect.tryPromise({
22
+ try: () =>
23
+ fetch(url, {
24
+ ...options,
25
+ method: request.method,
26
+ headers,
27
+ body,
28
+ duplex: request.body._tag === "Stream" ? "half" : undefined,
29
+ signal
30
+ } as any),
31
+ catch: (cause) =>
32
+ new Error.RequestError({
33
+ request,
34
+ reason: "Transport",
35
+ cause
36
+ })
37
+ }),
38
+ (response) => internalResponse.fromWeb(request, response)
39
+ )
40
+ switch (request.body._tag) {
41
+ case "Raw":
42
+ case "Uint8Array":
43
+ return send(request.body.body as any)
44
+ case "FormData":
45
+ return send(request.body.formData)
46
+ case "Stream":
47
+ return Effect.flatMap(Stream.toReadableStreamEffect(request.body.stream), send)
48
+ }
49
+ return send(undefined)
50
+ })
51
+
52
+ /** @internal */
53
+ export const layer = client.layerMergedContext(Effect.succeed(fetch))