This repository has been archived by the owner on Jul 16, 2024. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
279d79e
commit 8ece61b
Showing
5 changed files
with
149 additions
and
58 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,65 +1,19 @@ | ||
/** | ||
* @since 1.0.0 | ||
* | ||
* Also includes exports from [`@effect/platform/WebSocket`](https://effect-ts.github.io/platform/platform/WebSocket.ts.html). | ||
*/ | ||
import * as Chunk from "effect/Chunk" | ||
import * as Context from "effect/Context" | ||
import * as Effect from "effect/Effect" | ||
import type * as Scope from "effect/Scope" | ||
import * as Stream from "effect/Stream" | ||
|
||
interface Socket { | ||
send: (data: string | Blob) => Effect.Effect<never, never, void> | ||
messages: Stream.Stream<never, never, string | Blob | ArrayBuffer> | ||
errors: Stream.Stream<never, never, Event> | ||
} | ||
|
||
export const tag = Context.Tag<Socket>("@effect/platform/WebSocket") | ||
|
||
/** @internal */ | ||
const createSocket = (url: string | URL) => | ||
Effect.acquireRelease( | ||
Effect.flatMap( | ||
Effect.try({ | ||
try: () => new WebSocket(url), | ||
catch: (err) => err as Error | ||
}), | ||
(socket) => | ||
Effect.async<never, never, WebSocket>((resume) => { | ||
socket.addEventListener("open", () => { | ||
resume(Effect.succeed(socket)) | ||
}) | ||
}) | ||
), | ||
(s) => Effect.succeed(s.close()) // could fail | ||
) | ||
import * as internal from "./internal/webSocket.js" | ||
|
||
/** | ||
* @since 1.0.0 | ||
*/ | ||
export const layer = (url: string | URL): Effect.Effect<Scope.Scope, Error, Socket> => | ||
Effect.gen(function*(_) { | ||
const socket = yield* _(createSocket(url)) | ||
|
||
const send = (data: string | Blob) => Effect.sync(() => socket.send(data)) | ||
|
||
const messages = Stream.async<never, never, string | Blob | ArrayBuffer>((emit) => { | ||
socket.addEventListener("message", (event) => { | ||
emit(Effect.succeed(Chunk.of(event.data as string | Blob | ArrayBuffer))) | ||
}) | ||
}) | ||
|
||
const errors = Stream.async<never, never, Event>((emit) => { | ||
socket.addEventListener("message", (event) => { | ||
emit(Effect.succeed(Chunk.of(event.data))) | ||
}) | ||
}) | ||
export * from "@effect/platform/WebSocket" | ||
|
||
return tag.of({ send, messages, errors }) | ||
}) | ||
|
||
const test = Effect.gen(function*(_) { | ||
const ws = yield* _(layer("ws://localhost:3000")) | ||
yield* _(ws.send("abc")) | ||
}).pipe(Effect.scoped, Effect.tapError((err) => Effect.logError(err))) | ||
|
||
Effect.runFork(test) | ||
/** | ||
* Creates a WebSocket layer. | ||
* | ||
* @since 1.0.0 | ||
* @category models | ||
*/ | ||
export const layer = internal.layer |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
import * as PlatformError from "@effect/platform/Error" | ||
import * as Socket from "@effect/platform/WebSocket" | ||
import * as Chunk from "effect/Chunk" | ||
import * as Effect from "effect/Effect" | ||
import * as Layer from "effect/Layer" | ||
import * as Stream from "effect/Stream" | ||
|
||
const make = (impl: Omit<Socket.Socket, Socket.TypeId>) => | ||
Socket.Socket.of({ | ||
[Socket.TypeId]: Socket.TypeId, | ||
...impl | ||
}) | ||
|
||
/** @internal */ | ||
const createSocket = (url: string | URL) => | ||
Effect.acquireRelease( | ||
Effect.flatMap( | ||
Effect.try({ | ||
try: () => new WebSocket(url), | ||
catch: () => | ||
PlatformError.SystemError({ | ||
module: "WebSocket", | ||
reason: "InvalidData", | ||
message: "Unable to create WebSocket. Invalid host.", | ||
method: "", | ||
pathOrDescriptor: "" | ||
}) | ||
}), | ||
(socket) => | ||
Effect.async<never, never, WebSocket>((resume) => { | ||
socket.addEventListener("open", () => { | ||
resume(Effect.succeed(socket)) | ||
}) | ||
}) | ||
), | ||
(s) => Effect.succeed(s.close(1001)) // .close is unsafe.. | ||
) | ||
|
||
/** | ||
* @since 1.0.0 | ||
*/ | ||
export const layer = (url: string | URL): Layer.Layer<never, PlatformError.SystemError, Socket.Socket> => | ||
Layer.scoped( | ||
Socket.Socket, | ||
Effect.gen(function*(_) { | ||
const socket = yield* _(createSocket(url)) | ||
|
||
// Safe as long as the socket is fully connected. | ||
const send = (data: string | Blob) => Effect.sync(() => socket.send(data)) | ||
|
||
const messages = Stream.async<never, never, string | Blob | ArrayBuffer>((emit) => { | ||
socket.addEventListener("message", (event) => { | ||
emit(Effect.succeed(Chunk.of(event.data as string | Blob | ArrayBuffer))) | ||
}) | ||
}) | ||
|
||
const errors = Stream.async<never, never, Event>((emit) => { | ||
socket.addEventListener("error", (event) => { | ||
emit(Effect.succeed(Chunk.of(event))) | ||
}) | ||
}) | ||
|
||
return make({ send, messages, errors }) | ||
}) | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
import type * as Context from "effect/Context" | ||
import type * as Effect from "effect/Effect" | ||
import type * as Stream from "effect/Stream" | ||
import * as internal from "./internal/webSocket.js" | ||
|
||
/** | ||
* @since 1.0.0 | ||
* @category type id | ||
*/ | ||
export const TypeId: unique symbol = internal.TypeId | ||
|
||
/** | ||
* @since 1.0.0 | ||
* @category type id | ||
*/ | ||
export type TypeId = typeof TypeId | ||
|
||
/** | ||
* @since 1.0.0 | ||
* @category models | ||
*/ | ||
export interface Socket { | ||
readonly [TypeId]: TypeId | ||
send: (data: string | Blob) => Effect.Effect<never, never, void> | ||
messages: Stream.Stream<never, never, string | Blob | ArrayBuffer> | ||
errors: Stream.Stream<never, never, Event> | ||
} | ||
|
||
/** | ||
* @since 1.0.0 | ||
* @category constructors | ||
*/ | ||
export const make: ( | ||
impl: Omit<Socket, typeof TypeId> | ||
) => Socket = internal.make | ||
|
||
/** | ||
* @since 1.0.0 | ||
* @category tags | ||
*/ | ||
export const Socket: Context.Tag<Socket, Socket> = internal.tag |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
/** | ||
* @since 1.0.0 | ||
*/ | ||
import * as Context from "effect/Context" | ||
import type * as WS from "../WebSocket.js" | ||
|
||
export const TypeId: WS.TypeId = Symbol.for( | ||
"@effect/platform/WebSocket" | ||
) as WS.TypeId | ||
|
||
export const tag = Context.Tag<WS.Socket>("@effect/platform/WebSocket") | ||
|
||
export const make = (impl: Omit<WS.Socket, WS.TypeId>) => | ||
tag.of({ | ||
[TypeId]: TypeId, | ||
...impl | ||
}) |