@openlv/session 0.0.1-beta.2
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/LICENSE +165 -0
- package/README.md +3 -0
- package/dist/base.d.ts +35 -0
- package/dist/base.js +172 -0
- package/dist/base.js.map +1 -0
- package/dist/connection.d.ts +57 -0
- package/dist/connection.js +757 -0
- package/dist/connection.js.map +1 -0
- package/dist/encryption/asymmetric.d.ts +22 -0
- package/dist/encryption/asymmetric.js +90 -0
- package/dist/encryption/asymmetric.js.map +1 -0
- package/dist/encryption/asymmetric.test.d.ts +1 -0
- package/dist/encryption/asymmetric.test.js +38 -0
- package/dist/encryption/asymmetric.test.js.map +1 -0
- package/dist/encryption/base64.d.ts +2 -0
- package/dist/encryption/base64.js +16 -0
- package/dist/encryption/base64.js.map +1 -0
- package/dist/encryption/base64.test.d.ts +1 -0
- package/dist/encryption/base64.test.js +21 -0
- package/dist/encryption/base64.test.js.map +1 -0
- package/dist/encryption/handshake.d.ts +10 -0
- package/dist/encryption/handshake.js +37 -0
- package/dist/encryption/handshake.js.map +1 -0
- package/dist/encryption/handshake.test.d.ts +1 -0
- package/dist/encryption/handshake.test.js +16 -0
- package/dist/encryption/handshake.test.js.map +1 -0
- package/dist/encryption/hash.d.ts +13 -0
- package/dist/encryption/hash.js +32 -0
- package/dist/encryption/hash.js.map +1 -0
- package/dist/encryption/index.d.ts +4 -0
- package/dist/encryption/index.js +5 -0
- package/dist/encryption/index.js.map +1 -0
- package/dist/encryption/random.d.ts +8 -0
- package/dist/encryption/random.js +21 -0
- package/dist/encryption/random.js.map +1 -0
- package/dist/events.d.ts +4 -0
- package/dist/events.js +2 -0
- package/dist/events.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/index.test.d.ts +1 -0
- package/dist/index.test.js +41 -0
- package/dist/index.test.js.map +1 -0
- package/dist/messages/index.d.ts +11 -0
- package/dist/messages/index.js +2 -0
- package/dist/messages/index.js.map +1 -0
- package/dist/provider.d.ts +11 -0
- package/dist/provider.js +285 -0
- package/dist/provider.js.map +1 -0
- package/dist/session/index.d.ts +41 -0
- package/dist/session/index.js +99 -0
- package/dist/session/index.js.map +1 -0
- package/dist/session/index.test.d.ts +1 -0
- package/dist/session/index.test.js +32 -0
- package/dist/session/index.test.js.map +1 -0
- package/dist/signaling/base.d.ts +14 -0
- package/dist/signaling/base.js +2 -0
- package/dist/signaling/base.js.map +1 -0
- package/dist/signaling/index.d.ts +28 -0
- package/dist/signaling/index.js +152 -0
- package/dist/signaling/index.js.map +1 -0
- package/dist/signaling/index.test.d.ts +1 -0
- package/dist/signaling/index.test.js +62 -0
- package/dist/signaling/index.test.js.map +1 -0
- package/dist/signaling/messages/index.d.ts +48 -0
- package/dist/signaling/messages/index.js +2 -0
- package/dist/signaling/messages/index.js.map +1 -0
- package/dist/signaling/mqtt/index.d.ts +5 -0
- package/dist/signaling/mqtt/index.js +49 -0
- package/dist/signaling/mqtt/index.js.map +1 -0
- package/dist/signaling/mqtt.d.ts +31 -0
- package/dist/signaling/mqtt.js +152 -0
- package/dist/signaling/mqtt.js.map +1 -0
- package/dist/signaling/ntfy/index.d.ts +25 -0
- package/dist/signaling/ntfy/index.js +92 -0
- package/dist/signaling/ntfy/index.js.map +1 -0
- package/dist/signaling/ntfy/url.d.ts +12 -0
- package/dist/signaling/ntfy/url.js +37 -0
- package/dist/signaling/ntfy/url.js.map +1 -0
- package/dist/signaling/ntfy/url.test.d.ts +1 -0
- package/dist/signaling/ntfy/url.test.js +54 -0
- package/dist/signaling/ntfy/url.test.js.map +1 -0
- package/dist/transport/index.d.ts +11 -0
- package/dist/transport/index.js +8 -0
- package/dist/transport/index.js.map +1 -0
- package/dist/transport/webrtc/index.d.ts +1 -0
- package/dist/transport/webrtc/index.js +70 -0
- package/dist/transport/webrtc/index.js.map +1 -0
- package/dist/types.d.ts +56 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/encryption.d.ts +86 -0
- package/dist/utils/encryption.js +243 -0
- package/dist/utils/encryption.js.map +1 -0
- package/dist/utils/log.d.ts +1 -0
- package/dist/utils/log.js +2 -0
- package/dist/utils/log.js.map +1 -0
- package/dist/utils/url.d.ts +3 -0
- package/dist/utils/url.js +66 -0
- package/dist/utils/url.js.map +1 -0
- package/dist/utils/url.test.d.ts +1 -0
- package/dist/utils/url.test.js +29 -0
- package/dist/utils/url.test.js.map +1 -0
- package/package.json +65 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { EventEmitter } from 'eventemitter3';
|
|
2
|
+
import { match } from 'ts-pattern';
|
|
3
|
+
export const XR_PREFIX = 'x';
|
|
4
|
+
export const XR_H_PREFIX = 'h';
|
|
5
|
+
export const createSignalingLayer = (init) => {
|
|
6
|
+
// shared signaling layer logic goes here
|
|
7
|
+
return async ({ canEncrypt, encrypt, decrypt, rpDiscovered, k, publicKey, isHost, }) => {
|
|
8
|
+
// shared signaling layer logic goes here
|
|
9
|
+
const handshakeKey = k ? k : undefined;
|
|
10
|
+
let mode = 'disconnected';
|
|
11
|
+
const subscribeHandler = new EventEmitter();
|
|
12
|
+
const send = async (method, recipient, payload) => {
|
|
13
|
+
const prefix = match(method)
|
|
14
|
+
.with('handshake', () => XR_H_PREFIX)
|
|
15
|
+
.with('encrypted', () => XR_PREFIX)
|
|
16
|
+
.exhaustive();
|
|
17
|
+
const message = await match(method)
|
|
18
|
+
.with('handshake', () => {
|
|
19
|
+
if (!handshakeKey)
|
|
20
|
+
return;
|
|
21
|
+
return handshakeKey.encrypt(JSON.stringify(payload));
|
|
22
|
+
})
|
|
23
|
+
.with('encrypted', () => {
|
|
24
|
+
if (!canEncrypt())
|
|
25
|
+
return;
|
|
26
|
+
return encrypt(JSON.stringify(payload));
|
|
27
|
+
})
|
|
28
|
+
.exhaustive();
|
|
29
|
+
await init.publish(prefix + recipient + message);
|
|
30
|
+
};
|
|
31
|
+
const handleReceive = async (payload) => {
|
|
32
|
+
const prefix = payload.slice(0, XR_H_PREFIX.length);
|
|
33
|
+
const recipient = payload.slice(XR_H_PREFIX.length, XR_H_PREFIX.length + 1);
|
|
34
|
+
const body = payload.slice(XR_PREFIX.length + 1);
|
|
35
|
+
const isRecipient = (isHost ? 'h' : 'c') === recipient;
|
|
36
|
+
console.log('isRecipient', isRecipient);
|
|
37
|
+
if (!isRecipient)
|
|
38
|
+
return;
|
|
39
|
+
await match({ prefix, body })
|
|
40
|
+
.with({ prefix: XR_H_PREFIX }, async () => {
|
|
41
|
+
if (!handshakeKey)
|
|
42
|
+
return;
|
|
43
|
+
const message = await handshakeKey.decrypt(body);
|
|
44
|
+
const msg = JSON.parse(message);
|
|
45
|
+
await match(msg)
|
|
46
|
+
.with({ type: 'flash' }, async () => {
|
|
47
|
+
if (!isHost)
|
|
48
|
+
return;
|
|
49
|
+
mode = 'handshaking';
|
|
50
|
+
await send('handshake', 'c', {
|
|
51
|
+
type: 'pubkey',
|
|
52
|
+
payload: {
|
|
53
|
+
publicKey: publicKey.toString(),
|
|
54
|
+
},
|
|
55
|
+
timestamp: Date.now(),
|
|
56
|
+
});
|
|
57
|
+
})
|
|
58
|
+
.with({ type: 'pubkey' }, async ({ payload }) => {
|
|
59
|
+
if (isHost)
|
|
60
|
+
return;
|
|
61
|
+
await rpDiscovered(payload.publicKey);
|
|
62
|
+
mode = 'handshake-closed';
|
|
63
|
+
return await send('encrypted', 'h', {
|
|
64
|
+
type: 'pubkey',
|
|
65
|
+
payload: {
|
|
66
|
+
publicKey: publicKey.toString(),
|
|
67
|
+
},
|
|
68
|
+
timestamp: Date.now(),
|
|
69
|
+
});
|
|
70
|
+
})
|
|
71
|
+
.otherwise(() => {
|
|
72
|
+
console.log('Received invalid message H', msg);
|
|
73
|
+
});
|
|
74
|
+
})
|
|
75
|
+
.with({ prefix: XR_PREFIX }, async () => {
|
|
76
|
+
const message = await decrypt(body);
|
|
77
|
+
const msg = JSON.parse(message);
|
|
78
|
+
await match(msg)
|
|
79
|
+
.with({ type: 'pubkey' }, async ({ payload }) => {
|
|
80
|
+
if (!isHost)
|
|
81
|
+
return;
|
|
82
|
+
await rpDiscovered(payload.publicKey);
|
|
83
|
+
mode = 'xr-encrypted';
|
|
84
|
+
return await send('encrypted', 'c', {
|
|
85
|
+
type: 'ack',
|
|
86
|
+
payload: undefined,
|
|
87
|
+
timestamp: Date.now(),
|
|
88
|
+
});
|
|
89
|
+
})
|
|
90
|
+
.with({ type: 'ack' }, async () => {
|
|
91
|
+
if (isHost)
|
|
92
|
+
return;
|
|
93
|
+
mode = 'xr-encrypted';
|
|
94
|
+
})
|
|
95
|
+
.with({ type: 'data' }, async () => subscribeHandler.emit('message', msg.payload))
|
|
96
|
+
.otherwise(() => {
|
|
97
|
+
console.log('Received invalid message X', msg);
|
|
98
|
+
});
|
|
99
|
+
})
|
|
100
|
+
.otherwise(() => {
|
|
101
|
+
console.log('Received invalid message', payload);
|
|
102
|
+
});
|
|
103
|
+
};
|
|
104
|
+
return {
|
|
105
|
+
type: init.type,
|
|
106
|
+
async setup() {
|
|
107
|
+
mode = 'connecting';
|
|
108
|
+
await init.setup();
|
|
109
|
+
if (!canEncrypt()) {
|
|
110
|
+
if (!isHost) {
|
|
111
|
+
mode = 'handshaking';
|
|
112
|
+
await send('handshake', 'h', {
|
|
113
|
+
type: 'flash',
|
|
114
|
+
payload: {},
|
|
115
|
+
timestamp: Date.now(),
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
if (isHost) {
|
|
119
|
+
mode = 'handshake-open';
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
await init.subscribe(handleReceive);
|
|
123
|
+
},
|
|
124
|
+
async teardown() {
|
|
125
|
+
return await init.teardown();
|
|
126
|
+
},
|
|
127
|
+
send(message) {
|
|
128
|
+
if (!canEncrypt()) {
|
|
129
|
+
return Promise.reject(new Error('Cannot encrypt message before keys are exchanged'));
|
|
130
|
+
}
|
|
131
|
+
const them = isHost ? 'c' : 'h';
|
|
132
|
+
return send('encrypted', them, {
|
|
133
|
+
type: 'data',
|
|
134
|
+
payload: message,
|
|
135
|
+
timestamp: Date.now(),
|
|
136
|
+
});
|
|
137
|
+
},
|
|
138
|
+
/**
|
|
139
|
+
* Base Signaling Layer Subscriber Handler
|
|
140
|
+
*
|
|
141
|
+
* This handler is responsible for decryption of Handshake & XR Encrypted Messages
|
|
142
|
+
*/
|
|
143
|
+
subscribe(handler) {
|
|
144
|
+
subscribeHandler.on('message', handler);
|
|
145
|
+
},
|
|
146
|
+
getState() {
|
|
147
|
+
return { state: mode };
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
};
|
|
151
|
+
};
|
|
152
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/signaling/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { generateKeyPair } from '../encryption/asymmetric.js';
|
|
3
|
+
import { mqtt } from './mqtt/index.js';
|
|
4
|
+
import { ntfy } from './ntfy/index.js';
|
|
5
|
+
const hKey = 'test';
|
|
6
|
+
const layers = [
|
|
7
|
+
[mqtt, { topic: 'mytesttopic1111', url: 'wss://test.mosquitto.org:8081/mqtt' }],
|
|
8
|
+
[ntfy, { topic: 'mytesttopic1111', url: 'https://ntfy.sh/' }],
|
|
9
|
+
];
|
|
10
|
+
describe('Signaling layers', () => {
|
|
11
|
+
layers.forEach(([layer, props]) => {
|
|
12
|
+
const layername = layer['__name'];
|
|
13
|
+
it(`should create a signaling layer for ${layername} with encryption`, async () => {
|
|
14
|
+
const { encryptionKey: publicKey, decryptionKey } = await generateKeyPair();
|
|
15
|
+
const h = hKey;
|
|
16
|
+
const signalingLayer = await layer(props);
|
|
17
|
+
const signalA = await signalingLayer({
|
|
18
|
+
h,
|
|
19
|
+
rpDiscovered: (v) => console.log('rpKey', v),
|
|
20
|
+
canEncrypt: () => true,
|
|
21
|
+
encrypt: publicKey.encrypt,
|
|
22
|
+
decrypt: decryptionKey.decrypt,
|
|
23
|
+
publicKey,
|
|
24
|
+
isHost: true,
|
|
25
|
+
});
|
|
26
|
+
const signalB = await signalingLayer({
|
|
27
|
+
h,
|
|
28
|
+
rpDiscovered: (v) => console.log('rpKey', v),
|
|
29
|
+
canEncrypt: () => true,
|
|
30
|
+
encrypt: publicKey.encrypt,
|
|
31
|
+
decrypt: decryptionKey.decrypt,
|
|
32
|
+
publicKey,
|
|
33
|
+
isHost: false,
|
|
34
|
+
});
|
|
35
|
+
expect(signalA).toBeDefined();
|
|
36
|
+
expect(signalB).toBeDefined();
|
|
37
|
+
await signalA.setup();
|
|
38
|
+
await signalB.setup();
|
|
39
|
+
const messageReceivedA = new Promise((resolve) => {
|
|
40
|
+
signalA.subscribe((message) => {
|
|
41
|
+
console.log('messageReceivedA', message);
|
|
42
|
+
expect(message).toEqual({ data: 'test2' });
|
|
43
|
+
resolve();
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
const messageReceivedB = new Promise((resolve) => {
|
|
47
|
+
signalB.subscribe((message) => {
|
|
48
|
+
console.log('messageReceivedB', message);
|
|
49
|
+
expect(message).toEqual({ data: 'test1' });
|
|
50
|
+
resolve();
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
await signalA.send({ data: 'test1' });
|
|
54
|
+
await messageReceivedB;
|
|
55
|
+
await signalB.send({ data: 'test2' });
|
|
56
|
+
await messageReceivedA;
|
|
57
|
+
await signalA.teardown();
|
|
58
|
+
await signalB.teardown();
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
//# sourceMappingURL=index.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.test.js","sourceRoot":"","sources":["../../src/signaling/index.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export type SignalMessageBase<T extends string, P> = {
|
|
2
|
+
type: T;
|
|
3
|
+
payload: P;
|
|
4
|
+
timestamp: number;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Heartbeat message
|
|
8
|
+
*/
|
|
9
|
+
export type SignalMessagePing = SignalMessageBase<'ping', {
|
|
10
|
+
mailboxId: string;
|
|
11
|
+
}>;
|
|
12
|
+
/**
|
|
13
|
+
* Flash message
|
|
14
|
+
* Sent to initiate handshake by non-host
|
|
15
|
+
*/
|
|
16
|
+
export type SignalMessageFlash = SignalMessageBase<'flash', object>;
|
|
17
|
+
export type SignalMessagePubkey = SignalMessageBase<'pubkey', {
|
|
18
|
+
publicKey: string;
|
|
19
|
+
dAppInfo?: {
|
|
20
|
+
name: string;
|
|
21
|
+
url: string;
|
|
22
|
+
icon: string;
|
|
23
|
+
};
|
|
24
|
+
}>;
|
|
25
|
+
export type SignalMessageAck = SignalMessageBase<'ack', undefined>;
|
|
26
|
+
export type SignalMessageHello = SignalMessageBase<'hello', {
|
|
27
|
+
publicKey: string;
|
|
28
|
+
walletInfo: {
|
|
29
|
+
name: string;
|
|
30
|
+
version: string;
|
|
31
|
+
icon: string;
|
|
32
|
+
};
|
|
33
|
+
}>;
|
|
34
|
+
export type SignalMessageWebRTCOffer = SignalMessageBase<'webrtc-offer', {
|
|
35
|
+
type: 'offer';
|
|
36
|
+
sdp: string;
|
|
37
|
+
}>;
|
|
38
|
+
export type SignalMessageWebRTCAnswer = SignalMessageBase<'webrtc-answer', {
|
|
39
|
+
type: 'answer';
|
|
40
|
+
sdp: string;
|
|
41
|
+
}>;
|
|
42
|
+
export type SignalMessageICECandidate = SignalMessageBase<'ice-candidate', {
|
|
43
|
+
candidate: string;
|
|
44
|
+
sdpMid: string;
|
|
45
|
+
sdpMLineIndex: number;
|
|
46
|
+
}>;
|
|
47
|
+
export type SignalMessageData = SignalMessageBase<'data', object>;
|
|
48
|
+
export type SignalMessage = SignalMessagePing | SignalMessageFlash | SignalMessagePubkey | SignalMessageAck | SignalMessageHello | SignalMessageWebRTCOffer | SignalMessageWebRTCAnswer | SignalMessageICECandidate | SignalMessageData;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/signaling/messages/index.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { connect } from 'mqtt';
|
|
2
|
+
import { createSignalingLayer } from '../index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Signaling over MQTT
|
|
5
|
+
*/
|
|
6
|
+
export const mqtt = ({ url = 'wss://test.mosquitto.org:8081/mqtt', topic, }) => {
|
|
7
|
+
let connection;
|
|
8
|
+
return createSignalingLayer({
|
|
9
|
+
type: 'mqtt',
|
|
10
|
+
setup() {
|
|
11
|
+
connection = connect(url);
|
|
12
|
+
},
|
|
13
|
+
teardown() {
|
|
14
|
+
connection?.end();
|
|
15
|
+
connection = undefined;
|
|
16
|
+
},
|
|
17
|
+
async publish(payload) {
|
|
18
|
+
if (!connection) {
|
|
19
|
+
throw new Error('MQTT: No connection to publish to');
|
|
20
|
+
}
|
|
21
|
+
const encoded = new TextEncoder().encode(payload);
|
|
22
|
+
connection?.publish(topic, Buffer.from(encoded));
|
|
23
|
+
},
|
|
24
|
+
async subscribe(handler) {
|
|
25
|
+
if (!connection) {
|
|
26
|
+
throw new Error('MQTT: No connection to subscribe to');
|
|
27
|
+
}
|
|
28
|
+
console.log('MQTT: Subscribing to topic', topic);
|
|
29
|
+
connection.on('message', (receivedTopic, message) => {
|
|
30
|
+
console.log('MQTT: Received message on topic', topic);
|
|
31
|
+
if (receivedTopic !== topic)
|
|
32
|
+
return;
|
|
33
|
+
const decoded = new TextDecoder().decode(message);
|
|
34
|
+
handler(decoded);
|
|
35
|
+
});
|
|
36
|
+
return new Promise((resolve, reject) => {
|
|
37
|
+
connection?.subscribe(topic, (err) => {
|
|
38
|
+
if (err) {
|
|
39
|
+
console.error('MQTT: Error subscribing to topic', err);
|
|
40
|
+
reject(err);
|
|
41
|
+
}
|
|
42
|
+
resolve();
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
Object.defineProperty(mqtt, '__name', { value: 'mqtt' });
|
|
49
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/signaling/mqtt/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAG/B,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAEnD;;GAEG;AACH,MAAM,CAAC,MAAM,IAAI,GAAuB,CAAC,EACrC,GAAG,GAAG,oCAAoC,EAC1C,KAAK,GACc,EAAE,EAAE;IACvB,IAAI,UAAkC,CAAC;IAEvC,OAAO,oBAAoB,CAAC;QACxB,IAAI,EAAE,MAAM;QACZ,KAAK;YACD,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC9B,CAAC;QACD,QAAQ;YACJ,UAAU,EAAE,GAAG,EAAE,CAAC;YAClB,UAAU,GAAG,SAAS,CAAC;QAC3B,CAAC;QACD,KAAK,CAAC,OAAO,CAAC,OAAO;YACjB,IAAI,CAAC,UAAU,EAAE,CAAC;gBACd,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;YACzD,CAAC;YAED,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAElD,UAAU,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;QACrD,CAAC;QACD,KAAK,CAAC,SAAS,CAAC,OAAO;YACnB,IAAI,CAAC,UAAU,EAAE,CAAC;gBACd,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;YAC3D,CAAC;YAED,OAAO,CAAC,GAAG,CAAC,4BAA4B,EAAE,KAAK,CAAC,CAAC;YAEjD,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,aAAa,EAAE,OAAO,EAAE,EAAE;gBAChD,OAAO,CAAC,GAAG,CAAC,iCAAiC,EAAE,KAAK,CAAC,CAAC;gBAEtD,IAAI,aAAa,KAAK,KAAK;oBAAE,OAAO;gBAEpC,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;gBAElD,OAAO,CAAC,OAAO,CAAC,CAAC;YACrB,CAAC,CAAC,CAAC;YAEH,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBACnC,UAAU,EAAE,SAAS,CAAC,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE;oBACjC,IAAI,GAAG,EAAE,CAAC;wBACN,OAAO,CAAC,KAAK,CAAC,kCAAkC,EAAE,GAAG,CAAC,CAAC;wBACvD,MAAM,CAAC,GAAG,CAAC,CAAC;oBAChB,CAAC;oBAED,OAAO,EAAE,CAAC;gBACd,CAAC,CAAC,CAAC;YACP,CAAC,CAAC,CAAC;QACP,CAAC;KACJ,CAAC,CAAC;AACP,CAAC,CAAC;AAEF,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export type SignalingMessage = {
|
|
2
|
+
type: 'ping' | 'pubkey' | 'hello' | 'webrtc-offer' | 'webrtc-answer' | 'ice-candidate' | 'data';
|
|
3
|
+
payload: any;
|
|
4
|
+
sessionId: string;
|
|
5
|
+
timestamp: number;
|
|
6
|
+
senderId: string;
|
|
7
|
+
};
|
|
8
|
+
export type SignalingConfig = {
|
|
9
|
+
mqttUrl?: string;
|
|
10
|
+
protocol?: 'mqtt' | 'waku' | 'nostr';
|
|
11
|
+
};
|
|
12
|
+
export type MessageHandler = (message: SignalingMessage) => void;
|
|
13
|
+
export declare const contentTopic: ({ sessionId }: {
|
|
14
|
+
sessionId: string;
|
|
15
|
+
}) => string;
|
|
16
|
+
export declare class MQTTSignaling {
|
|
17
|
+
private client;
|
|
18
|
+
private sessionId?;
|
|
19
|
+
private messageHandler?;
|
|
20
|
+
private isConnected;
|
|
21
|
+
private mqttUrl;
|
|
22
|
+
constructor(config?: SignalingConfig);
|
|
23
|
+
private setupHandlers;
|
|
24
|
+
private handleMessage;
|
|
25
|
+
subscribe(sessionId: string, handler: MessageHandler): Promise<void>;
|
|
26
|
+
publish(message: SignalingMessage): Promise<void>;
|
|
27
|
+
waitForConnection(): Promise<void>;
|
|
28
|
+
reconnect(): void;
|
|
29
|
+
disconnect(): void;
|
|
30
|
+
get connected(): boolean;
|
|
31
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import mqtt from 'mqtt';
|
|
2
|
+
export const contentTopic = ({ sessionId }) => `/openlv/session/${sessionId}`;
|
|
3
|
+
export class MQTTSignaling {
|
|
4
|
+
constructor(config) {
|
|
5
|
+
Object.defineProperty(this, "client", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
configurable: true,
|
|
8
|
+
writable: true,
|
|
9
|
+
value: void 0
|
|
10
|
+
});
|
|
11
|
+
Object.defineProperty(this, "sessionId", {
|
|
12
|
+
enumerable: true,
|
|
13
|
+
configurable: true,
|
|
14
|
+
writable: true,
|
|
15
|
+
value: void 0
|
|
16
|
+
});
|
|
17
|
+
Object.defineProperty(this, "messageHandler", {
|
|
18
|
+
enumerable: true,
|
|
19
|
+
configurable: true,
|
|
20
|
+
writable: true,
|
|
21
|
+
value: void 0
|
|
22
|
+
});
|
|
23
|
+
Object.defineProperty(this, "isConnected", {
|
|
24
|
+
enumerable: true,
|
|
25
|
+
configurable: true,
|
|
26
|
+
writable: true,
|
|
27
|
+
value: false
|
|
28
|
+
});
|
|
29
|
+
Object.defineProperty(this, "mqttUrl", {
|
|
30
|
+
enumerable: true,
|
|
31
|
+
configurable: true,
|
|
32
|
+
writable: true,
|
|
33
|
+
value: void 0
|
|
34
|
+
});
|
|
35
|
+
this.mqttUrl = config?.mqttUrl ?? 'wss://test.mosquitto.org:8081/mqtt';
|
|
36
|
+
this.client = mqtt.connect(this.mqttUrl);
|
|
37
|
+
this.setupHandlers();
|
|
38
|
+
}
|
|
39
|
+
setupHandlers() {
|
|
40
|
+
this.client.on('connect', () => {
|
|
41
|
+
console.log('MQTT: Connected to broker');
|
|
42
|
+
this.isConnected = true;
|
|
43
|
+
});
|
|
44
|
+
this.client.on('error', (error) => {
|
|
45
|
+
console.error('MQTT: Connection error:', error);
|
|
46
|
+
this.isConnected = false;
|
|
47
|
+
});
|
|
48
|
+
this.client.on('disconnect', () => {
|
|
49
|
+
console.log('MQTT: Disconnected from broker');
|
|
50
|
+
this.isConnected = false;
|
|
51
|
+
});
|
|
52
|
+
this.client.on('message', (topic, message) => {
|
|
53
|
+
if (this.sessionId && topic === contentTopic({ sessionId: this.sessionId })) {
|
|
54
|
+
console.log('MQTT: Received message on topic', topic);
|
|
55
|
+
this.handleMessage(message.toString());
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
handleMessage(messageStr) {
|
|
60
|
+
try {
|
|
61
|
+
const message = JSON.parse(messageStr);
|
|
62
|
+
console.log('MQTT: Processing message:', message.type);
|
|
63
|
+
if (this.messageHandler) {
|
|
64
|
+
this.messageHandler(message);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
console.error('MQTT: Failed to parse message:', error);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async subscribe(sessionId, handler) {
|
|
72
|
+
this.sessionId = sessionId;
|
|
73
|
+
this.messageHandler = handler;
|
|
74
|
+
const topic = contentTopic({ sessionId });
|
|
75
|
+
return new Promise((resolve, reject) => {
|
|
76
|
+
this.client.subscribe(topic, (error) => {
|
|
77
|
+
if (error) {
|
|
78
|
+
console.error('MQTT: Error subscribing to topic:', error);
|
|
79
|
+
reject(error);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
console.log('MQTT: Successfully subscribed to topic:', topic);
|
|
83
|
+
resolve();
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
async publish(message) {
|
|
89
|
+
if (!this.sessionId || !this.isConnected) {
|
|
90
|
+
throw new Error('MQTT: Not connected or no session ID');
|
|
91
|
+
}
|
|
92
|
+
const topic = contentTopic({ sessionId: this.sessionId });
|
|
93
|
+
const payload = JSON.stringify(message);
|
|
94
|
+
return new Promise((resolve, reject) => {
|
|
95
|
+
this.client.publish(topic, payload, (error) => {
|
|
96
|
+
if (error) {
|
|
97
|
+
console.error('MQTT: Error publishing message:', error);
|
|
98
|
+
reject(error);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
console.log('MQTT: Message sent successfully:', message.type);
|
|
102
|
+
resolve();
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
async waitForConnection() {
|
|
108
|
+
if (this.isConnected) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
return new Promise((resolve, reject) => {
|
|
112
|
+
const timeout = setTimeout(() => {
|
|
113
|
+
reject(new Error('MQTT: Connection timeout'));
|
|
114
|
+
}, 10000);
|
|
115
|
+
const checkConnection = () => {
|
|
116
|
+
if (this.isConnected) {
|
|
117
|
+
clearTimeout(timeout);
|
|
118
|
+
resolve();
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
setTimeout(checkConnection, 100);
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
checkConnection();
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
reconnect() {
|
|
128
|
+
if (!this.isConnected) {
|
|
129
|
+
console.log('MQTT: Reconnecting...');
|
|
130
|
+
this.client = mqtt.connect(this.mqttUrl);
|
|
131
|
+
this.setupHandlers();
|
|
132
|
+
// Re-subscribe if we have a session
|
|
133
|
+
if (this.sessionId && this.messageHandler) {
|
|
134
|
+
this.client.on('connect', () => {
|
|
135
|
+
this.subscribe(this.sessionId, this.messageHandler);
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
disconnect() {
|
|
141
|
+
if (this.client) {
|
|
142
|
+
this.client.end(true);
|
|
143
|
+
}
|
|
144
|
+
this.isConnected = false;
|
|
145
|
+
this.sessionId = undefined;
|
|
146
|
+
this.messageHandler = undefined;
|
|
147
|
+
}
|
|
148
|
+
get connected() {
|
|
149
|
+
return this.isConnected;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=mqtt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mqtt.js","sourceRoot":"","sources":["../../src/signaling/mqtt.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { SignalLayerCreator } from '../base.js';
|
|
2
|
+
export type NtfyMessage = {
|
|
3
|
+
id: string;
|
|
4
|
+
time: number;
|
|
5
|
+
expires: number;
|
|
6
|
+
event: string;
|
|
7
|
+
topic: string;
|
|
8
|
+
message: string;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Signaling over NTFY
|
|
12
|
+
*
|
|
13
|
+
* Uses HTTP Post for publishing
|
|
14
|
+
* Uses WebSocket for subscribing
|
|
15
|
+
*
|
|
16
|
+
* URL format supports the ?auth= parameter
|
|
17
|
+
* example: https://ntfy.sh/mytopic?auth=mytoken
|
|
18
|
+
*
|
|
19
|
+
* The same goes for apprise ntfy urls
|
|
20
|
+
* example: ntfy://{token}@{hostname}/{topic}
|
|
21
|
+
* example: ntfy://{user}:{password}@{hostname}/{topic}
|
|
22
|
+
* https variant: ntfys://{user}:{password}@{hostname}/{topic}
|
|
23
|
+
*
|
|
24
|
+
*/
|
|
25
|
+
export declare const ntfy: SignalLayerCreator;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { createSignalingLayer } from '../index.js';
|
|
2
|
+
import { parseNtfyUrl } from './url.js';
|
|
3
|
+
/**
|
|
4
|
+
* Signaling over NTFY
|
|
5
|
+
*
|
|
6
|
+
* Uses HTTP Post for publishing
|
|
7
|
+
* Uses WebSocket for subscribing
|
|
8
|
+
*
|
|
9
|
+
* URL format supports the ?auth= parameter
|
|
10
|
+
* example: https://ntfy.sh/mytopic?auth=mytoken
|
|
11
|
+
*
|
|
12
|
+
* The same goes for apprise ntfy urls
|
|
13
|
+
* example: ntfy://{token}@{hostname}/{topic}
|
|
14
|
+
* example: ntfy://{user}:{password}@{hostname}/{topic}
|
|
15
|
+
* https variant: ntfys://{user}:{password}@{hostname}/{topic}
|
|
16
|
+
*
|
|
17
|
+
*/
|
|
18
|
+
export const ntfy = ({ topic, url }) => {
|
|
19
|
+
let connection = null;
|
|
20
|
+
const connectionInfo = parseNtfyUrl(url);
|
|
21
|
+
const wsProtocol = connectionInfo.protocol === 'https'
|
|
22
|
+
? 'wss'
|
|
23
|
+
: connectionInfo.protocol === 'http'
|
|
24
|
+
? 'ws'
|
|
25
|
+
: connectionInfo.protocol;
|
|
26
|
+
return createSignalingLayer({
|
|
27
|
+
type: 'ntfy',
|
|
28
|
+
async setup() {
|
|
29
|
+
console.log('NTFY: Setting up');
|
|
30
|
+
const wsUrl = wsProtocol +
|
|
31
|
+
'://' +
|
|
32
|
+
connectionInfo.host +
|
|
33
|
+
'/' +
|
|
34
|
+
topic +
|
|
35
|
+
'/ws' +
|
|
36
|
+
(connectionInfo.parameters || '');
|
|
37
|
+
console.log('NTFY: Connecting to WebSocket', wsUrl);
|
|
38
|
+
connection = new WebSocket(wsUrl);
|
|
39
|
+
connection.onerror = (event) => {
|
|
40
|
+
console.error('NTFY: Error on WebSocket', event);
|
|
41
|
+
};
|
|
42
|
+
connection.onclose = (event) => {
|
|
43
|
+
console.error('NTFY: Closed WebSocket', event);
|
|
44
|
+
};
|
|
45
|
+
const awaitOpenConfirm = new Promise((resolve) => {
|
|
46
|
+
connection.onmessage = (event) => {
|
|
47
|
+
console.log('NTFY: Received message:', event.data);
|
|
48
|
+
const data = JSON.parse(event.data);
|
|
49
|
+
if (data.event == 'open') {
|
|
50
|
+
resolve();
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
});
|
|
54
|
+
const awaitOpen = new Promise((resolve) => {
|
|
55
|
+
connection.onopen = () => {
|
|
56
|
+
console.log('NTFY: Connected to WebSocket');
|
|
57
|
+
resolve();
|
|
58
|
+
};
|
|
59
|
+
});
|
|
60
|
+
await awaitOpen;
|
|
61
|
+
await awaitOpenConfirm;
|
|
62
|
+
},
|
|
63
|
+
teardown() {
|
|
64
|
+
connection?.close();
|
|
65
|
+
},
|
|
66
|
+
async publish(body) {
|
|
67
|
+
const headers = { 'Content-Type': 'application/json' };
|
|
68
|
+
// TODO: Add response handling
|
|
69
|
+
// @ts-expect-error
|
|
70
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, unused-imports/no-unused-vars
|
|
71
|
+
const response = await fetch(connectionInfo.protocol + '://' + connectionInfo.host + '/' + topic, { method: 'POST', body, headers });
|
|
72
|
+
},
|
|
73
|
+
async subscribe(handler) {
|
|
74
|
+
if (!connection) {
|
|
75
|
+
console.error('NTFY: No connection to subscribe to');
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
connection.onmessage = (event) => {
|
|
79
|
+
console.log('NTFY: Received message:', event.data);
|
|
80
|
+
const data = JSON.parse(event.data);
|
|
81
|
+
console.log('NTFY: Message:', data);
|
|
82
|
+
if (data.event !== 'message') {
|
|
83
|
+
console.log('NTFY: Not a message event', data.event);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
handler(data.message);
|
|
87
|
+
};
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
};
|
|
91
|
+
Object.defineProperty(ntfy, '__name', { value: 'ntfy' });
|
|
92
|
+
//# sourceMappingURL=index.js.map
|