<S extends Spec>(
factory: {
readonly groupId: string
readonly [specSym]: FlatSpec
readonly [specTypeSym]?: S
readonly [groupSym]: RpcGroupOf<S>
},
...instances: ReadonlyArray<HyperlinkInstance<S>>
): Layer.Layer<HandlerContextOf<S>>The family server layer: serve many instances of one factory behind a
single contract group, dispatching each request to the right instance by the
per-call key header. Instances share one tagFor factory (one spec, one
RPC group); each is passed once via Hyperlink.instance.
Why one variadic call rather than one-layer-per-instance: composing instances as
sibling layers would silently keep only the last (Effect's Context is a map —
same-key layers last-write-wins). Passing them together is the foolproof shape:
every instance is wired, and a duplicate key throws at assembly.
const Queue = Hyperlink.tagFor("queue", { pause: Hyperlink.effect(Schema.Void) });
class Jobs extends Queue<Jobs>("@app/Jobs") {}
class Mail extends Queue<Mail>("@app/Mail") {}
const serveAll = Hyperlink.serveInstances(
Queue,
Hyperlink.instance(Jobs, jobsImpl),
Hyperlink.instance(Mail, mailImpl),
);const const serveInstances: <S extends Spec>(
factory: {
readonly groupId: string
readonly [specSym]: FlatSpec
readonly [specTypeSym]?: S
readonly [groupSym]: RpcGroupOf<S>
},
...instances: ReadonlyArray<
HyperlinkInstance<S>
>
) => Layer.Layer<HandlerContextOf<S>>
The family server layer: serve many instances of one factory behind a
single contract group, dispatching each request to the right instance by the
per-call key header. Instances share one
tagFor
factory (one spec, one
RPC group); each is passed once via
Hyperlink.instance
.
Why one variadic call rather than one-layer-per-instance: composing instances as
sibling layers would silently keep only the last (Effect's Context is a map —
same-key layers last-write-wins). Passing them together is the foolproof shape:
every instance is wired, and a duplicate key throws at assembly.
const Queue = Hyperlink.tagFor("queue", { pause: Hyperlink.effect(Schema.Void) });
class Jobs extends Queue<Jobs>("@app/Jobs") {}
class Mail extends Queue<Mail>("@app/Mail") {}
const serveAll = Hyperlink.serveInstances(
Queue,
Hyperlink.instance(Jobs, jobsImpl),
Hyperlink.instance(Mail, mailImpl),
);
serveInstances = <function (type parameter) S in <S extends Spec>(factory: {
readonly groupId: string;
readonly [specSym]: FlatSpec;
readonly [specTypeSym]?: S;
readonly [groupSym]: RpcGroupOf<S>;
}, ...instances: ReadonlyArray<HyperlinkInstance<S>>): Layer.Layer<HandlerContextOf<S>>
S extends Spec>(
factory: {
readonly groupId: string
readonly [specSym]: FlatSpec
readonly [specTypeSym]?: S
readonly [groupSym]: RpcGroupOf<S>
}
factory: {
readonly groupId: stringgroupId: string;
readonly [const specSym: typeof specSymWhere the contract spec is stowed on a Tag (hidden from the value surface). Exported so
the public
HyperlinkTag
type is nameable across modules.
specSym]: type FlatSpec = {
[x: string]: AnyMethod | AnyLocalMethod
}
A flat spec — a path-keyed record of leaves (no nested groups). The wire machinery runs on this;
a (possibly nested)
Spec
flattens to it via
flattenSpec
.
FlatSpec;
readonly [const specTypeSym: typeof specTypeSymPhantom carrier of a tag's (possibly nested) spec type S, so functions can infer S from a tag —
specSym
holds the flat spec at runtime and can't carry the nested S. Type-only, never set at
runtime.
specTypeSym]?: function (type parameter) S in <S extends Spec>(factory: {
readonly groupId: string;
readonly [specSym]: FlatSpec;
readonly [specTypeSym]?: S;
readonly [groupSym]: RpcGroupOf<S>;
}, ...instances: ReadonlyArray<HyperlinkInstance<S>>): Layer.Layer<HandlerContextOf<S>>
S;
readonly [const groupSym: typeof groupSymWhere the built RPC group is stowed on a Tag.
groupSym]: type RpcGroupOf<S extends Spec> =
RpcGroup.RpcGroup<RpcUnionOf<S, "">>
The precisely-typed RPC contract group for a
Spec
. Carrying this exact type
(rather than a loose Rpc<string, …>) is what keeps the remote client's requirement
channel honest: concrete schemas declare never encoding/decoding services, so
RpcClient.make infers a real R (just the transport Protocol) instead of any.
RpcGroupOf<function (type parameter) S in <S extends Spec>(factory: {
readonly groupId: string;
readonly [specSym]: FlatSpec;
readonly [specTypeSym]?: S;
readonly [groupSym]: RpcGroupOf<S>;
}, ...instances: ReadonlyArray<HyperlinkInstance<S>>): Layer.Layer<HandlerContextOf<S>>
S>;
},
...instances: ReadonlyArray<HyperlinkInstance<S>>instances: interface ReadonlyArray<T>ReadonlyArray<interface HyperlinkInstance<S extends Spec>One instance of a factory paired with its implementation — the element of
Hyperlink.serveInstances
. Built by
Hyperlink.instance
.
HyperlinkInstance<function (type parameter) S in <S extends Spec>(factory: {
readonly groupId: string;
readonly [specSym]: FlatSpec;
readonly [specTypeSym]?: S;
readonly [groupSym]: RpcGroupOf<S>;
}, ...instances: ReadonlyArray<HyperlinkInstance<S>>): Layer.Layer<HandlerContextOf<S>>
S>>
): 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<type HandlerContextOf<S extends Spec> =
RpcUnionOf<S, ""> extends Rpc.Rpc<
infer _Tag extends string,
infer _Payload extends Schema.Top,
infer _Success extends Schema.Top,
infer _Error extends Schema.Top,
infer _Middleware extends AnyService,
infer _Requires
>
? Rpc.Handler<_Tag>
: never
The context a server layer for a
Spec
provides: the handler for every method.
Used to pin the server layers' output type so their requirement channel stays
never — RpcGroup's own ToHandlerFn defaults that channel to any, so without
this the inferred server layer would re-leak any into anything that consumes it.
HandlerContextOf<function (type parameter) S in <S extends Spec>(factory: {
readonly groupId: string;
readonly [specSym]: FlatSpec;
readonly [specTypeSym]?: S;
readonly [groupSym]: RpcGroupOf<S>;
}, ...instances: ReadonlyArray<HyperlinkInstance<S>>): Layer.Layer<HandlerContextOf<S>>
S>> => {
const const group: RpcGroupOf<S>const group: {
requests: ReadonlyMap<string, R>;
annotations: Context.Context<never>;
add: (...rpcs: Rpcs2) => RpcGroup.RpcGroup<RpcUnionOf<S, ''> | Rpcs2[number]>;
merge: (...groups: Groups) => RpcGroup.RpcGroup<RpcUnionOf<S, ''> | RpcGroup.Rpcs<Groups[number]>>;
omit: (...tags: Tags) => RpcGroup.RpcGroup<Exclude<RpcUnionOf<S, ''>, { readonly _tag: Tags[number] }>>;
middleware: (middleware: M) => RpcGroup.RpcGroup<Rpc.AddMiddleware<RpcUnionOf<S, ''>, M>>;
prefix: (prefix: Prefix) => RpcGroup.RpcGroup<Rpc.Prefixed<RpcUnionOf<S, ''>, Prefix>>;
toHandlers: (build: Handlers | Effect.Effect<Handlers, EX, RX>) => Effect.Effect<Context.Context<Rpc.ToHandler<RpcUnionOf<S, ''>>>, EX, RX | RpcGroup.HandlersServices<RpcUnionOf<S, ''>, Handlers>>;
toLayer: (build: Handlers | Effect.Effect<Handlers, EX, RX>) => Layer.Layer<Rpc.ToHandler<RpcUnionOf<S, ''>>, EX, Exclude<RX, Scope.Scope> | RpcGroup.HandlersServices<RpcUnionOf<S, ''>, Handlers>>;
of: (handlers: Handlers) => Handlers;
toLayerHandler: (tag: Tag, build: Handler | Effect.Effect<Handler, EX, RX>) => Layer.Layer<Rpc.Handler<Tag>, EX, Exclude<RX, Scope.Scope> | RpcGroup.HandlerServices<RpcUnionOf<S, ''>, Tag, Handler>>;
accessHandler: (tag: Tag) => Effect.Effect<(payload: Rpc.Payload<Extract<RpcUnionOf<S, ''>, { readonly _tag: Tag }>>, options: { readonly client: Rpc.ServerClient; readonly requestId: RequestId; readonly headers: Headers.Headers }) => Rpc.ResultFrom<Extr…;
annotate: (service: Context.Key<I, S>, value: S) => RpcGroup.RpcGroup<RpcUnionOf<S, ''>>;
annotateRpcs: (service: Context.Key<I, S>, value: S) => RpcGroup.RpcGroup<RpcUnionOf<S, ''>>;
annotateMerge: (annotations: Context.Context<S>) => RpcGroup.RpcGroup<RpcUnionOf<S, ''>>;
annotateRpcsMerge: (annotations: Context.Context<S>) => RpcGroup.RpcGroup<RpcUnionOf<S, ''>>;
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; <…;
}
group = factory: {
readonly groupId: string
readonly [specSym]: FlatSpec
readonly [specTypeSym]?: S
readonly [groupSym]: RpcGroupOf<S>
}
factory[const groupSym: typeof groupSymWhere the built RPC group is stowed on a Tag.
groupSym];
const const spec: FlatSpecspec = factory: {
readonly groupId: string
readonly [specSym]: FlatSpec
readonly [specTypeSym]?: S
readonly [groupSym]: RpcGroupOf<S>
}
factory[const specSym: typeof specSymWhere the contract spec is stowed on a Tag (hidden from the value surface). Exported so
the public
HyperlinkTag
type is nameable across modules.
specSym];
// Build the routing table once, at assembly: key → instance impl (flattened to path keys so nested
// groups dispatch by the same path-keyed procedures the handlers use). A duplicate key is a wiring
// mistake — fail loudly rather than silently shadow an instance.
const const table: Map<
string,
Record<string, unknown>
>
table = new var Map: MapConstructor
new <string, Record<string, unknown>>(iterable?: Iterable<readonly [string, Record<string, unknown>]> | null | undefined) => Map<string, Record<string, unknown>> (+3 overloads)
Map<string, type Record<K extends keyof any, T> = {
[P in K]: T
}
Construct a type with a set of properties K of type T
Record<string, unknown>>();
for (const { const key: stringkey, const impl: WireServiceOf<S>impl } of instances: ReadonlyArray<HyperlinkInstance<S>>instances) {
if (const table: Map<
string,
Record<string, unknown>
>
table.Map<string, Record<string, unknown>>.has(key: string): booleanhas(const key: stringkey)) {
throw new constructor DuplicateInstance<{
readonly key: string;
}>(args: {
readonly key: string;
}): DuplicateInstance
An instance was passed to
Hyperlink.serveInstances
more than once.
DuplicateInstance({ key: stringkey });
}
const table: Map<
string,
Record<string, unknown>
>
table.Map<string, Record<string, unknown>>.set(key: string, value: Record<string, unknown>): Map<string, Record<string, unknown>>Adds a new element with a specified key and value to the Map. If an element with the same key already exists, the element will be updated.
set(const key: stringkey, const flattenImpl: (
impl: Record<string, unknown>,
flatSpec: FlatSpec
) => Record<string, unknown>
flattenImpl(const impl: WireServiceOf<S>impl as type Record<K extends keyof any, T> = {
[P in K]: T
}
Construct a type with a set of properties K of type T
Record<string, unknown>, const spec: FlatSpecspec));
}
// One handler per contract method; each reads the instance-key header, looks up the
// instance, and dispatches. A missing/unknown key is a protocol-level fault
// (the contract is satisfied) → die, not a typed domain error.
const const handlers: Record<
string,
(
payload: unknown,
options: {
readonly headers: Headers.Headers
}
) => unknown
>
handlers: type Record<K extends keyof any, T> = {
[P in K]: T
}
Construct a type with a set of properties K of type T
Record<
string,
(payload: unknownpayload: unknown, options: {
readonly headers: Headers.Headers
}
options: { readonly headers: Headers.Headersheaders: import HeadersHeaders.Headers }) => unknown
> = {};
for (const const key: stringkey of var Object: ObjectConstructorProvides functionality common to all JavaScript objects.
Object.ObjectConstructor.keys(o: {}): string[] (+1 overload)Returns the names of the enumerable string properties and methods of an object.
keys(const spec: FlatSpecspec)) {
// handlers are keyed by the wire tag (group-prefixed), matching the group's procedures.
const handlers: Record<
string,
(
payload: unknown,
options: {
readonly headers: Headers.Headers
}
) => unknown
>
handlers[const wireTag: (
groupId: string,
method: string
) => string
wireTag(factory: {
readonly groupId: string
readonly [specSym]: FlatSpec
readonly [specTypeSym]?: S
readonly [groupSym]: RpcGroupOf<S>
}
factory.groupId: stringgroupId, const key: stringkey)] = (payload: unknownpayload, options: {
readonly headers: Headers.Headers
}
options) => {
const const instanceKey: string | undefinedinstanceKey = import OptionOption.const getOrUndefined: <string>(
self: Option.Option<string>
) => string | undefined
Extracts the value from a Some, or returns undefined for None.
When to use
Use when you need to pass absent Option values to APIs that expect
undefined.
Details
Some → the inner value
None → undefined
Example (Unwrapping to undefined)
import { Option } from "effect"
console.log(Option.getOrUndefined(Option.some(1)))
// Output: 1
console.log(Option.getOrUndefined(Option.none()))
// Output: undefined
getOrUndefined(import HeadersHeaders.const get: (self: Headers.Headers, key: string) => Option.Option<string> (+1 overload)Gets a header value by name safely.
Details
The lookup lowercases the provided header name and returns Option.none() when absent.
get(options: {
readonly headers: Headers.Headers
}
options.headers: Headers.Headersheaders, const INSTANCE_KEY_HEADER: "key"The header carrying the target instance key, set per-call by
forwardClient
.
INSTANCE_KEY_HEADER));
if (const instanceKey: string | undefinedinstanceKey === var undefinedundefined) {
return import EffectEffect.const die: (
defect: unknown
) => Effect.Effect<never>
Creates an effect that terminates a fiber with a specified error.
When to use
Use when you need an Effect to report an unrecoverable defect instead of a
typed error.
Details
The die function is used to signal a defect, which represents a critical
and unexpected error in the code. When invoked, it produces an effect that
does not handle the error and instead terminates the fiber.
The error channel of the resulting effect is of type never, indicating that
it cannot recover from this failure.
Example (Failing on division by zero)
import { Effect } from "effect"
const divide = (a: number, b: number) =>
b === 0
? Effect.die(new Error("Cannot divide by zero"))
: Effect.succeed(a / b)
// ┌─── Effect<number, never, never>
// ▼
const program = divide(1, 0)
Effect.runPromise(program).catch(console.error)
// Output:
// (FiberFailure) Error: Cannot divide by zero
// ...stack trace...
die(
new constructor InstanceRoutingError<{
readonly method: string;
readonly reason: "missing-key" | "unknown-key";
readonly key?: string;
}>(args: {
readonly method: string;
readonly reason: "missing-key" | "unknown-key";
readonly key?: string | undefined;
}): InstanceRoutingError
A family request reached the server with no routable instance key header — a
protocol-level fault (the contract was satisfied), surfaced as a defect.
InstanceRoutingError({ method: stringmethod: const key: stringkey, reason: "missing-key" | "unknown-key"reason: "missing-key" }),
);
}
const const impl:
| Record<string, unknown>
| undefined
impl = const table: Map<
string,
Record<string, unknown>
>
table.Map<string, Record<string, unknown>>.get(key: string): Record<string, unknown> | undefinedReturns a specified element from the Map object. If the value that is associated to the provided key is an object, then you will get a reference to that object and any change made to that object will effectively modify it inside the Map.
get(const instanceKey: stringinstanceKey);
if (const impl:
| Record<string, unknown>
| undefined
impl === var undefinedundefined) {
return import EffectEffect.const die: (
defect: unknown
) => Effect.Effect<never>
Creates an effect that terminates a fiber with a specified error.
When to use
Use when you need an Effect to report an unrecoverable defect instead of a
typed error.
Details
The die function is used to signal a defect, which represents a critical
and unexpected error in the code. When invoked, it produces an effect that
does not handle the error and instead terminates the fiber.
The error channel of the resulting effect is of type never, indicating that
it cannot recover from this failure.
Example (Failing on division by zero)
import { Effect } from "effect"
const divide = (a: number, b: number) =>
b === 0
? Effect.die(new Error("Cannot divide by zero"))
: Effect.succeed(a / b)
// ┌─── Effect<number, never, never>
// ▼
const program = divide(1, 0)
Effect.runPromise(program).catch(console.error)
// Output:
// (FiberFailure) Error: Cannot divide by zero
// ...stack trace...
die(
new constructor InstanceRoutingError<{
readonly method: string;
readonly reason: "missing-key" | "unknown-key";
readonly key?: string;
}>(args: {
readonly method: string;
readonly reason: "missing-key" | "unknown-key";
readonly key?: string | undefined;
}): InstanceRoutingError
A family request reached the server with no routable instance key header — a
protocol-level fault (the contract was satisfied), surfaced as a defect.
InstanceRoutingError({ method: stringmethod: const key: stringkey, reason: "missing-key" | "unknown-key"reason: "unknown-key", key?: string | undefinedkey: const instanceKey: stringinstanceKey }),
);
}
const const member: unknownmember = (const impl: Record<string, unknown>impl as type Record<K extends keyof any, T> = {
[P in K]: T
}
Construct a type with a set of properties K of type T
Record<string, unknown>)[const key: stringkey];
return const invokeWireMethod: (
member: unknown,
method: AnyMethod,
payload: unknown
) => unknown
Invoke a wire impl member — spreads 2-tuple payloads when callStyle is "pair".
invokeWireMethod(const member: unknownmember, const spec: FlatSpecspec[const key: stringkey] as type AnyMethod = Method<
Schema.Top | Schema.Struct.Fields | undefined,
Schema.Top,
Schema.Top,
boolean,
MethodAnnotations,
never
>
Any
Method
, erased — the element type of a
Spec
.
AnyMethod, payload: unknownpayload);
};
}
// Boundary assertion (runtime-safe): handlers mirror the shared spec the group
// was built from, and RPC validates every payload/result at the wire. Output pinned
// to {@link HandlerContextOf} to keep the layer's requirement channel `never`.
const const handlerLayer: anyhandlerLayer: any = const group: RpcGroupOf<S>const group: {
requests: ReadonlyMap<string, R>;
annotations: Context.Context<never>;
add: (...rpcs: Rpcs2) => RpcGroup.RpcGroup<RpcUnionOf<S, ''> | Rpcs2[number]>;
merge: (...groups: Groups) => RpcGroup.RpcGroup<RpcUnionOf<S, ''> | RpcGroup.Rpcs<Groups[number]>>;
omit: (...tags: Tags) => RpcGroup.RpcGroup<Exclude<RpcUnionOf<S, ''>, { readonly _tag: Tags[number] }>>;
middleware: (middleware: M) => RpcGroup.RpcGroup<Rpc.AddMiddleware<RpcUnionOf<S, ''>, M>>;
prefix: (prefix: Prefix) => RpcGroup.RpcGroup<Rpc.Prefixed<RpcUnionOf<S, ''>, Prefix>>;
toHandlers: (build: Handlers | Effect.Effect<Handlers, EX, RX>) => Effect.Effect<Context.Context<Rpc.ToHandler<RpcUnionOf<S, ''>>>, EX, RX | RpcGroup.HandlersServices<RpcUnionOf<S, ''>, Handlers>>;
toLayer: (build: Handlers | Effect.Effect<Handlers, EX, RX>) => Layer.Layer<Rpc.ToHandler<RpcUnionOf<S, ''>>, EX, Exclude<RX, Scope.Scope> | RpcGroup.HandlersServices<RpcUnionOf<S, ''>, Handlers>>;
of: (handlers: Handlers) => Handlers;
toLayerHandler: (tag: Tag, build: Handler | Effect.Effect<Handler, EX, RX>) => Layer.Layer<Rpc.Handler<Tag>, EX, Exclude<RX, Scope.Scope> | RpcGroup.HandlerServices<RpcUnionOf<S, ''>, Tag, Handler>>;
accessHandler: (tag: Tag) => Effect.Effect<(payload: Rpc.Payload<Extract<RpcUnionOf<S, ''>, { readonly _tag: Tag }>>, options: { readonly client: Rpc.ServerClient; readonly requestId: RequestId; readonly headers: Headers.Headers }) => Rpc.ResultFrom<Extr…;
annotate: (service: Context.Key<I, S>, value: S) => RpcGroup.RpcGroup<RpcUnionOf<S, ''>>;
annotateRpcs: (service: Context.Key<I, S>, value: S) => RpcGroup.RpcGroup<RpcUnionOf<S, ''>>;
annotateMerge: (annotations: Context.Context<S>) => RpcGroup.RpcGroup<RpcUnionOf<S, ''>>;
annotateRpcsMerge: (annotations: Context.Context<S>) => RpcGroup.RpcGroup<RpcUnionOf<S, ''>>;
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; <…;
}
group.RpcGroup<RpcUnionOf<S, "">>.toLayer<any, never, never>(build: any): Layer.Layer<Rpc.ToHandler<RpcUnionOf<S, "">>, never, RpcGroup.HandlerServices<RpcUnionOf<S, "">, string, any>>Implement the handlers for the procedures in this group.
toLayer(const handlers: Record<
string,
(
payload: unknown,
options: {
readonly headers: Headers.Headers
}
) => unknown
>
handlers as any);
return const handlerLayer: anyhandlerLayer as any;
};