@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.
@@ -0,0 +1 @@
1
+ export default globalThis;
package/index.js CHANGED
@@ -1,4 +1,4 @@
1
- //import wrtc from '#wrtc'; // fixme
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.close();
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 = async () => { // Idempotent (except for logging), if we do not bash dataChannePromises[label] multiple times.
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.2",
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": "./lib/browser-wrtc.js"
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
@@ -9,7 +9,7 @@
9
9
  <script src="../jasmine-standalone-5.7.1/lib/jasmine-5.7.1/boot1.js"></script>
10
10
  <script type="module">
11
11
  import './webrtcSpec.js';
12
- import './webrtcCapacitySpec.js';
12
+ import './webrtcCapacitySpec.js';
13
13
  </script>
14
14
  </head>
15
15
  </html>
@@ -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 = true; // Does signal work direct/one-sided to the other? False makes a request that waits for a response.
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 * 1e3);
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
-