37 lines
1.4 KiB
TypeScript
37 lines
1.4 KiB
TypeScript
import type {
|
|||
|
|
AtomId,
|
||
|
|
Binding,
|
||
|
|
Conformance,
|
||
|
|
InterfaceRevisionId,
|
||
|
|
OperationBinding,
|
||
|
|
OperationId,
|
||
|
|
WorkspaceRevision,
|
||
|
|
} from "./types.js";
|
||
|
|
|
||
|
|
export type ConcreteBinding = Exclude<Binding, { kind: "interface" }>;
|
||
|
|
export type ResolvedBinding = { conformance: Conformance; entry: OperationBinding; binding: ConcreteBinding };
|
||
|
|
|
||
|
|
/** Follow public operations on the same receiver, never private attachment names. */
|
||
|
|
export function resolveForwardedBinding(
|
||
|
|
workspace: Pick<WorkspaceRevision, "conformances">,
|
||
|
|
atomId: AtomId,
|
||
|
|
interfaceRevisionId: InterfaceRevisionId,
|
||
|
|
operationId: OperationId,
|
||
|
|
): ResolvedBinding {
|
||
|
|
const visited = new Set<string>();
|
||
|
|
for (;;) {
|
||
|
|
const key = `${interfaceRevisionId}\0${operationId}`;
|
||
|
|
if (visited.has(key)) throw new Error(`Forwarding cycle at ${interfaceRevisionId}/${operationId}`);
|
||
|
|
visited.add(key);
|
||
|
|
const conformance = workspace.conformances.find(
|
||
|
|
(c) => c.atomId === atomId && c.interfaceRevisionId === interfaceRevisionId,
|
||
|
|
);
|
||
|
|
const entry = conformance?.operationBindings.find((b) => b.operationId === operationId);
|
||
|
|
if (!conformance || !entry)
|
||
|
|
throw new Error(`Missing forwarding target ${atomId} as ${interfaceRevisionId}/${operationId}`);
|
||
|
|
if (entry.binding.kind !== "interface") return { conformance, entry, binding: entry.binding };
|
||
|
|
interfaceRevisionId = entry.binding.interfaceRevisionId;
|
||
|
|
operationId = entry.binding.operationId;
|
||
|
|
}
|
||
|
|
}
|