Hyperlinkv0.8.0-beta.28

Hyperlink

Hyperlink.serveRemoteconstsrc/Hyperlink.ts:3572
<S extends Spec, Impl extends ServeImplOf<S, any>>(
  tag: {
    readonly groupId: string
    readonly [specSym]: FlatSpec
    readonly [specTypeSym]?: S
    readonly [groupSym]: RpcGroupOf<S>
  },
  impl: Impl | BuiltHyperlink<S, any>
): Layer.Layer<HandlerContextOf<S>, never, ServeRequirements<Impl>>

A resource's served-only handler layer — mounts the tag's group handlers (wire members only, no local grant), with the handlers' requirement R preserved (not erased). This is the served-only counterpart to serve, which additionally grants Local so members stay callable in-process. serveRemote's R rides the layer's requirement channel, so a per-resource Layer.provide discharges this resource's dependency in isolation:

Hyperlink.serveRemote(SeasonMatches, seasonMatchesImpl).pipe(Layer.provide(importHandlersLayer))

The point of serveRemote is the run-time-requirement case: N resources needing different implementations of the same tag, each isolated — merge the layers onto one RpcServer (groups are prefix-keyed).

servingserveLocal
Source src/Hyperlink.ts:357259 lines
export const serveRemote = <S extends Spec, Impl extends ServeImplOf<S, any>>(
  tag: {
    readonly groupId: string;
    readonly [specSym]: FlatSpec;
    readonly [specTypeSym]?: S;
    readonly [groupSym]: RpcGroupOf<S>;
  },
  impl: Impl | BuiltHyperlink<S, any>,
): Layer.Layer<HandlerContextOf<S>, never, ServeRequirements<Impl>> => {
  const group = tag[groupSym];
  const handlers: Record<string, (payload: unknown) => unknown> = {};
  const wireImpl = isBuiltHyperlink(impl) ? impl.impl : impl;
  const workerContext = isBuiltHyperlink(impl) ? impl.workerContext : undefined;
  // flatten a (possibly nested) impl to path keys matching the flat spec + path-keyed group procedures.
  const flatImpl = flattenImpl(wireImpl as Record<string, unknown>, tag[specSym]);
  for (const [key, member] of Object.entries(flatImpl)) {
    handlers[wireTag(tag.groupId, key)] = (payload) =>
      workerContext === undefined
        ? invokeWireMethod(member, tag[specSym][key] as AnyMethod, payload)
        : invokeWireMethodWithContext(
            member,
            tag[specSym][key] as AnyMethod,
            payload,
            workerContext,
          );
  }
  // dynamic handler construction (the group's `toLayer` boundary); the outer assertion **preserves**
  // the handlers' requirement `R` — extracted from `impl` by {@link ServeRequirements} — instead of
  // erasing it, so a per-resource `Layer.provide` can discharge it. `HandlerContextOf<S>` is the rpc
  // handler slots; the requirement is the union of the handlers' run-time needs.
  const handlerLayer: any = group.toLayer(handlers as any);
  // register into the served-resources registry when one is present (`httpServer` provides it), so the
  // shared server + `/health` discover this resource without the caller listing it twice. Merged (not
  // provided) so it isn't pruned as unused; a no-op when no registry is in context (standalone `serve`).
  const registration = Layer.effectDiscard(
    Effect.serviceOption(ServedHyperlinks).pipe(
      Effect.flatMap(
        Option.match({
          onNone: () => Effect.void,
          onSome: (registry) => {
            const bound = nodeOf(tag);
            const boundKinds = nodeKindsOf(tag);
            const kind = kindOf(tag) ?? "hyperlink";
            return registry.register({
              groupId: tag.groupId,
              group,
              kind,
              contractHash: hashContract(tag.groupId, kind, tag[specSym]),
              readiness: readinessCheckServed(tag, wireImpl),
              ...(bound !== undefined ? { nodeLogKey: bound.key } : {}),
              ...(boundKinds.length > 0 ? { nodeKinds: boundKinds } : {}),
            });
          },
        }),
      ),
    ),
  );
  return Layer.merge(handlerLayer, registration) as any;
};
Referenced by 8 symbols