@streaming-cdn/rtc-web 1.4.0 → 1.5.0
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/README.md +102 -2
- package/dist/index.d.ts +110 -4
- package/dist/native-room.d.ts +18 -0
- package/dist/rtc-web.es.js +479 -306
- package/dist/rtc-web.umd.js +1 -1
- package/dist/sfu-room.d.ts +9 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# RTC Web SDK 1.
|
|
1
|
+
# RTC Web SDK 1.5.0
|
|
2
2
|
|
|
3
3
|
The customer package contains compiled ESM/UMD JavaScript and TypeScript declarations. It intentionally excludes implementation source and source maps. Example source remains available under `examples/`.
|
|
4
4
|
|
|
@@ -140,7 +140,65 @@ origin. In practice:
|
|
|
140
140
|
so existing integrations keep working.
|
|
141
141
|
|
|
142
142
|
|
|
143
|
-
For outgoing UI, show `contacting` while mobile push is being delivered, move to `ringing` on `call.delivery`, and show `connected` only after
|
|
143
|
+
For outgoing UI, show `contacting` while mobile push is being delivered, move to `ringing` on `call.delivery`, and show `connected` only after `call.updated: accepted` **and the media client's `mediaconnected` event**. The media client's `connected` event means the signaling room was joined — it says nothing about media; a call marked connected on it can have zero media flowing. Do not call `join()` before acceptance. Cancel unanswered calls at the invitation `expiresAt` timestamp (45 seconds by default).
|
|
144
|
+
|
|
145
|
+
## Connect phase: what fires when, and how long to wait
|
|
146
|
+
|
|
147
|
+
`join()` resolves when the signaling room accepts this participant. Media
|
|
148
|
+
comes up afterwards, and the media client narrates every step of it:
|
|
149
|
+
|
|
150
|
+
| Event | Detail | Meaning |
|
|
151
|
+
| --- | --- | --- |
|
|
152
|
+
| `connected` | `{ participantId, resumed }` | Signaling room joined. **Not** media. |
|
|
153
|
+
| `participantjoined` | participant, plus `initial: true` for peers that were already in the room when you joined | The other side is in the room. This is the moment to anchor your connect budget. |
|
|
154
|
+
| `negotiation` | `{ participantId, phase, at }` — `at` is ms since `join()` | One negotiation step: `offer-created`, `offer-sent`, `offer-received`, `answer-created`, `answer-sent`, `answer-received`, `remote-description-set`, `ice-gathering-complete`, `ice-candidate-sent`, `ice-candidate-received`. Never SDP or candidate bodies. |
|
|
155
|
+
| `icestate` | `{ participantId, state }` | `iceConnectionState` changed (`peerconnectionstate` carries `connectionState`). |
|
|
156
|
+
| `mediaconnected` | `{ participantId, elapsedMs, first }` | Media reached this peer — its connection became `connected` or its first remote track arrived, whichever came first. Once per peer; `first` is true for the first peer of the session. **This is "call connected".** |
|
|
157
|
+
| `connecttimeout` | `{ elapsedMs, peers: [{ participantId, connectionState, iceState, signalingState, lastNegotiationPhase }], participantCount }` | `connectTimeoutMs` (default 30 000; `0` disables) elapsed after `join()` with no `mediaconnected`. Fired once. The client does **not** leave — decide that yourself. |
|
|
158
|
+
| `signalingerror` | `{ code: "send_dropped", messageType }` | A signaling frame was dropped because the socket was not open. Reconnect rebuilds every peer; the loss is reported, never thrown. |
|
|
159
|
+
|
|
160
|
+
On the SFU transport, negotiation is with the platform SFU, so `negotiation`,
|
|
161
|
+
`icestate` and `peerconnectionstate` are attributed to your own participant
|
|
162
|
+
id. `mediaconnected` fires for your own id once the SFU connection is up
|
|
163
|
+
(that is the `first` one), then per remote participant as their tracks
|
|
164
|
+
arrive.
|
|
165
|
+
|
|
166
|
+
`getConnectPhase()` returns `{ joinedAt, mediaConnected, mediaConnectedElapsedMs, timedOut, lastConnectTimeout }`
|
|
167
|
+
for code that attaches late.
|
|
168
|
+
|
|
169
|
+
### The staged connect budget
|
|
170
|
+
|
|
171
|
+
Measured on the platform (2026-09): a call that connects does so within a
|
|
172
|
+
few seconds of both sides being in the room, but *getting* both sides into
|
|
173
|
+
the room takes as long as the slower device's ring, wake and accept. The
|
|
174
|
+
budget is therefore staged, and **the timer for negotiation starts when the
|
|
175
|
+
other side is in the room (`participantCount === 2` in `stats`, or
|
|
176
|
+
`participantjoined`), not at `join()`**:
|
|
177
|
+
|
|
178
|
+
| Stage | Budget | Signal that ends it |
|
|
179
|
+
| --- | --- | --- |
|
|
180
|
+
| Join the signaling room | 20 s | `connected` (or `join()` rejects) |
|
|
181
|
+
| Wait for the other side to arrive | **at least 20 s, without hanging up** | `participantjoined` / `participantCount === 2` |
|
|
182
|
+
| Negotiation | 15 s from that moment | `mediaconnected` |
|
|
183
|
+
| Give up | **no earlier than 45 s after `join()`** | `connecttimeout`, or your own timer |
|
|
184
|
+
|
|
185
|
+
A callee who joined first spends the "wait for the other side" stage
|
|
186
|
+
watching the caller's `accepted` notification travel; a caller whose callee
|
|
187
|
+
was woken by push spends it watching the ring. Neither is a negotiation
|
|
188
|
+
failure. Set `connectTimeoutMs` at or above 45 000 unless your product has
|
|
189
|
+
its own staging, and treat `connecttimeout` as the diagnostic it is: its
|
|
190
|
+
`peers` snapshot says whether the other side never arrived
|
|
191
|
+
(`peers: []`), arrived and never answered (`lastNegotiationPhase:
|
|
192
|
+
"offer-sent"`), or exchanged descriptions and never found a route
|
|
193
|
+
(`iceState: "checking"` after `remote-description-set`).
|
|
194
|
+
|
|
195
|
+
The incoming-call client follows the same rule: `attachMediaClient()` no
|
|
196
|
+
longer disarms its call watchdog. The watchdog is disarmed by the media
|
|
197
|
+
client's `mediaconnected`; if that never comes, `callexpired` fires and the
|
|
198
|
+
call is released, media client included. The media client's join, media
|
|
199
|
+
establishment and connect timeout are also reported to the platform
|
|
200
|
+
through the existing bounded diagnostics channel (the same one that
|
|
201
|
+
carries signaling outages; no SDP, media, tokens or chat).
|
|
144
202
|
|
|
145
203
|
## Complete managed integration
|
|
146
204
|
|
|
@@ -357,6 +415,48 @@ operational diagnostics. Tokens, SDP, media, and chat content are never part of
|
|
|
357
415
|
that report. Set `reportSignalingDiagnostics: false` only when replacing this
|
|
358
416
|
with an application-owned diagnostics pipeline.
|
|
359
417
|
|
|
418
|
+
## 1.5.0 notes
|
|
419
|
+
|
|
420
|
+
New events and one option on the media client; nothing existing changes shape.
|
|
421
|
+
|
|
422
|
+
- **`mediaconnected` is the "call connected" signal.** `connected` was
|
|
423
|
+
documented as it, and it only ever meant the signaling room was joined.
|
|
424
|
+
Two calls on 2026-09-11 were marked connected with no media; the docs
|
|
425
|
+
were the defect.
|
|
426
|
+
- **`negotiation`, `icestate`** narrate the connect phase step by step —
|
|
427
|
+
message types and timing only, never SDP or candidate bodies.
|
|
428
|
+
- **`connectTimeoutMs`** (default 30 000; `0` disables) and the
|
|
429
|
+
**`connecttimeout`** event, which lists every peer's connection, ICE and
|
|
430
|
+
signaling state plus the last negotiation phase. The client does not
|
|
431
|
+
leave on its own.
|
|
432
|
+
- **`participantjoined` fires for peers already in the room**, marked
|
|
433
|
+
`initial: true`. The later arrival used to get no notice at all that
|
|
434
|
+
the other side was there.
|
|
435
|
+
- **`signalingerror { code: "send_dropped" }`** when a signaling frame is
|
|
436
|
+
dropped because the socket is not open. Still no throw.
|
|
437
|
+
- **The final quality sample is taken from a snapshot made before any
|
|
438
|
+
connection is closed**, so it describes the call rather than the
|
|
439
|
+
teardown; a final sample that lands on a running runtime sample now
|
|
440
|
+
waits its turn instead of being skipped. Samples also carry `statsTransport`,
|
|
441
|
+
`statsConnectionCount`, `statsRowCount` and `statsRowTypes`.
|
|
442
|
+
- **`attachMediaClient()` keeps the call watchdog armed** until the media
|
|
443
|
+
client reports `mediaconnected`. Attaching happened exactly when the
|
|
444
|
+
unguarded media phase began, so attaching used to end the only watch.
|
|
445
|
+
Media join, media establishment and connect timeout are reported to the
|
|
446
|
+
platform through the existing diagnostics channel, with the call id.
|
|
447
|
+
- **Staged connect budget published** above: join 20 s, wait for the other
|
|
448
|
+
side at least 20 s without hanging up, negotiation 15 s, give up no
|
|
449
|
+
earlier than 45 s — anchored at the moment the other side is in the room.
|
|
450
|
+
|
|
451
|
+
## 1.4.0 notes
|
|
452
|
+
|
|
453
|
+
- **Call-slot hardening ported from React Native.** Every path that could
|
|
454
|
+
leave a call id in the active set for the life of the page — hang-up
|
|
455
|
+
failing on the server, an unanswered outgoing call, an accepted call
|
|
456
|
+
whose media never came up, the `accepted` broadcast reaching a session
|
|
457
|
+
holding no media, a `startCall` in flight across `disconnect()` — now
|
|
458
|
+
releases the slot, so the next call always works.
|
|
459
|
+
|
|
360
460
|
## 1.3.28 notes
|
|
361
461
|
|
|
362
462
|
- **The idle pose measures which way is down.** 1.3.27 lowered the arms out
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type NativeRoomNegotiationPhase } from "./native-room";
|
|
1
2
|
export { createPictureInPictureController } from "./picture-in-picture";
|
|
2
3
|
export type { PictureInPictureController, PictureInPictureOptions } from "./picture-in-picture";
|
|
3
4
|
export { pickActiveSpeaker, SPEAKER_NOISE_FLOOR, SPEAKER_TAKEOVER_RATIO } from "./active-speaker";
|
|
@@ -26,6 +27,65 @@ export type RtcQuality = "auto" | "audio" | "low" | "medium" | "high";
|
|
|
26
27
|
export type RtcVideoCodec = "auto" | "vp8" | "h264";
|
|
27
28
|
export type RtcIceTransportPolicy = "all" | "relay";
|
|
28
29
|
export type RtcConnectionState = "initialized" | "joining" | "connected" | "reconnecting" | "disconnected" | "failed";
|
|
30
|
+
/**
|
|
31
|
+
* Default for `connectTimeoutMs`: how long after `join()` the client waits for
|
|
32
|
+
* the first peer to reach media before emitting `connecttimeout`. It only
|
|
33
|
+
* reports — the client never leaves on its own.
|
|
34
|
+
*/
|
|
35
|
+
export declare const DEFAULT_CONNECT_TIMEOUT_MS = 30000;
|
|
36
|
+
export type RtcNegotiationPhase = NativeRoomNegotiationPhase;
|
|
37
|
+
/** `negotiation` event: one step of a peer's media negotiation, `at` ms after `join()`. Never SDP or candidate bodies. */
|
|
38
|
+
export interface RtcNegotiationEvent {
|
|
39
|
+
participantId: string;
|
|
40
|
+
phase: RtcNegotiationPhase;
|
|
41
|
+
at: number;
|
|
42
|
+
}
|
|
43
|
+
/** `icestate` event: a peer connection's `iceConnectionState` changed. */
|
|
44
|
+
export interface RtcIceStateEvent {
|
|
45
|
+
participantId: string;
|
|
46
|
+
state: string;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* `mediaconnected` event: media reached this peer — its connection became
|
|
50
|
+
* `connected` or its first remote track arrived, whichever came first. Fired
|
|
51
|
+
* once per peer; `first` is true only for the first peer of the session.
|
|
52
|
+
*/
|
|
53
|
+
export interface RtcMediaConnectedEvent {
|
|
54
|
+
participantId: string;
|
|
55
|
+
elapsedMs: number;
|
|
56
|
+
first: boolean;
|
|
57
|
+
}
|
|
58
|
+
export interface RtcConnectTimeoutPeer {
|
|
59
|
+
participantId: string;
|
|
60
|
+
connectionState: string;
|
|
61
|
+
iceState: string;
|
|
62
|
+
signalingState: string;
|
|
63
|
+
lastNegotiationPhase: RtcNegotiationPhase | null;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* `connecttimeout` event: `connectTimeoutMs` elapsed after `join()` with no
|
|
67
|
+
* `mediaconnected`. Says what state every peer was in so the failure is
|
|
68
|
+
* diagnosable from the event alone. Fired at most once per session.
|
|
69
|
+
*/
|
|
70
|
+
export interface RtcConnectTimeoutEvent {
|
|
71
|
+
elapsedMs: number;
|
|
72
|
+
peers: RtcConnectTimeoutPeer[];
|
|
73
|
+
participantCount: number;
|
|
74
|
+
}
|
|
75
|
+
/** `signalingerror` detail when a signaling frame was dropped because the socket was not open. */
|
|
76
|
+
export interface RtcSignalingDropEvent {
|
|
77
|
+
code: "send_dropped";
|
|
78
|
+
messageType: string;
|
|
79
|
+
}
|
|
80
|
+
/** Where this session stands between `join()` and media, for integrators that attach late. */
|
|
81
|
+
export interface RtcConnectPhase {
|
|
82
|
+
joinedAt: string | null;
|
|
83
|
+
mediaConnected: boolean;
|
|
84
|
+
mediaConnectedElapsedMs: number | null;
|
|
85
|
+
timedOut: boolean;
|
|
86
|
+
/** The `connecttimeout` payload once it has fired; null otherwise. */
|
|
87
|
+
lastConnectTimeout: RtcConnectTimeoutEvent | null;
|
|
88
|
+
}
|
|
29
89
|
export interface RtcCredential {
|
|
30
90
|
signalingToken: string;
|
|
31
91
|
signalingUrl: string;
|
|
@@ -71,6 +131,13 @@ export interface RtcClientOptions {
|
|
|
71
131
|
/** Enables automatic quality changes when quality is "auto". */
|
|
72
132
|
adaptiveQuality?: boolean;
|
|
73
133
|
autoJoin?: boolean;
|
|
134
|
+
/**
|
|
135
|
+
* Milliseconds after `join()` before `connecttimeout` is emitted when no
|
|
136
|
+
* peer has reached media. Defaults to 30 000; `0` disables. Anchor your own
|
|
137
|
+
* budget at the moment the other side is in the room, not at `join()`: a
|
|
138
|
+
* callee who joined first waits here for the caller through the whole ring.
|
|
139
|
+
*/
|
|
140
|
+
connectTimeoutMs?: number;
|
|
74
141
|
statsInterval?: number;
|
|
75
142
|
telemetryInterval?: number;
|
|
76
143
|
autoReportStats?: boolean;
|
|
@@ -128,6 +195,16 @@ export interface RtcQualitySample {
|
|
|
128
195
|
qualityLimitationReason: string | null;
|
|
129
196
|
deviceMemoryGb: number | null;
|
|
130
197
|
effectiveConnectionType: string | null;
|
|
198
|
+
/**
|
|
199
|
+
* Collection diagnostics (S5): which transport was sampled, how many peer
|
|
200
|
+
* connections existed, and what `getStats()` actually returned — so a
|
|
201
|
+
* sample whose derived fields are null still says whether there was
|
|
202
|
+
* nothing to measure or nothing measurable.
|
|
203
|
+
*/
|
|
204
|
+
statsTransport: "mesh" | "sfu" | null;
|
|
205
|
+
statsConnectionCount: number | null;
|
|
206
|
+
statsRowCount: number | null;
|
|
207
|
+
statsRowTypes: string | null;
|
|
131
208
|
}
|
|
132
209
|
export interface RtcStats {
|
|
133
210
|
state: RtcConnectionState;
|
|
@@ -169,7 +246,7 @@ export declare class RtcClient extends EventTarget {
|
|
|
169
246
|
private latestMediaScore;
|
|
170
247
|
private latestQuality;
|
|
171
248
|
private statsTimer;
|
|
172
|
-
private
|
|
249
|
+
private statsTask;
|
|
173
250
|
private previousCounters;
|
|
174
251
|
private lastTelemetryAt;
|
|
175
252
|
private joinPromise;
|
|
@@ -177,10 +254,20 @@ export declare class RtcClient extends EventTarget {
|
|
|
177
254
|
private effectiveQuality;
|
|
178
255
|
private poorQualitySamples;
|
|
179
256
|
private goodQualitySamples;
|
|
257
|
+
private transport;
|
|
258
|
+
private joinStartedAt;
|
|
259
|
+
private connectTimer;
|
|
260
|
+
private connectTimedOut;
|
|
261
|
+
private lastConnectTimeout;
|
|
262
|
+
private readonly mediaConnectedPeers;
|
|
263
|
+
private firstMediaConnectedElapsedMs;
|
|
264
|
+
private readonly lastNegotiationPhase;
|
|
180
265
|
constructor(options: RtcClientOptions);
|
|
181
266
|
initialize(): Promise<this>;
|
|
182
267
|
join(): Promise<void>;
|
|
183
268
|
leave(): Promise<void>;
|
|
269
|
+
/** Where the session stands between `join()` and media. Lets a late `attachMediaClient` catch up. */
|
|
270
|
+
getConnectPhase(): RtcConnectPhase;
|
|
184
271
|
setMuted(muted: boolean): Promise<void>;
|
|
185
272
|
setCameraEnabled(enabled: boolean): Promise<void>;
|
|
186
273
|
setVideoTrack(track: MediaStreamTrack): Promise<void>;
|
|
@@ -214,13 +301,22 @@ export declare class RtcClient extends EventTarget {
|
|
|
214
301
|
requestMedia?: boolean;
|
|
215
302
|
probeUrl?: string;
|
|
216
303
|
}): Promise<RtcPreflightResult>;
|
|
217
|
-
|
|
304
|
+
/**
|
|
305
|
+
* `connectionState` names the state to report instead of reading it from the
|
|
306
|
+
* connection — `leave()` passes the snapshot it took before closing anything.
|
|
307
|
+
*/
|
|
308
|
+
collectQualitySample(sampleType?: RtcQualitySample["sampleType"], connectionState?: string): Promise<RtcQualitySample>;
|
|
218
309
|
reportQuality(sample: Partial<RtcQualitySample>): Promise<void>;
|
|
219
310
|
getStats(): RtcStats;
|
|
220
311
|
/** Escape hatch for advanced integrations. Prefer the stable SDK methods when possible. */
|
|
221
312
|
getNativeClient<T = unknown>(): T | null;
|
|
222
313
|
destroy(): void;
|
|
223
314
|
private handleNativeEvent;
|
|
315
|
+
private elapsedSinceJoin;
|
|
316
|
+
/** Fires `mediaconnected` once per peer, and disarms the connect watchdog on the first. */
|
|
317
|
+
private markMediaConnected;
|
|
318
|
+
private armConnectTimeout;
|
|
319
|
+
private clearConnectTimeout;
|
|
224
320
|
private startStats;
|
|
225
321
|
private sampleAndReport;
|
|
226
322
|
private stopStats;
|
|
@@ -320,6 +416,7 @@ export declare class RtcIncomingClient extends EventTarget {
|
|
|
320
416
|
private currentTokenExpiresAt?;
|
|
321
417
|
private readonly callAudio;
|
|
322
418
|
private readonly mediaClients;
|
|
419
|
+
private readonly mediaSubscriptions;
|
|
323
420
|
private readonly activeCallIds;
|
|
324
421
|
private readonly seenInvitationIds;
|
|
325
422
|
private readonly invitationExpiryTimers;
|
|
@@ -330,8 +427,17 @@ export declare class RtcIncomingClient extends EventTarget {
|
|
|
330
427
|
constructor(options: RtcIncomingClientOptions);
|
|
331
428
|
connect(): this;
|
|
332
429
|
setMount(target: HTMLElement | string | null): void;
|
|
333
|
-
/**
|
|
430
|
+
/**
|
|
431
|
+
* Binds media lifetime to a call. Terminal call states automatically leave
|
|
432
|
+
* and destroy it. The call watchdog keeps running until the media client
|
|
433
|
+
* reports `mediaconnected`; if it expires first, `callexpired` fires and the
|
|
434
|
+
* call — media included — is released. The media client's join, media
|
|
435
|
+
* establishment and connect timeout are reported to the platform through
|
|
436
|
+
* the same bounded diagnostics channel as signaling outages.
|
|
437
|
+
*/
|
|
334
438
|
attachMediaClient(callId: string, media: RtcClient): () => void;
|
|
439
|
+
/** True once the media client bound to this call has reached media on at least one peer. */
|
|
440
|
+
private mediaEstablished;
|
|
335
441
|
/**
|
|
336
442
|
* Arms the self-release timer for a call that holds the single call slot but
|
|
337
443
|
* has no media attached. Every path that claims the slot arms this, so no
|
|
@@ -403,4 +509,4 @@ export declare class RtcIncomingClient extends EventTarget {
|
|
|
403
509
|
private emit;
|
|
404
510
|
}
|
|
405
511
|
export declare function createRtcIncomingClient(options: RtcIncomingClientOptions): RtcIncomingClient;
|
|
406
|
-
export declare const version = "1.
|
|
512
|
+
export declare const version = "1.5.0";
|
package/dist/native-room.d.ts
CHANGED
|
@@ -30,6 +30,21 @@ export interface NativeRoomOptions {
|
|
|
30
30
|
}
|
|
31
31
|
export type NativeRoomQuality = "audio" | "low" | "medium" | "high";
|
|
32
32
|
export type NativeRoomVideoCodec = "auto" | "vp8" | "h264";
|
|
33
|
+
/**
|
|
34
|
+
* One step of a peer's media negotiation, as seen from this side. Emitted as
|
|
35
|
+
* `negotiation` with the peer's participant id; never the SDP or a candidate
|
|
36
|
+
* body. The phases are the same on the React Native adapter.
|
|
37
|
+
*/
|
|
38
|
+
export type NativeRoomNegotiationPhase = "offer-created" | "offer-sent" | "offer-received" | "answer-created" | "answer-sent" | "answer-received" | "remote-description-set" | "ice-gathering-complete" | "ice-candidate-sent" | "ice-candidate-received";
|
|
39
|
+
/** Snapshot of one connection's state machines, for diagnostics. */
|
|
40
|
+
export interface NativeRoomPeerState {
|
|
41
|
+
participantId: string;
|
|
42
|
+
connectionState: string;
|
|
43
|
+
iceState: string;
|
|
44
|
+
signalingState: string;
|
|
45
|
+
}
|
|
46
|
+
declare function peerStateOf(participantId: string, connection: RTCPeerConnection): NativeRoomPeerState;
|
|
47
|
+
export { peerStateOf as readPeerState };
|
|
33
48
|
export declare class NativeRoomClient {
|
|
34
49
|
private readonly options;
|
|
35
50
|
private socket;
|
|
@@ -71,12 +86,15 @@ export declare class NativeRoomClient {
|
|
|
71
86
|
stream: MediaStream;
|
|
72
87
|
}>;
|
|
73
88
|
getPrimaryPeerConnection(): RTCPeerConnection | null;
|
|
89
|
+
/** Every peer connection's state machines at this instant, one entry per remote participant. */
|
|
90
|
+
getPeerStates(): NativeRoomPeerState[];
|
|
74
91
|
sendChat(message: string, participantIds?: string[]): void;
|
|
75
92
|
sendControl(action: string, detail?: Record<string, unknown>): void;
|
|
76
93
|
private connect;
|
|
77
94
|
private openSocket;
|
|
78
95
|
private handleSignal;
|
|
79
96
|
private ensurePeer;
|
|
97
|
+
private negotiation;
|
|
80
98
|
private createOffer;
|
|
81
99
|
private performOffer;
|
|
82
100
|
private shouldInitiateOffer;
|