@agentdance/node-webrtc-ice 1.0.0

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.
@@ -0,0 +1,70 @@
1
+ import * as dgram from 'node:dgram';
2
+ import { EventEmitter } from 'node:events';
3
+ // ---------------------------------------------------------------------------
4
+ // Packet type detection (RFC 7983 / RFC 5764)
5
+ // STUN: first byte 0x00-0x03
6
+ // DTLS: first byte 0x14-0x19 (ContentType: change_cipher_spec=20...23=heartbeat)
7
+ // RTP/RTCP: first byte 0x80-0xFF (version 2 flag set)
8
+ // ---------------------------------------------------------------------------
9
+ export function detectPacketType(buf) {
10
+ if (buf.length === 0)
11
+ return 'unknown';
12
+ const b = buf[0];
13
+ if (b <= 0x03)
14
+ return 'stun';
15
+ if (b >= 0x14 && b <= 0x19)
16
+ return 'dtls';
17
+ if (b >= 0x80)
18
+ return 'rtp';
19
+ return 'unknown';
20
+ }
21
+ export class UdpTransport extends EventEmitter {
22
+ _socket;
23
+ _localPort = 0;
24
+ _localAddress = '0.0.0.0';
25
+ get localPort() {
26
+ return this._localPort;
27
+ }
28
+ get localAddress() {
29
+ return this._localAddress;
30
+ }
31
+ async bind(port = 0, address = '0.0.0.0') {
32
+ return new Promise((resolve, reject) => {
33
+ const socket = dgram.createSocket('udp4');
34
+ socket.on('error', (err) => {
35
+ this.emit('error', err);
36
+ });
37
+ socket.on('message', (buf, rinfo) => {
38
+ const type = detectPacketType(buf);
39
+ this.emit(type, buf, rinfo);
40
+ });
41
+ socket.bind(port, address, () => {
42
+ const addr = socket.address();
43
+ this._localPort = addr.port;
44
+ this._localAddress = addr.address;
45
+ this._socket = socket;
46
+ resolve();
47
+ });
48
+ socket.once('error', reject);
49
+ });
50
+ }
51
+ send(buf, port, address) {
52
+ return new Promise((resolve, reject) => {
53
+ this._socket.send(buf, port, address, (err) => {
54
+ if (err)
55
+ reject(err);
56
+ else
57
+ resolve();
58
+ });
59
+ });
60
+ }
61
+ close() {
62
+ try {
63
+ this._socket.close();
64
+ }
65
+ catch {
66
+ // already closed
67
+ }
68
+ }
69
+ }
70
+ //# sourceMappingURL=transport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,8EAA8E;AAC9E,8CAA8C;AAC9C,8BAA8B;AAC9B,kFAAkF;AAClF,sDAAsD;AACtD,8EAA8E;AAE9E,MAAM,UAAU,gBAAgB,CAC9B,GAAW;IAEX,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IACvC,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,CAAE,CAAC;IAClB,IAAI,CAAC,IAAI,IAAI;QAAE,OAAO,MAAM,CAAC;IAC7B,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI;QAAE,OAAO,MAAM,CAAC;IAC1C,IAAI,CAAC,IAAI,IAAI;QAAE,OAAO,KAAK,CAAC;IAC5B,OAAO,SAAS,CAAC;AACnB,CAAC;AAcD,MAAM,OAAO,YAAa,SAAQ,YAAY;IACpC,OAAO,CAAgB;IACvB,UAAU,GAAG,CAAC,CAAC;IACf,aAAa,GAAG,SAAS,CAAC;IAElC,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED,IAAI,YAAY;QACd,OAAO,IAAI,CAAC,aAAa,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,EAAE,OAAO,GAAG,SAAS;QACtC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,MAAM,GAAG,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;YAE1C,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;gBACzB,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;YAC1B,CAAC,CAAC,CAAC;YAEH,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,GAAW,EAAE,KAAuB,EAAE,EAAE;gBAC5D,MAAM,IAAI,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;gBACnC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;YAC9B,CAAC,CAAC,CAAC;YAEH,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE;gBAC9B,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;gBAC9B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC;gBAC5B,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC;gBAClC,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;gBACtB,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC,CAAC;YAEH,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC/B,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI,CAAC,GAAW,EAAE,IAAY,EAAE,OAAe;QAC7C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;gBAC5C,IAAI,GAAG;oBAAE,MAAM,CAAC,GAAG,CAAC,CAAC;;oBAChB,OAAO,EAAE,CAAC;YACjB,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK;QACH,IAAI,CAAC;YACH,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACvB,CAAC;QAAC,MAAM,CAAC;YACP,iBAAiB;QACnB,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,77 @@
1
+ export type CandidateType = 'host' | 'srflx' | 'relay' | 'prflx';
2
+ export type TransportProtocol = 'udp' | 'tcp';
3
+ export type IceRole = 'controlling' | 'controlled';
4
+ export declare enum IceAgentState {
5
+ New = "new",
6
+ Gathering = "gathering",
7
+ Complete = "complete",
8
+ Failed = "failed",
9
+ Closed = "closed"
10
+ }
11
+ export declare enum IceConnectionState {
12
+ New = "new",
13
+ Checking = "checking",
14
+ Connected = "connected",
15
+ Completed = "completed",
16
+ Failed = "failed",
17
+ Disconnected = "disconnected",
18
+ Closed = "closed"
19
+ }
20
+ export interface IceCandidate {
21
+ foundation: string;
22
+ component: 1 | 2;
23
+ transport: TransportProtocol;
24
+ priority: number;
25
+ address: string;
26
+ port: number;
27
+ type: CandidateType;
28
+ relatedAddress?: string;
29
+ relatedPort?: number;
30
+ tcpType?: 'active' | 'passive' | 'so';
31
+ generation?: number;
32
+ ufrag?: string;
33
+ networkId?: number;
34
+ }
35
+ export interface IceParameters {
36
+ usernameFragment: string;
37
+ password: string;
38
+ iceLite?: boolean;
39
+ }
40
+ export interface CandidatePair {
41
+ id: string;
42
+ local: IceCandidate;
43
+ remote: IceCandidate;
44
+ state: CandidatePairState;
45
+ priority: bigint;
46
+ nominated: boolean;
47
+ valid: boolean;
48
+ nominateOnSuccess: boolean;
49
+ lastBindingRequestReceived?: number;
50
+ lastBindingResponseReceived?: number;
51
+ retransmitCount: number;
52
+ retransmitTimer?: NodeJS.Timeout;
53
+ }
54
+ export declare enum CandidatePairState {
55
+ Waiting = "waiting",
56
+ InProgress = "in-progress",
57
+ Succeeded = "succeeded",
58
+ Failed = "failed",
59
+ Frozen = "frozen"
60
+ }
61
+ export interface IceAgentOptions {
62
+ ufrag?: string;
63
+ password?: string;
64
+ role?: IceRole;
65
+ tiebreaker?: bigint;
66
+ stunServers?: Array<{
67
+ host: string;
68
+ port: number;
69
+ }>;
70
+ lite?: boolean;
71
+ portRange?: {
72
+ min: number;
73
+ max: number;
74
+ };
75
+ nomination?: 'regular' | 'aggressive';
76
+ }
77
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;AACjE,MAAM,MAAM,iBAAiB,GAAG,KAAK,GAAG,KAAK,CAAC;AAC9C,MAAM,MAAM,OAAO,GAAG,aAAa,GAAG,YAAY,CAAC;AAEnD,oBAAY,aAAa;IACvB,GAAG,QAAQ;IACX,SAAS,cAAc;IACvB,QAAQ,aAAa;IACrB,MAAM,WAAW;IACjB,MAAM,WAAW;CAClB;AAED,oBAAY,kBAAkB;IAC5B,GAAG,QAAQ;IACX,QAAQ,aAAa;IACrB,SAAS,cAAc;IACvB,SAAS,cAAc;IACvB,MAAM,WAAW;IACjB,YAAY,iBAAiB;IAC7B,MAAM,WAAW;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,CAAC,GAAG,CAAC,CAAC;IACjB,SAAS,EAAE,iBAAiB,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,aAAa,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,IAAI,CAAC;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,YAAY,CAAC;IACpB,MAAM,EAAE,YAAY,CAAC;IACrB,KAAK,EAAE,kBAAkB,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;IACf,iBAAiB,EAAE,OAAO,CAAC;IAC3B,0BAA0B,CAAC,EAAE,MAAM,CAAC;IACpC,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC;CAClC;AAED,oBAAY,kBAAkB;IAC5B,OAAO,YAAY;IACnB,UAAU,gBAAgB;IAC1B,SAAS,cAAc;IACvB,MAAM,WAAW;IACjB,MAAM,WAAW;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,SAAS,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC,UAAU,CAAC,EAAE,SAAS,GAAG,YAAY,CAAC;CACvC"}
package/dist/types.js ADDED
@@ -0,0 +1,27 @@
1
+ export var IceAgentState;
2
+ (function (IceAgentState) {
3
+ IceAgentState["New"] = "new";
4
+ IceAgentState["Gathering"] = "gathering";
5
+ IceAgentState["Complete"] = "complete";
6
+ IceAgentState["Failed"] = "failed";
7
+ IceAgentState["Closed"] = "closed";
8
+ })(IceAgentState || (IceAgentState = {}));
9
+ export var IceConnectionState;
10
+ (function (IceConnectionState) {
11
+ IceConnectionState["New"] = "new";
12
+ IceConnectionState["Checking"] = "checking";
13
+ IceConnectionState["Connected"] = "connected";
14
+ IceConnectionState["Completed"] = "completed";
15
+ IceConnectionState["Failed"] = "failed";
16
+ IceConnectionState["Disconnected"] = "disconnected";
17
+ IceConnectionState["Closed"] = "closed";
18
+ })(IceConnectionState || (IceConnectionState = {}));
19
+ export var CandidatePairState;
20
+ (function (CandidatePairState) {
21
+ CandidatePairState["Waiting"] = "waiting";
22
+ CandidatePairState["InProgress"] = "in-progress";
23
+ CandidatePairState["Succeeded"] = "succeeded";
24
+ CandidatePairState["Failed"] = "failed";
25
+ CandidatePairState["Frozen"] = "frozen";
26
+ })(CandidatePairState || (CandidatePairState = {}));
27
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAIA,MAAM,CAAN,IAAY,aAMX;AAND,WAAY,aAAa;IACvB,4BAAW,CAAA;IACX,wCAAuB,CAAA;IACvB,sCAAqB,CAAA;IACrB,kCAAiB,CAAA;IACjB,kCAAiB,CAAA;AACnB,CAAC,EANW,aAAa,KAAb,aAAa,QAMxB;AAED,MAAM,CAAN,IAAY,kBAQX;AARD,WAAY,kBAAkB;IAC5B,iCAAW,CAAA;IACX,2CAAqB,CAAA;IACrB,6CAAuB,CAAA;IACvB,6CAAuB,CAAA;IACvB,uCAAiB,CAAA;IACjB,mDAA6B,CAAA;IAC7B,uCAAiB,CAAA;AACnB,CAAC,EARW,kBAAkB,KAAlB,kBAAkB,QAQ7B;AAuCD,MAAM,CAAN,IAAY,kBAMX;AAND,WAAY,kBAAkB;IAC5B,yCAAmB,CAAA;IACnB,gDAA0B,CAAA;IAC1B,6CAAuB,CAAA;IACvB,uCAAiB,CAAA;IACjB,uCAAiB,CAAA;AACnB,CAAC,EANW,kBAAkB,KAAlB,kBAAkB,QAM7B"}
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@agentdance/node-webrtc-ice",
3
+ "version": "1.0.0",
4
+ "description": "RFC 8445 ICE agent — host/srflx/prflx candidates, connectivity checks, nomination. Part of the @agentdance/node-webrtc stack.",
5
+ "keywords": [
6
+ "webrtc",
7
+ "ice",
8
+ "rfc8445",
9
+ "stun",
10
+ "p2p",
11
+ "typescript",
12
+ "node"
13
+ ],
14
+ "license": "MIT",
15
+ "homepage": "https://github.com/agent-dance/node-webrtc#readme",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/agent-dance/node-webrtc.git",
19
+ "directory": "packages/ice"
20
+ },
21
+ "bugs": {
22
+ "url": "https://github.com/agent-dance/node-webrtc/issues"
23
+ },
24
+ "engines": {
25
+ "node": ">=18"
26
+ },
27
+ "type": "module",
28
+ "main": "./dist/index.js",
29
+ "types": "./dist/index.d.ts",
30
+ "exports": {
31
+ ".": {
32
+ "import": "./dist/index.js",
33
+ "types": "./dist/index.d.ts"
34
+ }
35
+ },
36
+ "files": [
37
+ "dist",
38
+ "src"
39
+ ],
40
+ "sideEffects": false,
41
+ "publishConfig": {
42
+ "access": "public",
43
+ "registry": "https://registry.npmjs.org/"
44
+ },
45
+ "dependencies": {
46
+ "@agentdance/node-webrtc-stun": "1.0.0"
47
+ },
48
+ "devDependencies": {
49
+ "typescript": "*",
50
+ "vitest": "*",
51
+ "@types/node": "*"
52
+ },
53
+ "scripts": {
54
+ "build": "tsc",
55
+ "test": "vitest run",
56
+ "typecheck": "tsc --noEmit",
57
+ "clean": "rm -rf dist"
58
+ }
59
+ }