feat(core): programs' threads, and FILIUS' echo server and generic client after Java, measured against Java

Programs run as generators that yield what they wait for (threads.ts); the
engine resumes them after every event. TCP connect, send, receive, accept
and name resolution exist as such steps; the blocking calls wrap them.
EchoRun records eight stories in Java FILIUS, frames, tables and each
program's window lines; echoJava.test.ts plays them card by card. Not
taken from Java: a connection without a route hangs for ever; a server
switched off leaves its connections' last FIN unanswered.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-01 16:27:44 +00:00
co-authored by Claude Opus 5.5
parent 0abce60aef
commit de3a1d4dfe
24 changed files with 13454 additions and 28 deletions
+7 -3
View File
@@ -30,6 +30,7 @@ import {
type CapturedFrame,
type CaptureLayer,
type EthernetFrame,
type TopologySnapshot,
} from '../src/index.js';
import { snapshotOf, type Scenario } from './scenario.js';
@@ -117,8 +118,11 @@ export function engineText(frame: EthernetFrame): string {
return `${macs} UDP ${packet.senderIPAddress}:${datagram.sourcePort} > ${packet.receiverIPAddress}:${datagram.destinationPort} ${new TextDecoder().decode(datagram.payload)}`;
}
/** A started engine on the scenario's network whose frames can be read afterwards as FILIUS logs them. */
export function watchedEngine(scenario: Scenario) {
/**
* A started engine on the scenario's network (or on `snapshot`, made from
* it) whose frames can be read afterwards as FILIUS logs them.
*/
export function watchedEngine(scenario: Scenario, snapshot: TopologySnapshot = snapshotOf(scenario)) {
const engine = new NetworkRuntimeEngine(scenario.seed);
const framesByIdentity = new Map<number, EthernetFrame>();
const send = engine.sendEthernetFrame.bind(engine);
@@ -126,7 +130,7 @@ export function watchedEngine(scenario: Scenario) {
framesByIdentity.set(frame.identity, frame);
send(fromNodeId, outgoingPortId, frame);
};
engine.handle({ type: 'start', snapshot: snapshotOf(scenario), seed: scenario.seed, initialTimeMilliseconds: 0 });
engine.handle({ type: 'start', snapshot, seed: scenario.seed, initialTimeMilliseconds: 0 });
/** Every frame when it first shows on a host's card (sent, or received). */
const frames = (): SeenFrame[] => {