@zero-server/sdk 0.9.5 → 0.9.7

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.
Files changed (94) hide show
  1. package/README.md +54 -64
  2. package/index.js +116 -4
  3. package/lib/app.js +22 -22
  4. package/lib/auth/authorize.js +11 -11
  5. package/lib/auth/enrollment.js +5 -5
  6. package/lib/auth/jwt.js +9 -9
  7. package/lib/auth/oauth.js +1 -1
  8. package/lib/auth/session.js +5 -5
  9. package/lib/auth/trustedDevice.js +2 -2
  10. package/lib/auth/twoFactor.js +11 -11
  11. package/lib/auth/webauthn.js +6 -6
  12. package/lib/body/json.js +1 -1
  13. package/lib/body/raw.js +1 -1
  14. package/lib/body/rawBuffer.js +1 -1
  15. package/lib/body/text.js +1 -1
  16. package/lib/body/urlencoded.js +3 -3
  17. package/lib/cli.js +19 -4
  18. package/lib/cluster.js +3 -3
  19. package/lib/debug.js +10 -10
  20. package/lib/env/index.js +11 -11
  21. package/lib/errors.js +131 -16
  22. package/lib/fetch/index.js +1 -1
  23. package/lib/grpc/call.js +14 -14
  24. package/lib/grpc/client.js +4 -4
  25. package/lib/grpc/codec.js +7 -7
  26. package/lib/grpc/credentials.js +2 -2
  27. package/lib/grpc/frame.js +2 -2
  28. package/lib/grpc/health.js +3 -3
  29. package/lib/grpc/index.js +3 -3
  30. package/lib/grpc/metadata.js +3 -3
  31. package/lib/grpc/proto.js +5 -5
  32. package/lib/grpc/reflection.js +2 -2
  33. package/lib/grpc/server.js +3 -3
  34. package/lib/grpc/status.js +2 -2
  35. package/lib/grpc/watch.js +1 -1
  36. package/lib/http/request.js +13 -13
  37. package/lib/http/response.js +2 -2
  38. package/lib/lifecycle.js +5 -5
  39. package/lib/middleware/compress.js +4 -4
  40. package/lib/observe/health.js +1 -1
  41. package/lib/observe/index.js +1 -1
  42. package/lib/observe/logger.js +3 -3
  43. package/lib/observe/metrics.js +4 -4
  44. package/lib/observe/tracing.js +4 -4
  45. package/lib/orm/adapters/json.js +1 -1
  46. package/lib/orm/adapters/memory.js +2 -2
  47. package/lib/orm/adapters/mongo.js +2 -2
  48. package/lib/orm/adapters/mysql.js +2 -2
  49. package/lib/orm/adapters/postgres.js +2 -2
  50. package/lib/orm/adapters/sqlite.js +3 -3
  51. package/lib/orm/audit.js +1 -1
  52. package/lib/orm/index.js +7 -7
  53. package/lib/orm/migrate.js +1 -1
  54. package/lib/orm/model.js +15 -15
  55. package/lib/orm/procedures.js +1 -1
  56. package/lib/orm/profiler.js +1 -1
  57. package/lib/orm/query.js +9 -9
  58. package/lib/orm/schema.js +1 -1
  59. package/lib/orm/seed/data/person.js +1 -1
  60. package/lib/orm/seed/fake.js +10 -10
  61. package/lib/orm/seed/index.js +4 -4
  62. package/lib/orm/seed/rng.js +1 -1
  63. package/lib/orm/snapshot.js +2 -2
  64. package/lib/orm/tenancy.js +6 -6
  65. package/lib/orm/views.js +1 -1
  66. package/lib/router/index.js +9 -9
  67. package/lib/webrtc/bot.js +361 -0
  68. package/lib/webrtc/cli.js +182 -0
  69. package/lib/webrtc/cluster.js +350 -0
  70. package/lib/webrtc/e2ee.js +282 -0
  71. package/lib/webrtc/ice.js +370 -0
  72. package/lib/webrtc/index.js +132 -0
  73. package/lib/webrtc/joinToken.js +116 -0
  74. package/lib/webrtc/observe.js +229 -0
  75. package/lib/webrtc/peer.js +116 -0
  76. package/lib/webrtc/room.js +171 -0
  77. package/lib/webrtc/sdp.js +508 -0
  78. package/lib/webrtc/sfu/index.js +201 -0
  79. package/lib/webrtc/sfu/livekit.js +301 -0
  80. package/lib/webrtc/sfu/mediasoup.js +317 -0
  81. package/lib/webrtc/sfu/memory.js +204 -0
  82. package/lib/webrtc/signaling.js +546 -0
  83. package/lib/webrtc/stun.js +492 -0
  84. package/lib/webrtc/turn/codec.js +370 -0
  85. package/lib/webrtc/turn/credentials.js +141 -0
  86. package/lib/webrtc/turn/server.js +633 -0
  87. package/package.json +2 -2
  88. package/types/body.d.ts +1 -1
  89. package/types/cli.d.ts +1 -1
  90. package/types/index.d.ts +16 -4
  91. package/types/middleware.d.ts +1 -1
  92. package/types/orm.d.ts +3 -3
  93. package/types/request.d.ts +3 -3
  94. package/types/webrtc.d.ts +501 -0
@@ -0,0 +1,317 @@
1
+ /**
2
+ * @module webrtc/sfu/mediasoup
3
+ * @description mediasoup-backed SFU adapter (peerDependency on `mediasoup`).
4
+ *
5
+ * Wraps a single mediasoup `Worker` and one `Router` per createRouter()
6
+ * call. WebRTC transports are created with `router.createWebRtcTransport()`
7
+ * and produce / consume / pause / resume / close / stats all delegate to
8
+ * the native mediasoup objects.
9
+ *
10
+ * `mediasoup` is loaded lazily. Tests inject a stub via `opts.mediasoup`;
11
+ * in production the constructor `require('mediasoup')`s the real package
12
+ * and throws `WEBRTC_SFU_NOT_INSTALLED` if it is missing.
13
+ */
14
+ 'use strict';
15
+
16
+ const { SfuAdapter } = require('./index');
17
+ const { WebRTCError } = require('../../errors');
18
+
19
+ const DEFAULT_MEDIA_CODECS = [
20
+ { kind: 'audio', mimeType: 'audio/opus', clockRate: 48000, channels: 2 },
21
+ { kind: 'video', mimeType: 'video/VP8', clockRate: 90000 },
22
+ ];
23
+
24
+ const DEFAULT_WEBRTC_TRANSPORT_OPTS = {
25
+ listenIps: [{ ip: '0.0.0.0', announcedIp: null }],
26
+ enableUdp: true,
27
+ enableTcp: true,
28
+ preferUdp: true,
29
+ };
30
+
31
+ class MediasoupSfuAdapter extends SfuAdapter
32
+ {
33
+ /**
34
+ * @param {object} [opts]
35
+ * @param {object} [opts.mediasoup] Injected mediasoup module (testing); defaults to `require('mediasoup')`.
36
+ * @param {object} [opts.worker] Pre-created `mediasoup.Worker`; bypasses the lazy worker bootstrap.
37
+ * @param {object} [opts.workerSettings] Forwarded to `mediasoup.createWorker(...)`.
38
+ * @param {Array} [opts.mediaCodecs] Default router media codecs.
39
+ * @param {object} [opts.webRtcTransportOptions] Default `router.createWebRtcTransport(...)` options.
40
+ */
41
+ constructor(opts)
42
+ {
43
+ super();
44
+ const o = opts || {};
45
+
46
+ this._mediasoup = o.mediasoup || _tryRequireMediasoup();
47
+ this._workerSettings = o.workerSettings || {};
48
+ this._mediaCodecs = o.mediaCodecs || DEFAULT_MEDIA_CODECS;
49
+ this._webRtcTransportOpts = o.webRtcTransportOptions || DEFAULT_WEBRTC_TRANSPORT_OPTS;
50
+ this._worker = o.worker || null;
51
+ this._workerPromise = null;
52
+
53
+ this._routers = new Map(); // routerId -> native router
54
+ this._transports = new Map(); // transportId -> native transport
55
+ this._producers = new Map(); // producerId -> native producer
56
+ this._consumers = new Map(); // consumerId -> native consumer
57
+ this._routerOf = new Map(); // transportId -> routerId
58
+ }
59
+
60
+ /**
61
+ * Lazily create (or return) the single shared mediasoup Worker.
62
+ * Returns the native Worker handle.
63
+ */
64
+ async _ensureWorker()
65
+ {
66
+ if (this._worker) return this._worker;
67
+ if (!this._workerPromise)
68
+ {
69
+ this._workerPromise = Promise.resolve(this._mediasoup.createWorker(this._workerSettings))
70
+ .then((w) =>
71
+ {
72
+ this._worker = w;
73
+ if (typeof w.on === 'function')
74
+ {
75
+ w.on('died', (err) => this._emit('worker-died', { error: err && err.message }));
76
+ }
77
+ return w;
78
+ });
79
+ }
80
+ return this._workerPromise;
81
+ }
82
+
83
+ async createRouter(opts)
84
+ {
85
+ const worker = await this._ensureWorker();
86
+ const mediaCodecs = (opts && opts.mediaCodecs) || this._mediaCodecs;
87
+ const router = await worker.createRouter({ mediaCodecs });
88
+ this._routers.set(router.id, router);
89
+ if (typeof router.observer === 'object' && router.observer && typeof router.observer.on === 'function')
90
+ {
91
+ router.observer.on('close', () =>
92
+ {
93
+ this._routers.delete(router.id);
94
+ this._emit('router-close', { routerId: router.id });
95
+ });
96
+ }
97
+ this._emit('router-new', { routerId: router.id });
98
+ return {
99
+ id: router.id,
100
+ routerId: router.id,
101
+ rtpCapabilities: router.rtpCapabilities,
102
+ _native: router,
103
+ };
104
+ }
105
+
106
+ async createTransport(router, peer)
107
+ {
108
+ const routerId = router && router.id;
109
+ const native = routerId && this._routers.get(routerId);
110
+ if (!native)
111
+ {
112
+ throw new WebRTCError('createTransport: unknown router', { code: 'WEBRTC_SFU_NO_ROUTER' });
113
+ }
114
+ const transport = await native.createWebRtcTransport({
115
+ ...this._webRtcTransportOpts,
116
+ appData: { peer: peer || null },
117
+ });
118
+ this._transports.set(transport.id, transport);
119
+ this._routerOf.set(transport.id, routerId);
120
+ if (typeof transport.observer === 'object' && transport.observer && typeof transport.observer.on === 'function')
121
+ {
122
+ transport.observer.on('close', () =>
123
+ {
124
+ this._transports.delete(transport.id);
125
+ this._routerOf.delete(transport.id);
126
+ this._emit('transport-close', { transportId: transport.id });
127
+ });
128
+ }
129
+ this._emit('transport-new', { transportId: transport.id, routerId, peerId: peer && peer.id });
130
+ return {
131
+ id: transport.id,
132
+ transportId: transport.id,
133
+ routerId,
134
+ peer: peer || null,
135
+ iceParameters: transport.iceParameters,
136
+ iceCandidates: transport.iceCandidates,
137
+ dtlsParameters: transport.dtlsParameters,
138
+ sctpParameters: transport.sctpParameters || null,
139
+ _native: transport,
140
+ };
141
+ }
142
+
143
+ async produce(transport, kind, rtpParameters)
144
+ {
145
+ if (kind !== 'audio' && kind !== 'video')
146
+ {
147
+ throw new WebRTCError('produce: kind must be "audio" or "video"', { code: 'WEBRTC_SFU_INVALID_KIND' });
148
+ }
149
+ const native = transport && this._transports.get(transport.id);
150
+ if (!native)
151
+ {
152
+ throw new WebRTCError('produce: unknown transport', { code: 'WEBRTC_SFU_NO_TRANSPORT' });
153
+ }
154
+ const producer = await native.produce({ kind, rtpParameters });
155
+ this._producers.set(producer.id, producer);
156
+ if (typeof producer.on === 'function')
157
+ {
158
+ producer.on('transportclose', () =>
159
+ {
160
+ this._producers.delete(producer.id);
161
+ this._emit('producer-close', { producerId: producer.id, reason: 'transport-close' });
162
+ });
163
+ }
164
+ this._emit('producer-new', { producerId: producer.id, transportId: transport.id, kind });
165
+ return {
166
+ id: producer.id,
167
+ producerId: producer.id,
168
+ transportId: transport.id,
169
+ kind,
170
+ rtpParameters,
171
+ paused: !!producer.paused,
172
+ _native: producer,
173
+ };
174
+ }
175
+
176
+ async consume(transport, producerId, rtpCapabilities)
177
+ {
178
+ const native = transport && this._transports.get(transport.id);
179
+ if (!native)
180
+ {
181
+ throw new WebRTCError('consume: unknown transport', { code: 'WEBRTC_SFU_NO_TRANSPORT' });
182
+ }
183
+ const routerId = this._routerOf.get(transport.id);
184
+ const router = routerId && this._routers.get(routerId);
185
+ if (router && typeof router.canConsume === 'function'
186
+ && !router.canConsume({ producerId, rtpCapabilities }))
187
+ {
188
+ throw new WebRTCError('consume: router cannot consume producer with given rtpCapabilities',
189
+ { code: 'WEBRTC_SFU_CANNOT_CONSUME' });
190
+ }
191
+ let consumer;
192
+ try
193
+ {
194
+ consumer = await native.consume({ producerId, rtpCapabilities });
195
+ }
196
+ catch (err)
197
+ {
198
+ throw new WebRTCError(`consume failed: ${err.message}`, { code: 'WEBRTC_SFU_CONSUME_FAILED', cause: err });
199
+ }
200
+ this._consumers.set(consumer.id, consumer);
201
+ if (typeof consumer.on === 'function')
202
+ {
203
+ consumer.on('transportclose', () =>
204
+ {
205
+ this._consumers.delete(consumer.id);
206
+ this._emit('consumer-close', { consumerId: consumer.id, reason: 'transport-close' });
207
+ });
208
+ consumer.on('producerclose', () =>
209
+ {
210
+ this._consumers.delete(consumer.id);
211
+ this._emit('consumer-close', { consumerId: consumer.id, reason: 'producer-close' });
212
+ });
213
+ }
214
+ this._emit('consumer-new', { consumerId: consumer.id, transportId: transport.id, producerId });
215
+ return {
216
+ id: consumer.id,
217
+ consumerId: consumer.id,
218
+ transportId: transport.id,
219
+ producerId,
220
+ kind: consumer.kind,
221
+ rtpParameters: consumer.rtpParameters,
222
+ rtpCapabilities,
223
+ _native: consumer,
224
+ };
225
+ }
226
+
227
+ async pauseProducer(producerId)
228
+ {
229
+ const p = this._producers.get(producerId);
230
+ if (!p)
231
+ {
232
+ throw new WebRTCError('pauseProducer: unknown producer', { code: 'WEBRTC_SFU_NO_PRODUCER' });
233
+ }
234
+ await p.pause();
235
+ this._emit('producer-pause', { producerId });
236
+ }
237
+
238
+ async resumeProducer(producerId)
239
+ {
240
+ const p = this._producers.get(producerId);
241
+ if (!p)
242
+ {
243
+ throw new WebRTCError('resumeProducer: unknown producer', { code: 'WEBRTC_SFU_NO_PRODUCER' });
244
+ }
245
+ await p.resume();
246
+ this._emit('producer-resume', { producerId });
247
+ }
248
+
249
+ async closeRouter(routerId)
250
+ {
251
+ const r = this._routers.get(routerId);
252
+ if (!r) return;
253
+ // Native router.close() cascades to its transports; the observer
254
+ // 'close' handlers we registered in createTransport/createRouter
255
+ // emit transport-close / router-close events for us. Avoid
256
+ // emitting here to prevent duplicates.
257
+ await r.close();
258
+ }
259
+
260
+ async stats(scope)
261
+ {
262
+ if (scope && this._routers.has(scope))
263
+ {
264
+ const r = this._routers.get(scope);
265
+ const native = typeof r.getStats === 'function' ? await r.getStats() : null;
266
+ return { kind: 'router', routerId: scope, native };
267
+ }
268
+ if (scope && this._transports.has(scope))
269
+ {
270
+ const t = this._transports.get(scope);
271
+ const native = typeof t.getStats === 'function' ? await t.getStats() : null;
272
+ return { kind: 'transport', transportId: scope, routerId: this._routerOf.get(scope), native };
273
+ }
274
+ return {
275
+ kind: 'global',
276
+ routers: this._routers.size,
277
+ transports: this._transports.size,
278
+ producers: this._producers.size,
279
+ consumers: this._consumers.size,
280
+ };
281
+ }
282
+
283
+ /**
284
+ * Best-effort shutdown: closes every router, then the worker if we own it.
285
+ */
286
+ async close()
287
+ {
288
+ for (const id of [...this._routers.keys()])
289
+ {
290
+ try { await this.closeRouter(id); } catch (_) { /* swallow */ }
291
+ }
292
+ if (this._worker && typeof this._worker.close === 'function')
293
+ {
294
+ try { await this._worker.close(); } catch (_) { /* swallow */ }
295
+ }
296
+ this._worker = null;
297
+ this._workerPromise = null;
298
+ }
299
+ }
300
+
301
+ /**
302
+ * @private
303
+ * Try to `require('mediasoup')`; throw a clean install hint when missing.
304
+ */
305
+ function _tryRequireMediasoup()
306
+ {
307
+ try { return require('mediasoup'); }
308
+ catch (err)
309
+ {
310
+ throw new WebRTCError(
311
+ "SFU adapter 'mediasoup' requires the 'mediasoup' peerDependency: npm install mediasoup",
312
+ { code: 'WEBRTC_SFU_NOT_INSTALLED', cause: err },
313
+ );
314
+ }
315
+ }
316
+
317
+ module.exports = { MediasoupSfuAdapter };
@@ -0,0 +1,204 @@
1
+ /**
2
+ * @module webrtc/sfu/memory
3
+ * @description In-process "memory" SFU adapter.
4
+ *
5
+ * A passthrough router that never touches the network: every produce()
6
+ * call records a logical producer, every consume() call records a
7
+ * logical consumer, and events are emitted via {@link SfuAdapter#onEvent}.
8
+ * Perfect for unit tests, ≤ 4-peer audio-only rooms, and local dev
9
+ * where the cost of running mediasoup or LiveKit is unjustified.
10
+ *
11
+ * The adapter does NOT decode or forward media packets - it models
12
+ * bookkeeping only. Real packet forwarding lives in native adapters
13
+ * (mediasoup, LiveKit).
14
+ */
15
+ 'use strict';
16
+
17
+ const { SfuAdapter } = require('./index');
18
+ const { WebRTCError } = require('../../errors');
19
+
20
+ class MemorySfuAdapter extends SfuAdapter
21
+ {
22
+ constructor(opts)
23
+ {
24
+ super();
25
+ this._opts = opts || {};
26
+ this._counter = 0;
27
+ this._routers = new Map(); // routerId -> { id, opts, transports:Set, closed }
28
+ this._transports = new Map(); // transportId -> { id, routerId, peer, producers:Set, consumers:Set, closed }
29
+ this._producers = new Map(); // producerId -> { id, transportId, kind, rtpParams, paused, closed }
30
+ this._consumers = new Map(); // consumerId -> { id, transportId, producerId, rtpCaps, closed }
31
+ }
32
+
33
+ _nextId(prefix)
34
+ {
35
+ this._counter += 1;
36
+ return `${prefix}-${this._counter}`;
37
+ }
38
+
39
+ async createRouter(opts)
40
+ {
41
+ const id = this._nextId('router');
42
+ const router = { id, opts: opts || {}, transports: new Set(), closed: false };
43
+ this._routers.set(id, router);
44
+ this._emit('router-new', { routerId: id });
45
+ return { id, routerId: id };
46
+ }
47
+
48
+ async createTransport(router, peer)
49
+ {
50
+ const routerId = router && router.id;
51
+ const r = routerId && this._routers.get(routerId);
52
+ if (!r || r.closed)
53
+ {
54
+ throw new WebRTCError('createTransport: unknown router', { code: 'WEBRTC_SFU_NO_ROUTER' });
55
+ }
56
+ const id = this._nextId('transport');
57
+ const t = {
58
+ id,
59
+ transportId: id,
60
+ routerId,
61
+ peer: peer || null,
62
+ producers: new Set(),
63
+ consumers: new Set(),
64
+ closed: false,
65
+ iceParameters: { usernameFragment: id, password: id },
66
+ dtlsParameters: { role: 'auto', fingerprints: [] },
67
+ };
68
+ this._transports.set(id, t);
69
+ r.transports.add(id);
70
+ this._emit('transport-new', { transportId: id, routerId, peerId: peer && peer.id });
71
+ return t;
72
+ }
73
+
74
+ async produce(transport, kind, rtpParams)
75
+ {
76
+ if (kind !== 'audio' && kind !== 'video')
77
+ {
78
+ throw new WebRTCError('produce: kind must be "audio" or "video"', { code: 'WEBRTC_SFU_INVALID_KIND' });
79
+ }
80
+ const t = transport && this._transports.get(transport.id);
81
+ if (!t || t.closed)
82
+ {
83
+ throw new WebRTCError('produce: unknown transport', { code: 'WEBRTC_SFU_NO_TRANSPORT' });
84
+ }
85
+ const id = this._nextId('producer');
86
+ const p = { id, producerId: id, transportId: t.id, kind, rtpParams: rtpParams || {}, paused: false, closed: false };
87
+ this._producers.set(id, p);
88
+ t.producers.add(id);
89
+ this._emit('producer-new', { producerId: id, transportId: t.id, kind });
90
+ return p;
91
+ }
92
+
93
+ async consume(transport, producerId, rtpCaps)
94
+ {
95
+ const t = transport && this._transports.get(transport.id);
96
+ if (!t || t.closed)
97
+ {
98
+ throw new WebRTCError('consume: unknown transport', { code: 'WEBRTC_SFU_NO_TRANSPORT' });
99
+ }
100
+ const prod = this._producers.get(producerId);
101
+ if (!prod || prod.closed)
102
+ {
103
+ throw new WebRTCError('consume: unknown producer', { code: 'WEBRTC_SFU_NO_PRODUCER' });
104
+ }
105
+ const id = this._nextId('consumer');
106
+ const c = {
107
+ id,
108
+ consumerId: id,
109
+ transportId: t.id,
110
+ producerId,
111
+ kind: prod.kind,
112
+ rtpParams: prod.rtpParams,
113
+ rtpCaps: rtpCaps || {},
114
+ closed: false,
115
+ };
116
+ this._consumers.set(id, c);
117
+ t.consumers.add(id);
118
+ this._emit('consumer-new', { consumerId: id, transportId: t.id, producerId });
119
+ return c;
120
+ }
121
+
122
+ async pauseProducer(producerId)
123
+ {
124
+ const p = this._producers.get(producerId);
125
+ if (!p || p.closed)
126
+ {
127
+ throw new WebRTCError('pauseProducer: unknown producer', { code: 'WEBRTC_SFU_NO_PRODUCER' });
128
+ }
129
+ if (!p.paused)
130
+ {
131
+ p.paused = true;
132
+ this._emit('producer-pause', { producerId });
133
+ }
134
+ }
135
+
136
+ async resumeProducer(producerId)
137
+ {
138
+ const p = this._producers.get(producerId);
139
+ if (!p || p.closed)
140
+ {
141
+ throw new WebRTCError('resumeProducer: unknown producer', { code: 'WEBRTC_SFU_NO_PRODUCER' });
142
+ }
143
+ if (p.paused)
144
+ {
145
+ p.paused = false;
146
+ this._emit('producer-resume', { producerId });
147
+ }
148
+ }
149
+
150
+ async closeRouter(routerId)
151
+ {
152
+ const r = this._routers.get(routerId);
153
+ if (!r) return;
154
+ for (const tid of r.transports)
155
+ {
156
+ const t = this._transports.get(tid);
157
+ if (!t) continue;
158
+ for (const pid of t.producers)
159
+ {
160
+ const p = this._producers.get(pid);
161
+ if (p) { p.closed = true; this._emit('producer-close', { producerId: pid }); }
162
+ this._producers.delete(pid);
163
+ }
164
+ for (const cid of t.consumers)
165
+ {
166
+ const c = this._consumers.get(cid);
167
+ if (c) { c.closed = true; this._emit('consumer-close', { consumerId: cid }); }
168
+ this._consumers.delete(cid);
169
+ }
170
+ t.closed = true;
171
+ this._emit('transport-close', { transportId: tid });
172
+ this._transports.delete(tid);
173
+ }
174
+ r.closed = true;
175
+ this._emit('router-close', { routerId });
176
+ this._routers.delete(routerId);
177
+ }
178
+
179
+ async stats(scope)
180
+ {
181
+ if (scope && this._routers.has(scope))
182
+ {
183
+ const r = this._routers.get(scope);
184
+ return { kind: 'router', routerId: scope, transports: r.transports.size };
185
+ }
186
+ if (scope && this._transports.has(scope))
187
+ {
188
+ const t = this._transports.get(scope);
189
+ return {
190
+ kind: 'transport', transportId: scope, routerId: t.routerId,
191
+ producers: t.producers.size, consumers: t.consumers.size,
192
+ };
193
+ }
194
+ return {
195
+ kind: 'global',
196
+ routers: this._routers.size,
197
+ transports: this._transports.size,
198
+ producers: this._producers.size,
199
+ consumers: this._consumers.size,
200
+ };
201
+ }
202
+ }
203
+
204
+ module.exports = { MemorySfuAdapter };