83 lines
2.3 KiB
JavaScript
83 lines
2.3 KiB
JavaScript
// Phase 0 spike: prove the minimal binding can do a full WebRTC handshake
|
|||
|
|
// (offer/answer + ICE + DataChannel) between two local PeerConnections.
|
||
|
|
"use strict";
|
||
|
|
const { PeerConnection } = require("./build/Release/datachannel_min.node");
|
||
|
|
|
||
|
|
function log(...args) {
|
||
|
|
console.log("[spike]", ...args);
|
||
|
|
}
|
||
|
|
|
||
|
|
async function main() {
|
||
|
|
const pcA = new PeerConnection({ iceServers: [] });
|
||
|
|
const pcB = new PeerConnection({ iceServers: [] });
|
||
|
|
|
||
|
|
const stateLog = [];
|
||
|
|
pcA.onStateChange((s) => {
|
||
|
|
stateLog.push(`A:${s}`);
|
||
|
|
log("A state:", s);
|
||
|
|
});
|
||
|
|
pcB.onStateChange((s) => {
|
||
|
|
stateLog.push(`B:${s}`);
|
||
|
|
log("B state:", s);
|
||
|
|
});
|
||
|
|
|
||
|
|
// B waits for incoming DataChannel
|
||
|
|
const received = new Promise((resolve) => {
|
||
|
|
pcB.onDataChannel((dc) => {
|
||
|
|
log("B got incoming DataChannel");
|
||
|
|
dc.onOpen(() => log("B DataChannel open"));
|
||
|
|
dc.onMessage((msg) => {
|
||
|
|
log("B received message:", msg);
|
||
|
|
dc.send("pong from B");
|
||
|
|
resolve(msg);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
// A creates an outgoing DataChannel
|
||
|
|
const dcA = pcA.createDataChannel("test");
|
||
|
|
dcA.onOpen(() => {
|
||
|
|
log("A DataChannel open — sending hello");
|
||
|
|
dcA.send("hello from A");
|
||
|
|
});
|
||
|
|
dcA.onMessage((msg) => {
|
||
|
|
log("A received reply:", msg);
|
||
|
|
});
|
||
|
|
|
||
|
|
// Offer/answer dance
|
||
|
|
log("A createOffer...");
|
||
|
|
const offer = await pcA.createOffer();
|
||
|
|
log("Offer SDP bytes:", offer.length);
|
||
|
|
log("B createAnswer...");
|
||
|
|
const answer = await pcB.createAnswer(offer);
|
||
|
|
log("Answer SDP bytes:", answer.length);
|
||
|
|
const setupMatch = answer.match(/a=setup:(\S+)/);
|
||
|
|
log("Answer setup role:", setupMatch ? setupMatch[1] : "NONE");
|
||
|
|
pcA.setRemoteDescription(answer, "answer");
|
||
|
|
|
||
|
|
// Wait for message roundtrip
|
||
|
|
const msg = await Promise.race([
|
||
|
|
received,
|
||
|
|
new Promise((_, rej) => setTimeout(() => rej(new Error("TIMEOUT waiting for datachannel message")), 15000)),
|
||
|
|
]);
|
||
|
|
|
||
|
|
log("ROUNDTRIP OK — B got:", msg);
|
||
|
|
log("States:", stateLog.join(" | "));
|
||
|
|
|
||
|
|
const aState = pcA.state();
|
||
|
|
const bState = pcB.state();
|
||
|
|
log("Final states — A:", aState, "B:", bState);
|
||
|
|
|
||
|
|
pcA.close();
|
||
|
|
pcB.close();
|
||
|
|
|
||
|
|
if (msg !== "hello from A") throw new Error("wrong message");
|
||
|
|
if (aState !== "connected" && aState !== "disconnected") throw new Error("A not connected: " + aState);
|
||
|
|
log("SPIKE PASSED ✅");
|
||
|
|
}
|
||
|
|
|
||
|
|
main().catch((e) => {
|
||
|
|
console.error("SPIKE FAILED:", e.message);
|
||
|
|
process.exit(1);
|
||
|
|
});
|