@abndnce/pulsar-client 0.0.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/dist/index.d.mts +32 -0
- package/dist/index.mjs +81 -0
- package/package.json +24 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
//#region lib/connection/types.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Every client connection, regardless of transport mode, satisfies this interface.
|
|
4
|
+
*/
|
|
5
|
+
interface PulsarClientConnection {
|
|
6
|
+
keepalive: RTCDataChannel;
|
|
7
|
+
pc: RTCPeerConnection;
|
|
8
|
+
close(): Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region lib/connection/direct.d.ts
|
|
12
|
+
/**
|
|
13
|
+
* Connect to a remote Pulsar server in direct mode.
|
|
14
|
+
*
|
|
15
|
+
* Designed for browsers, using the native `RTCPeerConnection` API.
|
|
16
|
+
*
|
|
17
|
+
* @param host Server IP address
|
|
18
|
+
* @param port Server UDP port
|
|
19
|
+
* @returns A connected PulsarClientConnection with an open keepalive channel.
|
|
20
|
+
*/
|
|
21
|
+
declare function connectDirect(host: string, port: number): Promise<PulsarClientConnection>;
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region lib/connection/nostr.d.ts
|
|
24
|
+
/**
|
|
25
|
+
* Connect via a Nostr relay (placeholder).
|
|
26
|
+
*
|
|
27
|
+
* In the future, this will implement a Nostr-based signaling layer for
|
|
28
|
+
* establishing peer connections through a Nostr relay.
|
|
29
|
+
*/
|
|
30
|
+
declare function connectNostr(_relay: string, _pubkey: string): Promise<PulsarClientConnection>;
|
|
31
|
+
//#endregion
|
|
32
|
+
export { type PulsarClientConnection, connectDirect, connectNostr };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
//#region ../core/credentials.ts
|
|
2
|
+
const PULSAR_UFRAG = "pulsar";
|
|
3
|
+
const PULSAR_PWD = "pulsarpulsarpulsarpuls";
|
|
4
|
+
const PULSAR_FINGERPRINT = "F1:85:10:8F:36:FF:58:D8:D0:4B:52:D7:ED:DC:5C:28:AE:7D:DB:54:0E:2A:DD:C7:C3:94:EA:A1:27:D0:4E:78";
|
|
5
|
+
//#endregion
|
|
6
|
+
//#region lib/connection/direct.ts
|
|
7
|
+
/**
|
|
8
|
+
* Connect to a remote Pulsar server in direct mode.
|
|
9
|
+
*
|
|
10
|
+
* Designed for browsers, using the native `RTCPeerConnection` API.
|
|
11
|
+
*
|
|
12
|
+
* @param host Server IP address
|
|
13
|
+
* @param port Server UDP port
|
|
14
|
+
* @returns A connected PulsarClientConnection with an open keepalive channel.
|
|
15
|
+
*/
|
|
16
|
+
async function connectDirect(host, port) {
|
|
17
|
+
const pc = new RTCPeerConnection();
|
|
18
|
+
const keepalive = pc.createDataChannel("keepalive", { ordered: true });
|
|
19
|
+
const offer = await pc.createOffer();
|
|
20
|
+
const mungedSdp = offer.sdp.replace(/^a=ice-ufrag:.*$/m, `a=ice-ufrag:${PULSAR_UFRAG}`).replace(/^a=ice-pwd:.*$/m, `a=ice-pwd:${PULSAR_PWD}`).replace(/^a=fingerprint:.*$/m, `a=fingerprint:sha-256 ${PULSAR_FINGERPRINT}`);
|
|
21
|
+
if (mungedSdp === offer.sdp) throw new Error("SDP munging failed – could not replace ICE credentials");
|
|
22
|
+
await pc.setLocalDescription({
|
|
23
|
+
type: "offer",
|
|
24
|
+
sdp: mungedSdp
|
|
25
|
+
});
|
|
26
|
+
const remoteSdp = [
|
|
27
|
+
"v=0",
|
|
28
|
+
"o=- 111 222 IN IP4 0.0.0.0",
|
|
29
|
+
"s=-",
|
|
30
|
+
"t=0 0",
|
|
31
|
+
`m=application ${port} UDP/DTLS/SCTP webrtc-datachannel`,
|
|
32
|
+
`c=IN IP4 ${host}`,
|
|
33
|
+
"a=mid:0",
|
|
34
|
+
`a=ice-ufrag:${PULSAR_UFRAG}`,
|
|
35
|
+
`a=ice-pwd:${PULSAR_PWD}`,
|
|
36
|
+
`a=fingerprint:sha-256 ${PULSAR_FINGERPRINT}`,
|
|
37
|
+
"a=setup:active",
|
|
38
|
+
"a=sctp-port:5000",
|
|
39
|
+
`a=candidate:1 1 UDP 2130706431 ${host} ${port} typ host`,
|
|
40
|
+
"a=end-of-candidates"
|
|
41
|
+
].join("\r\n");
|
|
42
|
+
await pc.setRemoteDescription({
|
|
43
|
+
type: "answer",
|
|
44
|
+
sdp: remoteSdp
|
|
45
|
+
});
|
|
46
|
+
await new Promise((resolve, reject) => {
|
|
47
|
+
const timeout = setTimeout(() => {
|
|
48
|
+
reject(/* @__PURE__ */ new Error("Connection timed out after 30s"));
|
|
49
|
+
}, 3e4);
|
|
50
|
+
pc.onconnectionstatechange = () => {
|
|
51
|
+
if (pc.connectionState === "connected") {
|
|
52
|
+
clearTimeout(timeout);
|
|
53
|
+
resolve();
|
|
54
|
+
} else if (pc.connectionState === "failed" || pc.connectionState === "disconnected") {
|
|
55
|
+
clearTimeout(timeout);
|
|
56
|
+
reject(/* @__PURE__ */ new Error(`Connection failed: ${pc.connectionState}`));
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
return {
|
|
61
|
+
keepalive,
|
|
62
|
+
pc,
|
|
63
|
+
async close() {
|
|
64
|
+
keepalive.close();
|
|
65
|
+
pc.close();
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
//#endregion
|
|
70
|
+
//#region lib/connection/nostr.ts
|
|
71
|
+
/**
|
|
72
|
+
* Connect via a Nostr relay (placeholder).
|
|
73
|
+
*
|
|
74
|
+
* In the future, this will implement a Nostr-based signaling layer for
|
|
75
|
+
* establishing peer connections through a Nostr relay.
|
|
76
|
+
*/
|
|
77
|
+
async function connectNostr(_relay, _pubkey) {
|
|
78
|
+
throw new Error("Nostr mode not yet implemented");
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
81
|
+
export { connectDirect, connectNostr };
|
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@abndnce/pulsar-client",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"homepage": "https://github.com/abndnce/pulsar",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./dist/index.mjs",
|
|
9
|
+
"./package.json": "./package.json"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist"
|
|
13
|
+
],
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"tsdown": "^0.22.0",
|
|
16
|
+
"typescript": "^6.0.3"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsdown",
|
|
20
|
+
"dev": "tsdown --watch",
|
|
21
|
+
"test": "vitest",
|
|
22
|
+
"typecheck": "tsc --noEmit"
|
|
23
|
+
}
|
|
24
|
+
}
|