Hyperlinkv0.8.0-beta.28

Hyperlink

Hyperlink.lookupClientconstsrc/Hyperlink.ts:4978

Lookup-resolved nodeless client (D7/D4) — you do not pass a Node; Lookup chooses the dial target. Contrast client(Tag, node), where you name the Node.

Resolution order: Lookup.Identityresolve(tag.key), else Lookup.DirectorynodesServing(tag.key).

Fail-closed by default: missing or more than one directory row → LookupClientError. When N>1, a live Lookup.Advice prefer that matches a directory row wins before D4 { pick }. Opt into soft pick with { pick: "first" } or a sync (rows) => DirectoryEntry. Identity resolve ignores advice / pick (unique by key).

Bake name sketch was unsafeLookupClient (“trust Lookup or die”); bare lookupClient(Tag) keeps that fail-closed contract when advice is absent/stale.

// Sole endpoint (identity winner or one directory row):
Hyperlink.lookupClient(Mail).pipe(Layer.provide(Lookup.layer))

// Coordinator published advice — bare client honors prefer:
yield* Lookup.advise({ resourceKey: Mail.key, prefer: "fleet/Mail#w2" })
Hyperlink.lookupClient(Mail)

// N>1 replicas — opt-in pick when no advice (still fail on 0):
Hyperlink.lookupClient(Mail, { pick: "first" })

// You already know an addressed Node — client auto-connects:
Hyperlink.client(Mail, East)
clientsNodeclientLookup.IdentityLookup.DirectoryLookupClientErrorLookup.Advice
Source src/Hyperlink.ts:497856 lines
export const lookupClient = <Self, S extends Spec>(
  tag: HyperlinkTag<Self, S>,
  options?: LookupClientOptions,
): Layer.Layer<
  Self,
  LookupClientError,
  LookupIdentity | LookupDirectory | LookupAdvice
> =>
  Layer.unwrap(
    Effect.gen(function* () {
      const Lookup = yield* Effect.promise(() => import("./Lookup"));
      const identity = yield* Lookup.Identity;
      const resolved = yield* identity.resolve(
        new Lookup.ResolveRequest({ key: tag.key }),
      );
      if (Option.isSome(resolved)) {
        return clientLayerForEndpoint(tag, resolved.value);
      }
      const directory = yield* Lookup.Directory;
      const entries = yield* directory.nodesServing(
        new Lookup.NodesServingRequest({ resourceKey: tag.key }),
      );
      if (entries.length === 0) {
        return yield* new LookupClientError({
          tag: tag.key,
          reason: "missing",
          count: 0,
        });
      }
      if (entries.length === 1) {
        return clientLayerForEndpoint(tag, entries[0]!);
      }
      // M5 — honor placement advice when the preferred node is still advertised.
      const prefer = yield* Lookup.preferred(tag.key);
      if (Option.isSome(prefer)) {
        const advised = entries.find((row) => row.nodeKey === prefer.value);
        if (advised !== undefined) {
          return clientLayerForEndpoint(tag, advised);
        }
      }
      const pick = options?.pick;
      if (pick === undefined) {
        return yield* new LookupClientError({
          tag: tag.key,
          reason: "ambiguous",
          count: entries.length,
        });
      }
      const chosen = pick === "first" ? entries[0]! : pick(entries);
      return clientLayerForEndpoint(tag, chosen);
    }),
  ) as Layer.Layer<
    Self,
    LookupClientError,
    LookupIdentity | LookupDirectory | LookupAdvice
  >;
Referenced by 2 symbols