<Self, RIn>(
node: NodeKey<Self>,
protocol: Layer.Layer<RpcClient.Protocol, never, RIn>
): Layer.Layer<Self, never, RIn>
<RIn>(protocol: Layer.Layer<RpcClient.Protocol, never, RIn>): <Self>(
node: NodeKey<Self>
) => Layer.Layer<Self, never, RIn>
<Self>(node: AddressedNode<Self>): Layer.Layer<Self>Wire a Node's transport — the transport-agnostic primitive, dual:
MyNode.pipe(Node.connect) // derive the transport from the node's declared kind + url
MyNode.pipe(Node.connect(protocol)) // data-last: an explicit RpcClient.Protocol
Node.connect(MyNode) // data-first, derived (needs an AddressedNode)
Node.connect(MyNode, protocol) // data-first, explicitThe derived forms read the node's ProtocolKind — so a node that declares kind: "WebSocket"
dials WS and one that declares "Http" dials http; picking the wrong transport isn't
expressible. MyNode.pipe(Node.connect) only type-checks for an AddressedNode (a node
with both url/path and kind); a bare node is a compile error pointing you to declare its
address or pass a protocol.
Derived connect Layers are WeakMap-memoized per Node class so multiple
Hyperlink.client(Tag, MyNode) call sites share one MemoMap transport.
export const const connect: {
<Self, RIn>(
node: NodeKey<Self>,
protocol: Layer.Layer<
RpcClient.Protocol,
never,
RIn
>
): Layer.Layer<Self, never, RIn>
<RIn>(
protocol: Layer.Layer<
RpcClient.Protocol,
never,
RIn
>
): <Self>(
node: NodeKey<Self>
) => Layer.Layer<Self, never, RIn>
<Self>(
node: AddressedNode<Self>
): Layer.Layer<Self>
}
Wire a
Node
's transport — the transport-agnostic primitive, dual:
MyNode.pipe(Node.connect) // derive the transport from the node's declared kind + url
MyNode.pipe(Node.connect(protocol)) // data-last: an explicit RpcClient.Protocol
Node.connect(MyNode) // data-first, derived (needs an AddressedNode)
Node.connect(MyNode, protocol) // data-first, explicit
The derived forms read the node's
ProtocolKind
— so a node that declares kind: "WebSocket"
dials WS and one that declares "Http" dials http; picking the wrong transport isn't
expressible. MyNode.pipe(Node.connect) only type-checks for an
AddressedNode
(a node
with both url/path and kind); a bare node is a compile error pointing you to declare its
address or pass a protocol.
Derived connect Layers are WeakMap-memoized per Node class so multiple
Hyperlink.client(Tag, MyNode) call sites share one MemoMap transport.
connect: {
// Order matters: TS selects the LAST matching overload when the function is used as a bare value
// (`node.pipe(connect)`), so the node→Layer form is last to make the pipe form resolve to it; direct
// calls still match top-down by arg count / shape.
<function (type parameter) Self in <Self, RIn>(node: NodeKey<Self>, protocol: Layer.Layer<RpcClient.Protocol, never, RIn>): Layer.Layer<Self, never, RIn>Self, function (type parameter) RIn in <Self, RIn>(node: NodeKey<Self>, protocol: Layer.Layer<RpcClient.Protocol, never, RIn>): Layer.Layer<Self, never, RIn>RIn>(
node: NodeKey<Self>(parameter) node: {
key: string;
Service: {
protocol: Context.Service.Shape<typeof RpcClient.Protocol>;
ping: Effect.Effect<number, NodeUnreachable>;
status: { readonly get: Effect.Effect<NodeStatusSnapshot, NodeUnreachable>; readonly changes: Stream.Stream<NodeStatusSnapshot, NodeUnreachable> };
logs: { readonly stream: Stream.Stream<LogEntry, NodeUnreachable>; readonly query: (options: { readonly limit: number }) => Effect.Effect<ReadonlyArray<LogEntry>, NodeUnreachable> };
};
Identifier: Identifier;
stack: string | undefined;
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;
}
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, RIn>(node: NodeKey<Self>, protocol: Layer.Layer<RpcClient.Protocol, never, RIn>): Layer.Layer<Self, never, RIn>Self>,
protocol: Layer.Layer<
RpcClient.Protocol,
never,
RIn
>
(parameter) protocol: {
build: (memoMap: MemoMap, scope: Scope.Scope) => Effect<Context.Context<Protocol>, never, RIn>;
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; <…;
}
protocol: 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 RpcClientRpcClient.class Protocolclass Protocol {
key: Identifier;
Service: {
run: (clientId: number, f: (data: FromServerEncoded) => Effect.Effect<void>) => Effect.Effect<never>;
send: (clientId: number, request: FromClientEncoded, transferables?: ReadonlyArray<globalThis.Transferable>) => Effect.Effect<void, RpcClientError>;
supportsAck: boolean;
supportsTransferables: boolean;
};
}
Defines the service interface for an RPC client transport, responsible for running the
receive loop and sending encoded client messages.
When to use
Use to provide the transport boundary for RPC clients over HTTP, WebSocket,
workers, sockets, or custom protocols.
Protocol, never, function (type parameter) RIn in <Self, RIn>(node: NodeKey<Self>, protocol: Layer.Layer<RpcClient.Protocol, never, RIn>): Layer.Layer<Self, never, RIn>RIn>,
): 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, RIn>(node: NodeKey<Self>, protocol: Layer.Layer<RpcClient.Protocol, never, RIn>): Layer.Layer<Self, never, RIn>Self, never, function (type parameter) RIn in <Self, RIn>(node: NodeKey<Self>, protocol: Layer.Layer<RpcClient.Protocol, never, RIn>): Layer.Layer<Self, never, RIn>RIn>;
<function (type parameter) RIn in <RIn>(protocol: Layer.Layer<RpcClient.Protocol, never, RIn>): <Self>(node: NodeKey<Self>) => Layer.Layer<Self, never, RIn>RIn>(
protocol: Layer.Layer<
RpcClient.Protocol,
never,
RIn
>
(parameter) protocol: {
build: (memoMap: MemoMap, scope: Scope.Scope) => Effect<Context.Context<Protocol>, never, RIn>;
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; <…;
}
protocol: 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 RpcClientRpcClient.class Protocolclass Protocol {
key: Identifier;
Service: {
run: (clientId: number, f: (data: FromServerEncoded) => Effect.Effect<void>) => Effect.Effect<never>;
send: (clientId: number, request: FromClientEncoded, transferables?: ReadonlyArray<globalThis.Transferable>) => Effect.Effect<void, RpcClientError>;
supportsAck: boolean;
supportsTransferables: boolean;
};
}
Defines the service interface for an RPC client transport, responsible for running the
receive loop and sending encoded client messages.
When to use
Use to provide the transport boundary for RPC clients over HTTP, WebSocket,
workers, sockets, or custom protocols.
Protocol, never, function (type parameter) RIn in <RIn>(protocol: Layer.Layer<RpcClient.Protocol, never, RIn>): <Self>(node: NodeKey<Self>) => Layer.Layer<Self, never, RIn>RIn>,
): <function (type parameter) Self in <Self>(node: NodeKey<Self>): Layer.Layer<Self, never, RIn>Self>(node: NodeKey<Self>(parameter) node: {
key: string;
Service: {
protocol: Context.Service.Shape<typeof RpcClient.Protocol>;
ping: Effect.Effect<number, NodeUnreachable>;
status: { readonly get: Effect.Effect<NodeStatusSnapshot, NodeUnreachable>; readonly changes: Stream.Stream<NodeStatusSnapshot, NodeUnreachable> };
logs: { readonly stream: Stream.Stream<LogEntry, NodeUnreachable>; readonly query: (options: { readonly limit: number }) => Effect.Effect<ReadonlyArray<LogEntry>, NodeUnreachable> };
};
Identifier: Identifier;
stack: string | undefined;
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;
}
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>): Layer.Layer<Self, never, RIn>Self>) => 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>): Layer.Layer<Self, never, RIn>Self, never, function (type parameter) RIn in <RIn>(protocol: Layer.Layer<RpcClient.Protocol, never, RIn>): <Self>(node: NodeKey<Self>) => Layer.Layer<Self, never, RIn>RIn>;
/** Derived transport — only {@link AddressedNode}; error channel is empty (address proven). */
<function (type parameter) Self in <Self>(node: AddressedNode<Self>): Layer.Layer<Self>Self>(node: AddressedNode<Self>node: type AddressedNode<HSelf> = NodeKey<HSelf> & ({
readonly kind: "IpcSocket";
readonly path: string;
readonly url: string | undefined;
} | {
readonly kind: "Http";
readonly url: string;
readonly path: string | undefined;
} | {
readonly kind: "WebSocket";
readonly url: string;
readonly path: string | undefined;
} | {
readonly kind: "Http" | "WebSocket";
readonly url: string;
readonly path: string | undefined;
})
An
AnyNode
that can derive
connect
with no protocol argument —
kind set, and either a url (Http/WebSocket) or a Unix path (IpcSocket).
AddressedNode<function (type parameter) Self in <Self>(node: AddressedNode<Self>): Layer.Layer<Self>Self>): 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: AddressedNode<Self>): Layer.Layer<Self>Self>;
} = import FnFn.const dual: <(...args: Array<any>) => any, (node: AnyNode, protocol?: Layer.Layer<RpcClient.Protocol, never, never>) => Layer.Layer<never, UnaddressedNode | InvalidHttpTarget, never>>(isDataFirst: (args: IArguments) => boolean, body: (node: AnyNode, protocol?: Layer.Layer<RpcClient.Protocol, never, never>) => Layer.Layer<never, UnaddressedNode | InvalidHttpTarget, never>) => ((...args: Array<any>) => any) & ((node: AnyNode, protocol?: Layer.Layer<RpcClient.Protocol, never, never>) => Layer.Layer<never, UnaddressedNode | InvalidHttpTarget, never>) (+1 overload)Creates a function that can be called in data-first style or data-last
(pipe-friendly) style.
When to use
Use to expose one implementation through both direct and pipe-friendly
call styles.
Details
Pass either the arity of the uncurried function or a predicate that decides
whether the current call is data-first. Arity is the common case. Use a
predicate when optional arguments make arity ambiguous.
Example (Selecting data-first or data-last style by arity)
import { Function, pipe } from "effect"
const sum = Function.dual<
(that: number) => (self: number) => number,
(self: number, that: number) => number
>(2, (self, that) => self + that)
console.log(sum(2, 3)) // 5
console.log(pipe(2, sum(3))) // 5
Example (Defining overloads with call signatures)
import { Function, pipe } from "effect"
const sum: {
(that: number): (self: number) => number
(self: number, that: number): number
} = Function.dual(2, (self: number, that: number): number => self + that)
console.log(sum(2, 3)) // 5
console.log(pipe(2, sum(3))) // 5
Example (Selecting data-first or data-last style with a predicate)
import { Function, pipe } from "effect"
const sum = Function.dual<
(that: number) => (self: number) => number,
(self: number, that: number) => number
>(
(args) => args.length === 2,
(self, that) => self + that
)
console.log(sum(2, 3)) // 5
console.log(pipe(2, sum(3))) // 5
dual(
// data-first when there are two args, or when the single arg is a node (not a protocol layer).
(args: IArgumentsargs: IArguments) => args: IArgumentsargs.IArguments.length: numberlength >= 2 || !import LayerLayer.const isLayer: (
u: unknown
) => u is Layer<unknown, unknown, unknown>
Returns true if the specified value is a Layer, false otherwise.
Example (Checking whether a value is a layer)
import { Context, Effect, Layer } from "effect"
class Database extends Context.Service<Database, {
readonly query: (sql: string) => Effect.Effect<string>
}>()("Database") {}
const dbLayer = Layer.succeed(Database, {
query: Effect.fn("Database.query")((sql: string) => Effect.succeed("result"))
})
const notALayer = { someProperty: "value" }
console.log(Layer.isLayer(dbLayer)) // true
console.log(Layer.isLayer(notALayer)) // false
isLayer(args: IArgumentsargs[0]),
(
node: AnyNodenode: 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,
protocol: Layer.Layer<
RpcClient.Protocol,
never,
never
>
protocol?: 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 RpcClientRpcClient.class Protocolclass Protocol {
key: Identifier;
Service: {
run: (clientId: number, f: (data: FromServerEncoded) => Effect.Effect<void>) => Effect.Effect<never>;
send: (clientId: number, request: FromClientEncoded, transferables?: ReadonlyArray<globalThis.Transferable>) => Effect.Effect<void, RpcClientError>;
supportsAck: boolean;
supportsTransferables: boolean;
};
}
Defines the service interface for an RPC client transport, responsible for running the
receive loop and sending encoded client messages.
When to use
Use to provide the transport boundary for RPC clients over HTTP, WebSocket,
workers, sockets, or custom protocols.
Protocol, never, never>,
): 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<never, 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 | class InvalidHttpTargetclass InvalidHttpTarget {
name: string;
message: 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;
target: string;
}
resolveHttpTarget
/ a positional Node.Tag()(name, badString) got a string that is
neither a port (":3009"), a port number, nor an http(s):// url. Surfaces on the
Layer / Effect error channel (same precedent as
UnaddressedNode
) — never a
sync throw. Catch via Exit / CatchTag when building connect / protocol derivation.
InvalidHttpTarget, never> => {
if (protocol: Layer.Layer<
RpcClient.Protocol,
never,
never
>
protocol !== var undefinedundefined) {
return 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: AnyNodenode, protocol: Layer.Layer<
RpcClient.Protocol,
never,
never
>
(parameter) protocol: {
build: (memoMap: MemoMap, scope: Scope.Scope) => Effect<Context.Context<Protocol>, 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; <…;
}
protocol);
}
// Addressed path — canonical memoized Layer (same object Hyperlink.client auto-connect uses).
if (
(node: AnyNodenode.kind: ProtocolKind | undefinedkind === "IpcSocket" && typeof node: AnyNodenode.path: string | undefinedpath === "string") ||
((node: AnyNodenode.kind: ProtocolKind | undefinedkind === "Http" || node: AnyNodenode.kind: ProtocolKind | undefinedkind === "WebSocket") &&
typeof node: AnyNodenode.url: string | undefinedurl === "string")
) {
return connectAddressed<Self>(node: AddressedNode<Self>): Layer.Layer<Self>Derive (and memoize) the connect Layer for an
AddressedNode
.
connectAddressed(node: AnyNodenode as type AddressedNode<HSelf> = NodeKey<HSelf> & ({
readonly kind: "IpcSocket";
readonly path: string;
readonly url: string | undefined;
} | {
readonly kind: "Http";
readonly url: string;
readonly path: string | undefined;
} | {
readonly kind: "WebSocket";
readonly url: string;
readonly path: string | undefined;
} | {
readonly kind: "Http" | "WebSocket";
readonly url: string;
readonly path: string | undefined;
})
An
AnyNode
that can derive
connect
with no protocol argument —
kind set, and either a url (Http/WebSocket) or a Unix path (IpcSocket).
AddressedNode<unknown>);
}
return 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: AnyNodenode, function protocolForNode(node: AnyNode): Layer.Layer<RpcClient.Protocol, UnaddressedNode | InvalidHttpTarget>Protocol for any node — invalid target / unaddressed → typed Layer fail.
protocolForNode(node: AnyNodenode));
},
);