<Self>(
node: NodeKey<Self> & {
readonly url?: string
readonly endpoints?: Endpoints
},
options?: {
readonly url?: string
readonly serialization?: Layer.Layer<RpcSerialization.RpcSerialization>
}
): Layer.Layer<Self, NodeUnreachable | UnaddressedNode>Wire a Node's transport over a WebSocket — the browser counterpart to
http. Every stream (each HyperService's status + metrics + logs) rides one
multiplexed connection, so a dashboard never trips the browser's ~6-connection-per-origin
HTTP/1.1 limit that starves streams over http (in a browser http now
fails hard — see its note). The server must be a wsServer.
The url may be a same-origin path ("/rpc" — resolved against the page location, so the
browser follows its own host + scheme, http→ws / https→wss), an http(s):// url (scheme
swapped), or an absolute ws(s):// url. Resolution is lazy, so this is safe to call at module
scope in a file a Node server also imports. Uses the browser's global WebSocket.
const EdgeLive = Hyperlink.ws(EdgeNode, { url: "/rpc" }); // same origin as the pageconst const ws: <Self>(
node: NodeKey<Self> & {
readonly url?: string
readonly endpoints?: Endpoints
},
options?: {
readonly url?: string
readonly serialization?: Layer.Layer<RpcSerialization.RpcSerialization>
}
) => Layer.Layer<
Self,
NodeUnreachable | UnaddressedNode
>
Wire a
Node
's transport over a WebSocket — the browser counterpart to
http
. Every stream (each HyperService's status + metrics + logs) rides one
multiplexed connection, so a dashboard never trips the browser's ~6-connection-per-origin
HTTP/1.1 limit that starves streams over
http
(in a browser
http
now
fails hard — see its note). The server must be a
wsServer
.
The url may be a same-origin path ("/rpc" — resolved against the page location, so the
browser follows its own host + scheme, http→ws / https→wss), an http(s):// url (scheme
swapped), or an absolute ws(s):// url. Resolution is lazy, so this is safe to call at module
scope in a file a Node server also imports. Uses the browser's global WebSocket.
const EdgeLive = Hyperlink.ws(EdgeNode, { url: "/rpc" }); // same origin as the page
ws = <function (type parameter) Self in <Self>(node: NodeKey<Self> & {
readonly url?: string;
readonly endpoints?: Endpoints;
}, options?: {
readonly url?: string;
readonly serialization?: Layer.Layer<RpcSerialization.RpcSerialization>;
}): Layer.Layer<Self, NodeUnreachable | UnaddressedNode>
Self>(
node: NodeKey<Self> & {
readonly url?: string
readonly endpoints?: Endpoints
}
node: type NodeKey<HSelf> = Context.Key<HSelf, NodeProtocol>The Context key of a
Node
(HSelf = its identity): a service whose value is the
transport
NodeProtocol
. Stored on a node-bearing tag under
nodeSym
; read by
Hyperlink.client
to resolve where to connect (its requirement channel).
NodeKey<function (type parameter) Self in <Self>(node: NodeKey<Self> & {
readonly url?: string;
readonly endpoints?: Endpoints;
}, options?: {
readonly url?: string;
readonly serialization?: Layer.Layer<RpcSerialization.RpcSerialization>;
}): Layer.Layer<Self, NodeUnreachable | UnaddressedNode>
Self> & { readonly url?: string | undefinedurl?: string; readonly endpoints?: Endpoints | undefinedendpoints?: Endpoints },
options: {
readonly url?: string
readonly serialization?: Layer.Layer<RpcSerialization.RpcSerialization>
}
options?: {
readonly url?: string | undefinedurl?: string;
readonly serialization?: Layer.Layer<RpcSerialization.RpcSerialization>serialization?: import LayerLayer.interface Layer<in ROut, out E = never, out RIn = never>A Layer describes how to build one or more services for dependency injection.
When to use
Use to model construction of application services for dependency injection,
especially when services have dependencies, can fail during construction, or
need scoped setup and release.
Details
A Layer<ROut, E, RIn> represents ROut as the services this layer
provides, E as the possible errors during layer construction, and RIn as
the services this layer requires as dependencies.
Layer<import RpcSerializationRpcSerialization.class RpcSerializationclass RpcSerialization {
key: Identifier;
Service: {
makeUnsafe: () => RpcSerialization.Parser;
contentType: string;
includesFraming: boolean;
};
}
Service that describes how RPC protocol messages are encoded and decoded,
including the content type and whether the serialization format provides
message framing.
When to use
Use to provide the serialization boundary shared by RPC clients and servers
for a chosen wire format.
RpcSerialization>;
},
): import LayerLayer.interface Layer<in ROut, out E = never, out RIn = never>A Layer describes how to build one or more services for dependency injection.
When to use
Use to model construction of application services for dependency injection,
especially when services have dependencies, can fail during construction, or
need scoped setup and release.
Details
A Layer<ROut, E, RIn> represents ROut as the services this layer
provides, E as the possible errors during layer construction, and RIn as
the services this layer requires as dependencies.
Layer<function (type parameter) Self in <Self>(node: NodeKey<Self> & {
readonly url?: string;
readonly endpoints?: Endpoints;
}, options?: {
readonly url?: string;
readonly serialization?: Layer.Layer<RpcSerialization.RpcSerialization>;
}): Layer.Layer<Self, NodeUnreachable | UnaddressedNode>
Self, class NodeUnreachableclass NodeUnreachable {
message: string;
name: string;
stack: string;
cause: unknown;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
_tag: Tag;
node: string;
url: string;
}
A remote
Node
that didn't answer at its declared address — down, wrong port/url, or (for a
socket node) a server not speaking the socket protocol. Surfaced eagerly by
verifyConnection
so a client fails fast at startup instead of hanging or erroring opaquely at the first call.
NodeUnreachable | class UnaddressedNodeclass UnaddressedNode {
message: string;
name: string;
stack: string;
cause: unknown;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
_tag: Tag;
node: string;
}
Deriving a transport from a node that never declared one — a bare Node.Tag()("x") has no
address/kind, so connect / listen can't know how to reach it. Surfaces on the Layer / Effect
error channel (never a sync throw).
UnaddressedNode> => {
// The client sibling of {@link Node.ws} = `connect` + {@link protocolWebsocket}. Prefers the node's
// own WebSocket endpoint (multi-protocol — its primary `url` is the Http one on an `{ http, ws }`
// node), then the primary `url`, then `"/rpc"`; `options.url` overrides.
const const url: stringurl =
options: {
readonly url?: string
readonly serialization?: Layer.Layer<RpcSerialization.RpcSerialization>
}
options?.url?: string | undefinedurl ?? node: NodeKey<Self> & {
readonly url?: string
readonly endpoints?: Endpoints
}
node.endpoints?: Endpoints | undefinedendpoints?.Endpoints.WebSocket?: {
readonly url: string;
} | undefined
WebSocket?.url: string | undefinedurl ?? node: NodeKey<Self> & {
readonly url?: string
readonly endpoints?: Endpoints
}
node.url?: string | undefinedurl ?? "/rpc";
const const wired: Layer.Layer<
Self,
never,
never
>
const wired: {
build: (memoMap: MemoMap, scope: Scope.Scope) => Effect<Context.Context<Self>, never, never>;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
}
wired = connectLayer<Self, E, RIn>(node: NodeKey<Self>, protocol: Layer.Layer<RpcClient.Protocol, E, RIn>): Layer.Layer<Self, E, RIn>Provide a node handle for node over protocol — its { protocol } plus lazy status accessors.
connectLayer(
node: NodeKey<Self> & {
readonly url?: string
readonly endpoints?: Endpoints
}
node,
const protocolWebsocket: (
target?: number | string,
serialization?: Layer.Layer<RpcSerialization.RpcSerialization>
) => Layer.Layer<RpcClient.Protocol>
Build a WebSocket client Protocol (one multiplexed connection + ndjson) for one endpoint
url (default "/rpc"). The url may be a same-origin path ("/rpc" — resolved against the
page location, http→ws / https→wss), an http(s):// url (scheme swapped), or an absolute
ws(s):// url; resolution is lazy, so this is safe at module scope in a file a Node server also
imports. The browser transport — every stream rides one connection, past the ~6-connection cap that
starves streams over
protocolHttp
.
protocolWebsocket(const url: stringurl, options: {
readonly url?: string
readonly serialization?: Layer.Layer<RpcSerialization.RpcSerialization>
}
options?.serialization?: Layer.Layer<RpcSerialization.RpcSerialization>serialization),
);
return import LayerLayer.const unwrap: <A, E1, R1, E, R>(
self: Effect<Layer<A, E1, R1>, E, R>
) => Layer<
A,
E | E1,
R1 | Exclude<R, Scope.Scope>
>
Unwraps a Layer from an Effect, flattening the nested structure.
When to use
Use when you have an Effect that produces a Layer and you want to
use that layer directly.
Details
The resulting Layer will have the combined error and dependency types from
both the outer Effect and the inner Layer.
Example (Unwrapping an effectful layer)
import { Context, Effect, Layer } from "effect"
class Database extends Context.Service<Database, {
readonly query: (sql: string) => Effect.Effect<string>
}>()("Database") {}
const layerEffect = Effect.succeed(
Layer.succeed(Database, { query: Effect.fn("Database.query")((sql: string) => Effect.succeed("result")) })
)
const unwrappedLayer = Layer.unwrap(layerEffect)
unwrap(
import EffectEffect.const gen: {
<Eff extends Effect<any, any, any>, AEff>(
f: () => Generator<Eff, AEff, never>
): Effect<
AEff,
[Eff] extends [never]
? never
: [Eff] extends [
Effect<infer _A, infer E, infer _R>
]
? E
: never,
[Eff] extends [never]
? never
: [Eff] extends [
Effect<infer _A, infer _E, infer R>
]
? R
: never
>
<Self, Eff extends Effect<any, any, any>, AEff>(
options: { readonly self: Self },
f: (this: Self) => Generator<Eff, AEff, never>
): Effect<
AEff,
[Eff] extends [never]
? never
: [Eff] extends [
Effect<infer _A, infer E, infer _R>
]
? E
: never,
[Eff] extends [never]
? never
: [Eff] extends [
Effect<infer _A, infer _E, infer R>
]
? R
: never
>
}
Provides a way to write effectful code using generator functions, simplifying
control flow and error handling.
When to use
Use when you want to write effectful code that looks and behaves like
synchronous code, while still handling asynchronous tasks, errors, and complex
control flow such as loops and conditions.
Generator functions work similarly to async/await but keep errors,
requirements, and interruption in the Effect type. You can yield* values
from effects and return the final result at the end.
Example (Sequencing effects with generators)
import { Data, Effect } from "effect"
class DiscountRateError extends Data.TaggedError("DiscountRateError")<{}> {}
const addServiceCharge = (amount: number) => amount + 1
const applyDiscount = (
total: number,
discountRate: number
): Effect.Effect<number, DiscountRateError> =>
discountRate === 0
? Effect.fail(new DiscountRateError())
: Effect.succeed(total - (total * discountRate) / 100)
const fetchTransactionAmount = Effect.promise(() => Promise.resolve(100))
const fetchDiscountRate = Effect.promise(() => Promise.resolve(5))
export const program = Effect.gen(function*() {
const transactionAmount = yield* fetchTransactionAmount
const discountRate = yield* fetchDiscountRate
const discountedAmount = yield* applyDiscount(
transactionAmount,
discountRate
)
const finalAmount = addServiceCharge(discountedAmount)
return `Final amount to charge: ${finalAmount}`
})
gen(function* () {
// Relative same-origin paths (`"/rpc"`) are the browser dial target. Resolve to `ws(s)://`
// when `location` exists so verify probes WebSocket (not Http GET). Elsewhere skip — do
// **not** fall through to the addressed node's stamped `127.0.0.1` (that reintroduces F5).
if (const isProbeableAddress: (options?: {
readonly url?: string
readonly path?: string
}) => boolean
Absolute dial targets we can probe at layer build (skip relative same-origin /rpc).
isProbeableAddress({ url?: string | undefinedurl })) {
yield* const applyClientVerify: (
node: AnyNode,
options?: ClientVerifyProbeOptions
) => Effect.Effect<void, ClientVerifyError>
Default-on client verify (§8.6) — "reject" unless
ClientVerify
overrides.
When serviceKey + contractHash are set (tag-aware clients), escalates to deep F3/F4.
applyClientVerify(node: NodeKey<Self> & {
readonly url?: string
readonly endpoints?: Endpoints
}
node as type AnyNode = NodeKey<unknown> & {
readonly url: string | undefined;
readonly path: string | undefined;
readonly kind: ProtocolKind | undefined;
readonly endpoints?: Endpoints;
readonly onConflict?: OnConflict;
readonly [portSym]?: number;
}
A
Tag
erased — its transport endpoints set, plus the primary address
(url and/or Unix path) and
ProtocolKind
kind (the first-declared endpoint, kept for
single-protocol readers), so a tag's distributed set is self-describing about where AND how to
reach each one.
AnyNode, { url?: string | undefinedurl });
} else if (typeof var location: LocationThe Window.location read-only property returns a Location object with information about the current location of the document.
location !== "undefined") {
yield* const applyClientVerify: (
node: AnyNode,
options?: ClientVerifyProbeOptions
) => Effect.Effect<void, ClientVerifyError>
Default-on client verify (§8.6) — "reject" unless
ClientVerify
overrides.
When serviceKey + contractHash are set (tag-aware clients), escalates to deep F3/F4.
applyClientVerify(node: NodeKey<Self> & {
readonly url?: string
readonly endpoints?: Endpoints
}
node as type AnyNode = NodeKey<unknown> & {
readonly url: string | undefined;
readonly path: string | undefined;
readonly kind: ProtocolKind | undefined;
readonly endpoints?: Endpoints;
readonly onConflict?: OnConflict;
readonly [portSym]?: number;
}
A
Tag
erased — its transport endpoints set, plus the primary address
(url and/or Unix path) and
ProtocolKind
kind (the first-declared endpoint, kept for
single-protocol readers), so a tag's distributed set is self-describing about where AND how to
reach each one.
AnyNode, { url?: string | undefinedurl: const toWebSocketUrl: (
raw: string
) => string
toWebSocketUrl(const url: stringurl) });
}
return const wired: Layer.Layer<
Self,
never,
never
>
const wired: {
build: (memoMap: MemoMap, scope: Scope.Scope) => Effect<Context.Context<Self>, never, never>;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
}
wired;
}),
) as import LayerLayer.interface Layer<in ROut, out E = never, out RIn = never>A Layer describes how to build one or more services for dependency injection.
When to use
Use to model construction of application services for dependency injection,
especially when services have dependencies, can fail during construction, or
need scoped setup and release.
Details
A Layer<ROut, E, RIn> represents ROut as the services this layer
provides, E as the possible errors during layer construction, and RIn as
the services this layer requires as dependencies.
Layer<function (type parameter) Self in <Self>(node: NodeKey<Self> & {
readonly url?: string;
readonly endpoints?: Endpoints;
}, options?: {
readonly url?: string;
readonly serialization?: Layer.Layer<RpcSerialization.RpcSerialization>;
}): Layer.Layer<Self, NodeUnreachable | UnaddressedNode>
Self, class NodeUnreachableclass NodeUnreachable {
message: string;
name: string;
stack: string;
cause: unknown;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
_tag: Tag;
node: string;
url: string;
}
A remote
Node
that didn't answer at its declared address — down, wrong port/url, or (for a
socket node) a server not speaking the socket protocol. Surfaced eagerly by
verifyConnection
so a client fails fast at startup instead of hanging or erroring opaquely at the first call.
NodeUnreachable | class UnaddressedNodeclass UnaddressedNode {
message: string;
name: string;
stack: string;
cause: unknown;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
_tag: Tag;
node: string;
}
Deriving a transport from a node that never declared one — a bare Node.Tag()("x") has no
address/kind, so connect / listen can't know how to reach it. Surfaces on the Layer / Effect
error channel (never a sync throw).
UnaddressedNode>;
};