mockrtc 0.1.0 → 0.2.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.
Files changed (68) hide show
  1. package/README.md +2 -2
  2. package/dist/client/mockrtc-admin-request-builder.d.ts +21 -0
  3. package/dist/client/mockrtc-admin-request-builder.js +171 -0
  4. package/dist/client/mockrtc-admin-request-builder.js.map +1 -0
  5. package/dist/client/mockrtc-client.d.ts +5 -1
  6. package/dist/client/mockrtc-client.js +20 -16
  7. package/dist/client/mockrtc-client.js.map +1 -1
  8. package/dist/client/mockrtc-remote-peer.d.ts +3 -2
  9. package/dist/client/mockrtc-remote-peer.js.map +1 -1
  10. package/dist/handling/handler-step-definitions.d.ts +3 -2
  11. package/dist/handling/handler-step-definitions.js.map +1 -1
  12. package/dist/handling/handler-steps.js +1 -1
  13. package/dist/handling/handler-steps.js.map +1 -1
  14. package/dist/main-browser.d.ts +2 -0
  15. package/dist/main-browser.js +5 -1
  16. package/dist/main-browser.js.map +1 -1
  17. package/dist/main.d.ts +5 -4
  18. package/dist/main.js +5 -1
  19. package/dist/main.js.map +1 -1
  20. package/dist/mockrtc-peer.d.ts +37 -6
  21. package/dist/mockrtc.d.ts +138 -1
  22. package/dist/mockrtc.js +1 -0
  23. package/dist/mockrtc.js.map +1 -1
  24. package/dist/server/mockrtc-admin-plugin.d.ts +2 -2
  25. package/dist/server/mockrtc-admin-plugin.js +147 -2
  26. package/dist/server/mockrtc-admin-plugin.js.map +1 -1
  27. package/dist/server/mockrtc-server-peer.d.ts +8 -2
  28. package/dist/server/mockrtc-server-peer.js +106 -5
  29. package/dist/server/mockrtc-server-peer.js.map +1 -1
  30. package/dist/server/mockrtc-server.d.ts +11 -3
  31. package/dist/server/mockrtc-server.js +44 -6
  32. package/dist/server/mockrtc-server.js.map +1 -1
  33. package/dist/webrtc/datachannel-stream.d.ts +2 -0
  34. package/dist/webrtc/datachannel-stream.js +12 -0
  35. package/dist/webrtc/datachannel-stream.js.map +1 -1
  36. package/dist/webrtc/mediatrack-stream.d.ts +4 -0
  37. package/dist/webrtc/mediatrack-stream.js +13 -1
  38. package/dist/webrtc/mediatrack-stream.js.map +1 -1
  39. package/dist/webrtc/mockrtc-connection.d.ts +1 -1
  40. package/dist/webrtc/mockrtc-connection.js +77 -60
  41. package/dist/webrtc/mockrtc-connection.js.map +1 -1
  42. package/dist/webrtc/rtc-connection.d.ts +23 -4
  43. package/dist/webrtc/rtc-connection.js +45 -6
  44. package/dist/webrtc/rtc-connection.js.map +1 -1
  45. package/dist/webrtc-hooks.js +4 -2
  46. package/dist/webrtc-hooks.js.map +1 -1
  47. package/package.json +12 -6
  48. package/src/client/mockrtc-admin-request-builder.ts +184 -0
  49. package/src/client/mockrtc-client.ts +26 -22
  50. package/src/client/mockrtc-remote-peer.ts +9 -8
  51. package/src/handling/handler-step-definitions.ts +6 -4
  52. package/src/handling/handler-steps.ts +6 -5
  53. package/src/main-browser.ts +4 -0
  54. package/src/main.ts +18 -4
  55. package/src/mockrtc-peer.ts +41 -6
  56. package/src/mockrtc.ts +163 -1
  57. package/src/server/mockrtc-admin-plugin.ts +159 -6
  58. package/src/server/mockrtc-server-peer.ts +183 -6
  59. package/src/server/mockrtc-server.ts +75 -14
  60. package/src/webrtc/datachannel-stream.ts +16 -0
  61. package/src/webrtc/mediatrack-stream.ts +16 -3
  62. package/src/webrtc/mockrtc-connection.ts +21 -6
  63. package/src/webrtc/rtc-connection.ts +79 -14
  64. package/src/webrtc-hooks.ts +7 -4
  65. package/test/integration/events.spec.ts +536 -0
  66. package/test/integration/matching.spec.ts +68 -0
  67. package/test/integration/proxy.spec.ts +66 -1
  68. package/test/test-setup.ts +19 -0
@@ -3,7 +3,10 @@
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
 
6
+ import { MockRTCSessionDescription } from './mockrtc';
7
+
6
8
  export interface MockRTCPeerOptions {
9
+ debug?: boolean;
7
10
  recordMessages?: boolean;
8
11
  }
9
12
 
@@ -111,7 +114,7 @@ export interface MockRTCSession {
111
114
  /**
112
115
  * Create a new offer for this session, to renegotiate the existing connection.
113
116
  */
114
- createOffer(options?: OfferOptions): Promise<RTCSessionDescriptionInit>;
117
+ createOffer(options?: OfferOptions): Promise<MockRTCSessionDescription>;
115
118
 
116
119
  /**
117
120
  * Provide an answer to complete an offer for this session, to renegotiate the existing connection.
@@ -121,17 +124,17 @@ export interface MockRTCSession {
121
124
  /**
122
125
  * Get an answer given an offer from elsewhere, to renegotiate the existing connection.
123
126
  */
124
- answerOffer(offer: RTCSessionDescriptionInit, options?: AnswerOptions): Promise<RTCSessionDescriptionInit>;
127
+ answerOffer(offer: RTCSessionDescriptionInit, options?: AnswerOptions): Promise<MockRTCSessionDescription>;
125
128
  }
126
129
 
127
130
  export interface MockRTCOfferParams {
128
- offer: RTCSessionDescriptionInit;
131
+ offer: MockRTCSessionDescription;
129
132
  setAnswer: (answer: RTCSessionDescriptionInit) => Promise<void>;
130
133
  session: MockRTCSession;
131
134
  }
132
135
 
133
136
  export interface MockRTCAnswerParams {
134
- answer: RTCSessionDescriptionInit;
137
+ answer: MockRTCSessionDescription;
135
138
  session: MockRTCSession;
136
139
  }
137
140
 
@@ -152,6 +155,16 @@ export interface OfferOptions {
152
155
  * This option has no effect if mirrorSDP is not set.
153
156
  */
154
157
  addDataStream?: boolean;
158
+
159
+ /**
160
+ * Extra metadata to associate with the connection. This will be exposed on
161
+ * events like peer-connected, and can be used to add context to connections.
162
+ *
163
+ * If this value is provided during renegotiation, it is merged key-wise with
164
+ * any existing metadata value for the connection (i.e. existing metadata
165
+ * values will not change, unless a new value for the same key is provided).
166
+ */
167
+ connectionMetadata?: ConnectionMetadata;
155
168
  }
156
169
 
157
170
  export interface AnswerOptions {
@@ -160,17 +173,39 @@ export interface AnswerOptions {
160
173
  * create an equivalent answer, including the same media params.
161
174
  */
162
175
  mirrorSDP?: string;
176
+
177
+ /**
178
+ * Extra metadata to associate with the connection. This will be exposed on
179
+ * events like peer-connected, and can be used to add context to connections.
180
+ *
181
+ * If this value is provided during renegotiation, it is merged key-wise with
182
+ * any existing metadata value for the connection (i.e. existing metadata
183
+ * values will not change, unless a new value for the same key is provided).
184
+ */
185
+ connectionMetadata?: ConnectionMetadata;
186
+ }
187
+
188
+ /**
189
+ * Extra metadata to associate with the connection. This will be exposed on
190
+ * events like peer-connected, and can be used to add context to connections.
191
+ *
192
+ * The only defined value is 'userAgent' which must be a client user agent
193
+ * string (in a browser, the navigator.userAgent value) if defined.
194
+ */
195
+ export interface ConnectionMetadata {
196
+ userAgent?: string;
197
+ [k: string]: any;
163
198
  }
164
199
 
165
200
  export interface MockRTCExternalOfferParams {
166
201
  id: string; // Used for external attach control messages
167
- offer: RTCSessionDescriptionInit;
202
+ offer: MockRTCSessionDescription;
168
203
  setAnswer: (answer: RTCSessionDescriptionInit) => Promise<void>;
169
204
  session: MockRTCSession;
170
205
  }
171
206
 
172
207
  export interface MockRTCExternalAnswerParams {
173
208
  id: string; // Used for external attach control messagesz
174
- answer: RTCSessionDescriptionInit;
209
+ answer: MockRTCSessionDescription;
175
210
  session: MockRTCSession;
176
211
  }
package/src/mockrtc.ts CHANGED
@@ -4,11 +4,17 @@
4
4
  */
5
5
 
6
6
  import type { MockRTCHandlerBuilder } from "./handling/handler-builder";
7
- import type { MockRTCPeer } from "./mockrtc-peer";
7
+ import type { ConnectionMetadata, MockRTCPeer } from "./mockrtc-peer";
8
8
 
9
9
  export interface MockRTCPeerBuilder extends MockRTCHandlerBuilder<MockRTCPeer> {}
10
10
 
11
11
  export interface MockRTCOptions {
12
+
13
+ /**
14
+ * Should the server print extra debug information?
15
+ */
16
+ debug?: boolean;
17
+
12
18
  /**
13
19
  * Whether or not all DataChannel messages should be saved for later examination.
14
20
  * This can be useful in quick testing, but may use large amounts of data if
@@ -19,6 +25,136 @@ export interface MockRTCOptions {
19
25
  recordMessages?: boolean;
20
26
  }
21
27
 
28
+ export interface MockRTCSessionDescription {
29
+ type: 'offer' | 'answer';
30
+ sdp: string;
31
+ }
32
+
33
+ export interface SelectedRTCCandidate {
34
+ address: string;
35
+ port: number;
36
+ protocol: 'udp' | 'tcp';
37
+ type: RTCIceCandidateType;
38
+ };
39
+
40
+ export interface TimingEvents {
41
+ // Milliseconds since unix epoch
42
+ startTime: number;
43
+
44
+ // High-precision floating-point monotonically increasing timestamps.
45
+ // Comparable and precise, but not related to specific current time.
46
+ connectTimestamp: number;
47
+ disconnectTimestamp?: number;
48
+ externalAttachTimestamp?: number;
49
+
50
+ // Other events (everything that might not be a on-off) each come with their
51
+ // eventTimestamp property for that specific event.
52
+ }
53
+
54
+ export type MockRTCEventData = {
55
+ "peer-connected": {
56
+ peerId: string;
57
+ sessionId: string;
58
+ metadata: ConnectionMetadata;
59
+ localSessionDescription: MockRTCSessionDescription;
60
+ remoteSessionDescription: MockRTCSessionDescription;
61
+ selectedLocalCandidate: SelectedRTCCandidate;
62
+ selectedRemoteCandidate: SelectedRTCCandidate;
63
+
64
+ timingEvents: TimingEvents;
65
+ },
66
+ "peer-disconnected": {
67
+ peerId: string;
68
+ sessionId: string;
69
+
70
+ timingEvents: TimingEvents;
71
+ },
72
+ "external-peer-attached": {
73
+ peerId: string;
74
+ sessionId: string;
75
+ externalConnection: {
76
+ peerId: string;
77
+ sessionId: string;
78
+ localSessionDescription: MockRTCSessionDescription;
79
+ remoteSessionDescription: MockRTCSessionDescription;
80
+ selectedLocalCandidate: SelectedRTCCandidate;
81
+ selectedRemoteCandidate: SelectedRTCCandidate;
82
+ };
83
+ timingEvents: TimingEvents;
84
+ },
85
+ "data-channel-opened": {
86
+ peerId: string;
87
+ sessionId: string;
88
+ channelId: number;
89
+ channelLabel: string;
90
+ channelProtocol: string;
91
+
92
+ eventTimestamp: number;
93
+ timingEvents: TimingEvents;
94
+ },
95
+ "data-channel-message-sent": {
96
+ peerId: string;
97
+ sessionId: string;
98
+ channelId: number;
99
+ direction: 'sent';
100
+ content: Buffer;
101
+ isBinary: boolean;
102
+
103
+ eventTimestamp: number;
104
+ timingEvents: TimingEvents;
105
+ },
106
+ "data-channel-message-received": {
107
+ peerId: string;
108
+ sessionId: string;
109
+ channelId: number;
110
+ direction: 'received';
111
+ content: Buffer;
112
+ isBinary: boolean;
113
+
114
+ eventTimestamp: number;
115
+ timingEvents: TimingEvents;
116
+ },
117
+ "data-channel-closed": {
118
+ peerId: string;
119
+ sessionId: string;
120
+ channelId: number;
121
+
122
+ eventTimestamp: number;
123
+ timingEvents: TimingEvents;
124
+ }
125
+ "media-track-opened": {
126
+ peerId: string;
127
+ sessionId: string;
128
+ trackMid: string;
129
+ trackType: string;
130
+ trackDirection: string;
131
+
132
+ eventTimestamp: number;
133
+ timingEvents: TimingEvents;
134
+ },
135
+ "media-track-stats": {
136
+ peerId: string;
137
+ sessionId: string;
138
+ trackMid: string;
139
+
140
+ totalBytesSent: number;
141
+ totalBytesReceived: number;
142
+
143
+ eventTimestamp: number;
144
+ timingEvents: TimingEvents;
145
+ },
146
+ "media-track-closed": {
147
+ peerId: string;
148
+ sessionId: string;
149
+ trackMid: string;
150
+
151
+ eventTimestamp: number;
152
+ timingEvents: TimingEvents;
153
+ }
154
+ };
155
+
156
+ export type MockRTCEvent = keyof MockRTCEventData;
157
+
22
158
  export interface MockRTC {
23
159
 
24
160
  /**
@@ -26,11 +162,37 @@ export interface MockRTC {
26
162
  * must be configured with the mock peer's settings. Once configured the
27
163
  * peer can be created by calling any `.thenX()` method to define the
28
164
  * peer's behaviour.
165
+ *
166
+ * This API allows you to define a single set of handling steps, and then
167
+ * connect directly to the resulting peer to run those steps directly.
168
+ *
169
+ * To instead define multiple behaviours that match different conditions, and
170
+ * then connect clients who may each see different behaviour, define your
171
+ * rules using the `forX()` methods, and connect by using `getMatchingPeer()`.
29
172
  */
30
173
  buildPeer(): MockRTCPeerBuilder;
31
174
 
175
+ /**
176
+ * Get the rule-matching peer.
177
+ *
178
+ * This peer accepts connections, matches them against defined rules (defined
179
+ * via the `.forX()` methods) and then handles them according to the steps
180
+ * for the defined rule.
181
+ *
182
+ * To more directly define a set of steps and make a connection that will
183
+ * follow those steps, define a peer with `.buildPeer()` and then connect
184
+ * to that directly.
185
+ *
186
+ * The default behaviour of this peer for unmatched connections is equivalent
187
+ * to `.thenPassThrough()` - it will accept all incoming data without response
188
+ * initially, and proxy all data to a remote peer if one is attached.
189
+ */
190
+ getMatchingPeer(): MockRTCPeer;
191
+
32
192
  start(): Promise<void>;
33
193
 
34
194
  stop(): Promise<void>;
35
195
 
196
+ on<E extends MockRTCEvent>(event: E, callback: (param: MockRTCEventData[E]) => void): Promise<void>;
197
+
36
198
  }
@@ -3,13 +3,15 @@
3
3
  * SPDX-License-Identifier: Apache-2.0
4
4
  */
5
5
 
6
+ import * as _ from 'lodash';
6
7
  import * as stream from 'stream';
7
8
  import { gql } from 'graphql-tag';
8
9
  import { PluggableAdmin } from 'mockttp';
9
10
  import type { IResolvers } from "@graphql-tools/utils";
11
+ import { PubSub } from "graphql-subscriptions";
10
12
 
11
13
  import { HandlerStep, StepLookup } from '../handling/handler-steps';
12
- import { MockRTCOptions } from '../mockrtc';
14
+ import { MockRTCOptions, MockRTCSessionDescription } from '../mockrtc';
13
15
  import { MockRTCServer } from './mockrtc-server';
14
16
  import { AnswerOptions, OfferOptions } from '../mockrtc-peer';
15
17
 
@@ -18,9 +20,22 @@ type SerializedValue<T> = PluggableAdmin.Serialization.SerializedValue<T>;
18
20
 
19
21
  export interface SessionData {
20
22
  id: string;
21
- description: RTCSessionDescriptionInit
23
+ description: MockRTCSessionDescription
22
24
  }
23
25
 
26
+ const EVENTS = [
27
+ 'peer-connected',
28
+ 'peer-disconnected',
29
+ 'external-peer-attached',
30
+ 'data-channel-opened',
31
+ 'data-channel-message-sent',
32
+ 'data-channel-message-received',
33
+ 'data-channel-closed',
34
+ 'media-track-opened',
35
+ 'media-track-stats',
36
+ 'media-track-closed'
37
+ ] as const;
38
+
24
39
  export class MockRTCAdminPlugin implements PluggableAdmin.AdminPlugin<MockRTCOptions, {}> {
25
40
 
26
41
  private mockRTCServer!: MockRTCServer;
@@ -30,7 +45,9 @@ export class MockRTCAdminPlugin implements PluggableAdmin.AdminPlugin<MockRTCOpt
30
45
  return this.mockRTCServer.start();
31
46
  }
32
47
 
33
- reset() {}
48
+ reset() {
49
+ this.mockRTCServer.reset();
50
+ }
34
51
 
35
52
  stop() {
36
53
  return this.mockRTCServer.stop();
@@ -76,9 +93,137 @@ export class MockRTCAdminPlugin implements PluggableAdmin.AdminPlugin<MockRTCOpt
76
93
  }
77
94
 
78
95
  scalar HandlerStep
96
+
97
+ extend type Subscription {
98
+ peerConnected: RTCPeerConnectionEvent!
99
+ peerDisconnected: RTCPeerDisconnectionEvent!
100
+ externalPeerAttached: RTCAttachmentEvent!
101
+
102
+ dataChannelOpened: DataChannelOpenEvent!
103
+ dataChannelMessageSent: DataChannelMessageEvent!
104
+ dataChannelMessageReceived: DataChannelMessageEvent!
105
+ dataChannelClosed: DataChannelCloseEvent!
106
+
107
+ mediaTrackOpened: MediaTrackOpenEvent!
108
+ mediaTrackStats: MediaTrackStatsEvent!
109
+ mediaTrackClosed: MediaTrackCloseEvent!
110
+ }
111
+
112
+ type RTCPeerConnectionEvent {
113
+ peerId: ID!
114
+ sessionId: ID!
115
+
116
+ metadata: Raw!
117
+ timingEvents: Raw!
118
+
119
+ localSessionDescription: SessionDescriptionResult!
120
+ remoteSessionDescription: SessionDescriptionResult!
121
+ selectedLocalCandidate: RTCSelectedCandidate!
122
+ selectedRemoteCandidate: RTCSelectedCandidate!
123
+ }
124
+
125
+ type RTCSelectedCandidate {
126
+ address: String!
127
+ port: Int!
128
+ protocol: String!
129
+ type: String!
130
+ }
131
+
132
+ type RTCPeerDisconnectionEvent {
133
+ peerId: ID!
134
+ sessionId: ID!
135
+ timingEvents: Raw!
136
+ }
137
+
138
+ type RTCAttachmentEvent {
139
+ peerId: ID!
140
+ sessionId: ID!
141
+ externalConnection: RTCExternalPeerConnectionEvent!
142
+ timingEvents: Raw!
143
+ }
144
+
145
+ type RTCExternalPeerConnectionEvent {
146
+ sessionId: ID!
147
+ localSessionDescription: SessionDescriptionResult!
148
+ remoteSessionDescription: SessionDescriptionResult!
149
+ selectedLocalCandidate: RTCSelectedCandidate!
150
+ selectedRemoteCandidate: RTCSelectedCandidate!
151
+ }
152
+
153
+ type DataChannelOpenEvent {
154
+ peerId: ID!
155
+ sessionId: ID!
156
+ channelId: Int!
157
+ channelLabel: String!
158
+ channelProtocol: String!
159
+
160
+ eventTimestamp: Float!
161
+ timingEvents: Raw!
162
+ }
163
+
164
+ type DataChannelMessageEvent {
165
+ peerId: ID!
166
+ sessionId: ID!
167
+ channelId: Int!
168
+ direction: String!
169
+ content: Buffer!
170
+ isBinary: Boolean!
171
+
172
+ eventTimestamp: Float!
173
+ timingEvents: Raw!
174
+ }
175
+
176
+ type DataChannelCloseEvent {
177
+ peerId: ID!
178
+ sessionId: ID!
179
+ channelId: Int!
180
+
181
+ eventTimestamp: Float!
182
+ timingEvents: Raw!
183
+ }
184
+
185
+ type MediaTrackOpenEvent {
186
+ peerId: ID!
187
+ sessionId: ID!
188
+ trackMid: ID!
189
+ trackType: String!
190
+ trackDirection: String!
191
+
192
+ eventTimestamp: Float!
193
+ timingEvents: Raw!
194
+ }
195
+
196
+ type MediaTrackStatsEvent {
197
+ peerId: ID!
198
+ sessionId: ID!
199
+ trackMid: ID!
200
+
201
+ totalBytesSent: Int!
202
+ totalBytesReceived: Int!
203
+
204
+ eventTimestamp: Float!
205
+ timingEvents: Raw!
206
+ }
207
+
208
+ type MediaTrackCloseEvent {
209
+ peerId: ID!
210
+ sessionId: ID!
211
+ trackMid: ID!
212
+
213
+ eventTimestamp: Float!
214
+ timingEvents: Raw!
215
+ }
79
216
  `;
80
217
 
81
218
  buildResolvers(adminStream: stream.Duplex, ruleParams: {}): IResolvers {
219
+ const pubsub = new PubSub();
220
+
221
+ EVENTS.forEach((eventName) => {
222
+ this.mockRTCServer.on(eventName, (peer) => {
223
+ pubsub.publish(eventName, { [_.camelCase(eventName)]: peer });
224
+ });
225
+ });
226
+
82
227
  return {
83
228
  Mutation: {
84
229
  createPeer: (__: any, { data: { steps } }: { data: {
@@ -128,7 +273,7 @@ export class MockRTCAdminPlugin implements PluggableAdmin.AdminPlugin<MockRTCOpt
128
273
  completeOffer: async (__: any, { peerId, sessionId, answer } : {
129
274
  peerId: string,
130
275
  sessionId: string,
131
- answer: RTCSessionDescriptionInit
276
+ answer: MockRTCSessionDescription
132
277
  }): Promise<void> => {
133
278
  const session = this.mockRTCServer.getPeer(peerId).getSession(sessionId);
134
279
  await session.completeOffer(answer);
@@ -136,7 +281,7 @@ export class MockRTCAdminPlugin implements PluggableAdmin.AdminPlugin<MockRTCOpt
136
281
  answerOffer: async (__: any, { peerId, sessionId, offer, options } : {
137
282
  peerId: string,
138
283
  sessionId?: string,
139
- offer: RTCSessionDescriptionInit,
284
+ offer: MockRTCSessionDescription,
140
285
  options?: AnswerOptions
141
286
  }): Promise<SessionData> => {
142
287
  const peer = this.mockRTCServer.getPeer(peerId);
@@ -156,7 +301,7 @@ export class MockRTCAdminPlugin implements PluggableAdmin.AdminPlugin<MockRTCOpt
156
301
  },
157
302
  answerExternalOffer: async (__: any, { peerId, offer, options } : {
158
303
  peerId: string,
159
- offer: RTCSessionDescriptionInit,
304
+ offer: MockRTCSessionDescription,
160
305
  options?: AnswerOptions
161
306
  }): Promise<SessionData> => {
162
307
  const peer = this.mockRTCServer.getPeer(peerId);
@@ -190,6 +335,14 @@ export class MockRTCAdminPlugin implements PluggableAdmin.AdminPlugin<MockRTCOpt
190
335
  }
191
336
  });
192
337
  }
338
+ },
339
+ Subscription: {
340
+ ...(EVENTS.reduce((acc, eventName) => ({
341
+ ...acc,
342
+ [_.camelCase(eventName)]: {
343
+ subscribe: () => pubsub.asyncIterator(eventName)
344
+ }
345
+ }), {}))
193
346
  }
194
347
  };
195
348
  }