@effect/platform 0.47.0 → 0.48.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.
Files changed (133) hide show
  1. package/dist/cjs/Error.js +40 -4
  2. package/dist/cjs/Error.js.map +1 -1
  3. package/dist/cjs/Http/Body.js +8 -1
  4. package/dist/cjs/Http/Body.js.map +1 -1
  5. package/dist/cjs/Http/ClientError.js +21 -2
  6. package/dist/cjs/Http/ClientError.js.map +1 -1
  7. package/dist/cjs/Http/Headers.js +22 -1
  8. package/dist/cjs/Http/Headers.js.map +1 -1
  9. package/dist/cjs/Http/IncomingMessage.js +1 -1
  10. package/dist/cjs/Http/IncomingMessage.js.map +1 -1
  11. package/dist/cjs/Http/Multipart.js +21 -6
  12. package/dist/cjs/Http/Multipart.js.map +1 -1
  13. package/dist/cjs/Http/ServerError.js +30 -7
  14. package/dist/cjs/Http/ServerError.js.map +1 -1
  15. package/dist/cjs/Http/UrlParams.js +7 -2
  16. package/dist/cjs/Http/UrlParams.js.map +1 -1
  17. package/dist/cjs/PlatformLogger.js +1 -1
  18. package/dist/cjs/Socket.js +104 -22
  19. package/dist/cjs/Socket.js.map +1 -1
  20. package/dist/cjs/Transferable.js +1 -1
  21. package/dist/cjs/Transferable.js.map +1 -1
  22. package/dist/cjs/WorkerError.js.map +1 -1
  23. package/dist/cjs/internal/http/body.js +1 -1
  24. package/dist/cjs/internal/http/body.js.map +1 -1
  25. package/dist/cjs/internal/http/client.js +5 -5
  26. package/dist/cjs/internal/http/client.js.map +1 -1
  27. package/dist/cjs/internal/http/clientError.js +1 -36
  28. package/dist/cjs/internal/http/clientError.js.map +1 -1
  29. package/dist/cjs/internal/http/clientResponse.js +8 -8
  30. package/dist/cjs/internal/http/clientResponse.js.map +1 -1
  31. package/dist/cjs/internal/http/multipart.js +15 -11
  32. package/dist/cjs/internal/http/multipart.js.map +1 -1
  33. package/dist/cjs/internal/http/multiplex.js +1 -1
  34. package/dist/cjs/internal/http/multiplex.js.map +1 -1
  35. package/dist/cjs/internal/http/router.js +1 -1
  36. package/dist/cjs/internal/http/router.js.map +1 -1
  37. package/dist/cjs/internal/http/serverError.js +1 -15
  38. package/dist/cjs/internal/http/serverError.js.map +1 -1
  39. package/dist/cjs/internal/http/serverRequest.js +8 -8
  40. package/dist/cjs/internal/http/serverRequest.js.map +1 -1
  41. package/dist/dts/Error.d.ts +22 -0
  42. package/dist/dts/Error.d.ts.map +1 -1
  43. package/dist/dts/Http/Body.d.ts +9 -1
  44. package/dist/dts/Http/Body.d.ts.map +1 -1
  45. package/dist/dts/Http/ClientError.d.ts +21 -33
  46. package/dist/dts/Http/ClientError.d.ts.map +1 -1
  47. package/dist/dts/Http/Headers.d.ts +19 -0
  48. package/dist/dts/Http/Headers.d.ts.map +1 -1
  49. package/dist/dts/Http/Multipart.d.ts +21 -6
  50. package/dist/dts/Http/Multipart.d.ts.map +1 -1
  51. package/dist/dts/Http/ServerError.d.ts +33 -47
  52. package/dist/dts/Http/ServerError.d.ts.map +1 -1
  53. package/dist/dts/Http/ServerRequest.d.ts +3 -3
  54. package/dist/dts/Http/ServerRequest.d.ts.map +1 -1
  55. package/dist/dts/Http/UrlParams.d.ts +5 -0
  56. package/dist/dts/Http/UrlParams.d.ts.map +1 -1
  57. package/dist/dts/PlatformLogger.d.ts +1 -1
  58. package/dist/dts/Runtime.d.ts +4 -1
  59. package/dist/dts/Runtime.d.ts.map +1 -1
  60. package/dist/dts/Socket.d.ts +89 -13
  61. package/dist/dts/Socket.d.ts.map +1 -1
  62. package/dist/dts/Worker.d.ts +6 -6
  63. package/dist/dts/Worker.d.ts.map +1 -1
  64. package/dist/dts/WorkerError.d.ts +10 -5
  65. package/dist/dts/WorkerError.d.ts.map +1 -1
  66. package/dist/dts/WorkerRunner.d.ts +1 -1
  67. package/dist/dts/WorkerRunner.d.ts.map +1 -1
  68. package/dist/esm/Error.js +38 -3
  69. package/dist/esm/Error.js.map +1 -1
  70. package/dist/esm/Http/Body.js +6 -0
  71. package/dist/esm/Http/Body.js.map +1 -1
  72. package/dist/esm/Http/ClientError.js +19 -2
  73. package/dist/esm/Http/ClientError.js.map +1 -1
  74. package/dist/esm/Http/Headers.js +21 -1
  75. package/dist/esm/Http/Headers.js.map +1 -1
  76. package/dist/esm/Http/IncomingMessage.js +1 -1
  77. package/dist/esm/Http/IncomingMessage.js.map +1 -1
  78. package/dist/esm/Http/Multipart.js +20 -5
  79. package/dist/esm/Http/Multipart.js.map +1 -1
  80. package/dist/esm/Http/ServerError.js +26 -7
  81. package/dist/esm/Http/ServerError.js.map +1 -1
  82. package/dist/esm/Http/UrlParams.js +5 -0
  83. package/dist/esm/Http/UrlParams.js.map +1 -1
  84. package/dist/esm/PlatformLogger.js +1 -1
  85. package/dist/esm/Socket.js +98 -20
  86. package/dist/esm/Socket.js.map +1 -1
  87. package/dist/esm/Transferable.js +1 -1
  88. package/dist/esm/Transferable.js.map +1 -1
  89. package/dist/esm/WorkerError.js.map +1 -1
  90. package/dist/esm/internal/http/body.js +1 -1
  91. package/dist/esm/internal/http/body.js.map +1 -1
  92. package/dist/esm/internal/http/client.js +5 -5
  93. package/dist/esm/internal/http/client.js.map +1 -1
  94. package/dist/esm/internal/http/clientError.js +0 -10
  95. package/dist/esm/internal/http/clientError.js.map +1 -1
  96. package/dist/esm/internal/http/clientResponse.js +8 -8
  97. package/dist/esm/internal/http/clientResponse.js.map +1 -1
  98. package/dist/esm/internal/http/multipart.js +10 -9
  99. package/dist/esm/internal/http/multipart.js.map +1 -1
  100. package/dist/esm/internal/http/multiplex.js +1 -1
  101. package/dist/esm/internal/http/multiplex.js.map +1 -1
  102. package/dist/esm/internal/http/router.js +1 -1
  103. package/dist/esm/internal/http/router.js.map +1 -1
  104. package/dist/esm/internal/http/serverError.js +0 -14
  105. package/dist/esm/internal/http/serverError.js.map +1 -1
  106. package/dist/esm/internal/http/serverRequest.js +8 -8
  107. package/dist/esm/internal/http/serverRequest.js.map +1 -1
  108. package/package.json +3 -3
  109. package/src/Error.ts +61 -0
  110. package/src/Http/Body.ts +14 -1
  111. package/src/Http/ClientError.ts +21 -38
  112. package/src/Http/Headers.ts +29 -1
  113. package/src/Http/IncomingMessage.ts +1 -1
  114. package/src/Http/Multipart.ts +25 -7
  115. package/src/Http/ServerError.ts +25 -56
  116. package/src/Http/ServerRequest.ts +5 -5
  117. package/src/Http/UrlParams.ts +10 -0
  118. package/src/PlatformLogger.ts +1 -1
  119. package/src/Runtime.ts +4 -1
  120. package/src/Socket.ts +179 -39
  121. package/src/Transferable.ts +1 -1
  122. package/src/Worker.ts +6 -6
  123. package/src/WorkerError.ts +9 -7
  124. package/src/WorkerRunner.ts +8 -8
  125. package/src/internal/http/body.ts +4 -2
  126. package/src/internal/http/client.ts +5 -6
  127. package/src/internal/http/clientError.ts +0 -14
  128. package/src/internal/http/clientResponse.ts +15 -14
  129. package/src/internal/http/multipart.ts +16 -12
  130. package/src/internal/http/multiplex.ts +1 -1
  131. package/src/internal/http/router.ts +1 -1
  132. package/src/internal/http/serverError.ts +0 -20
  133. package/src/internal/http/serverRequest.ts +22 -18
@@ -33,6 +33,12 @@ export type TypeId = typeof TypeId
33
33
  */
34
34
  export type Part = Field | File
35
35
 
36
+ /**
37
+ * @since 1.0.0
38
+ * @category refinements
39
+ */
40
+ export const isPart: (u: unknown) => u is Part = internal.isPart
41
+
36
42
  /**
37
43
  * @since 1.0.0
38
44
  */
@@ -58,6 +64,12 @@ export interface Field extends Part.Proto {
58
64
  readonly value: string
59
65
  }
60
66
 
67
+ /**
68
+ * @since 1.0.0
69
+ * @category refinements
70
+ */
71
+ export const isField: (u: unknown) => u is Field = internal.isField
72
+
61
73
  /**
62
74
  * @since 1.0.0
63
75
  * @category models
@@ -70,6 +82,12 @@ export interface File extends Part.Proto {
70
82
  readonly content: Stream.Stream<Uint8Array, MultipartError>
71
83
  }
72
84
 
85
+ /**
86
+ * @since 1.0.0
87
+ * @category refinements
88
+ */
89
+ export const isFile: (u: unknown) => u is File = internal.isFile
90
+
73
91
  /**
74
92
  * @since 1.0.0
75
93
  * @category models
@@ -82,6 +100,12 @@ export interface PersistedFile extends Part.Proto {
82
100
  readonly path: string
83
101
  }
84
102
 
103
+ /**
104
+ * @since 1.0.0
105
+ * @category refinements
106
+ */
107
+ export const isPersistedFile: (u: unknown) => u is PersistedFile = internal.isPersistedFile
108
+
85
109
  /**
86
110
  * @since 1.0.0
87
111
  * @category models
@@ -122,12 +146,6 @@ export const MultipartError: (
122
146
  error: unknown
123
147
  ) => MultipartError = internal.MultipartError
124
148
 
125
- /**
126
- * @since 1.0.0
127
- * @category refinements
128
- */
129
- export const isField: (u: unknown) => u is Field = internal.isField
130
-
131
149
  /**
132
150
  * @since 1.0.0
133
151
  * @category fiber refs
@@ -209,7 +227,7 @@ export const schemaJson: <A, I, R>(
209
227
  * @since 1.0.0
210
228
  * @category schema
211
229
  */
212
- export const schemaPersisted: <R, I extends Persisted, A>(
230
+ export const schemaPersisted: <R, I extends Partial<Persisted>, A>(
213
231
  schema: Schema.Schema<A, I, R>
214
232
  ) => (persisted: Persisted) => Effect.Effect<A, ParseResult.ParseError, R> = internal.schemaPersisted
215
233
 
@@ -3,6 +3,7 @@
3
3
  */
4
4
  import type * as Cause from "effect/Cause"
5
5
  import type * as FiberId from "effect/FiberId"
6
+ import { RefailError, TypeIdError } from "../Error.js"
6
7
  import * as internal from "../internal/http/serverError.js"
7
8
  import type * as ServerRequest from "./ServerRequest.js"
8
9
  import type * as ServerResponse from "./ServerResponse.js"
@@ -25,34 +26,21 @@ export type TypeId = typeof TypeId
25
26
  */
26
27
  export type HttpServerError = RequestError | ResponseError | RouteNotFound | ServeError
27
28
 
28
- /**
29
- * @since 1.0.0
30
- */
31
- export declare namespace HttpError {
32
- /**
33
- * @since 1.0.0
34
- * @category models
35
- */
36
- export interface Proto {
37
- readonly [TypeId]: TypeId
38
- readonly _tag: string
39
- }
40
-
41
- /**
42
- * @since 1.0.0
43
- */
44
- export type ProvidedFields = TypeId | "_tag"
45
- }
46
-
47
29
  /**
48
30
  * @since 1.0.0
49
31
  * @category error
50
32
  */
51
- export interface RequestError extends HttpError.Proto {
52
- readonly _tag: "RequestError"
33
+ export class RequestError extends RefailError(TypeId, "RequestError")<{
53
34
  readonly request: ServerRequest.ServerRequest
54
35
  readonly reason: "Transport" | "Decode"
55
- readonly error: unknown
36
+ }> {
37
+ get methodAndUrl() {
38
+ return `${this.request.method} ${this.request.url}`
39
+ }
40
+
41
+ get message() {
42
+ return `${this.reason} error (${this.methodAndUrl}): ${super.message}`
43
+ }
56
44
  }
57
45
 
58
46
  /**
@@ -65,57 +53,38 @@ export const isServerError: (u: unknown) => u is HttpServerError = internal.isSe
65
53
  * @since 1.0.0
66
54
  * @category error
67
55
  */
68
- export const RequestError: (props: Omit<RequestError, HttpError.ProvidedFields>) => RequestError = internal.requestError
69
-
70
- /**
71
- * @since 1.0.0
72
- * @category error
73
- */
74
- export interface RouteNotFound extends HttpError.Proto {
75
- readonly _tag: "RouteNotFound"
56
+ export class RouteNotFound extends TypeIdError(TypeId, "RouteNotFound")<{
76
57
  readonly request: ServerRequest.ServerRequest
58
+ }> {
59
+ get message() {
60
+ return `${this.request.method} ${this.request.url} not found`
61
+ }
77
62
  }
78
63
 
79
64
  /**
80
65
  * @since 1.0.0
81
66
  * @category error
82
67
  */
83
- export const RouteNotFound: (props: Omit<RouteNotFound, HttpError.ProvidedFields>) => RouteNotFound =
84
- internal.routeNotFound
85
-
86
- /**
87
- * @since 1.0.0
88
- * @category error
89
- */
90
- export interface ResponseError extends HttpError.Proto {
91
- readonly _tag: "ResponseError"
68
+ export class ResponseError extends RefailError(TypeId, "ResponseError")<{
92
69
  readonly request: ServerRequest.ServerRequest
93
70
  readonly response: ServerResponse.ServerResponse
94
71
  readonly reason: "Decode"
95
- readonly error: unknown
96
- }
97
-
98
- /**
99
- * @since 1.0.0
100
- * @category error
101
- */
102
- export const ResponseError: (props: Omit<ResponseError, HttpError.ProvidedFields>) => ResponseError =
103
- internal.responseError
72
+ }> {
73
+ get methodAndUrl() {
74
+ return `${this.request.method} ${this.request.url}`
75
+ }
104
76
 
105
- /**
106
- * @since 1.0.0
107
- * @category error
108
- */
109
- export interface ServeError extends HttpError.Proto {
110
- readonly _tag: "ServeError"
111
- readonly error: unknown
77
+ get message() {
78
+ return `${this.reason} error (${this.response.status} ${this.methodAndUrl}): ${super.message}`
79
+ }
112
80
  }
113
81
 
114
82
  /**
115
83
  * @since 1.0.0
116
84
  * @category error
117
85
  */
118
- export const ServeError: (props: Omit<ServeError, HttpError.ProvidedFields>) => ServeError = internal.serveError
86
+ export class ServeError extends RefailError(TypeId, "ServeError")<{}> {
87
+ }
119
88
 
120
89
  /**
121
90
  * @since 1.0.0
@@ -96,7 +96,7 @@ export const upgrade: Effect.Effect<Socket.Socket, Error.RequestError, ServerReq
96
96
  */
97
97
  export const upgradeChannel: <IE = never>() => Channel<
98
98
  Chunk<Uint8Array>,
99
- Chunk<Uint8Array>,
99
+ Chunk<Uint8Array | Socket.CloseEvent>,
100
100
  Error.RequestError | IE | Socket.SocketError,
101
101
  IE,
102
102
  void,
@@ -124,12 +124,12 @@ export const schemaBodyJson: <A, I, R>(
124
124
  * @since 1.0.0
125
125
  * @category schema
126
126
  */
127
- export const schemaBodyForm: <R, I extends Multipart.Persisted, A>(
127
+ export const schemaBodyForm: <R, I extends Partial<Multipart.Persisted>, A>(
128
128
  schema: Schema.Schema<A, I, R>
129
129
  ) => Effect.Effect<
130
130
  A,
131
- Multipart.MultipartError | Error.RequestError | ParseResult.ParseError,
132
- ServerRequest | Scope.Scope | FileSystem.FileSystem | Path.Path | R
131
+ Multipart.MultipartError | ParseResult.ParseError | Error.RequestError,
132
+ R | ServerRequest | Scope.Scope | FileSystem.FileSystem | Path.Path
133
133
  > = internal.schemaBodyForm
134
134
 
135
135
  /**
@@ -144,7 +144,7 @@ export const schemaBodyUrlParams: <R, I extends Readonly<Record<string, string>>
144
144
  * @since 1.0.0
145
145
  * @category schema
146
146
  */
147
- export const schemaBodyMultipart: <R, I extends Multipart.Persisted, A>(
147
+ export const schemaBodyMultipart: <R, I extends Partial<Multipart.Persisted>, A>(
148
148
  schema: Schema.Schema<A, I, R>
149
149
  ) => Effect.Effect<
150
150
  A,
@@ -31,6 +31,16 @@ export const fromInput = (input: Input): UrlParams => {
31
31
  return ReadonlyArray.fromIterable(Object.entries(input))
32
32
  }
33
33
 
34
+ /**
35
+ * @since 1.0.0
36
+ * @category schemas
37
+ */
38
+ export const schema: Schema.Schema<UrlParams, ReadonlyArray<readonly [string, string]>> = Schema.array(
39
+ Schema.tuple(Schema.string, Schema.string)
40
+ ).pipe(
41
+ Schema.identifier("UrlParams")
42
+ )
43
+
34
44
  /**
35
45
  * @since 1.0.0
36
46
  * @category constructors
@@ -19,7 +19,7 @@ import * as internal from "./internal/platformLogger.js"
19
19
  * import { Effect, Layer, Logger } from "effect"
20
20
  *
21
21
  * const fileLogger = Logger.logfmtLogger.pipe(
22
- * PlatformLogger.toFile("log.txt")
22
+ * PlatformLogger.toFile("/tmp/log.txt")
23
23
  * )
24
24
  * const LoggerLive = Logger.replaceScoped(Logger.defaultLogger, fileLogger).pipe(
25
25
  * Layer.provide(NodeFileSystem.layer)
package/src/Runtime.ts CHANGED
@@ -31,6 +31,9 @@ export const defaultTeardown: Teardown = <E, A>(
31
31
  export interface RunMain {
32
32
  <E, A>(
33
33
  effect: Effect.Effect<A, E>,
34
- teardown?: Teardown
34
+ options?: {
35
+ readonly disableErrorReporting?: boolean
36
+ readonly teardown?: Teardown
37
+ }
35
38
  ): void
36
39
  }
package/src/Socket.ts CHANGED
@@ -5,27 +5,30 @@ import * as Cause from "effect/Cause"
5
5
  import * as Channel from "effect/Channel"
6
6
  import * as Chunk from "effect/Chunk"
7
7
  import * as Context from "effect/Context"
8
- import * as Data from "effect/Data"
8
+ import type { DurationInput } from "effect/Duration"
9
9
  import * as Effect from "effect/Effect"
10
10
  import * as Exit from "effect/Exit"
11
+ import * as Fiber from "effect/Fiber"
11
12
  import * as FiberSet from "effect/FiberSet"
12
13
  import * as Layer from "effect/Layer"
14
+ import * as Predicate from "effect/Predicate"
13
15
  import * as Queue from "effect/Queue"
14
16
  import * as Scope from "effect/Scope"
15
17
  import type * as AsyncProducer from "effect/SingleProducerAsyncInput"
16
18
  import IsoWebSocket from "isomorphic-ws"
19
+ import { RefailError, TypeIdError } from "./Error.js"
17
20
 
18
21
  /**
19
22
  * @since 1.0.0
20
23
  * @category type ids
21
24
  */
22
- export const SocketTypeId = Symbol.for("@effect/platform/Socket")
25
+ export const TypeId = Symbol.for("@effect/platform/Socket")
23
26
 
24
27
  /**
25
28
  * @since 1.0.0
26
29
  * @category type ids
27
30
  */
28
- export type SocketTypeId = typeof SocketTypeId
31
+ export type TypeId = typeof TypeId
29
32
 
30
33
  /**
31
34
  * @since 1.0.0
@@ -40,26 +43,117 @@ export const Socket: Context.Tag<Socket, Socket> = Context.GenericTag<Socket>(
40
43
  * @category models
41
44
  */
42
45
  export interface Socket {
43
- readonly [SocketTypeId]: SocketTypeId
46
+ readonly [TypeId]: TypeId
44
47
  readonly run: <R, E, _>(
45
48
  handler: (_: Uint8Array) => Effect.Effect<_, E, R>
46
49
  ) => Effect.Effect<void, SocketError | E, R>
47
- readonly writer: Effect.Effect<(chunk: Uint8Array) => Effect.Effect<void>, never, Scope.Scope>
50
+ readonly writer: Effect.Effect<(chunk: Uint8Array | CloseEvent) => Effect.Effect<void>, never, Scope.Scope>
48
51
  }
49
52
 
53
+ /**
54
+ * @since 1.0.0
55
+ * @category type ids
56
+ */
57
+ export const CloseEventTypeId = Symbol.for("@effect/platform/Socket/CloseEvent")
58
+
59
+ /**
60
+ * @since 1.0.0
61
+ * @category type ids
62
+ */
63
+ export type CloseEventTypeId = typeof CloseEventTypeId
64
+
65
+ /**
66
+ * @since 1.0.0
67
+ * @category models
68
+ */
69
+ export class CloseEvent {
70
+ /**
71
+ * @since 1.0.0
72
+ */
73
+ readonly [CloseEventTypeId]: CloseEventTypeId
74
+ constructor(readonly code = 1000, readonly reason?: string) {
75
+ this[CloseEventTypeId] = CloseEventTypeId
76
+ }
77
+ /**
78
+ * @since 1.0.0
79
+ */
80
+ toString() {
81
+ return this.reason ? `${this.code}: ${this.reason}` : `${this.code}`
82
+ }
83
+ }
84
+
85
+ /**
86
+ * @since 1.0.0
87
+ * @category refinements
88
+ */
89
+ export const isCloseEvent = (u: unknown): u is CloseEvent => Predicate.hasProperty(u, CloseEventTypeId)
90
+
91
+ /**
92
+ * @since 1.0.0
93
+ * @category type ids
94
+ */
95
+ export const SocketErrorTypeId = Symbol.for("@effect/platform/Socket/SocketError")
96
+
97
+ /**
98
+ * @since 1.0.0
99
+ * @category type ids
100
+ */
101
+ export type SocketErrorTypeId = typeof SocketErrorTypeId
102
+
103
+ /**
104
+ * @since 1.0.0
105
+ * @category refinements
106
+ */
107
+ export const isSocketError = (u: unknown): u is SocketError => Predicate.hasProperty(u, SocketErrorTypeId)
108
+
50
109
  /**
51
110
  * @since 1.0.0
52
111
  * @category errors
53
112
  */
54
- export class SocketError extends Data.TaggedError("SocketError")<{
55
- readonly reason: "Write" | "Read" | "Open" | "Close"
56
- readonly error: unknown
113
+ export type SocketError = SocketGenericError | SocketCloseError
114
+
115
+ /**
116
+ * @since 1.0.0
117
+ * @category errors
118
+ */
119
+ export class SocketGenericError extends RefailError(SocketErrorTypeId, "SocketError")<{
120
+ readonly reason: "Write" | "Read" | "Open" | "OpenTimeout"
121
+ }> {
122
+ get message() {
123
+ return `${this.reason}: ${super.message}`
124
+ }
125
+ }
126
+
127
+ /**
128
+ * @since 1.0.0
129
+ * @category errors
130
+ */
131
+ export class SocketCloseError extends TypeIdError(SocketErrorTypeId, "SocketError")<{
132
+ readonly reason: "Close"
133
+ readonly code: number
134
+ readonly closeReason?: string
57
135
  }> {
58
136
  /**
59
137
  * @since 1.0.0
60
138
  */
61
- toString(): string {
62
- return `SocketError: ${this.reason} - ${this.error}`
139
+ static is(u: unknown): u is SocketCloseError {
140
+ return isSocketError(u) && u.reason === "Close"
141
+ }
142
+
143
+ /**
144
+ * @since 1.0.0
145
+ */
146
+ static isClean(isClean: (code: number) => boolean) {
147
+ return function(u: unknown): u is SocketCloseError {
148
+ return SocketCloseError.is(u) && isClean(u.code)
149
+ }
150
+ }
151
+
152
+ get message() {
153
+ if (this.closeReason) {
154
+ return `${this.reason}: ${this.code}: ${this.closeReason}`
155
+ }
156
+ return `${this.reason}: ${this.code}`
63
157
  }
64
158
  }
65
159
 
@@ -69,14 +163,21 @@ export class SocketError extends Data.TaggedError("SocketError")<{
69
163
  */
70
164
  export const toChannel = <IE>(
71
165
  self: Socket
72
- ): Channel.Channel<Chunk.Chunk<Uint8Array>, Chunk.Chunk<Uint8Array>, SocketError | IE, IE, void, unknown> =>
166
+ ): Channel.Channel<
167
+ Chunk.Chunk<Uint8Array>,
168
+ Chunk.Chunk<Uint8Array | CloseEvent>,
169
+ SocketError | IE,
170
+ IE,
171
+ void,
172
+ unknown
173
+ > =>
73
174
  Channel.unwrap(
74
175
  Effect.gen(function*(_) {
75
176
  const writeScope = yield* _(Scope.make())
76
177
  const write = yield* _(Scope.extend(self.writer, writeScope))
77
178
  const exitQueue = yield* _(Queue.unbounded<Exit.Exit<Chunk.Chunk<Uint8Array>, SocketError | IE>>())
78
179
 
79
- const input: AsyncProducer.AsyncInputProducer<IE, Chunk.Chunk<Uint8Array>, unknown> = {
180
+ const input: AsyncProducer.AsyncInputProducer<IE, Chunk.Chunk<Uint8Array | CloseEvent>, unknown> = {
80
181
  awaitRead: () => Effect.unit,
81
182
  emit(chunk) {
82
183
  return Effect.catchAllCause(
@@ -123,8 +224,14 @@ export const toChannel = <IE>(
123
224
  export const toChannelWith = <IE = never>() =>
124
225
  (
125
226
  self: Socket
126
- ): Channel.Channel<Chunk.Chunk<Uint8Array>, Chunk.Chunk<Uint8Array>, SocketError | IE, IE, void, unknown> =>
127
- toChannel(self)
227
+ ): Channel.Channel<
228
+ Chunk.Chunk<Uint8Array>,
229
+ Chunk.Chunk<Uint8Array | CloseEvent>,
230
+ SocketError | IE,
231
+ IE,
232
+ void,
233
+ unknown
234
+ > => toChannel(self)
128
235
 
129
236
  /**
130
237
  * @since 1.0.0
@@ -132,7 +239,7 @@ export const toChannelWith = <IE = never>() =>
132
239
  */
133
240
  export const makeChannel = <IE = never>(): Channel.Channel<
134
241
  Chunk.Chunk<Uint8Array>,
135
- Chunk.Chunk<Uint8Array>,
242
+ Chunk.Chunk<Uint8Array | CloseEvent>,
136
243
  SocketError | IE,
137
244
  IE,
138
245
  void,
@@ -167,6 +274,7 @@ export const WebSocket: Context.Tag<WebSocket, globalThis.WebSocket> = Context.G
167
274
  */
168
275
  export const makeWebSocket = (url: string | Effect.Effect<string>, options?: {
169
276
  readonly closeCodeIsError?: (code: number) => boolean
277
+ readonly openTimeout?: DurationInput
170
278
  }): Effect.Effect<Socket> =>
171
279
  fromWebSocket(
172
280
  Effect.acquireRelease(
@@ -190,11 +298,12 @@ export const fromWebSocket = (
190
298
  acquire: Effect.Effect<globalThis.WebSocket, SocketError, Scope.Scope>,
191
299
  options?: {
192
300
  readonly closeCodeIsError?: (code: number) => boolean
301
+ readonly openTimeout?: DurationInput
193
302
  }
194
303
  ): Effect.Effect<Socket> =>
195
304
  Effect.gen(function*(_) {
196
305
  const closeCodeIsError = options?.closeCodeIsError ?? defaultCloseCodeIsError
197
- const sendQueue = yield* _(Queue.unbounded<Uint8Array>())
306
+ const sendQueue = yield* _(Queue.unbounded<Uint8Array | CloseEvent>())
198
307
 
199
308
  const run = <R, E, _>(handler: (_: Uint8Array) => Effect.Effect<_, E, R>) =>
200
309
  Effect.gen(function*(_) {
@@ -219,23 +328,38 @@ export const fromWebSocket = (
219
328
  }
220
329
 
221
330
  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
- }))
331
+ yield* _(
332
+ Effect.async<void, SocketError, never>((resume) => {
333
+ ws.onopen = () => {
334
+ resume(Effect.unit)
335
+ }
336
+ ws.onerror = (error_) => {
337
+ resume(Effect.fail(new SocketGenericError({ reason: "Open", error: (error_ as any).message })))
338
+ }
339
+ }),
340
+ Effect.timeoutFail({
341
+ duration: options?.openTimeout ?? 10000,
342
+ onTimeout: () => new SocketGenericError({ reason: "OpenTimeout", error: "timeout waiting for \"open\"" })
343
+ })
344
+ )
230
345
  }
231
346
 
232
- yield* _(
347
+ const writeFiber = yield* _(
233
348
  Queue.take(sendQueue),
234
349
  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
- })
350
+ isCloseEvent(chunk) ?
351
+ Effect.failSync(() => {
352
+ ws.close(chunk.code, chunk.reason)
353
+ return new SocketCloseError({
354
+ reason: "Close",
355
+ code: chunk.code,
356
+ closeReason: chunk.reason
357
+ })
358
+ }) :
359
+ Effect.try({
360
+ try: () => ws.send(chunk),
361
+ catch: (error) => new SocketGenericError({ reason: "Write", error: (error as any).message })
362
+ })
239
363
  ),
240
364
  Effect.forever,
241
365
  Effect.fork
@@ -244,25 +368,34 @@ export const fromWebSocket = (
244
368
  yield* _(
245
369
  Effect.async<void, SocketError, never>((resume) => {
246
370
  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
- }
371
+ resume(
372
+ Effect.fail(
373
+ new SocketCloseError({
374
+ reason: "Close",
375
+ code: event.code,
376
+ closeReason: event.reason
377
+ })
378
+ )
379
+ )
252
380
  }
253
381
  ws.onerror = (error) => {
254
- resume(Effect.fail(new SocketError({ reason: "Read", error: (error as any).message })))
382
+ resume(Effect.fail(new SocketGenericError({ reason: "Read", error: (error as any).message })))
255
383
  }
256
384
  }),
257
- Effect.raceFirst(FiberSet.join(fiberSet))
385
+ Effect.raceFirst(FiberSet.join(fiberSet)),
386
+ Effect.raceFirst(Fiber.join(writeFiber)),
387
+ Effect.catchIf(
388
+ SocketCloseError.isClean((_) => !closeCodeIsError(_)),
389
+ (_) => Effect.unit
390
+ )
258
391
  )
259
392
  }).pipe(Effect.scoped)
260
393
 
261
- const write = (chunk: Uint8Array) => Queue.offer(sendQueue, chunk)
394
+ const write = (chunk: Uint8Array | CloseEvent) => Queue.offer(sendQueue, chunk)
262
395
  const writer = Effect.succeed(write)
263
396
 
264
397
  return Socket.of({
265
- [SocketTypeId]: SocketTypeId,
398
+ [TypeId]: TypeId,
266
399
  run,
267
400
  writer
268
401
  })
@@ -277,7 +410,14 @@ export const makeWebSocketChannel = <IE = never>(
277
410
  options?: {
278
411
  readonly closeCodeIsError?: (code: number) => boolean
279
412
  }
280
- ): Channel.Channel<Chunk.Chunk<Uint8Array>, Chunk.Chunk<Uint8Array>, SocketError | IE, IE, void, unknown> =>
413
+ ): Channel.Channel<
414
+ Chunk.Chunk<Uint8Array>,
415
+ Chunk.Chunk<Uint8Array | CloseEvent>,
416
+ SocketError | IE,
417
+ IE,
418
+ void,
419
+ unknown
420
+ > =>
281
421
  Channel.unwrapScoped(
282
422
  Effect.map(makeWebSocket(url, options), toChannelWith<IE>())
283
423
  )
@@ -91,7 +91,7 @@ export const schema: {
91
91
  f: (_: I) => Iterable<globalThis.Transferable>
92
92
  ) =>
93
93
  Schema.transformOrFail(
94
- Schema.from(self),
94
+ Schema.encodedSchema(self),
95
95
  self,
96
96
  ParseResult.succeed,
97
97
  (i) => Effect.as(addAll(f(i)), i)
package/src/Worker.ts CHANGED
@@ -134,7 +134,7 @@ export declare namespace Worker {
134
134
  | readonly [id: number, end: 1]
135
135
  | readonly [id: number, end: 1, ReadonlyArray<O>]
136
136
  | readonly [id: number, error: 2, E]
137
- | readonly [id: number, defect: 3, Schema.CauseFrom<WorkerErrorFrom>]
137
+ | readonly [id: number, defect: 3, Schema.CauseEncoded<WorkerErrorFrom>]
138
138
  }
139
139
 
140
140
  /**
@@ -246,12 +246,12 @@ export interface SerializedWorker<I extends Schema.TaggedRequest.Any> {
246
246
  readonly id: number
247
247
  readonly execute: <Req extends I>(
248
248
  message: Req
249
- ) => Req extends Serializable.WithResult<infer R, infer _IE, infer E, infer _IA, infer A>
249
+ ) => Req extends Serializable.WithResult<infer A, infer _I, infer E, infer _EI, infer R>
250
250
  ? Stream.Stream<A, E | WorkerError | ParseResult.ParseError, R>
251
251
  : never
252
252
  readonly executeEffect: <Req extends I>(
253
253
  message: Req
254
- ) => Req extends Serializable.WithResult<infer R, infer _IE, infer E, infer _IA, infer A>
254
+ ) => Req extends Serializable.WithResult<infer A, infer _I, infer E, infer _EI, infer R>
255
255
  ? Effect.Effect<A, E | WorkerError | ParseResult.ParseError, R>
256
256
  : never
257
257
  }
@@ -290,17 +290,17 @@ export interface SerializedWorkerPool<I extends Schema.TaggedRequest.Any> {
290
290
  readonly backing: Pool.Pool<SerializedWorker<I>, WorkerError>
291
291
  readonly broadcast: <Req extends I>(
292
292
  message: Req
293
- ) => Req extends Serializable.WithResult<infer R, infer _IE, infer E, infer _IA, infer _A>
293
+ ) => Req extends Serializable.WithResult<infer _A, infer _I, infer E, infer _EI, infer R>
294
294
  ? Effect.Effect<void, E | WorkerError | ParseResult.ParseError, R>
295
295
  : never
296
296
  readonly execute: <Req extends I>(
297
297
  message: Req
298
- ) => Req extends Serializable.WithResult<infer R, infer _IE, infer E, infer _IA, infer A>
298
+ ) => Req extends Serializable.WithResult<infer A, infer _I, infer E, infer _EI, infer R>
299
299
  ? Stream.Stream<A, E | WorkerError | ParseResult.ParseError, R>
300
300
  : never
301
301
  readonly executeEffect: <Req extends I>(
302
302
  message: Req
303
- ) => Req extends Serializable.WithResult<infer R, infer _IE, infer E, infer _IA, infer A>
303
+ ) => Req extends Serializable.WithResult<infer A, infer _I, infer E, infer _EI, infer R>
304
304
  ? Effect.Effect<A, E | WorkerError | ParseResult.ParseError, R>
305
305
  : never
306
306
  }