@player-tools/devtools-messenger 0.8.0-next.0 → 0.8.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/src/index.ts CHANGED
@@ -12,7 +12,13 @@ import type {
12
12
  TransactionMetadata,
13
13
  } from "@player-tools/devtools-types";
14
14
 
15
- const counters: Record<string, number> = {};
15
+ const internalEvents: Array<InternalEvent<BaseEvent<string, unknown>>["type"]> =
16
+ [
17
+ "MESSENGER_BEACON",
18
+ "MESSENGER_DISCONNECT",
19
+ "MESSENGER_REQUEST_LOST_EVENTS",
20
+ "MESSENGER_EVENT_BATCH",
21
+ ];
16
22
 
17
23
  /**
18
24
  * Messenger<EventsType>
@@ -57,14 +63,14 @@ const counters: Record<string, number> = {};
57
63
  * ```
58
64
  */
59
65
  export class Messenger<T extends BaseEvent<string, unknown>> {
60
- /** static record of events per context */
61
- private static contextEvents: Record<
66
+ /** static record of events by isntance ID */
67
+ private static events: Record<
62
68
  string,
63
- Array<BaseEvent<string, unknown>>
69
+ Array<MessageEvent<BaseEvent<string, unknown>>>
64
70
  > = {};
65
71
 
66
- /** connections record */
67
- private connections: Record<string, Connection> = {};
72
+ /** static connections record by instance ID */
73
+ private static connections: Record<string, Record<string, Connection>> = {};
68
74
 
69
75
  /** beacon interval */
70
76
  private beaconInterval: NodeJS.Timeout | null = null;
@@ -96,11 +102,6 @@ export class Messenger<T extends BaseEvent<string, unknown>> {
96
102
 
97
103
  // add listener:
98
104
  this.options.addListener(this.handleMessage);
99
-
100
- // if events for this context don't exist, create an empty array
101
- if (!(Messenger.contextEvents[this.options.context] as T[])) {
102
- (Messenger.contextEvents[this.options.context] as T[]) = [];
103
- }
104
105
  }
105
106
 
106
107
  private log(message: string) {
@@ -111,40 +112,62 @@ export class Messenger<T extends BaseEvent<string, unknown>> {
111
112
  }
112
113
  }
113
114
 
114
- /** generate a sequential id for each non-internal message */
115
- private getTransactionID(message: MessengerEvent<T>, target?: string) {
116
- const internalEvents: Array<InternalEvent<T>["type"]> = [
117
- "MESSENGER_BEACON",
118
- "MESSENGER_DISCONNECT",
119
- "MESSENGER_REQUEST_LOST_EVENTS",
120
- "MESSENGER_EVENT_BATCH",
121
- ];
115
+ private getConnection(id: string) {
116
+ if (!Messenger.connections[this.id]) {
117
+ Messenger.connections[this.id] = {};
118
+ }
119
+
120
+ return Messenger.connections[this.id][id];
121
+ }
122
+
123
+ private addConnection(id: string) {
124
+ Messenger.connections[this.id][id] = {
125
+ id,
126
+ messagesReceived: 0,
127
+ messagesSent: 0,
128
+ desync: false,
129
+ };
130
+ }
131
+
132
+ private getEvents() {
133
+ if (!Messenger.events[this.id]) {
134
+ Messenger.events[this.id] = [];
135
+ }
122
136
 
137
+ return Messenger.events[this.id] as unknown as MessengerEvent<T>[];
138
+ }
139
+
140
+ private addEvent(event: MessengerEvent<T>) {
141
+ const events = this.getEvents();
142
+ events.push(event);
143
+ }
144
+
145
+ /** generate a sequential id for each non-internal message */
146
+ private getTransactionID(message: MessengerEvent<T>) {
123
147
  if (
124
- !target ||
148
+ !message.target ||
125
149
  internalEvents.includes(message.type as InternalEvent<T>["type"])
126
150
  ) {
127
151
  return -1;
128
152
  }
129
153
 
130
- if (!counters[target]) {
131
- counters[target] = 0;
154
+ if (!this.getConnection(message.target)) {
155
+ this.addConnection(message.target);
132
156
  }
133
157
 
134
- return counters[target]++;
158
+ const connection = this.getConnection(message.target);
159
+ connection.messagesSent += 1;
160
+ return connection.messagesSent;
135
161
  }
136
162
 
137
- private addTransactionMetadata(
138
- event: MessengerEvent<T>,
139
- target?: string
140
- ): Transaction<T> {
163
+ private addTransactionMetadata(event: MessengerEvent<T>): Transaction<T> {
141
164
  const metadata = {
142
165
  _messenger_: true,
143
- id: this.getTransactionID(event, target),
166
+ id: this.getTransactionID(event),
144
167
  sender: this.id,
145
168
  timestamp: Date.now(),
146
169
  context: this.options.context,
147
- ...(target && { target }),
170
+ ...(event.target && { target: event.target }),
148
171
  };
149
172
 
150
173
  return {
@@ -171,8 +194,9 @@ export class Messenger<T extends BaseEvent<string, unknown>> {
171
194
  const isFromMessenger = parsed._messenger_;
172
195
  const isFromSelf = parsed.sender === this.id;
173
196
  const isFromSameContext = parsed.context === this.options.context;
174
- const isTargetingOthers = parsed.target && parsed.target !== this.id;
175
- const isKnownConnection = this.connections[parsed.sender];
197
+ const isTargetingOthers = parsed.target ? parsed.target !== this.id : false;
198
+ const connection = this.getConnection(parsed.sender);
199
+ const isKnownConnection = Boolean(connection);
176
200
 
177
201
  if (
178
202
  !isFromMessenger ||
@@ -200,25 +224,29 @@ export class Messenger<T extends BaseEvent<string, unknown>> {
200
224
  if (isKnownConnection) {
201
225
  const isBatch = parsed.type === "MESSENGER_EVENT_BATCH";
202
226
 
203
- // if batch, get the first message id, so we can check for missing messages:
204
227
  const transactionID = isBatch
205
228
  ? (parsed.payload as EventsBatchEvent<T>["payload"]).events[0].id
206
229
  : parsed.id;
207
230
 
208
- const { lastReceivedMessageId, desync } = this.connections[parsed.sender];
231
+ const { messagesReceived, desync } = connection;
209
232
 
210
233
  // if we already received this message, ignore:
211
- if (transactionID <= lastReceivedMessageId) {
234
+ if (transactionID > -1 && transactionID <= messagesReceived) {
212
235
  return;
213
236
  }
214
237
 
215
238
  // if we missed messages, request them, unless we already did:
216
- if (!desync && transactionID > lastReceivedMessageId + 1) {
239
+ if (
240
+ !desync &&
241
+ transactionID > -1 &&
242
+ transactionID > messagesReceived + 1
243
+ ) {
217
244
  const message: RequestLostEventsEvent = {
218
245
  type: "MESSENGER_REQUEST_LOST_EVENTS",
219
246
  payload: {
220
- lastReceivedMessageId,
247
+ messagesReceived,
221
248
  },
249
+ target: parsed.sender,
222
250
  };
223
251
 
224
252
  this.options.sendMessage(this.addTransactionMetadata(message));
@@ -228,7 +256,7 @@ export class Messenger<T extends BaseEvent<string, unknown>> {
228
256
  );
229
257
 
230
258
  // set desync, so we don't request again:
231
- this.connections[parsed.sender].desync = true;
259
+ connection.desync = true;
232
260
 
233
261
  // don't process this message, since we requested missing ones:
234
262
  return;
@@ -236,17 +264,13 @@ export class Messenger<T extends BaseEvent<string, unknown>> {
236
264
 
237
265
  if (isBatch) {
238
266
  // clear desync flag on event batch:
239
- this.connections[parsed.sender].desync = false;
267
+ connection.desync = false;
268
+ connection.messagesReceived += (
269
+ parsed.payload as EventsBatchEvent<T>["payload"]
270
+ ).events.length;
271
+ } else {
272
+ connection.messagesReceived += 1;
240
273
  }
241
-
242
- // if batch, get the last message id:
243
- const newLastMessageIdx = isBatch
244
- ? (parsed.payload as EventsBatchEvent<T>["payload"]).events.slice(-1)[0]
245
- .id
246
- : parsed.id;
247
-
248
- // update last received message id:
249
- this.connections[parsed.sender].lastReceivedMessageId = newLastMessageIdx;
250
274
  }
251
275
 
252
276
  this.options.messageCallback(parsed);
@@ -257,74 +281,76 @@ export class Messenger<T extends BaseEvent<string, unknown>> {
257
281
  }
258
282
 
259
283
  private handleBeaconMessage(parsed: Transaction<T>) {
260
- // if we reach here, we assume a new connection and send all events:
261
- if ((Messenger.contextEvents[this.options.context] as T[]).length > 0) {
284
+ if (this.getConnection(parsed.sender)) {
285
+ return;
286
+ }
287
+
288
+ this.addConnection(parsed.sender);
289
+ const events = this.getEvents();
290
+
291
+ if (events.length > 0) {
262
292
  const message: EventsBatchEvent<T> = {
263
293
  type: "MESSENGER_EVENT_BATCH",
264
294
  payload: {
265
- events: (Messenger.contextEvents[this.options.context] as T[]).map(
266
- (event) => this.addTransactionMetadata(event, parsed.sender)
267
- ),
295
+ events: events.map((event) => this.addTransactionMetadata(event)),
268
296
  },
297
+ target: parsed.sender,
269
298
  };
270
299
 
271
- this.options.sendMessage(
272
- this.addTransactionMetadata(message, parsed.sender)
273
- );
300
+ this.options.sendMessage(this.addTransactionMetadata(message));
274
301
 
275
302
  this.log(
276
- `messages [0 - ${
277
- (Messenger.contextEvents[this.options.context] as T[]).length - 1
278
- }] sent to ${parsed.context}:${parsed.sender}`
303
+ `messages [0 - ${events.length - 1}] sent to ${parsed.context}:${
304
+ parsed.sender
305
+ }`
279
306
  );
280
- }
281
-
282
- const newConnection: Connection = {
283
- id: parsed.sender,
284
- lastSentMessageId:
285
- (Messenger.contextEvents[this.options.context] as T[]).length - 1,
286
- lastReceivedMessageId: -1,
287
- desync: false,
288
- };
289
307
 
290
- this.connections[parsed.sender] = newConnection;
308
+ const connection = this.getConnection(parsed.sender);
309
+ connection.messagesSent = events.length;
310
+ }
291
311
 
292
312
  this.log(`new connection added - ${parsed.context}:${parsed.sender}`);
293
313
  }
294
314
 
295
315
  private handleLostEventsRequest(parsed: Transaction<T>) {
296
- const lastMessageIdx = (parsed.payload as RequestLostEventsEvent["payload"])
297
- .lastReceivedMessageId;
298
-
299
- const missingEvents = (
300
- Messenger.contextEvents[this.options.context] as T[]
301
- ).slice(
302
- lastMessageIdx + 1,
303
- (Messenger.contextEvents[this.options.context] as T[]).length
304
- );
316
+ const connection = this.getConnection(parsed.sender);
317
+ const events = this.getEvents();
318
+
319
+ if (!connection || events.length === 0) {
320
+ return;
321
+ }
322
+
323
+ const missingEvents = events.slice(connection.messagesSent, events.length);
324
+
325
+ if (missingEvents.length === 0) {
326
+ return;
327
+ }
305
328
 
306
329
  const message: EventsBatchEvent<T> = {
307
330
  type: "MESSENGER_EVENT_BATCH",
308
331
  payload: {
309
332
  events: missingEvents.map((event) =>
310
- this.addTransactionMetadata(event, parsed.sender)
333
+ this.addTransactionMetadata(event)
311
334
  ),
312
335
  },
336
+ target: parsed.sender,
313
337
  };
314
338
 
315
339
  this.options.sendMessage(this.addTransactionMetadata(message));
316
340
 
341
+ connection.messagesSent = events.length;
342
+
317
343
  this.log(
318
- `messages [0 - ${
319
- (Messenger.contextEvents[this.options.context] as T[]).length - 1
320
- }] sent to ${parsed.context}:${parsed.sender}`
344
+ `messages [0 - ${events.length - 1}] sent to ${parsed.context}:${
345
+ parsed.sender
346
+ }`
321
347
  );
322
348
  }
323
349
 
324
350
  private handleDisconnectMessage(
325
351
  parsed: TransactionMetadata & MessengerEvent<T>
326
352
  ) {
327
- delete this.connections[parsed.sender];
353
+ delete Messenger.connections[parsed.sender];
328
354
 
329
355
  this.log(`disconnected - ${parsed.context}:${parsed.sender}`);
330
356
  }
@@ -333,26 +359,24 @@ export class Messenger<T extends BaseEvent<string, unknown>> {
333
359
  const parsed: T =
334
360
  typeof message === "string" ? JSON.parse(message) : message;
335
361
 
336
- (Messenger.contextEvents[this.options.context] as T[]).push(parsed);
337
-
338
- // send to all connections:
339
- Object.values(this.connections).forEach(({ id }) => {
340
- const msg = this.addTransactionMetadata(parsed, id);
341
-
342
- this.options
343
- .sendMessage(msg)
344
- .then(() => {
345
- this.connections[id].lastSentMessageId = msg.id;
346
- })
347
- .catch(() => {
348
- this.options.handleFailedMessage?.(msg);
349
-
350
- this.log(
351
- `message failed: ${msg.context}:${id} - index: ${
352
- (Messenger.contextEvents[this.options.context] as T[]).length
353
- }`
354
- );
355
- });
362
+ this.addEvent(parsed);
363
+
364
+ const target = parsed.target || null;
365
+ const msg = this.addTransactionMetadata(parsed);
366
+ const connection = target ? this.getConnection(target) : null;
367
+
368
+ if (connection) {
369
+ connection.messagesSent += 1;
370
+ }
371
+
372
+ this.options.sendMessage(msg).catch(() => {
373
+ this.options.handleFailedMessage?.(msg);
374
+
375
+ this.log(
376
+ `failed to send message: ${
377
+ (parsed as BaseEvent<string, unknown>).type
378
+ } from ${this.id} to ${target || "all"}`
379
+ );
356
380
  });
357
381
  }
358
382
 
@@ -363,19 +387,23 @@ export class Messenger<T extends BaseEvent<string, unknown>> {
363
387
 
364
388
  this.options.removeListener(this.handleMessage);
365
389
 
366
- Object.keys(this.connections).forEach((connection) => {
390
+ Object.keys(Messenger.connections).forEach((connection) => {
367
391
  const event: DisconnectEvent = {
368
392
  type: "MESSENGER_DISCONNECT",
369
393
  payload: null,
394
+ target: connection,
370
395
  };
371
- const message = this.addTransactionMetadata(event, connection);
396
+ const message = this.addTransactionMetadata(event);
372
397
  this.options.sendMessage(message);
373
398
  });
374
399
 
400
+ Messenger.reset();
375
401
  this.log("destroyed");
376
402
  }
377
403
 
378
- static resetEvents() {
379
- Messenger.contextEvents = {};
404
+ /** reset static records */
405
+ static reset() {
406
+ Messenger.events = {};
407
+ Messenger.connections = {};
380
408
  }
381
409
  }
package/types/index.d.ts CHANGED
@@ -43,10 +43,10 @@ import type { BaseEvent, MessengerOptions } from "@player-tools/devtools-types";
43
43
  */
44
44
  export declare class Messenger<T extends BaseEvent<string, unknown>> {
45
45
  private options;
46
- /** static record of events per context */
47
- private static contextEvents;
48
- /** connections record */
49
- private connections;
46
+ /** static record of events by isntance ID */
47
+ private static events;
48
+ /** static connections record by instance ID */
49
+ private static connections;
50
50
  /** beacon interval */
51
51
  private beaconInterval;
52
52
  /** time between beacons milliseconds */
@@ -57,6 +57,10 @@ export declare class Messenger<T extends BaseEvent<string, unknown>> {
57
57
  private id;
58
58
  constructor(options: MessengerOptions<T>);
59
59
  private log;
60
+ private getConnection;
61
+ private addConnection;
62
+ private getEvents;
63
+ private addEvent;
60
64
  /** generate a sequential id for each non-internal message */
61
65
  private getTransactionID;
62
66
  private addTransactionMetadata;
@@ -69,6 +73,7 @@ export declare class Messenger<T extends BaseEvent<string, unknown>> {
69
73
  private handleDisconnectMessage;
70
74
  sendMessage(message: T | string): void;
71
75
  destroy(): void;
72
- static resetEvents(): void;
76
+ /** reset static records */
77
+ static reset(): void;
73
78
  }
74
79
  //# sourceMappingURL=index.d.ts.map