@networkdiagnostics/sdk 0.1.0-pre.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/LICENSE +21 -0
- package/README.md +178 -0
- package/dist/index.cjs +415 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +127 -0
- package/dist/index.d.ts +127 -0
- package/dist/index.mjs +410 -0
- package/dist/index.mjs.map +1 -0
- package/dist/networktests.umd.js +2 -0
- package/dist/networktests.umd.js.map +1 -0
- package/dist/react/index.cjs +481 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.cts +113 -0
- package/dist/react/index.d.ts +113 -0
- package/dist/react/index.mjs +478 -0
- package/dist/react/index.mjs.map +1 -0
- package/package.json +82 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/udp/webrtc.ts","../src/udp/packetLoop.ts","../src/udp/signaling.ts","../src/udp/index.ts","../src/dnsleak/trigger.ts","../src/dnsleak/index.ts","../src/client.ts"],"names":["mergeSignals"],"mappings":"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","file":"index.cjs","sourcesContent":["// Error hierarchy. Keep this small — three classes cover every failure\n// mode we care about for v0.1. Customers can `instanceof` to branch on\n// recoverable vs not.\n\nexport class NetworkTestsError extends Error {\n public readonly code: string;\n\n constructor(code: string, message: string) {\n super(message);\n this.name = \"NetworkTestsError\";\n this.code = code;\n }\n}\n\n/** Raised when the caller's AbortSignal fires. */\nexport class AbortError extends NetworkTestsError {\n constructor(message = \"Operation aborted\") {\n super(\"ABORTED\", message);\n this.name = \"AbortError\";\n }\n}\n\n/** Raised when a hard timeout fires before the test completes. */\nexport class TimeoutError extends NetworkTestsError {\n constructor(message = \"Operation timed out\") {\n super(\"TIMEOUT\", message);\n this.name = \"TimeoutError\";\n }\n}\n","// RTCPeerConnection wrapper. Encapsulates the offer/answer/ICE dance.\n//\n// Browser-native WebRTC only — no shims. The server uses `werift` and\n// speaks vanilla SDP, so any modern browser RTC stack interoperates.\n\nimport { NetworkTestsError } from \"../errors.js\";\n\nexport interface WebrtcSession {\n pc: RTCPeerConnection;\n dataChannel: RTCDataChannel;\n /** Resolves once the DataChannel is open. */\n ready: Promise<void>;\n close: () => void;\n}\n\nexport function createPeerConnection(iceServers: RTCIceServer[]): RTCPeerConnection {\n return new RTCPeerConnection({ iceServers });\n}\n\nexport function setupDataChannel(pc: RTCPeerConnection): {\n channel: RTCDataChannel;\n ready: Promise<void>;\n} {\n // Server expects a client-initiated DataChannel. Label \"test\" matches\n // what the consumer site has always used; server doesn't filter on it\n // but keeping it stable simplifies the wire trace.\n const channel = pc.createDataChannel(\"test\", { ordered: false });\n\n const ready = new Promise<void>((resolve, reject) => {\n const onOpen = () => {\n cleanup();\n resolve();\n };\n const onError = () => {\n cleanup();\n reject(new NetworkTestsError(\"DC_ERROR\", \"DataChannel error before open\"));\n };\n const onClose = () => {\n cleanup();\n reject(new NetworkTestsError(\"DC_CLOSED\", \"DataChannel closed before open\"));\n };\n function cleanup() {\n channel.removeEventListener(\"open\", onOpen);\n channel.removeEventListener(\"error\", onError);\n channel.removeEventListener(\"close\", onClose);\n }\n channel.addEventListener(\"open\", onOpen);\n channel.addEventListener(\"error\", onError);\n channel.addEventListener(\"close\", onClose);\n });\n\n return { channel, ready };\n}\n\nexport async function createOffer(pc: RTCPeerConnection): Promise<RTCSessionDescriptionInit> {\n const offer = await pc.createOffer();\n await pc.setLocalDescription(offer);\n return offer;\n}\n\nexport async function applyAnswer(\n pc: RTCPeerConnection,\n sdp: string,\n): Promise<void> {\n await pc.setRemoteDescription({ type: \"answer\", sdp });\n}\n","// DataChannel side of the packet-loss test.\n//\n// Server drives the test: it pushes packets at the configured rate, we\n// ack each one back. Server tracks send/ack timestamps and computes\n// loss + latency, then ships the final result via the WebSocket as\n// `test-complete`.\n//\n// Local job here is:\n// 1. Decode incoming server packets ({ type: \"packet\", seq, p? })\n// 2. Send ack back over DC ({ type: \"ack\", seq })\n// 3. Surface progress to the caller (sent/received/loss%/elapsed)\n\nimport type { UdpProgress } from \"../types.js\";\n\nexport interface PacketLoopHandle {\n /** Stop ack'ing future packets. Safe to call multiple times. */\n stop(): void;\n}\n\nexport interface PacketLoopOptions {\n channel: RTCDataChannel;\n onProgress?: (progress: UdpProgress) => void;\n /** Throttle progress callbacks to once per this many ms (default 100). */\n progressIntervalMs?: number;\n}\n\nexport function startPacketLoop(opts: PacketLoopOptions): PacketLoopHandle {\n const { channel, onProgress } = opts;\n const progressMs = opts.progressIntervalMs ?? 100;\n const startTs = Date.now();\n\n // Server sends them; we count locally for the onProgress callback.\n // The server's count is the source of truth in the final result.\n let received = 0;\n // We don't know what the server has sent (it tracks that side), but\n // we surface \"received\" as the progress signal. lossPercent below is\n // a coarse local estimate based on expected packets per second; we\n // intentionally don't try to be exact — the server's final number is.\n let highestSeq = -1;\n let stopped = false;\n\n const onMsg = (ev: MessageEvent<string>) => {\n if (stopped) return;\n try {\n const msg = JSON.parse(ev.data);\n if (msg && msg.type === \"packet\" && typeof msg.seq === \"number\") {\n received++;\n if (msg.seq > highestSeq) highestSeq = msg.seq;\n // Fire-and-forget ack. Send may throw if DC closed mid-flight;\n // silently drop — the server handles the missing ack as a lost\n // packet which is the same as a real network loss.\n try {\n channel.send(JSON.stringify({ type: \"ack\", seq: msg.seq }));\n } catch { /* DC closed; loop will exit when WS closes */ }\n }\n } catch { /* malformed packet — ignore */ }\n };\n\n channel.addEventListener(\"message\", onMsg);\n\n let progressTimer: ReturnType<typeof setInterval> | null = null;\n if (onProgress) {\n progressTimer = setInterval(() => {\n if (stopped) return;\n // highestSeq is 0-indexed; +1 gives count of packets the server\n // has sent us. lossPercent is locally estimated and approximate;\n // server emits the authoritative number in test-complete.\n const sent = highestSeq + 1;\n const loss = sent > 0 ? ((sent - received) / sent) * 100 : 0;\n onProgress({\n packetsSent: sent,\n packetsReceived: received,\n lossPercent: Math.round(loss * 100) / 100,\n elapsedMs: Date.now() - startTs,\n });\n }, progressMs);\n }\n\n return {\n stop() {\n if (stopped) return;\n stopped = true;\n channel.removeEventListener(\"message\", onMsg);\n if (progressTimer) clearInterval(progressTimer);\n },\n };\n}\n","// WebSocket protocol contract with server/websocket/signaling.js on the\n// networktests.com API server. Keep these in lockstep with the server.\n\nexport interface WelcomeMsg {\n type: \"welcome\";\n clientId: string;\n turn?: { urls: string[]; username: string; credential: string };\n}\n\nexport interface AnswerMsg {\n type: \"answer\";\n /** Server wraps the SDP in a nested object. */\n sdp: { type: \"answer\"; sdp: string };\n}\n\nexport interface IceCandidateMsg {\n type: \"ice-candidate\";\n candidate: RTCIceCandidateInit;\n}\n\nexport interface DataChannelOpenMsg { type: \"datachannel-open\"; }\nexport interface TestStartedMsg {\n type: \"test-started\";\n test: \"packet-loss\";\n duration: number;\n rate: number;\n packetSize: number;\n}\nexport interface TestStoppedMsg { type: \"test-stopped\"; }\nexport interface ErrorMsg { type: \"error\"; message: string; }\n\nexport interface TestCompleteMsg {\n type: \"test-complete\";\n test: \"packet-loss\";\n results: {\n packetsSent: number;\n packetsReceived: number;\n packetsLost: number;\n lossPercent: number;\n uploadReceived: number;\n latency: {\n avg: number;\n min: number;\n max: number;\n p50: number;\n p95: number;\n samples?: number[];\n };\n jitter: number;\n };\n}\n\nexport type ServerMsg =\n | WelcomeMsg\n | AnswerMsg\n | IceCandidateMsg\n | DataChannelOpenMsg\n | TestStartedMsg\n | TestStoppedMsg\n | TestCompleteMsg\n | ErrorMsg;\n\nexport type ClientMsg =\n | { type: \"offer\"; sdp: string }\n | { type: \"ice-candidate\"; candidate: RTCIceCandidateInit }\n | { type: \"datachannel-ready\" }\n | { type: \"start-packet-test\"; duration?: number; rate?: number; packetSize?: number }\n | { type: \"stop-test\" };\n\n/** Type guard for runtime ServerMsg validation. */\nexport function isServerMsg(value: unknown): value is ServerMsg {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as { type?: unknown }).type === \"string\"\n );\n}\n","// Public entry: runUdpTest(session, options).\n//\n// Sequence (must match server/websocket/signaling.js):\n// 1. Open WS → server sends `welcome` with optional TURN credentials\n// 2. Create RTCPeerConnection (iceServers from session ∪ welcome.turn)\n// 3. Create DataChannel (client-initiated, label \"test\")\n// 4. Create offer → send `{ type: \"offer\", sdp }`\n// 5. Receive `answer` → setRemoteDescription\n// 6. Trickle ICE in both directions\n// 7. Wait for DataChannel to open + server's `datachannel-open` ack\n// 8. Send `{ type: \"start-packet-test\", duration, rate, packetSize }`\n// 9. Receive `test-started` → start packet loop (ack incoming packets)\n// 10. Receive `test-complete` → close WS, resolve with results\n//\n// Any \"error\" message from the server or any unhandled WS/PC close\n// rejects the promise with NetworkTestsError.\n\nimport { NetworkTestsError, AbortError, TimeoutError } from \"../errors.js\";\nimport type {\n UdpSession,\n UdpResult,\n RunUdpOptions,\n} from \"../types.js\";\nimport {\n createPeerConnection,\n setupDataChannel,\n createOffer,\n applyAnswer,\n} from \"./webrtc.js\";\nimport { startPacketLoop } from \"./packetLoop.js\";\nimport type { ServerMsg, ClientMsg } from \"./signaling.js\";\nimport { isServerMsg } from \"./signaling.js\";\n\nconst DEFAULT_ICE_SERVERS: RTCIceServer[] = [\n { urls: \"stun:stun.l.google.com:19302\" },\n];\n\nexport async function runUdpTest(\n session: UdpSession,\n options: RunUdpOptions = {},\n): Promise<UdpResult> {\n const startedAt = Date.now();\n const { signal, onProgress } = options;\n const timeoutMs = options.timeoutMs ?? ((session.duration ?? 30) + 10) * 1000;\n\n if (signal?.aborted) throw new AbortError();\n\n const ws = new WebSocket(session.wsUrl);\n ws.binaryType = \"arraybuffer\";\n\n let pc: RTCPeerConnection | null = null;\n let dataChannel: RTCDataChannel | null = null;\n let packetLoop: ReturnType<typeof startPacketLoop> | null = null;\n let timeoutHandle: ReturnType<typeof setTimeout> | null = null;\n let abortHandler: (() => void) | null = null;\n\n const cleanup = () => {\n if (timeoutHandle) clearTimeout(timeoutHandle);\n if (abortHandler && signal) signal.removeEventListener(\"abort\", abortHandler);\n packetLoop?.stop();\n try { dataChannel?.close(); } catch { /* ignore */ }\n try { pc?.close(); } catch { /* ignore */ }\n if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {\n try { ws.close(); } catch { /* ignore */ }\n }\n };\n\n return new Promise<UdpResult>((resolve, reject) => {\n const fail = (err: Error) => { cleanup(); reject(err); };\n const succeed = (result: UdpResult) => { cleanup(); resolve(result); };\n\n timeoutHandle = setTimeout(\n () => fail(new TimeoutError(`UDP test exceeded ${timeoutMs}ms`)),\n timeoutMs,\n );\n if (signal) {\n abortHandler = () => fail(new AbortError());\n signal.addEventListener(\"abort\", abortHandler, { once: true });\n }\n\n const send = (msg: ClientMsg) => {\n if (ws.readyState === WebSocket.OPEN) ws.send(JSON.stringify(msg));\n };\n\n ws.addEventListener(\"error\", () =>\n fail(new NetworkTestsError(\"WS_ERROR\", \"WebSocket error\")),\n );\n\n ws.addEventListener(\"close\", (ev) => {\n // Close before result = unexpected. After resolve we've already\n // cleaned up so this handler is a no-op.\n if (ws.readyState === WebSocket.CLOSED) {\n const reason = ev.reason || `WebSocket closed (code ${ev.code})`;\n fail(new NetworkTestsError(\"WS_CLOSED\", reason));\n }\n });\n\n ws.addEventListener(\"open\", async () => {\n try {\n // The PC creation waits until the welcome arrives so we can\n // merge server-provided TURN creds into the iceServers list.\n } catch (err) {\n fail(toError(err));\n }\n });\n\n ws.addEventListener(\"message\", async (ev) => {\n let msg: unknown;\n try { msg = JSON.parse(String(ev.data)); } catch { return; }\n if (!isServerMsg(msg)) return;\n const m = msg as ServerMsg;\n\n try {\n switch (m.type) {\n case \"welcome\": {\n const iceServers: RTCIceServer[] = [\n ...(session.iceServers ?? DEFAULT_ICE_SERVERS),\n ];\n if (m.turn) {\n iceServers.push({\n urls: m.turn.urls,\n username: m.turn.username,\n credential: m.turn.credential,\n });\n }\n pc = createPeerConnection(iceServers);\n pc.addEventListener(\"icecandidate\", (e) => {\n if (e.candidate) {\n send({ type: \"ice-candidate\", candidate: e.candidate.toJSON() });\n }\n });\n const dcSetup = setupDataChannel(pc);\n dataChannel = dcSetup.channel;\n const offer = await createOffer(pc);\n send({ type: \"offer\", sdp: offer.sdp ?? \"\" });\n // Wait for DC to open then notify server.\n dcSetup.ready\n .then(() => send({ type: \"datachannel-ready\" }))\n .catch((err) => fail(toError(err)));\n break;\n }\n\n case \"answer\": {\n if (!pc) throw new NetworkTestsError(\"BAD_STATE\", \"answer before welcome\");\n await applyAnswer(pc, m.sdp.sdp);\n break;\n }\n\n case \"ice-candidate\": {\n if (!pc) return;\n try { await pc.addIceCandidate(m.candidate); } catch { /* end-of-cands */ }\n break;\n }\n\n case \"datachannel-open\": {\n if (!dataChannel) throw new NetworkTestsError(\"BAD_STATE\", \"no DC\");\n packetLoop = startPacketLoop({ channel: dataChannel, onProgress });\n send({\n type: \"start-packet-test\",\n duration: session.duration,\n rate: session.rate,\n packetSize: session.packetSize,\n });\n break;\n }\n\n case \"test-started\":\n // Server confirmed test parameters; nothing further to do\n // until packets arrive on the DC.\n break;\n\n case \"test-complete\": {\n const r = m.results;\n succeed({\n sessionId: session.sessionId,\n packetsSent: r.packetsSent,\n packetsReceived: r.packetsReceived,\n packetsLost: r.packetsLost,\n lossPercent: r.lossPercent,\n jitter: r.jitter,\n latency: {\n avg: r.latency.avg,\n min: r.latency.min,\n max: r.latency.max,\n p50: r.latency.p50,\n p95: r.latency.p95,\n samples: r.latency.samples,\n },\n durationMs: Date.now() - startedAt,\n completedAt: new Date().toISOString(),\n });\n break;\n }\n\n case \"test-stopped\":\n fail(new NetworkTestsError(\"TEST_STOPPED\", \"Server stopped the test\"));\n break;\n\n case \"error\":\n fail(new NetworkTestsError(\"SERVER_ERROR\", m.message));\n break;\n }\n } catch (err) {\n fail(toError(err));\n }\n });\n });\n}\n\nfunction toError(value: unknown): Error {\n if (value instanceof Error) return value;\n return new NetworkTestsError(\"UNKNOWN\", String(value));\n}\n","// Triggers a DNS resolution of a single FQDN from the browser.\n//\n// Our authoritative DNS server responds to every query with 192.0.2.1\n// (TEST-NET-1, guaranteed unreachable per RFC 5737) — the HTTP request\n// is *expected* to fail. We only care that the recursive resolver had\n// to look up the name, so its IP gets recorded server-side.\n//\n// Strategy (most-reliable first):\n// 1. new Image() — no CORS, no fetch policy. Triggers DNS even on\n// CSP-restricted pages. Errors are silent (image fails to decode).\n// 2. fetch(no-cors) — fallback. Some environments (workers, certain\n// WebView shells) lack DOM image support.\n//\n// Both error paths are normal. The function resolves on any terminal\n// state (success or error), never throws.\n\nconst ABORT_AFTER_MS = 5000;\n\nexport function triggerDnsResolution(fqdn: string, signal?: AbortSignal): Promise<void> {\n if (signal?.aborted) return Promise.resolve();\n\n // Prefer Image — broader browser support, no CORS.\n if (typeof Image !== \"undefined\") {\n return triggerViaImage(fqdn, signal);\n }\n return triggerViaFetch(fqdn, signal);\n}\n\nfunction triggerViaImage(fqdn: string, signal?: AbortSignal): Promise<void> {\n return new Promise<void>((resolve) => {\n const img = new Image();\n let settled = false;\n const finish = () => {\n if (settled) return;\n settled = true;\n img.src = \"\"; // cancel any pending request\n resolve();\n };\n\n const onAbort = () => finish();\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n\n const timer = setTimeout(finish, ABORT_AFTER_MS);\n\n img.onload = () => { clearTimeout(timer); finish(); };\n img.onerror = () => { clearTimeout(timer); finish(); };\n\n // Cache-bust so subsequent runs against the same FQDN actually\n // re-resolve. Path doesn't matter — server returns 192.0.2.1\n // regardless.\n img.src = `https://${fqdn}/_/leak.gif?t=${Date.now()}-${Math.random().toString(36).slice(2)}`;\n });\n}\n\nfunction triggerViaFetch(fqdn: string, signal?: AbortSignal): Promise<void> {\n const controller = new AbortController();\n const abortSignal = signal\n ? mergeSignals(signal, controller.signal)\n : controller.signal;\n const timer = setTimeout(() => controller.abort(), ABORT_AFTER_MS);\n\n return fetch(`https://${fqdn}/_/leak?t=${Date.now()}`, {\n method: \"GET\",\n mode: \"no-cors\",\n cache: \"no-store\",\n signal: abortSignal,\n })\n .catch(() => undefined)\n .finally(() => clearTimeout(timer))\n .then(() => undefined);\n}\n\nfunction mergeSignals(a: AbortSignal, b: AbortSignal): AbortSignal {\n const c = new AbortController();\n const onAbort = () => c.abort();\n if (a.aborted || b.aborted) c.abort();\n else {\n a.addEventListener(\"abort\", onAbort, { once: true });\n b.addEventListener(\"abort\", onAbort, { once: true });\n }\n return c.signal;\n}\n","// runDnsLeakTest(session, options) — triggers DNS resolution of every\n// FQDN in the session in parallel and resolves once all triggers have\n// fired (or errored — errors are expected).\n\nimport { AbortError, TimeoutError } from \"../errors.js\";\nimport { triggerDnsResolution } from \"./trigger.js\";\nimport type { DnsLeakSession, RunDnsLeakOptions } from \"../types.js\";\n\nexport async function runDnsLeakTest(\n session: DnsLeakSession,\n options: RunDnsLeakOptions = {},\n): Promise<void> {\n if (options.signal?.aborted) throw new AbortError();\n const timeoutMs = options.timeoutMs ?? 15000;\n\n let timeoutHandle: ReturnType<typeof setTimeout> | null = null;\n const timeoutController = new AbortController();\n const combinedSignal = mergeSignals(options.signal, timeoutController.signal);\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutHandle = setTimeout(() => {\n timeoutController.abort();\n reject(new TimeoutError(`DNS-leak test exceeded ${timeoutMs}ms`));\n }, timeoutMs);\n });\n\n const abortPromise = new Promise<never>((_, reject) => {\n options.signal?.addEventListener(\n \"abort\",\n () => reject(new AbortError()),\n { once: true },\n );\n });\n\n const triggers = session.fqdns.map((fqdn) =>\n triggerDnsResolution(fqdn, combinedSignal).then(() => {\n options.onResolved?.(fqdn);\n }),\n );\n\n try {\n await Promise.race([\n Promise.all(triggers),\n timeoutPromise,\n abortPromise,\n ]);\n } finally {\n if (timeoutHandle) clearTimeout(timeoutHandle);\n }\n}\n\nfunction mergeSignals(a: AbortSignal | undefined, b: AbortSignal): AbortSignal {\n if (!a) return b;\n const c = new AbortController();\n if (a.aborted || b.aborted) c.abort();\n else {\n a.addEventListener(\"abort\", () => c.abort(), { once: true });\n b.addEventListener(\"abort\", () => c.abort(), { once: true });\n }\n return c.signal;\n}\n","import { runUdpTest } from \"./udp/index.js\";\nimport { runDnsLeakTest } from \"./dnsleak/index.js\";\nimport type {\n UdpSession,\n DnsLeakSession,\n UdpResult,\n RunUdpOptions,\n RunDnsLeakOptions,\n} from \"./types.js\";\n\n/**\n * Entry point for the networktests.com browser SDK.\n *\n * The constructor takes no arguments — the SDK is keyless on the wire.\n * Customers' backends mint session tickets via the authenticated\n * api.networktests.com API and ship them to the browser.\n *\n * @example\n * const nt = new NetworkTests();\n * const session = await fetch(\"/my-backend/start-udp\").then(r => r.json());\n * const result = await nt.runUdpTest(session);\n */\nexport class NetworkTests {\n /**\n * Run the UDP throughput probe to completion.\n *\n * Opens a WebSocket to `session.wsUrl`, negotiates a WebRTC DataChannel,\n * runs the packet-loss test, and resolves with the final stats once the\n * server emits `test-complete`.\n */\n runUdpTest(session: UdpSession, options?: RunUdpOptions): Promise<UdpResult> {\n return runUdpTest(session, options);\n }\n\n /**\n * Trigger DNS resolution of every FQDN in the session. Returns once all\n * triggers have fired (or errored — errors are expected because the\n * fake server returns 192.0.2.1).\n *\n * Actual resolver IPs are observed server-side. Customer's backend\n * polls `GET /v1/probe/dns-leak/sessions/:id` to retrieve them.\n */\n runDnsLeakTest(session: DnsLeakSession, options?: RunDnsLeakOptions): Promise<void> {\n return runDnsLeakTest(session, options);\n }\n}\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UDP throughput probe session. Returned by the customer's backend from
|
|
3
|
+
* `POST /v1/probe/udp/sessions` on api.networktests.com.
|
|
4
|
+
*
|
|
5
|
+
* The frontend passes this to `nt.runUdpTest(session)`.
|
|
6
|
+
*/
|
|
7
|
+
interface UdpSession {
|
|
8
|
+
/** Opaque session ticket. Single-use; valid for ~30 minutes. */
|
|
9
|
+
sessionId: string;
|
|
10
|
+
/** WebSocket URL the SDK must connect to (region-pinned). */
|
|
11
|
+
wsUrl: string;
|
|
12
|
+
/** ICE servers to seed the RTCPeerConnection. Server may override via `welcome` message. */
|
|
13
|
+
iceServers?: RTCIceServer[];
|
|
14
|
+
/** Test duration in seconds (defaults to server config). */
|
|
15
|
+
duration?: number;
|
|
16
|
+
/** Packet rate (packets per second). */
|
|
17
|
+
rate?: number;
|
|
18
|
+
/** Packet size in bytes (max 1400). */
|
|
19
|
+
packetSize?: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* DNS leak probe session. Returned by `POST /v1/probe/dns-leak/sessions`.
|
|
23
|
+
*
|
|
24
|
+
* The SDK iterates `fqdns` and triggers DNS resolution of each one. Our
|
|
25
|
+
* authoritative DNS server records which recursive resolver did the
|
|
26
|
+
* lookup; the customer's backend later calls
|
|
27
|
+
* `GET /v1/probe/dns-leak/sessions/:id` to retrieve the resolver list.
|
|
28
|
+
*/
|
|
29
|
+
interface DnsLeakSession {
|
|
30
|
+
sessionId: string;
|
|
31
|
+
fqdns: string[];
|
|
32
|
+
}
|
|
33
|
+
interface LatencyStats {
|
|
34
|
+
/** Mean RTT in milliseconds. */
|
|
35
|
+
avg: number;
|
|
36
|
+
min: number;
|
|
37
|
+
max: number;
|
|
38
|
+
/** 50th percentile. */
|
|
39
|
+
p50: number;
|
|
40
|
+
/** 95th percentile. */
|
|
41
|
+
p95: number;
|
|
42
|
+
/** Down-sampled to 200 evenly-spaced points across the full test. */
|
|
43
|
+
samples?: number[];
|
|
44
|
+
}
|
|
45
|
+
interface UdpResult {
|
|
46
|
+
sessionId: string;
|
|
47
|
+
packetsSent: number;
|
|
48
|
+
packetsReceived: number;
|
|
49
|
+
packetsLost: number;
|
|
50
|
+
/** Packet loss percentage (0-100). */
|
|
51
|
+
lossPercent: number;
|
|
52
|
+
/** Inter-packet arrival jitter, in ms. */
|
|
53
|
+
jitter: number;
|
|
54
|
+
latency: LatencyStats;
|
|
55
|
+
/** Total test duration in ms (wall clock from WS open to result). */
|
|
56
|
+
durationMs: number;
|
|
57
|
+
completedAt: string;
|
|
58
|
+
}
|
|
59
|
+
interface UdpProgress {
|
|
60
|
+
packetsSent: number;
|
|
61
|
+
packetsReceived: number;
|
|
62
|
+
lossPercent: number;
|
|
63
|
+
elapsedMs: number;
|
|
64
|
+
}
|
|
65
|
+
interface RunUdpOptions {
|
|
66
|
+
/** Per-packet progress callback. Fires ~10x/sec, debounced. */
|
|
67
|
+
onProgress?: (progress: UdpProgress) => void;
|
|
68
|
+
/** AbortSignal — abort cancels the test cleanly (closes WS + DC). */
|
|
69
|
+
signal?: AbortSignal;
|
|
70
|
+
/** Hard timeout in ms. Defaults to (session.duration + 10) * 1000. */
|
|
71
|
+
timeoutMs?: number;
|
|
72
|
+
}
|
|
73
|
+
interface RunDnsLeakOptions {
|
|
74
|
+
/** Called after each FQDN trigger resolves or errors. */
|
|
75
|
+
onResolved?: (fqdn: string) => void;
|
|
76
|
+
/** AbortSignal — cancels any in-flight image/fetch triggers. */
|
|
77
|
+
signal?: AbortSignal;
|
|
78
|
+
/** Hard timeout in ms. Default 15000. */
|
|
79
|
+
timeoutMs?: number;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Entry point for the networktests.com browser SDK.
|
|
84
|
+
*
|
|
85
|
+
* The constructor takes no arguments — the SDK is keyless on the wire.
|
|
86
|
+
* Customers' backends mint session tickets via the authenticated
|
|
87
|
+
* api.networktests.com API and ship them to the browser.
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* const nt = new NetworkTests();
|
|
91
|
+
* const session = await fetch("/my-backend/start-udp").then(r => r.json());
|
|
92
|
+
* const result = await nt.runUdpTest(session);
|
|
93
|
+
*/
|
|
94
|
+
declare class NetworkTests {
|
|
95
|
+
/**
|
|
96
|
+
* Run the UDP throughput probe to completion.
|
|
97
|
+
*
|
|
98
|
+
* Opens a WebSocket to `session.wsUrl`, negotiates a WebRTC DataChannel,
|
|
99
|
+
* runs the packet-loss test, and resolves with the final stats once the
|
|
100
|
+
* server emits `test-complete`.
|
|
101
|
+
*/
|
|
102
|
+
runUdpTest(session: UdpSession, options?: RunUdpOptions): Promise<UdpResult>;
|
|
103
|
+
/**
|
|
104
|
+
* Trigger DNS resolution of every FQDN in the session. Returns once all
|
|
105
|
+
* triggers have fired (or errored — errors are expected because the
|
|
106
|
+
* fake server returns 192.0.2.1).
|
|
107
|
+
*
|
|
108
|
+
* Actual resolver IPs are observed server-side. Customer's backend
|
|
109
|
+
* polls `GET /v1/probe/dns-leak/sessions/:id` to retrieve them.
|
|
110
|
+
*/
|
|
111
|
+
runDnsLeakTest(session: DnsLeakSession, options?: RunDnsLeakOptions): Promise<void>;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
declare class NetworkTestsError extends Error {
|
|
115
|
+
readonly code: string;
|
|
116
|
+
constructor(code: string, message: string);
|
|
117
|
+
}
|
|
118
|
+
/** Raised when the caller's AbortSignal fires. */
|
|
119
|
+
declare class AbortError extends NetworkTestsError {
|
|
120
|
+
constructor(message?: string);
|
|
121
|
+
}
|
|
122
|
+
/** Raised when a hard timeout fires before the test completes. */
|
|
123
|
+
declare class TimeoutError extends NetworkTestsError {
|
|
124
|
+
constructor(message?: string);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export { AbortError, type DnsLeakSession, type LatencyStats, NetworkTests, NetworkTestsError, type RunDnsLeakOptions, type RunUdpOptions, TimeoutError, type UdpProgress, type UdpResult, type UdpSession };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UDP throughput probe session. Returned by the customer's backend from
|
|
3
|
+
* `POST /v1/probe/udp/sessions` on api.networktests.com.
|
|
4
|
+
*
|
|
5
|
+
* The frontend passes this to `nt.runUdpTest(session)`.
|
|
6
|
+
*/
|
|
7
|
+
interface UdpSession {
|
|
8
|
+
/** Opaque session ticket. Single-use; valid for ~30 minutes. */
|
|
9
|
+
sessionId: string;
|
|
10
|
+
/** WebSocket URL the SDK must connect to (region-pinned). */
|
|
11
|
+
wsUrl: string;
|
|
12
|
+
/** ICE servers to seed the RTCPeerConnection. Server may override via `welcome` message. */
|
|
13
|
+
iceServers?: RTCIceServer[];
|
|
14
|
+
/** Test duration in seconds (defaults to server config). */
|
|
15
|
+
duration?: number;
|
|
16
|
+
/** Packet rate (packets per second). */
|
|
17
|
+
rate?: number;
|
|
18
|
+
/** Packet size in bytes (max 1400). */
|
|
19
|
+
packetSize?: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* DNS leak probe session. Returned by `POST /v1/probe/dns-leak/sessions`.
|
|
23
|
+
*
|
|
24
|
+
* The SDK iterates `fqdns` and triggers DNS resolution of each one. Our
|
|
25
|
+
* authoritative DNS server records which recursive resolver did the
|
|
26
|
+
* lookup; the customer's backend later calls
|
|
27
|
+
* `GET /v1/probe/dns-leak/sessions/:id` to retrieve the resolver list.
|
|
28
|
+
*/
|
|
29
|
+
interface DnsLeakSession {
|
|
30
|
+
sessionId: string;
|
|
31
|
+
fqdns: string[];
|
|
32
|
+
}
|
|
33
|
+
interface LatencyStats {
|
|
34
|
+
/** Mean RTT in milliseconds. */
|
|
35
|
+
avg: number;
|
|
36
|
+
min: number;
|
|
37
|
+
max: number;
|
|
38
|
+
/** 50th percentile. */
|
|
39
|
+
p50: number;
|
|
40
|
+
/** 95th percentile. */
|
|
41
|
+
p95: number;
|
|
42
|
+
/** Down-sampled to 200 evenly-spaced points across the full test. */
|
|
43
|
+
samples?: number[];
|
|
44
|
+
}
|
|
45
|
+
interface UdpResult {
|
|
46
|
+
sessionId: string;
|
|
47
|
+
packetsSent: number;
|
|
48
|
+
packetsReceived: number;
|
|
49
|
+
packetsLost: number;
|
|
50
|
+
/** Packet loss percentage (0-100). */
|
|
51
|
+
lossPercent: number;
|
|
52
|
+
/** Inter-packet arrival jitter, in ms. */
|
|
53
|
+
jitter: number;
|
|
54
|
+
latency: LatencyStats;
|
|
55
|
+
/** Total test duration in ms (wall clock from WS open to result). */
|
|
56
|
+
durationMs: number;
|
|
57
|
+
completedAt: string;
|
|
58
|
+
}
|
|
59
|
+
interface UdpProgress {
|
|
60
|
+
packetsSent: number;
|
|
61
|
+
packetsReceived: number;
|
|
62
|
+
lossPercent: number;
|
|
63
|
+
elapsedMs: number;
|
|
64
|
+
}
|
|
65
|
+
interface RunUdpOptions {
|
|
66
|
+
/** Per-packet progress callback. Fires ~10x/sec, debounced. */
|
|
67
|
+
onProgress?: (progress: UdpProgress) => void;
|
|
68
|
+
/** AbortSignal — abort cancels the test cleanly (closes WS + DC). */
|
|
69
|
+
signal?: AbortSignal;
|
|
70
|
+
/** Hard timeout in ms. Defaults to (session.duration + 10) * 1000. */
|
|
71
|
+
timeoutMs?: number;
|
|
72
|
+
}
|
|
73
|
+
interface RunDnsLeakOptions {
|
|
74
|
+
/** Called after each FQDN trigger resolves or errors. */
|
|
75
|
+
onResolved?: (fqdn: string) => void;
|
|
76
|
+
/** AbortSignal — cancels any in-flight image/fetch triggers. */
|
|
77
|
+
signal?: AbortSignal;
|
|
78
|
+
/** Hard timeout in ms. Default 15000. */
|
|
79
|
+
timeoutMs?: number;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Entry point for the networktests.com browser SDK.
|
|
84
|
+
*
|
|
85
|
+
* The constructor takes no arguments — the SDK is keyless on the wire.
|
|
86
|
+
* Customers' backends mint session tickets via the authenticated
|
|
87
|
+
* api.networktests.com API and ship them to the browser.
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* const nt = new NetworkTests();
|
|
91
|
+
* const session = await fetch("/my-backend/start-udp").then(r => r.json());
|
|
92
|
+
* const result = await nt.runUdpTest(session);
|
|
93
|
+
*/
|
|
94
|
+
declare class NetworkTests {
|
|
95
|
+
/**
|
|
96
|
+
* Run the UDP throughput probe to completion.
|
|
97
|
+
*
|
|
98
|
+
* Opens a WebSocket to `session.wsUrl`, negotiates a WebRTC DataChannel,
|
|
99
|
+
* runs the packet-loss test, and resolves with the final stats once the
|
|
100
|
+
* server emits `test-complete`.
|
|
101
|
+
*/
|
|
102
|
+
runUdpTest(session: UdpSession, options?: RunUdpOptions): Promise<UdpResult>;
|
|
103
|
+
/**
|
|
104
|
+
* Trigger DNS resolution of every FQDN in the session. Returns once all
|
|
105
|
+
* triggers have fired (or errored — errors are expected because the
|
|
106
|
+
* fake server returns 192.0.2.1).
|
|
107
|
+
*
|
|
108
|
+
* Actual resolver IPs are observed server-side. Customer's backend
|
|
109
|
+
* polls `GET /v1/probe/dns-leak/sessions/:id` to retrieve them.
|
|
110
|
+
*/
|
|
111
|
+
runDnsLeakTest(session: DnsLeakSession, options?: RunDnsLeakOptions): Promise<void>;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
declare class NetworkTestsError extends Error {
|
|
115
|
+
readonly code: string;
|
|
116
|
+
constructor(code: string, message: string);
|
|
117
|
+
}
|
|
118
|
+
/** Raised when the caller's AbortSignal fires. */
|
|
119
|
+
declare class AbortError extends NetworkTestsError {
|
|
120
|
+
constructor(message?: string);
|
|
121
|
+
}
|
|
122
|
+
/** Raised when a hard timeout fires before the test completes. */
|
|
123
|
+
declare class TimeoutError extends NetworkTestsError {
|
|
124
|
+
constructor(message?: string);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export { AbortError, type DnsLeakSession, type LatencyStats, NetworkTests, NetworkTestsError, type RunDnsLeakOptions, type RunUdpOptions, TimeoutError, type UdpProgress, type UdpResult, type UdpSession };
|