Dial the same-machine Lookup path (defaultIpcPath or options.path).
Effect precedent: options factory beside a default Layer value (layerAgentOptions).
export const const clientOptions: (options?: {
readonly path?: string
}) => Layer.Layer<Services, LookupUnaddressed>
Dial the same-machine Lookup path (
defaultIpcPath
or options.path).
Effect precedent: options factory beside a default Layer value (layerAgentOptions).
clientOptions = (options: | {
readonly path?: string
}
| undefined
options?: {
readonly path?: string | undefinedpath?: string;
}): 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 Services =
| Identity
| Directory
| Advice
Services a Lookup client layer provides — identity, directory, and placement advice.
Services, class LookupUnaddressedclass LookupUnaddressed {
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;
node: string;
}
Lookup node has no dialable address — need { path } / url, or use
layer
/
layerOptions
. Layer error channel (not a sync throw).
LookupUnaddressed> => {
const const path: stringpath = options: | {
readonly path?: string
}
| undefined
options?.path?: string | undefinedpath ?? const defaultIpcPath: "/tmp/hyperlink-ts-lookup.sock"Well-known Unix socket path for same-machine default lookup (L1).
Prefer overriding in tests so suites don't collide on one machine.
defaultIpcPath;
const const node: ServiceClass<
unknown,
string,
NodeProtocol
> &
IpcAddress & {
readonly logs: unknown
readonly onConflict: OnConflict
readonly [catalogSym]?: undefined
} & { readonly isLookupNode: true }
node = NodeTag<Self, ROut = never>(): { (key: string): NodeTagClass<Self, ROut, BareAddress>; (key: string, target: { readonly path: string; readonly kind?: 'IpcSocket'; readonly onConflict?: OnConflict }): NodeTagClass<Self, ROut, IpcAddress>; (key: string, target: number | `:${number}`): NodeTagClass<Self, ROut, HttpAddress>; (key: string, target: `ws://${string}` | `wss://${string}`): NodeTagClass<Self, ROut, WsAddress>; (key: string, target: `http://${string}` | `https://${string}`): NodeTagClass<Self, ROut, HttpAddress>; (key: string, target: { readonly url: `ws://${string}` | `wss://${string}`; readonly kind?: 'WebSocket'; readonly onConflict?: OnConflict }): NodeTagClass<Self, ROut, WsAddress>; (key: string, target: { readonly url: string; readonly kind: 'WebSocket'; readonly onConflict?: OnConflict }): NodeTagClass<Self, ROut, WsAddress>; (key: string, target: { readonly url: string; readonly kind: 'Http'; readonly onConflict?: OnConflict }): NodeTagClass<Self, ROut, HttpAddress>; <T extends ShorthandTarget>(key: string, target: T): NodeTagClass<Self, ROut, MultiAddress<KindsOf<T>>>; (key: string, target: string | { readonly url: string; readonly kind?: ProtocolKind; readonly onConflict?: OnConflict }): NodeTagClass<Self, ROut, UrlAddressLoose>; (key: string, target?: LooseNodeTarget): NodeTagClass<Self, ROut, BareAddress | IpcAddress | HttpAddress | WsAddress | UrlAddressLoose | MultiAddress<ProtocolKind>> }Declare a node — a named transport endpoint a HyperService connects to. Two-stage and keyed by
a string, mirroring Effect's Context.Service<Self, Shape>()(key) (a node is a Context.Key,
resolved by its key in the Context map) and every sibling factory (Hyperlink.Tag<Self>(), …).
The second call infers the target shape, so the { http, ws } shorthand types its
ProtocolKind
set precisely. Optional catalog type param ROut (C2) — prefer import type
for those handles (C4). Templates (no address until cloned) live on
Node
.Prototype:
class EdgeNode extends Node.Tag<EdgeNode>()("edge") {} // no address yet
class Worker extends Node.Tag<Worker>()("worker", 3001) {} // → http://localhost:3001/rpc, kind "Http"
class Mail extends Node.Tag<Mail>()("mail", "https://mail.internal/rpc") {} // full url, as-is, kind "Http"
class Live extends Node.Tag<Live>()("live", { url: "wss://live/rpc" }) {} // kind "WebSocket" (inferred from ws url)
class Push extends Node.Tag<Push>()("push", { url: "/rpc", kind: "WebSocket" }) {} // same-origin path, explicit kind
class Local extends Node.Tag<Local>()("local", { path: "/tmp/local.sock" }) {} // kind "IpcSocket" (Unix domain)
class Droplet extends Node.Tag<Droplet>()("droplet", { http: "http://d/rpc", ws: "ws://d/rpc" }) {} // multi-protocol
import type { Jobs, Emails } from "@app/contracts"
class AppWorker extends Node.Tag<AppWorker, Jobs | Emails>()("app/Worker", { path: "/tmp/w.sock" }) {}
class MailWorker extends Node.Prototype<MailWorker, Mail>("app/MailWorker") {}
The key is the service key. The optional address matches a dial target: a port
(3001 or ":3001" → http://localhost:3001/rpc), a full url (used as-is), { url, kind } for
an explicit endpoint, { path } for a Unix-domain socket (kind: "IpcSocket"), or the
{ http, ws, ipc } multi-protocol shorthand. The node carries
ProtocolKind
so the topology
is self-describing about where AND how:
connect
(node) derives the transport with no
protocol argument.
Dialable targets return an
AddressedNode
(kind: ProtocolKind) so
Hyperlink.client(Tag, Worker) can auto-wire
connect
. Bare Node.Tag()("x")
stays address-less (kind: undefined) — still needs explicit connect / lookup.
NodeTag()("hyperlink-ts/Lookup/default", { path: stringpath }).Pipeable.pipe<NodeTagClass<unknown, never, IpcAddress>, ServiceClass<unknown, string, NodeProtocol> & IpcAddress & {
readonly logs: unknown;
readonly onConflict: OnConflict;
readonly [catalogSym]?: undefined;
} & {
readonly isLookupNode: true;
}>(this: NodeTagClass<...>, ab: (_: NodeTagClass<unknown, never, IpcAddress>) => ServiceClass<unknown, string, NodeProtocol> & IpcAddress & {
readonly logs: unknown;
readonly onConflict: OnConflict;
readonly [catalogSym]?: undefined;
} & {
readonly isLookupNode: true;
}): ServiceClass<...> & ... 2 more ... & {
readonly isLookupNode: true;
} (+21 overloads)
pipe(const asLookup: <N extends AnyNode>(
node: N
) => N & { readonly isLookupNode: true }
Brand a node as the fleet lookup / identity server — piped onto a
Tag
node (sibling to
withProtocol
). Reuses the node verbatim (address, transports, key, served HyperServices) and
folds its advertise policy to a concrete default: an explicit { onConflict } survives, while an
ordinary node's "inherit" (or none) becomes "livenessReplace" — a lookup root never inherits.
class Directory extends Node.Tag<Directory>()("app/Lookup", { path: "/tmp/lookup.sock" }).pipe(Node.asLookup) {}
class Dual extends Node.Tag<Dual>()("app/Lookup", { http: "http://l/rpc", ws: "ws://l/rpc" }).pipe(Node.asLookup) {}
asLookup);
return const client: (
node: AnyNode & { readonly path?: string }
) => Layer.Layer<Services, LookupUnaddressed>
Client for
Identity
+
Directory
via an ipc
LookupNode
.
client(const node: ServiceClass<
unknown,
string,
NodeProtocol
> &
IpcAddress & {
readonly logs: unknown
readonly onConflict: OnConflict
readonly [catalogSym]?: undefined
} & { readonly isLookupNode: true }
node);
};