@effect/platform-browser 0.74.0 → 4.0.0-beta.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 (170) hide show
  1. package/README.md +0 -6
  2. package/dist/BrowserHttpClient.d.ts +49 -0
  3. package/dist/BrowserHttpClient.d.ts.map +1 -0
  4. package/dist/{esm/internal/httpClient.js → BrowserHttpClient.js} +98 -60
  5. package/dist/BrowserHttpClient.js.map +1 -0
  6. package/dist/BrowserKeyValueStore.d.ts +24 -0
  7. package/dist/BrowserKeyValueStore.d.ts.map +1 -0
  8. package/dist/BrowserKeyValueStore.js +20 -0
  9. package/dist/BrowserKeyValueStore.js.map +1 -0
  10. package/dist/BrowserRuntime.d.ts +28 -0
  11. package/dist/BrowserRuntime.d.ts.map +1 -0
  12. package/dist/BrowserRuntime.js +13 -0
  13. package/dist/BrowserRuntime.js.map +1 -0
  14. package/dist/{dts/BrowserSocket.d.ts → BrowserSocket.d.ts} +4 -4
  15. package/dist/BrowserSocket.d.ts.map +1 -0
  16. package/dist/{esm/BrowserSocket.js → BrowserSocket.js} +5 -5
  17. package/dist/BrowserSocket.js.map +1 -0
  18. package/dist/BrowserStream.d.ts +35 -0
  19. package/dist/BrowserStream.d.ts.map +1 -0
  20. package/dist/BrowserStream.js +25 -0
  21. package/dist/BrowserStream.js.map +1 -0
  22. package/dist/BrowserWorker.d.ts +13 -0
  23. package/dist/BrowserWorker.d.ts.map +1 -0
  24. package/dist/{esm/internal/worker.js → BrowserWorker.js} +23 -17
  25. package/dist/BrowserWorker.js.map +1 -0
  26. package/dist/BrowserWorkerRunner.d.ts +18 -0
  27. package/dist/BrowserWorkerRunner.d.ts.map +1 -0
  28. package/dist/BrowserWorkerRunner.js +143 -0
  29. package/dist/BrowserWorkerRunner.js.map +1 -0
  30. package/dist/{dts/Clipboard.d.ts → Clipboard.d.ts} +13 -30
  31. package/dist/Clipboard.d.ts.map +1 -0
  32. package/dist/{esm/Clipboard.js → Clipboard.js} +12 -18
  33. package/dist/Clipboard.js.map +1 -0
  34. package/dist/Geolocation.d.ts +97 -0
  35. package/dist/Geolocation.d.ts.map +1 -0
  36. package/dist/Geolocation.js +99 -0
  37. package/dist/Geolocation.js.map +1 -0
  38. package/dist/Permissions.d.ts +80 -0
  39. package/dist/Permissions.d.ts.map +1 -0
  40. package/dist/Permissions.js +70 -0
  41. package/dist/Permissions.js.map +1 -0
  42. package/dist/index.d.ts +44 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/{esm/index.js → index.js} +4 -0
  45. package/dist/index.js.map +1 -0
  46. package/package.json +51 -111
  47. package/src/BrowserHttpClient.ts +364 -16
  48. package/src/BrowserKeyValueStore.ts +15 -8
  49. package/src/BrowserRuntime.ts +30 -4
  50. package/src/BrowserSocket.ts +5 -5
  51. package/src/BrowserStream.ts +20 -10
  52. package/src/BrowserWorker.ts +58 -21
  53. package/src/BrowserWorkerRunner.ts +151 -18
  54. package/src/Clipboard.ts +16 -38
  55. package/src/Geolocation.ts +80 -40
  56. package/src/Permissions.ts +49 -34
  57. package/src/index.ts +16 -10
  58. package/BrowserHttpClient/package.json +0 -6
  59. package/BrowserKeyValueStore/package.json +0 -6
  60. package/BrowserRuntime/package.json +0 -6
  61. package/BrowserSocket/package.json +0 -6
  62. package/BrowserStream/package.json +0 -6
  63. package/BrowserWorker/package.json +0 -6
  64. package/BrowserWorkerRunner/package.json +0 -6
  65. package/Clipboard/package.json +0 -6
  66. package/Geolocation/package.json +0 -6
  67. package/Permissions/package.json +0 -6
  68. package/dist/cjs/BrowserHttpClient.js +0 -31
  69. package/dist/cjs/BrowserHttpClient.js.map +0 -1
  70. package/dist/cjs/BrowserKeyValueStore.js +0 -23
  71. package/dist/cjs/BrowserKeyValueStore.js.map +0 -1
  72. package/dist/cjs/BrowserRuntime.js +0 -14
  73. package/dist/cjs/BrowserRuntime.js.map +0 -1
  74. package/dist/cjs/BrowserSocket.js +0 -27
  75. package/dist/cjs/BrowserSocket.js.map +0 -1
  76. package/dist/cjs/BrowserStream.js +0 -23
  77. package/dist/cjs/BrowserStream.js.map +0 -1
  78. package/dist/cjs/BrowserWorker.js +0 -29
  79. package/dist/cjs/BrowserWorker.js.map +0 -1
  80. package/dist/cjs/BrowserWorkerRunner.js +0 -31
  81. package/dist/cjs/BrowserWorkerRunner.js.map +0 -1
  82. package/dist/cjs/Clipboard.js +0 -86
  83. package/dist/cjs/Clipboard.js.map +0 -1
  84. package/dist/cjs/Geolocation.js +0 -73
  85. package/dist/cjs/Geolocation.js.map +0 -1
  86. package/dist/cjs/Permissions.js +0 -59
  87. package/dist/cjs/Permissions.js.map +0 -1
  88. package/dist/cjs/index.js +0 -28
  89. package/dist/cjs/index.js.map +0 -1
  90. package/dist/cjs/internal/httpClient.js +0 -266
  91. package/dist/cjs/internal/httpClient.js.map +0 -1
  92. package/dist/cjs/internal/keyValueStore.js +0 -13
  93. package/dist/cjs/internal/keyValueStore.js.map +0 -1
  94. package/dist/cjs/internal/runtime.js +0 -16
  95. package/dist/cjs/internal/runtime.js.map +0 -1
  96. package/dist/cjs/internal/stream.js +0 -19
  97. package/dist/cjs/internal/stream.js.map +0 -1
  98. package/dist/cjs/internal/worker.js +0 -60
  99. package/dist/cjs/internal/worker.js.map +0 -1
  100. package/dist/cjs/internal/workerRunner.js +0 -144
  101. package/dist/cjs/internal/workerRunner.js.map +0 -1
  102. package/dist/dts/BrowserHttpClient.d.ts +0 -33
  103. package/dist/dts/BrowserHttpClient.d.ts.map +0 -1
  104. package/dist/dts/BrowserKeyValueStore.d.ts +0 -20
  105. package/dist/dts/BrowserKeyValueStore.d.ts.map +0 -1
  106. package/dist/dts/BrowserRuntime.d.ts +0 -10
  107. package/dist/dts/BrowserRuntime.d.ts.map +0 -1
  108. package/dist/dts/BrowserSocket.d.ts.map +0 -1
  109. package/dist/dts/BrowserStream.d.ts +0 -25
  110. package/dist/dts/BrowserStream.d.ts.map +0 -1
  111. package/dist/dts/BrowserWorker.d.ts +0 -26
  112. package/dist/dts/BrowserWorker.d.ts.map +0 -1
  113. package/dist/dts/BrowserWorkerRunner.d.ts +0 -27
  114. package/dist/dts/BrowserWorkerRunner.d.ts.map +0 -1
  115. package/dist/dts/Clipboard.d.ts.map +0 -1
  116. package/dist/dts/Geolocation.d.ts +0 -67
  117. package/dist/dts/Geolocation.d.ts.map +0 -1
  118. package/dist/dts/Permissions.d.ts +0 -67
  119. package/dist/dts/Permissions.d.ts.map +0 -1
  120. package/dist/dts/index.d.ts +0 -41
  121. package/dist/dts/index.d.ts.map +0 -1
  122. package/dist/dts/internal/httpClient.d.ts +0 -2
  123. package/dist/dts/internal/httpClient.d.ts.map +0 -1
  124. package/dist/dts/internal/keyValueStore.d.ts +0 -2
  125. package/dist/dts/internal/keyValueStore.d.ts.map +0 -1
  126. package/dist/dts/internal/runtime.d.ts +0 -2
  127. package/dist/dts/internal/runtime.d.ts.map +0 -1
  128. package/dist/dts/internal/stream.d.ts +0 -5
  129. package/dist/dts/internal/stream.d.ts.map +0 -1
  130. package/dist/dts/internal/worker.d.ts +0 -2
  131. package/dist/dts/internal/worker.d.ts.map +0 -1
  132. package/dist/dts/internal/workerRunner.d.ts +0 -2
  133. package/dist/dts/internal/workerRunner.d.ts.map +0 -1
  134. package/dist/esm/BrowserHttpClient.js +0 -23
  135. package/dist/esm/BrowserHttpClient.js.map +0 -1
  136. package/dist/esm/BrowserKeyValueStore.js +0 -16
  137. package/dist/esm/BrowserKeyValueStore.js.map +0 -1
  138. package/dist/esm/BrowserRuntime.js +0 -7
  139. package/dist/esm/BrowserRuntime.js.map +0 -1
  140. package/dist/esm/BrowserSocket.js.map +0 -1
  141. package/dist/esm/BrowserStream.js +0 -15
  142. package/dist/esm/BrowserStream.js.map +0 -1
  143. package/dist/esm/BrowserWorker.js +0 -22
  144. package/dist/esm/BrowserWorker.js.map +0 -1
  145. package/dist/esm/BrowserWorkerRunner.js +0 -23
  146. package/dist/esm/BrowserWorkerRunner.js.map +0 -1
  147. package/dist/esm/Clipboard.js.map +0 -1
  148. package/dist/esm/Geolocation.js +0 -63
  149. package/dist/esm/Geolocation.js.map +0 -1
  150. package/dist/esm/Permissions.js +0 -50
  151. package/dist/esm/Permissions.js.map +0 -1
  152. package/dist/esm/index.js.map +0 -1
  153. package/dist/esm/internal/httpClient.js.map +0 -1
  154. package/dist/esm/internal/keyValueStore.js +0 -6
  155. package/dist/esm/internal/keyValueStore.js.map +0 -1
  156. package/dist/esm/internal/runtime.js +0 -10
  157. package/dist/esm/internal/runtime.js.map +0 -1
  158. package/dist/esm/internal/stream.js +0 -9
  159. package/dist/esm/internal/stream.js.map +0 -1
  160. package/dist/esm/internal/worker.js.map +0 -1
  161. package/dist/esm/internal/workerRunner.js +0 -135
  162. package/dist/esm/internal/workerRunner.js.map +0 -1
  163. package/dist/esm/package.json +0 -4
  164. package/index/package.json +0 -6
  165. package/src/internal/httpClient.ts +0 -324
  166. package/src/internal/keyValueStore.ts +0 -7
  167. package/src/internal/runtime.ts +0 -8
  168. package/src/internal/stream.ts +0 -27
  169. package/src/internal/worker.ts +0 -58
  170. package/src/internal/workerRunner.ts +0 -155
@@ -1,37 +1,385 @@
1
1
  /**
2
2
  * @since 1.0.0
3
3
  */
4
- import type * as HttpClient from "@effect/platform/HttpClient"
5
- import * as Context from "effect/Context"
6
- import type { Effect } from "effect/Effect"
7
- import type * as FiberRef from "effect/FiberRef"
4
+ import * as Cause from "effect/Cause"
5
+ import * as Effect from "effect/Effect"
8
6
  import type { LazyArg } from "effect/Function"
7
+ import * as Inspectable from "effect/Inspectable"
9
8
  import type * as Layer from "effect/Layer"
10
- import * as internal from "./internal/httpClient.js"
9
+ import * as Queue from "effect/Queue"
10
+ import * as ServiceMap from "effect/ServiceMap"
11
+ import * as Stream from "effect/Stream"
12
+ import * as Cookies from "effect/unstable/http/Cookies"
13
+ import * as Headers from "effect/unstable/http/Headers"
14
+ import * as HttpClient from "effect/unstable/http/HttpClient"
15
+ import * as HttpClientError from "effect/unstable/http/HttpClientError"
16
+ import type * as HttpClientRequest from "effect/unstable/http/HttpClientRequest"
17
+ import * as HttpClientResponse from "effect/unstable/http/HttpClientResponse"
18
+ import * as HttpIncomingMessage from "effect/unstable/http/HttpIncomingMessage"
19
+ import * as UrlParams from "effect/unstable/http/UrlParams"
20
+ import * as HeaderParser from "multipasta/HeadersParser"
21
+
22
+ // =============================================================================
23
+ // Fetch
24
+ // =============================================================================
25
+
26
+ export {
27
+ /**
28
+ * @since 1.0.0
29
+ * @category Fetch
30
+ */
31
+ Fetch,
32
+ /**
33
+ * @since 1.0.0
34
+ * @category Fetch
35
+ */
36
+ layer as layerFetch,
37
+ /**
38
+ * @since 1.0.0
39
+ * @category Fetch
40
+ */
41
+ RequestInit
42
+ } from "effect/unstable/http/FetchHttpClient"
43
+
44
+ // =============================================================================
45
+ // XML Http Request
46
+ // =============================================================================
11
47
 
12
48
  /**
13
49
  * @since 1.0.0
14
- * @category layers
50
+ * @category Models
15
51
  */
16
- export const layerXMLHttpRequest: Layer.Layer<HttpClient.HttpClient> = internal.layerXMLHttpRequest
52
+ export type XHRResponseType = "arraybuffer" | "text"
17
53
 
18
54
  /**
19
55
  * @since 1.0.0
20
- * @category tags
56
+ * @category References
21
57
  */
22
- export class XMLHttpRequest extends Context.Tag(internal.xhrTagKey)<
23
- XMLHttpRequest,
24
- LazyArg<globalThis.XMLHttpRequest>
25
- >() {}
58
+ export const CurrentXHRResponseType: ServiceMap.Reference<XHRResponseType> = ServiceMap.Reference(
59
+ "@effect/platform-browser/BrowserHttpClient/CurrentXHRResponseType",
60
+ { defaultValue: (): XHRResponseType => "text" }
61
+ )
62
+
63
+ /**
64
+ * @since 1.0.0
65
+ * @category References
66
+ */
67
+ export const withXHRArrayBuffer = <A, E, R>(
68
+ self: Effect.Effect<A, E, R>
69
+ ): Effect.Effect<A, E, R> =>
70
+ Effect.provideService(
71
+ self,
72
+ CurrentXHRResponseType,
73
+ "arraybuffer"
74
+ )
26
75
 
27
76
  /**
28
77
  * @since 1.0.0
29
- * @category fiber refs
78
+ * @category Services
30
79
  */
31
- export const currentXHRResponseType: FiberRef.FiberRef<"text" | "arraybuffer"> = internal.currentXHRResponseType
80
+ export class XMLHttpRequest extends ServiceMap.Service<
81
+ XMLHttpRequest,
82
+ LazyArg<globalThis.XMLHttpRequest>
83
+ >()("@effect/platform-browser/BrowserHttpClient/XMLHttpRequest") {}
84
+
85
+ const makeXhrRequest = () => new globalThis.XMLHttpRequest()
86
+
87
+ const makeXmlHttpRequest = HttpClient.make(
88
+ (request, url, signal, fiber) =>
89
+ Effect.suspend(() => {
90
+ const xhr = ServiceMap.getOrElse(
91
+ fiber.services,
92
+ XMLHttpRequest,
93
+ () => makeXhrRequest
94
+ )()
95
+ signal.addEventListener("abort", () => {
96
+ xhr.abort()
97
+ xhr.onreadystatechange = null
98
+ }, { once: true })
99
+ xhr.open(request.method, url.toString(), true)
100
+ xhr.responseType = fiber.getRef(CurrentXHRResponseType)
101
+ Object.entries(request.headers).forEach(([k, v]) => {
102
+ xhr.setRequestHeader(k, v)
103
+ })
104
+ return Effect.andThen(
105
+ sendBody(xhr, request),
106
+ Effect.callback<ClientResponseImpl, HttpClientError.HttpClientError>((resume) => {
107
+ let sent = false
108
+ const onChange = () => {
109
+ if (!sent && xhr.readyState >= 2) {
110
+ sent = true
111
+ resume(Effect.succeed(new ClientResponseImpl(request, xhr)))
112
+ }
113
+ }
114
+ xhr.onreadystatechange = onChange
115
+ xhr.onerror = (_event) => {
116
+ resume(Effect.fail(
117
+ new HttpClientError.HttpClientError({
118
+ reason: new HttpClientError.TransportError({
119
+ request,
120
+ cause: xhr.statusText
121
+ })
122
+ })
123
+ ))
124
+ }
125
+ onChange()
126
+ return Effect.void
127
+ })
128
+ )
129
+ })
130
+ )
131
+
132
+ const sendBody = (
133
+ xhr: globalThis.XMLHttpRequest,
134
+ request: HttpClientRequest.HttpClientRequest
135
+ ): Effect.Effect<void, HttpClientError.HttpClientError> => {
136
+ const body = request.body
137
+ switch (body._tag) {
138
+ case "Empty":
139
+ return Effect.sync(() => xhr.send())
140
+ case "Raw":
141
+ return Effect.sync(() => xhr.send(body.body as any))
142
+ case "Uint8Array":
143
+ return Effect.sync(() => xhr.send(body.body as any))
144
+ case "FormData":
145
+ return Effect.sync(() => xhr.send(body.formData))
146
+ case "Stream":
147
+ return Effect.matchEffect(
148
+ Stream.runFold(body.stream, () => new Uint8Array(0), (acc, chunk) => {
149
+ const next = new Uint8Array(acc.length + chunk.length)
150
+ next.set(acc, 0)
151
+ next.set(chunk, acc.length)
152
+ return next
153
+ }),
154
+ {
155
+ onFailure: (cause) =>
156
+ Effect.fail(
157
+ new HttpClientError.HttpClientError({
158
+ reason: new HttpClientError.EncodeError({
159
+ request,
160
+ cause
161
+ })
162
+ })
163
+ ),
164
+ onSuccess: (body) => Effect.sync(() => xhr.send(body))
165
+ }
166
+ )
167
+ }
168
+ }
169
+
170
+ const encoder = new TextEncoder()
171
+
172
+ abstract class IncomingMessageImpl<E> extends Inspectable.Class implements HttpIncomingMessage.HttpIncomingMessage<E> {
173
+ readonly [HttpIncomingMessage.TypeId]: typeof HttpIncomingMessage.TypeId
174
+ readonly source: globalThis.XMLHttpRequest
175
+ readonly onError: (error: unknown) => E
176
+
177
+ constructor(source: globalThis.XMLHttpRequest, onError: (error: unknown) => E) {
178
+ super()
179
+ this[HttpIncomingMessage.TypeId] = HttpIncomingMessage.TypeId
180
+ this.source = source
181
+ this.onError = onError
182
+ this._rawHeaderString = source.getAllResponseHeaders()
183
+ }
184
+
185
+ private _rawHeaderString: string
186
+ private _rawHeaders: Record<string, string | Array<string>> | undefined
187
+ private _headers: Headers.Headers | undefined
188
+ get headers() {
189
+ if (this._headers) {
190
+ return this._headers
191
+ }
192
+ if (this._rawHeaderString === "") {
193
+ return this._headers = Headers.empty
194
+ }
195
+ const parser = HeaderParser.make()
196
+ const result = parser(encoder.encode(this._rawHeaderString + "\r\n"), 0)
197
+ this._rawHeaders = result._tag === "Headers" ? result.headers : undefined
198
+ const parsed = result._tag === "Headers" ? Headers.fromInput(result.headers) : Headers.empty
199
+ return this._headers = parsed
200
+ }
201
+
202
+ cachedCookies: Cookies.Cookies | undefined
203
+ get cookies() {
204
+ if (this.cachedCookies) {
205
+ return this.cachedCookies
206
+ }
207
+ if (this._rawHeaders === undefined) {
208
+ return Cookies.empty
209
+ } else if (this._rawHeaders["set-cookie"] === undefined) {
210
+ return this.cachedCookies = Cookies.empty
211
+ }
212
+ return this.cachedCookies = Cookies.fromSetCookie(this._rawHeaders["set-cookie"])
213
+ }
214
+
215
+ get remoteAddress() {
216
+ return undefined
217
+ }
218
+
219
+ _textEffect: Effect.Effect<string, E> | undefined
220
+ get text(): Effect.Effect<string, E> {
221
+ if (this._textEffect) {
222
+ return this._textEffect
223
+ }
224
+ return this._textEffect = Effect.callback<string, E>((resume) => {
225
+ if (this.source.readyState === 4) {
226
+ resume(Effect.succeed(this.source.responseText))
227
+ return
228
+ }
229
+
230
+ const onReadyStateChange = () => {
231
+ if (this.source.readyState === 4) {
232
+ resume(Effect.succeed(this.source.responseText))
233
+ }
234
+ }
235
+ const onError = () => {
236
+ resume(Effect.fail(this.onError(this.source.statusText)))
237
+ }
238
+ this.source.addEventListener("readystatechange", onReadyStateChange)
239
+ this.source.addEventListener("error", onError)
240
+ return Effect.sync(() => {
241
+ this.source.removeEventListener("readystatechange", onReadyStateChange)
242
+ this.source.removeEventListener("error", onError)
243
+ })
244
+ }).pipe(
245
+ Effect.cached,
246
+ Effect.runSync
247
+ )
248
+ }
249
+
250
+ get json(): Effect.Effect<unknown, E> {
251
+ return Effect.flatMap(this.text, (text) =>
252
+ Effect.try({
253
+ try: () => text === "" ? null : JSON.parse(text) as unknown,
254
+ catch: this.onError
255
+ }))
256
+ }
257
+
258
+ get urlParamsBody(): Effect.Effect<UrlParams.UrlParams, E> {
259
+ return Effect.flatMap(this.text, (text) =>
260
+ Effect.try({
261
+ try: () => UrlParams.fromInput(new URLSearchParams(text)),
262
+ catch: this.onError
263
+ }))
264
+ }
265
+
266
+ get stream(): Stream.Stream<Uint8Array, E> {
267
+ return Stream.callback<Uint8Array, E>((queue) => {
268
+ let offset = 0
269
+ const onReadyStateChange = () => {
270
+ if (this.source.readyState === 3) {
271
+ const encoded = encoder.encode(this.source.responseText.slice(offset))
272
+ Queue.offerUnsafe(queue, encoded)
273
+ offset = this.source.responseText.length
274
+ } else if (this.source.readyState === 4) {
275
+ const encoded = encoder.encode(this.source.responseText.slice(offset))
276
+ if (offset < this.source.responseText.length) {
277
+ Queue.offerUnsafe(queue, encoded)
278
+ }
279
+ Queue.endUnsafe(queue)
280
+ }
281
+ }
282
+ const onError = () => {
283
+ Queue.failCauseUnsafe(queue, Cause.fail(this.onError(this.source.statusText)))
284
+ }
285
+ this.source.addEventListener("readystatechange", onReadyStateChange)
286
+ this.source.addEventListener("error", onError)
287
+ onReadyStateChange()
288
+ return Effect.sync(() => {
289
+ this.source.removeEventListener("readystatechange", onReadyStateChange)
290
+ this.source.removeEventListener("error", onError)
291
+ })
292
+ })
293
+ }
294
+
295
+ _arrayBufferEffect: Effect.Effect<ArrayBuffer, E> | undefined
296
+ get arrayBuffer(): Effect.Effect<ArrayBuffer, E> {
297
+ if (this._arrayBufferEffect) {
298
+ return this._arrayBufferEffect
299
+ }
300
+ return this._arrayBufferEffect = Effect.callback<ArrayBuffer, E>((resume) => {
301
+ if (this.source.readyState === 4) {
302
+ resume(Effect.succeed(this.source.response))
303
+ return
304
+ }
305
+
306
+ const onReadyStateChange = () => {
307
+ if (this.source.readyState === 4) {
308
+ resume(Effect.succeed(this.source.response))
309
+ }
310
+ }
311
+ const onError = () => {
312
+ resume(Effect.fail(this.onError(this.source.statusText)))
313
+ }
314
+ this.source.addEventListener("readystatechange", onReadyStateChange)
315
+ this.source.addEventListener("error", onError)
316
+ return Effect.sync(() => {
317
+ this.source.removeEventListener("readystatechange", onReadyStateChange)
318
+ this.source.removeEventListener("error", onError)
319
+ })
320
+ }).pipe(
321
+ Effect.map((response) => {
322
+ if (typeof response === "string") {
323
+ const arr = encoder.encode(response)
324
+ return arr.byteLength !== arr.buffer.byteLength
325
+ ? arr.buffer.slice(arr.byteOffset, arr.byteOffset + arr.byteLength)
326
+ : arr.buffer
327
+ }
328
+ return response
329
+ }),
330
+ Effect.cached,
331
+ Effect.runSync
332
+ )
333
+ }
334
+ }
335
+
336
+ class ClientResponseImpl extends IncomingMessageImpl<HttpClientError.HttpClientError>
337
+ implements HttpClientResponse.HttpClientResponse
338
+ {
339
+ readonly [HttpClientResponse.TypeId]: typeof HttpClientResponse.TypeId
340
+ readonly request: HttpClientRequest.HttpClientRequest
341
+
342
+ constructor(
343
+ request: HttpClientRequest.HttpClientRequest,
344
+ source: globalThis.XMLHttpRequest
345
+ ) {
346
+ super(source, (cause) =>
347
+ new HttpClientError.HttpClientError({
348
+ reason: new HttpClientError.DecodeError({
349
+ request,
350
+ response: this,
351
+ cause
352
+ })
353
+ }))
354
+ this.request = request
355
+ this[HttpClientResponse.TypeId] = HttpClientResponse.TypeId
356
+ }
357
+
358
+ get status() {
359
+ return this.source.status
360
+ }
361
+
362
+ get formData(): Effect.Effect<FormData, HttpClientError.HttpClientError> {
363
+ return Effect.die("Not implemented")
364
+ }
365
+
366
+ override toString(): string {
367
+ return `ClientResponse(${this.status})`
368
+ }
369
+
370
+ toJSON(): unknown {
371
+ return HttpIncomingMessage.inspect(this, {
372
+ _id: "@effect/platform/HttpClientResponse",
373
+ request: this.request.toJSON(),
374
+ status: this.status
375
+ })
376
+ }
377
+ }
32
378
 
33
379
  /**
34
380
  * @since 1.0.0
35
- * @category fiber refs
381
+ * @category Layers
36
382
  */
37
- export const withXHRArrayBuffer: <A, E, R>(effect: Effect<A, E, R>) => Effect<A, E, R> = internal.withXHRArrayBuffer
383
+ export const layerXMLHttpRequest: Layer.Layer<HttpClient.HttpClient> = HttpClient.layerMergedServices(
384
+ Effect.succeed(makeXmlHttpRequest)
385
+ )
@@ -1,22 +1,29 @@
1
1
  /**
2
2
  * @since 1.0.0
3
3
  */
4
- import type * as KeyValueStore from "@effect/platform/KeyValueStore"
5
4
  import type * as Layer from "effect/Layer"
6
- import * as internal from "./internal/keyValueStore.js"
5
+ import * as KeyValueStore from "effect/unstable/persistence/KeyValueStore"
7
6
 
8
7
  /**
9
- * Creates a KeyValueStore layer that uses the browser's localStorage api. Values are stored between sessions.
8
+ * Creates a `KeyValueStore` layer that uses the browser's `localStorage` api.
9
+ *
10
+ * Values are stored between sessions.
10
11
  *
11
12
  * @since 1.0.0
12
- * @category models
13
+ * @category Layers
13
14
  */
14
- export const layerLocalStorage: Layer.Layer<KeyValueStore.KeyValueStore> = internal.layerLocalStorage
15
+ export const layerLocalStorage: Layer.Layer<KeyValueStore.KeyValueStore> = KeyValueStore.layerStorage(() =>
16
+ globalThis.localStorage
17
+ )
15
18
 
16
19
  /**
17
- * Creates a KeyValueStore layer that uses the browser's sessionStorage api. Values are stored only for the current session.
20
+ * Creates a `KeyValueStore` layer that uses the browser's `sessionStorage` api.
21
+ *
22
+ * Values are stored only for the current session.
18
23
  *
19
24
  * @since 1.0.0
20
- * @category models
25
+ * @category Layers
21
26
  */
22
- export const layerSessionStorage: Layer.Layer<KeyValueStore.KeyValueStore> = internal.layerSessionStorage
27
+ export const layerSessionStorage: Layer.Layer<KeyValueStore.KeyValueStore> = KeyValueStore.layerStorage(() =>
28
+ globalThis.sessionStorage
29
+ )
@@ -1,11 +1,37 @@
1
1
  /**
2
2
  * @since 1.0.0
3
3
  */
4
- import type { RunMain } from "@effect/platform/Runtime"
5
- import * as internal from "./internal/runtime.js"
4
+ import type * as Effect from "effect/Effect"
5
+ import { makeRunMain, type Teardown } from "effect/Runtime"
6
6
 
7
7
  /**
8
8
  * @since 1.0.0
9
- * @category runtime
9
+ * @category Runtime
10
10
  */
11
- export const runMain: RunMain = internal.runMain
11
+ export const runMain: {
12
+ /**
13
+ * @since 1.0.0
14
+ * @category Runtime
15
+ */
16
+ (
17
+ options?: {
18
+ readonly disableErrorReporting?: boolean | undefined
19
+ readonly teardown?: Teardown | undefined
20
+ }
21
+ ): <E, A>(effect: Effect.Effect<A, E>) => void
22
+ /**
23
+ * @since 1.0.0
24
+ * @category Runtime
25
+ */
26
+ <E, A>(
27
+ effect: Effect.Effect<A, E>,
28
+ options?: {
29
+ readonly disableErrorReporting?: boolean | undefined
30
+ readonly teardown?: Teardown | undefined
31
+ }
32
+ ): void
33
+ } = makeRunMain(({ fiber }) => {
34
+ globalThis.addEventListener("beforeunload", () => {
35
+ fiber.interruptUnsafe(fiber.id)
36
+ })
37
+ })
@@ -1,25 +1,25 @@
1
1
  /**
2
2
  * @since 1.0.0
3
3
  */
4
- import * as Socket from "@effect/platform/Socket"
5
4
  import * as Layer from "effect/Layer"
5
+ import * as Socket from "effect/unstable/socket/Socket"
6
6
 
7
7
  /**
8
8
  * @since 1.0.0
9
- * @category layers
9
+ * @category Layers
10
10
  */
11
11
  export const layerWebSocket = (url: string, options?: {
12
12
  readonly closeCodeIsError?: (code: number) => boolean
13
13
  }): Layer.Layer<Socket.Socket> =>
14
- Layer.scoped(Socket.Socket, Socket.makeWebSocket(url, options)).pipe(
14
+ Layer.effect(Socket.Socket, Socket.makeWebSocket(url, options)).pipe(
15
15
  Layer.provide(layerWebSocketConstructor)
16
16
  )
17
17
 
18
18
  /**
19
- * A WebSocket constructor that uses globalThis.WebSocket.
19
+ * A WebSocket constructor that uses `globalThis.WebSocket`.
20
20
  *
21
21
  * @since 1.0.0
22
- * @category layers
22
+ * @category Layers
23
23
  */
24
24
  export const layerWebSocketConstructor: Layer.Layer<Socket.WebSocketConstructor> =
25
25
  Socket.layerWebSocketConstructorGlobal
@@ -2,33 +2,43 @@
2
2
  * @since 1.0.0
3
3
  */
4
4
 
5
- import type * as Stream from "effect/Stream"
6
- import * as internal from "./internal/stream.js"
5
+ import * as Stream from "effect/Stream"
7
6
 
8
7
  /**
9
- * Creates a `Stream` from window.addEventListener.
8
+ * Creates a `Stream` from `window.addEventListener`.
9
+ *
10
+ * By default, the underlying buffer is unbounded in size. You can customize the
11
+ * buffer size an object as the second argument with the `bufferSize` field.
12
+ *
10
13
  * @since 1.0.0
14
+ * @category Streams
11
15
  */
12
- export const fromEventListenerWindow: <K extends keyof WindowEventMap>(
16
+ export const fromEventListenerWindow = <K extends keyof WindowEventMap>(
13
17
  type: K,
14
18
  options?: boolean | {
15
19
  readonly capture?: boolean
16
20
  readonly passive?: boolean
17
21
  readonly once?: boolean
18
- readonly bufferSize?: number | "unbounded" | undefined
22
+ readonly bufferSize?: number | undefined
19
23
  } | undefined
20
- ) => Stream.Stream<WindowEventMap[K], never, never> = internal.fromEventListenerWindow
24
+ ): Stream.Stream<WindowEventMap[K], never, never> => Stream.fromEventListener<WindowEventMap[K]>(window, type, options)
21
25
 
22
26
  /**
23
- * Creates a `Stream` from document.addEventListener.
27
+ * Creates a `Stream` from `document.addEventListener`.
28
+ *
29
+ * By default, the underlying buffer is unbounded in size. You can customize the
30
+ * buffer size an object as the second argument with the `bufferSize` field.
31
+ *
24
32
  * @since 1.0.0
33
+ * @category Streams
25
34
  */
26
- export const fromEventListenerDocument: <K extends keyof DocumentEventMap>(
35
+ export const fromEventListenerDocument = <K extends keyof DocumentEventMap>(
27
36
  type: K,
28
37
  options?: boolean | {
29
38
  readonly capture?: boolean
30
39
  readonly passive?: boolean
31
40
  readonly once?: boolean
32
- readonly bufferSize?: number | "unbounded" | undefined
41
+ readonly bufferSize?: number | undefined
33
42
  } | undefined
34
- ) => Stream.Stream<DocumentEventMap[K], never, never> = internal.fromEventListenerDocument
43
+ ): Stream.Stream<DocumentEventMap[K], never, never> =>
44
+ Stream.fromEventListener<DocumentEventMap[K]>(document, type, options)
@@ -1,33 +1,70 @@
1
1
  /**
2
2
  * @since 1.0.0
3
3
  */
4
- import type * as Worker from "@effect/platform/Worker"
5
- import type * as Layer from "effect/Layer"
6
- import * as internal from "./internal/worker.js"
7
- /**
8
- * @since 1.0.0
9
- * @category layers
10
- */
11
- export const layerManager: Layer.Layer<Worker.WorkerManager> = internal.layerManager
12
-
13
- /**
14
- * @since 1.0.0
15
- * @category layers
16
- */
17
- export const layerWorker: Layer.Layer<Worker.PlatformWorker> = internal.layerWorker
4
+ import * as Deferred from "effect/Deferred"
5
+ import * as Effect from "effect/Effect"
6
+ import * as Layer from "effect/Layer"
7
+ import * as Scope from "effect/Scope"
8
+ import * as Worker from "effect/unstable/workers/Worker"
9
+ import { WorkerError, WorkerReceiveError } from "effect/unstable/workers/WorkerError"
18
10
 
19
11
  /**
20
12
  * @since 1.0.0
21
- * @category layers
13
+ * @category Layers
22
14
  */
23
- export const layer: (
15
+ export const layer = (
24
16
  spawn: (id: number) => Worker | SharedWorker | MessagePort
25
- ) => Layer.Layer<Worker.WorkerManager | Worker.Spawner> = internal.layer
17
+ ): Layer.Layer<Worker.WorkerPlatform | Worker.Spawner> =>
18
+ Layer.merge(
19
+ layerPlatform,
20
+ Worker.layerSpawner(spawn)
21
+ )
26
22
 
27
23
  /**
28
24
  * @since 1.0.0
29
- * @category layers
25
+ * @category Layers
30
26
  */
31
- export const layerPlatform: (
32
- spawn: (id: number) => globalThis.Worker | globalThis.SharedWorker | MessagePort
33
- ) => Layer.Layer<Worker.PlatformWorker | Worker.Spawner> = internal.layerPlatform
27
+ export const layerPlatform: Layer.Layer<Worker.WorkerPlatform> = Layer.succeed(Worker.WorkerPlatform)(
28
+ Worker.makePlatform<globalThis.SharedWorker | globalThis.Worker | MessagePort>()({
29
+ setup({ scope, worker }) {
30
+ const port = "port" in worker ? worker.port : worker
31
+ return Effect.as(
32
+ Scope.addFinalizer(
33
+ scope,
34
+ Effect.sync(() => {
35
+ port.postMessage([1])
36
+ })
37
+ ),
38
+ port
39
+ )
40
+ },
41
+ listen({ deferred, emit, port, scope }) {
42
+ function onMessage(event: MessageEvent) {
43
+ emit(event.data)
44
+ }
45
+ function onError(event: ErrorEvent) {
46
+ Deferred.doneUnsafe(
47
+ deferred,
48
+ new WorkerError({
49
+ reason: new WorkerReceiveError({
50
+ message: "An error event was emitter",
51
+ cause: event.error ?? event.message
52
+ })
53
+ }).asEffect()
54
+ )
55
+ }
56
+ port.addEventListener("message", onMessage as any)
57
+ port.addEventListener("error", onError as any)
58
+ if ("start" in port) {
59
+ port.start()
60
+ }
61
+ return Scope.addFinalizer(
62
+ scope,
63
+ Effect.sync(() => {
64
+ port.removeEventListener("message", onMessage as any)
65
+ port.removeEventListener("error", onError as any)
66
+ })
67
+ )
68
+ }
69
+ })
70
+ )