@yz-social/webrtc 0.1.2 → 0.1.4
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/browser-wrtc.js +1 -0
- package/index.js +6 -4
- package/package.json +7 -6
- package/spec/portal.js +2 -0
- package/spec/test.html +1 -1
- package/spec/webrtcSpec.js +13 -9
- package/routes/index.js +0 -27
package/browser-wrtc.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default globalThis;
|
package/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//import wrtc from '#wrtc';
|
|
1
|
+
// import wrtc from '#wrtc';
|
|
2
2
|
const wrtc = (typeof(process) === 'undefined') ? globalThis : (await import('#wrtc')).default;
|
|
3
3
|
|
|
4
4
|
export class WebRTC {
|
|
@@ -80,7 +80,9 @@ export class WebRTC {
|
|
|
80
80
|
// Do not try to close or wait for data channels. It confuses Safari.
|
|
81
81
|
const pc = this.pc;
|
|
82
82
|
if (!pc) return null;
|
|
83
|
-
pc.
|
|
83
|
+
const state = pc.connectionState;
|
|
84
|
+
if (state === 'connected' || state === 'failed') pc.close();
|
|
85
|
+
else this.flog("WebRTC close in unexpected state", state);
|
|
84
86
|
this.closed.resolve(pc); // We do not automatically receive 'connectionstatechange' when our side explicitly closes. (Only if the other does.)
|
|
85
87
|
this.cleanup();
|
|
86
88
|
return this.closed;
|
|
@@ -233,7 +235,8 @@ export class WebRTC {
|
|
|
233
235
|
this.log('setupChannel:', label, dc.id, readyState, 'negotiated:', dc.negotiated);
|
|
234
236
|
const kind = isTheirs ? 'Theirs' : 'Ours';
|
|
235
237
|
dc.webrtc = this;
|
|
236
|
-
dc.onopen =
|
|
238
|
+
dc.onopen = () => { // Idempotent (except for logging), if we do not bash dataChannePromises[label] multiple times.
|
|
239
|
+
dc.onopen = null;
|
|
237
240
|
this.log('channel onopen:', label, dc.id, readyState, 'negotiated:', dc.negotiated);
|
|
238
241
|
this[this.restrictablePromiseKey()][label]?.resolve(dc);
|
|
239
242
|
this[this.restrictablePromiseKey(kind)][label]?.resolve(dc);
|
|
@@ -246,7 +249,6 @@ export class WebRTC {
|
|
|
246
249
|
// This our chance to setupChannel, just as if we had called createChannel
|
|
247
250
|
this.log('ondatachannel:', dc.label, dc.id, dc.readyState, dc.negotiated);
|
|
248
251
|
this.setupChannel(dc);
|
|
249
|
-
dc.onopen(); // It had been opened before we setup, so invoke handler now.
|
|
250
252
|
}
|
|
251
253
|
channelId = 128; // Non-negotiated channel.id get assigned at open by the peer, starting with 0. This avoids conflicts.
|
|
252
254
|
createChannel(name = 'data', {negotiated = false, id = this.channelId++, ...options} = {}) { // Explicitly create channel and set it up.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yz-social/webrtc",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Streamlined portable webrtc management of p2p and client2server.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"webrtc",
|
|
@@ -12,22 +12,23 @@
|
|
|
12
12
|
"imports": {
|
|
13
13
|
"#wrtc": {
|
|
14
14
|
"node": "@roamhq/wrtc",
|
|
15
|
-
"default": "./
|
|
15
|
+
"default": "./browser-wrtc.js"
|
|
16
16
|
}
|
|
17
17
|
},
|
|
18
18
|
"exports": {
|
|
19
|
-
".": "./index.js"
|
|
20
|
-
"./router": "./routes/index.js"
|
|
19
|
+
".": "./index.js"
|
|
21
20
|
},
|
|
22
21
|
"scripts": {
|
|
23
|
-
"test": "npx jasmine"
|
|
22
|
+
"test": "npx jasmine",
|
|
23
|
+
"testServer": "node spec/portal.js"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@roamhq/wrtc": "^0.9.1"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"express": "^5.2.1",
|
|
30
|
-
"jasmine": "^5.12.0"
|
|
30
|
+
"jasmine": "^5.12.0",
|
|
31
|
+
"morgan": "^1.10.1"
|
|
31
32
|
},
|
|
32
33
|
"repository": {
|
|
33
34
|
"type": "git",
|
package/spec/portal.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import process from 'node:process';
|
|
2
2
|
import cluster from 'node:cluster';
|
|
3
3
|
import express from 'express';
|
|
4
|
+
import logger from 'morgan';
|
|
4
5
|
import path from 'path';
|
|
5
6
|
import { fileURLToPath } from 'url';
|
|
6
7
|
import { WebRTC } from '../index.js';
|
|
@@ -26,6 +27,7 @@ if (cluster.isPrimary) { // Parent process with portal webserver through which c
|
|
|
26
27
|
});
|
|
27
28
|
}
|
|
28
29
|
const workers = Object.values(cluster.workers);
|
|
30
|
+
app.use(logger(':date[iso] :status :method :url :res[content-length] - :response-time ms'));
|
|
29
31
|
app.use(express.json());
|
|
30
32
|
app.use(express.static(path.resolve(__dirname, '..'))); // Serve files needed for testing browsers.
|
|
31
33
|
app.post('/join/:to', async (req, res, next) => { // Handler for JSON POST requests that provide an array of signals and get signals back.
|
package/spec/test.html
CHANGED
package/spec/webrtcSpec.js
CHANGED
|
@@ -5,7 +5,7 @@ describe("WebRTC", function () {
|
|
|
5
5
|
const isBrowser = typeof(process) === 'undefined';
|
|
6
6
|
let connections = [];
|
|
7
7
|
describe("direct in-process signaling", function () {
|
|
8
|
-
function makePair({debug = false, delay = 0, index = 0} = {}) {
|
|
8
|
+
async function makePair({debug = false, delay = 0, index = 0} = {}) {
|
|
9
9
|
//const configuration = { iceServers: [] };
|
|
10
10
|
const configuration = { iceServers: WebRTC.iceServers };
|
|
11
11
|
const A = new WebRTC({name: `A (impolite) ${index}`, polite: false, debug, configuration});
|
|
@@ -64,7 +64,7 @@ describe("WebRTC", function () {
|
|
|
64
64
|
promises.push(B.getDataChannelPromise('data', 'Theirs').then(sendingSetup));
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
const direct =
|
|
67
|
+
const direct = false; // Does signal work direct/one-sided to the other? False makes a request that waits for a response.
|
|
68
68
|
if (direct) {
|
|
69
69
|
A.signal = message => B.onSignal(message);
|
|
70
70
|
B.signal = message => A.onSignal(message);
|
|
@@ -72,6 +72,7 @@ describe("WebRTC", function () {
|
|
|
72
72
|
A.transferSignals = messages => B.respond(messages);
|
|
73
73
|
B.transferSignals = messages => A.respond(messages);
|
|
74
74
|
}
|
|
75
|
+
await WebRTC.delay(1); // TODO: This is crazy, but without out, the FIRST connection in chrome hangs!
|
|
75
76
|
return connections[index] = {A, B, bothOpen: Promise.all(promises)};
|
|
76
77
|
}
|
|
77
78
|
function standardBehavior(setup, {includeConflictCheck = isBrowser, includeSecondChannel = false} = {}) {
|
|
@@ -94,11 +95,12 @@ describe("WebRTC", function () {
|
|
|
94
95
|
const start = Date.now();
|
|
95
96
|
console.log(new Date(), 'start setup', nPairs, 'pairs');
|
|
96
97
|
for (let index = 0; index < nPairs; index++) {
|
|
98
|
+
console.log('setup', index);
|
|
97
99
|
await setup({index});
|
|
98
100
|
}
|
|
99
101
|
//await Promise.all(connections.map(connection => connection.bothOpen));
|
|
100
102
|
console.log('end setup', Date.now() - start);
|
|
101
|
-
}, nPairs *
|
|
103
|
+
}, nPairs * 2e3);
|
|
102
104
|
for (let index = 0; index < nPairs; index++) {
|
|
103
105
|
it(`connects ${index}.`, function () {
|
|
104
106
|
const {A, B} = connections[index];
|
|
@@ -169,7 +171,7 @@ describe("WebRTC", function () {
|
|
|
169
171
|
describe('non-negotiated', function () {
|
|
170
172
|
beforeAll(function () {console.log('one-sided non-negotiated'); });
|
|
171
173
|
standardBehavior(async function ({index}) {
|
|
172
|
-
const {A, B, bothOpen} = makePair({index});
|
|
174
|
+
const {A, B, bothOpen} = await makePair({index});
|
|
173
175
|
A.createChannel('data', {negotiated: false});
|
|
174
176
|
await bothOpen;
|
|
175
177
|
}, {includeConflictCheck: false, includeSecondChannel: false});
|
|
@@ -177,7 +179,7 @@ describe("WebRTC", function () {
|
|
|
177
179
|
describe("negotiated on first signal", function () {
|
|
178
180
|
beforeAll(function () {console.log('one-sided negotiated'); });
|
|
179
181
|
standardBehavior(async function ({index}) {
|
|
180
|
-
const {A, B, bothOpen} = makePair({index});
|
|
182
|
+
const {A, B, bothOpen} = await makePair({index});
|
|
181
183
|
// There isn't really a direct, automated way to have one side open another with negotiated:true,
|
|
182
184
|
// because the receiving RTCPeerConnection does not fire 'datachannel' when the sender was negotiated:true.
|
|
183
185
|
// However, what the app can do is wake up and create an explicit createChannel when it first receives an
|
|
@@ -201,7 +203,7 @@ describe("WebRTC", function () {
|
|
|
201
203
|
describe("impolite first", function () {
|
|
202
204
|
beforeAll(function () {console.log('two-sided negotiated impolite-first'); });
|
|
203
205
|
standardBehavior(async function ({index}) {
|
|
204
|
-
const {A, B, bothOpen} = makePair({index});
|
|
206
|
+
const {A, B, bothOpen} = await makePair({index});
|
|
205
207
|
A.createChannel("data", {negotiated: true});
|
|
206
208
|
B.createChannel("data", {negotiated: true});
|
|
207
209
|
await bothOpen;
|
|
@@ -210,7 +212,8 @@ describe("WebRTC", function () {
|
|
|
210
212
|
describe("polite first", function () {
|
|
211
213
|
beforeAll(function () {console.log('two-sided negotiated polite-first');});
|
|
212
214
|
standardBehavior(async function ({index}) {
|
|
213
|
-
const {A, B, bothOpen} = makePair({index});
|
|
215
|
+
const {A, B, bothOpen} = await makePair({index,});
|
|
216
|
+
//await WebRTC.delay(1); // TODO: Why is this needed?
|
|
214
217
|
B.createChannel("data", {negotiated: true});
|
|
215
218
|
A.createChannel("data", {negotiated: true});
|
|
216
219
|
await bothOpen;
|
|
@@ -223,7 +226,8 @@ describe("WebRTC", function () {
|
|
|
223
226
|
describe("impolite first", function () {
|
|
224
227
|
beforeAll(function () {console.log('two-sided non-negotiated impolite-first');});
|
|
225
228
|
standardBehavior(async function ({index}) {
|
|
226
|
-
const {A, B, bothOpen} = makePair({delay, index, debug});
|
|
229
|
+
const {A, B, bothOpen} = await makePair({delay, index, debug});
|
|
230
|
+
//await WebRTC.delay(1); // TODO: Why is this needed?
|
|
227
231
|
A.createChannel("data", {negotiated: false});
|
|
228
232
|
B.createChannel("data", {negotiated: false});
|
|
229
233
|
await bothOpen;
|
|
@@ -232,7 +236,7 @@ describe("WebRTC", function () {
|
|
|
232
236
|
describe("polite first", function () {
|
|
233
237
|
beforeAll(function () {console.log('two-sided non-negotiated polite-first');});
|
|
234
238
|
standardBehavior(async function ({index}) {
|
|
235
|
-
const {A, B, bothOpen} = makePair({delay, index, debug});
|
|
239
|
+
const {A, B, bothOpen} = await makePair({delay, index, debug});
|
|
236
240
|
B.createChannel("data", {negotiated: false});
|
|
237
241
|
A.createChannel("data", {negotiated: false});
|
|
238
242
|
await bothOpen;
|
package/routes/index.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import express from 'express';
|
|
2
|
-
import { WebRTC } from '../index.js';
|
|
3
|
-
export const router = express.Router();
|
|
4
|
-
|
|
5
|
-
const testConnections = {}; // Is it really necessary to keep these around, against garbage collection?
|
|
6
|
-
router.post('/data/echo/:tag', async (req, res, next) => { // Test endpoint for WebRTC.
|
|
7
|
-
const {params, body} = req;
|
|
8
|
-
const tag = params.tag;
|
|
9
|
-
let incomingSignals = body;
|
|
10
|
-
let connection = testConnections[tag];
|
|
11
|
-
if (!connection) {
|
|
12
|
-
connection = testConnections[tag] = new WebRTC({label: tag});
|
|
13
|
-
const timer = setTimeout(() => connection.close(), 15e3); // Enough to complete test, then cleanup.
|
|
14
|
-
const dataPromise = connection.dataChannelPromise = connection.ensureDataChannel('echo', {}, incomingSignals);
|
|
15
|
-
incomingSignals = [];
|
|
16
|
-
dataPromise.then(dataChannel => {
|
|
17
|
-
dataChannel.onclose = () => {
|
|
18
|
-
clearTimeout(timer);
|
|
19
|
-
connection.close();
|
|
20
|
-
delete testConnections[tag];
|
|
21
|
-
};
|
|
22
|
-
dataChannel.onmessage = event => dataChannel.send(event.data); // Just echo what we are given.
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
res.send(await connection.respond(incomingSignals));
|
|
26
|
-
});
|
|
27
|
-
|