@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.
@@ -34,7 +34,12 @@ __export(src_exports, {
34
34
  });
35
35
  module.exports = __toCommonJS(src_exports);
36
36
  var import_tiny_uid = __toESM(require("tiny-uid"));
37
- var counters = {};
37
+ var internalEvents = [
38
+ "MESSENGER_BEACON",
39
+ "MESSENGER_DISCONNECT",
40
+ "MESSENGER_REQUEST_LOST_EVENTS",
41
+ "MESSENGER_EVENT_BATCH"
42
+ ];
38
43
  var Messenger = class _Messenger {
39
44
  constructor(options) {
40
45
  this.options = options;
@@ -46,14 +51,11 @@ var Messenger = class _Messenger {
46
51
  );
47
52
  this.handleMessage = this._handleMessage.bind(this);
48
53
  this.options.addListener(this.handleMessage);
49
- if (!_Messenger.contextEvents[this.options.context]) {
50
- _Messenger.contextEvents[this.options.context] = [];
51
- }
52
54
  }
53
- /** static record of events per context */
54
- static contextEvents = {};
55
- /** connections record */
56
- connections = {};
55
+ /** static record of events by isntance ID */
56
+ static events = {};
57
+ /** static connections record by instance ID */
58
+ static connections = {};
57
59
  /** beacon interval */
58
60
  beaconInterval = null;
59
61
  /** time between beacons milliseconds */
@@ -69,30 +71,50 @@ var Messenger = class _Messenger {
69
71
  );
70
72
  }
71
73
  }
74
+ getConnection(id) {
75
+ if (!_Messenger.connections[this.id]) {
76
+ _Messenger.connections[this.id] = {};
77
+ }
78
+ return _Messenger.connections[this.id][id];
79
+ }
80
+ addConnection(id) {
81
+ _Messenger.connections[this.id][id] = {
82
+ id,
83
+ messagesReceived: 0,
84
+ messagesSent: 0,
85
+ desync: false
86
+ };
87
+ }
88
+ getEvents() {
89
+ if (!_Messenger.events[this.id]) {
90
+ _Messenger.events[this.id] = [];
91
+ }
92
+ return _Messenger.events[this.id];
93
+ }
94
+ addEvent(event) {
95
+ const events = this.getEvents();
96
+ events.push(event);
97
+ }
72
98
  /** generate a sequential id for each non-internal message */
73
- getTransactionID(message, target) {
74
- const internalEvents = [
75
- "MESSENGER_BEACON",
76
- "MESSENGER_DISCONNECT",
77
- "MESSENGER_REQUEST_LOST_EVENTS",
78
- "MESSENGER_EVENT_BATCH"
79
- ];
80
- if (!target || internalEvents.includes(message.type)) {
99
+ getTransactionID(message) {
100
+ if (!message.target || internalEvents.includes(message.type)) {
81
101
  return -1;
82
102
  }
83
- if (!counters[target]) {
84
- counters[target] = 0;
103
+ if (!this.getConnection(message.target)) {
104
+ this.addConnection(message.target);
85
105
  }
86
- return counters[target]++;
106
+ const connection = this.getConnection(message.target);
107
+ connection.messagesSent += 1;
108
+ return connection.messagesSent;
87
109
  }
88
- addTransactionMetadata(event, target) {
110
+ addTransactionMetadata(event) {
89
111
  const metadata = {
90
112
  _messenger_: true,
91
- id: this.getTransactionID(event, target),
113
+ id: this.getTransactionID(event),
92
114
  sender: this.id,
93
115
  timestamp: Date.now(),
94
116
  context: this.options.context,
95
- ...target && { target }
117
+ ...event.target && { target: event.target }
96
118
  };
97
119
  return {
98
120
  ...metadata,
@@ -114,8 +136,9 @@ var Messenger = class _Messenger {
114
136
  const isFromMessenger = parsed._messenger_;
115
137
  const isFromSelf = parsed.sender === this.id;
116
138
  const isFromSameContext = parsed.context === this.options.context;
117
- const isTargetingOthers = parsed.target && parsed.target !== this.id;
118
- const isKnownConnection = this.connections[parsed.sender];
139
+ const isTargetingOthers = parsed.target ? parsed.target !== this.id : false;
140
+ const connection = this.getConnection(parsed.sender);
141
+ const isKnownConnection = Boolean(connection);
119
142
  if (!isFromMessenger || isFromSelf || isFromSameContext || isTargetingOthers || isKnownConnection && parsed.type === "MESSENGER_BEACON") {
120
143
  return;
121
144
  }
@@ -132,29 +155,31 @@ var Messenger = class _Messenger {
132
155
  if (isKnownConnection) {
133
156
  const isBatch = parsed.type === "MESSENGER_EVENT_BATCH";
134
157
  const transactionID = isBatch ? parsed.payload.events[0].id : parsed.id;
135
- const { lastReceivedMessageId, desync } = this.connections[parsed.sender];
136
- if (transactionID <= lastReceivedMessageId) {
158
+ const { messagesReceived, desync } = connection;
159
+ if (transactionID > -1 && transactionID <= messagesReceived) {
137
160
  return;
138
161
  }
139
- if (!desync && transactionID > lastReceivedMessageId + 1) {
162
+ if (!desync && transactionID > -1 && transactionID > messagesReceived + 1) {
140
163
  const message = {
141
164
  type: "MESSENGER_REQUEST_LOST_EVENTS",
142
165
  payload: {
143
- lastReceivedMessageId
144
- }
166
+ messagesReceived
167
+ },
168
+ target: parsed.sender
145
169
  };
146
170
  this.options.sendMessage(this.addTransactionMetadata(message));
147
171
  this.log(
148
172
  `requesting lost messages from ${parsed.context}:${parsed.sender}`
149
173
  );
150
- this.connections[parsed.sender].desync = true;
174
+ connection.desync = true;
151
175
  return;
152
176
  }
153
177
  if (isBatch) {
154
- this.connections[parsed.sender].desync = false;
178
+ connection.desync = false;
179
+ connection.messagesReceived += parsed.payload.events.length;
180
+ } else {
181
+ connection.messagesReceived += 1;
155
182
  }
156
- const newLastMessageIdx = isBatch ? parsed.payload.events.slice(-1)[0].id : parsed.id;
157
- this.connections[parsed.sender].lastReceivedMessageId = newLastMessageIdx;
158
183
  }
159
184
  this.options.messageCallback(parsed);
160
185
  this.log(
@@ -162,67 +187,71 @@ var Messenger = class _Messenger {
162
187
  );
163
188
  }
164
189
  handleBeaconMessage(parsed) {
165
- if (_Messenger.contextEvents[this.options.context].length > 0) {
190
+ if (this.getConnection(parsed.sender)) {
191
+ return;
192
+ }
193
+ this.addConnection(parsed.sender);
194
+ const events = this.getEvents();
195
+ if (events.length > 0) {
166
196
  const message = {
167
197
  type: "MESSENGER_EVENT_BATCH",
168
198
  payload: {
169
- events: _Messenger.contextEvents[this.options.context].map(
170
- (event) => this.addTransactionMetadata(event, parsed.sender)
171
- )
172
- }
199
+ events: events.map((event) => this.addTransactionMetadata(event))
200
+ },
201
+ target: parsed.sender
173
202
  };
174
- this.options.sendMessage(
175
- this.addTransactionMetadata(message, parsed.sender)
176
- );
203
+ this.options.sendMessage(this.addTransactionMetadata(message));
177
204
  this.log(
178
- `messages [0 - ${_Messenger.contextEvents[this.options.context].length - 1}] sent to ${parsed.context}:${parsed.sender}`
205
+ `messages [0 - ${events.length - 1}] sent to ${parsed.context}:${parsed.sender}`
179
206
  );
207
+ const connection = this.getConnection(parsed.sender);
208
+ connection.messagesSent = events.length;
180
209
  }
181
- const newConnection = {
182
- id: parsed.sender,
183
- lastSentMessageId: _Messenger.contextEvents[this.options.context].length - 1,
184
- lastReceivedMessageId: -1,
185
- desync: false
186
- };
187
- this.connections[parsed.sender] = newConnection;
188
210
  this.log(`new connection added - ${parsed.context}:${parsed.sender}`);
189
211
  }
190
212
  handleLostEventsRequest(parsed) {
191
- const lastMessageIdx = parsed.payload.lastReceivedMessageId;
192
- const missingEvents = _Messenger.contextEvents[this.options.context].slice(
193
- lastMessageIdx + 1,
194
- _Messenger.contextEvents[this.options.context].length
195
- );
213
+ const connection = this.getConnection(parsed.sender);
214
+ const events = this.getEvents();
215
+ if (!connection || events.length === 0) {
216
+ return;
217
+ }
218
+ const missingEvents = events.slice(connection.messagesSent, events.length);
219
+ if (missingEvents.length === 0) {
220
+ return;
221
+ }
196
222
  const message = {
197
223
  type: "MESSENGER_EVENT_BATCH",
198
224
  payload: {
199
225
  events: missingEvents.map(
200
- (event) => this.addTransactionMetadata(event, parsed.sender)
226
+ (event) => this.addTransactionMetadata(event)
201
227
  )
202
- }
228
+ },
229
+ target: parsed.sender
203
230
  };
204
231
  this.options.sendMessage(this.addTransactionMetadata(message));
232
+ connection.messagesSent = events.length;
205
233
  this.log(
206
- `messages [0 - ${_Messenger.contextEvents[this.options.context].length - 1}] sent to ${parsed.context}:${parsed.sender}`
234
+ `messages [0 - ${events.length - 1}] sent to ${parsed.context}:${parsed.sender}`
207
235
  );
208
236
  }
209
237
  handleDisconnectMessage(parsed) {
210
- delete this.connections[parsed.sender];
238
+ delete _Messenger.connections[parsed.sender];
211
239
  this.log(`disconnected - ${parsed.context}:${parsed.sender}`);
212
240
  }
213
241
  sendMessage(message) {
214
242
  const parsed = typeof message === "string" ? JSON.parse(message) : message;
215
- _Messenger.contextEvents[this.options.context].push(parsed);
216
- Object.values(this.connections).forEach(({ id }) => {
217
- const msg = this.addTransactionMetadata(parsed, id);
218
- this.options.sendMessage(msg).then(() => {
219
- this.connections[id].lastSentMessageId = msg.id;
220
- }).catch(() => {
221
- this.options.handleFailedMessage?.(msg);
222
- this.log(
223
- `message failed: ${msg.context}:${id} - index: ${_Messenger.contextEvents[this.options.context].length}`
224
- );
225
- });
243
+ this.addEvent(parsed);
244
+ const target = parsed.target || null;
245
+ const msg = this.addTransactionMetadata(parsed);
246
+ const connection = target ? this.getConnection(target) : null;
247
+ if (connection) {
248
+ connection.messagesSent += 1;
249
+ }
250
+ this.options.sendMessage(msg).catch(() => {
251
+ this.options.handleFailedMessage?.(msg);
252
+ this.log(
253
+ `failed to send message: ${parsed.type} from ${this.id} to ${target || "all"}`
254
+ );
226
255
  });
227
256
  }
228
257
  destroy() {
@@ -230,18 +259,22 @@ var Messenger = class _Messenger {
230
259
  clearInterval(this.beaconInterval);
231
260
  }
232
261
  this.options.removeListener(this.handleMessage);
233
- Object.keys(this.connections).forEach((connection) => {
262
+ Object.keys(_Messenger.connections).forEach((connection) => {
234
263
  const event = {
235
264
  type: "MESSENGER_DISCONNECT",
236
- payload: null
265
+ payload: null,
266
+ target: connection
237
267
  };
238
- const message = this.addTransactionMetadata(event, connection);
268
+ const message = this.addTransactionMetadata(event);
239
269
  this.options.sendMessage(message);
240
270
  });
271
+ _Messenger.reset();
241
272
  this.log("destroyed");
242
273
  }
243
- static resetEvents() {
244
- _Messenger.contextEvents = {};
274
+ /** reset static records */
275
+ static reset() {
276
+ _Messenger.events = {};
277
+ _Messenger.connections = {};
245
278
  }
246
279
  };
247
280
  // Annotate the CommonJS export names for ESM import in node:
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/devtools/messenger/src/index.ts"],"sourcesContent":["import uid from \"tiny-uid\";\nimport type {\n BaseEvent,\n Connection,\n DisconnectEvent,\n EventsBatchEvent,\n InternalEvent,\n MessengerEvent,\n MessengerOptions,\n RequestLostEventsEvent,\n Transaction,\n TransactionMetadata,\n} from \"@player-tools/devtools-types\";\n\nconst counters: Record<string, number> = {};\n\n/**\n * Messenger<EventsType>\n *\n * Self-sufficient, lossless communication between instances.\n *\n * @param options\n * @param options.context - context to use for this instance\n * @param options.id - unique id for this instance, will be generated if not provided\n * @param options.beaconIntervalMS - time to wait between beacons in milliseconds, defaults to 1000\n * @param options.debug - if true, will log debug messages to console, defaults to false\n * @param options.messageCallback - callback to handle messages\n * @param options.sendMessage - function to send messages\n * @param options.addListener - function to add a listener\n * @param options.removeListener - function to remove a listener\n * @param options.handleFailedMessage - function to handle failed messages\n * @param option.log - function to handle logging\n * @returns Messenger\n * @example\n * ```typescript\n * const messenger = new Messenger({{\n * context: \"devtools\",\n * target: \"player\",\n * messageCallback: (message) => dispatch(message),\n * sendMessage: (message) =>\n * browser.tabs\n * ? browser.tabs\n * .query({ active: true, currentWindow: true })\n * .then((tabs) => {\n * if (tabs[0].id) {\n * browser.tabs.sendMessage(tabs[0].id, message);\n * }\n * })\n * : browser.runtime.sendMessage(message),\n * addListener: (callback) => {\n * browser.runtime.onMessage.addListener(callback);\n * },\n * removeListener: (callback) => {\n * browser.runtime.onMessage.removeListener(callback);\n * },\n * });\n * ```\n */\nexport class Messenger<T extends BaseEvent<string, unknown>> {\n /** static record of events per context */\n private static contextEvents: Record<\n string,\n Array<BaseEvent<string, unknown>>\n > = {};\n\n /** connections record */\n private connections: Record<string, Connection> = {};\n\n /** beacon interval */\n private beaconInterval: NodeJS.Timeout | null = null;\n\n /** time between beacons milliseconds */\n private beaconIntervalMS: number;\n\n /** callback to handle messages, here for instance binding */\n private handleMessage: (\n message: TransactionMetadata & MessengerEvent<T>\n ) => void;\n\n /** unique id */\n private id: string;\n\n constructor(private options: MessengerOptions<T>) {\n // set defaults:\n this.id = options.id || uid();\n this.beaconIntervalMS = options.beaconIntervalMS || 1000;\n\n // start beacon interval:\n this.beaconInterval = setInterval(\n this.beacon.bind(this),\n this.beaconIntervalMS\n );\n\n // bind message handler:\n this.handleMessage = this._handleMessage.bind(this);\n\n // add listener:\n this.options.addListener(this.handleMessage);\n\n // if events for this context don't exist, create an empty array\n if (!(Messenger.contextEvents[this.options.context] as T[])) {\n (Messenger.contextEvents[this.options.context] as T[]) = [];\n }\n }\n\n private log(message: string) {\n if (this.options.debug) {\n this.options.logger.log(\n `[MESSENGER-${this.id}](${this.options.context}): ${message}`\n );\n }\n }\n\n /** generate a sequential id for each non-internal message */\n private getTransactionID(message: MessengerEvent<T>, target?: string) {\n const internalEvents: Array<InternalEvent<T>[\"type\"]> = [\n \"MESSENGER_BEACON\",\n \"MESSENGER_DISCONNECT\",\n \"MESSENGER_REQUEST_LOST_EVENTS\",\n \"MESSENGER_EVENT_BATCH\",\n ];\n\n if (\n !target ||\n internalEvents.includes(message.type as InternalEvent<T>[\"type\"])\n ) {\n return -1;\n }\n\n if (!counters[target]) {\n counters[target] = 0;\n }\n\n return counters[target]++;\n }\n\n private addTransactionMetadata(\n event: MessengerEvent<T>,\n target?: string\n ): Transaction<T> {\n const metadata = {\n _messenger_: true,\n id: this.getTransactionID(event, target),\n sender: this.id,\n timestamp: Date.now(),\n context: this.options.context,\n ...(target && { target }),\n };\n\n return {\n ...metadata,\n ...event,\n };\n }\n\n /** there is no persistent layer bookkeeping connections,\n * so beacon to inform others of its presence */\n private beacon() {\n this.options.sendMessage(\n this.addTransactionMetadata({\n type: \"MESSENGER_BEACON\",\n payload: null,\n })\n );\n }\n\n private _handleMessage(transaction: Transaction<T>) {\n const parsed: Transaction<T> =\n typeof transaction === \"string\" ? JSON.parse(transaction) : transaction;\n\n const isFromMessenger = parsed._messenger_;\n const isFromSelf = parsed.sender === this.id;\n const isFromSameContext = parsed.context === this.options.context;\n const isTargetingOthers = parsed.target && parsed.target !== this.id;\n const isKnownConnection = this.connections[parsed.sender];\n\n if (\n !isFromMessenger ||\n isFromSelf ||\n isFromSameContext ||\n isTargetingOthers ||\n (isKnownConnection && parsed.type === \"MESSENGER_BEACON\")\n ) {\n return;\n }\n\n const handlers: Record<string, (parsed: Transaction<T>) => void> = {\n MESSENGER_BEACON: this.handleBeaconMessage.bind(this),\n MESSENGER_DISCONNECT: this.handleDisconnectMessage.bind(this),\n MESSENGER_REQUEST_LOST_EVENTS: this.handleLostEventsRequest.bind(this),\n };\n\n const handler = handlers[(parsed as BaseEvent<string, unknown>).type];\n\n if (handler) {\n handler(parsed);\n return;\n }\n\n if (isKnownConnection) {\n const isBatch = parsed.type === \"MESSENGER_EVENT_BATCH\";\n\n // if batch, get the first message id, so we can check for missing messages:\n const transactionID = isBatch\n ? (parsed.payload as EventsBatchEvent<T>[\"payload\"]).events[0].id\n : parsed.id;\n\n const { lastReceivedMessageId, desync } = this.connections[parsed.sender];\n\n // if we already received this message, ignore:\n if (transactionID <= lastReceivedMessageId) {\n return;\n }\n\n // if we missed messages, request them, unless we already did:\n if (!desync && transactionID > lastReceivedMessageId + 1) {\n const message: RequestLostEventsEvent = {\n type: \"MESSENGER_REQUEST_LOST_EVENTS\",\n payload: {\n lastReceivedMessageId,\n },\n };\n\n this.options.sendMessage(this.addTransactionMetadata(message));\n\n this.log(\n `requesting lost messages from ${parsed.context}:${parsed.sender}`\n );\n\n // set desync, so we don't request again:\n this.connections[parsed.sender].desync = true;\n\n // don't process this message, since we requested missing ones:\n return;\n }\n\n if (isBatch) {\n // clear desync flag on event batch:\n this.connections[parsed.sender].desync = false;\n }\n\n // if batch, get the last message id:\n const newLastMessageIdx = isBatch\n ? (parsed.payload as EventsBatchEvent<T>[\"payload\"]).events.slice(-1)[0]\n .id\n : parsed.id;\n\n // update last received message id:\n this.connections[parsed.sender].lastReceivedMessageId = newLastMessageIdx;\n }\n\n this.options.messageCallback(parsed);\n\n this.log(\n `message received: ${(parsed as BaseEvent<string, unknown>).type}`\n );\n }\n\n private handleBeaconMessage(parsed: Transaction<T>) {\n // if we reach here, we assume a new connection and send all events:\n if ((Messenger.contextEvents[this.options.context] as T[]).length > 0) {\n const message: EventsBatchEvent<T> = {\n type: \"MESSENGER_EVENT_BATCH\",\n payload: {\n events: (Messenger.contextEvents[this.options.context] as T[]).map(\n (event) => this.addTransactionMetadata(event, parsed.sender)\n ),\n },\n };\n\n this.options.sendMessage(\n this.addTransactionMetadata(message, parsed.sender)\n );\n\n this.log(\n `messages [0 - ${\n (Messenger.contextEvents[this.options.context] as T[]).length - 1\n }] sent to ${parsed.context}:${parsed.sender}`\n );\n }\n\n const newConnection: Connection = {\n id: parsed.sender,\n lastSentMessageId:\n (Messenger.contextEvents[this.options.context] as T[]).length - 1,\n lastReceivedMessageId: -1,\n desync: false,\n };\n\n this.connections[parsed.sender] = newConnection;\n\n this.log(`new connection added - ${parsed.context}:${parsed.sender}`);\n }\n\n private handleLostEventsRequest(parsed: Transaction<T>) {\n const lastMessageIdx = (parsed.payload as RequestLostEventsEvent[\"payload\"])\n .lastReceivedMessageId;\n\n const missingEvents = (\n Messenger.contextEvents[this.options.context] as T[]\n ).slice(\n lastMessageIdx + 1,\n (Messenger.contextEvents[this.options.context] as T[]).length\n );\n\n const message: EventsBatchEvent<T> = {\n type: \"MESSENGER_EVENT_BATCH\",\n payload: {\n events: missingEvents.map((event) =>\n this.addTransactionMetadata(event, parsed.sender)\n ),\n },\n };\n\n this.options.sendMessage(this.addTransactionMetadata(message));\n\n this.log(\n `messages [0 - ${\n (Messenger.contextEvents[this.options.context] as T[]).length - 1\n }] sent to ${parsed.context}:${parsed.sender}`\n );\n }\n\n private handleDisconnectMessage(\n parsed: TransactionMetadata & MessengerEvent<T>\n ) {\n delete this.connections[parsed.sender];\n\n this.log(`disconnected - ${parsed.context}:${parsed.sender}`);\n }\n\n public sendMessage(message: T | string) {\n const parsed: T =\n typeof message === \"string\" ? JSON.parse(message) : message;\n\n (Messenger.contextEvents[this.options.context] as T[]).push(parsed);\n\n // send to all connections:\n Object.values(this.connections).forEach(({ id }) => {\n const msg = this.addTransactionMetadata(parsed, id);\n\n this.options\n .sendMessage(msg)\n .then(() => {\n this.connections[id].lastSentMessageId = msg.id;\n })\n .catch(() => {\n this.options.handleFailedMessage?.(msg);\n\n this.log(\n `message failed: ${msg.context}:${id} - index: ${\n (Messenger.contextEvents[this.options.context] as T[]).length\n }`\n );\n });\n });\n }\n\n public destroy() {\n if (this.beaconInterval) {\n clearInterval(this.beaconInterval);\n }\n\n this.options.removeListener(this.handleMessage);\n\n Object.keys(this.connections).forEach((connection) => {\n const event: DisconnectEvent = {\n type: \"MESSENGER_DISCONNECT\",\n payload: null,\n };\n const message = this.addTransactionMetadata(event, connection);\n this.options.sendMessage(message);\n });\n\n this.log(\"destroyed\");\n }\n\n static resetEvents() {\n Messenger.contextEvents = {};\n }\n}\n"],"mappings":"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","names":["uid"]}
1
+ {"version":3,"sources":["../../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/devtools/messenger/src/index.ts"],"sourcesContent":["import uid from \"tiny-uid\";\nimport type {\n BaseEvent,\n Connection,\n DisconnectEvent,\n EventsBatchEvent,\n InternalEvent,\n MessengerEvent,\n MessengerOptions,\n RequestLostEventsEvent,\n Transaction,\n TransactionMetadata,\n} from \"@player-tools/devtools-types\";\n\nconst internalEvents: Array<InternalEvent<BaseEvent<string, unknown>>[\"type\"]> =\n [\n \"MESSENGER_BEACON\",\n \"MESSENGER_DISCONNECT\",\n \"MESSENGER_REQUEST_LOST_EVENTS\",\n \"MESSENGER_EVENT_BATCH\",\n ];\n\n/**\n * Messenger<EventsType>\n *\n * Self-sufficient, lossless communication between instances.\n *\n * @param options\n * @param options.context - context to use for this instance\n * @param options.id - unique id for this instance, will be generated if not provided\n * @param options.beaconIntervalMS - time to wait between beacons in milliseconds, defaults to 1000\n * @param options.debug - if true, will log debug messages to console, defaults to false\n * @param options.messageCallback - callback to handle messages\n * @param options.sendMessage - function to send messages\n * @param options.addListener - function to add a listener\n * @param options.removeListener - function to remove a listener\n * @param options.handleFailedMessage - function to handle failed messages\n * @param option.log - function to handle logging\n * @returns Messenger\n * @example\n * ```typescript\n * const messenger = new Messenger({{\n * context: \"devtools\",\n * target: \"player\",\n * messageCallback: (message) => dispatch(message),\n * sendMessage: (message) =>\n * browser.tabs\n * ? browser.tabs\n * .query({ active: true, currentWindow: true })\n * .then((tabs) => {\n * if (tabs[0].id) {\n * browser.tabs.sendMessage(tabs[0].id, message);\n * }\n * })\n * : browser.runtime.sendMessage(message),\n * addListener: (callback) => {\n * browser.runtime.onMessage.addListener(callback);\n * },\n * removeListener: (callback) => {\n * browser.runtime.onMessage.removeListener(callback);\n * },\n * });\n * ```\n */\nexport class Messenger<T extends BaseEvent<string, unknown>> {\n /** static record of events by isntance ID */\n private static events: Record<\n string,\n Array<MessageEvent<BaseEvent<string, unknown>>>\n > = {};\n\n /** static connections record by instance ID */\n private static connections: Record<string, Record<string, Connection>> = {};\n\n /** beacon interval */\n private beaconInterval: NodeJS.Timeout | null = null;\n\n /** time between beacons milliseconds */\n private beaconIntervalMS: number;\n\n /** callback to handle messages, here for instance binding */\n private handleMessage: (\n message: TransactionMetadata & MessengerEvent<T>\n ) => void;\n\n /** unique id */\n private id: string;\n\n constructor(private options: MessengerOptions<T>) {\n // set defaults:\n this.id = options.id || uid();\n this.beaconIntervalMS = options.beaconIntervalMS || 1000;\n\n // start beacon interval:\n this.beaconInterval = setInterval(\n this.beacon.bind(this),\n this.beaconIntervalMS\n );\n\n // bind message handler:\n this.handleMessage = this._handleMessage.bind(this);\n\n // add listener:\n this.options.addListener(this.handleMessage);\n }\n\n private log(message: string) {\n if (this.options.debug) {\n this.options.logger.log(\n `[MESSENGER-${this.id}](${this.options.context}): ${message}`\n );\n }\n }\n\n private getConnection(id: string) {\n if (!Messenger.connections[this.id]) {\n Messenger.connections[this.id] = {};\n }\n\n return Messenger.connections[this.id][id];\n }\n\n private addConnection(id: string) {\n Messenger.connections[this.id][id] = {\n id,\n messagesReceived: 0,\n messagesSent: 0,\n desync: false,\n };\n }\n\n private getEvents() {\n if (!Messenger.events[this.id]) {\n Messenger.events[this.id] = [];\n }\n\n return Messenger.events[this.id] as unknown as MessengerEvent<T>[];\n }\n\n private addEvent(event: MessengerEvent<T>) {\n const events = this.getEvents();\n events.push(event);\n }\n\n /** generate a sequential id for each non-internal message */\n private getTransactionID(message: MessengerEvent<T>) {\n if (\n !message.target ||\n internalEvents.includes(message.type as InternalEvent<T>[\"type\"])\n ) {\n return -1;\n }\n\n if (!this.getConnection(message.target)) {\n this.addConnection(message.target);\n }\n\n const connection = this.getConnection(message.target);\n connection.messagesSent += 1;\n return connection.messagesSent;\n }\n\n private addTransactionMetadata(event: MessengerEvent<T>): Transaction<T> {\n const metadata = {\n _messenger_: true,\n id: this.getTransactionID(event),\n sender: this.id,\n timestamp: Date.now(),\n context: this.options.context,\n ...(event.target && { target: event.target }),\n };\n\n return {\n ...metadata,\n ...event,\n };\n }\n\n /** there is no persistent layer bookkeeping connections,\n * so beacon to inform others of its presence */\n private beacon() {\n this.options.sendMessage(\n this.addTransactionMetadata({\n type: \"MESSENGER_BEACON\",\n payload: null,\n })\n );\n }\n\n private _handleMessage(transaction: Transaction<T>) {\n const parsed: Transaction<T> =\n typeof transaction === \"string\" ? JSON.parse(transaction) : transaction;\n\n const isFromMessenger = parsed._messenger_;\n const isFromSelf = parsed.sender === this.id;\n const isFromSameContext = parsed.context === this.options.context;\n const isTargetingOthers = parsed.target ? parsed.target !== this.id : false;\n const connection = this.getConnection(parsed.sender);\n const isKnownConnection = Boolean(connection);\n\n if (\n !isFromMessenger ||\n isFromSelf ||\n isFromSameContext ||\n isTargetingOthers ||\n (isKnownConnection && parsed.type === \"MESSENGER_BEACON\")\n ) {\n return;\n }\n\n const handlers: Record<string, (parsed: Transaction<T>) => void> = {\n MESSENGER_BEACON: this.handleBeaconMessage.bind(this),\n MESSENGER_DISCONNECT: this.handleDisconnectMessage.bind(this),\n MESSENGER_REQUEST_LOST_EVENTS: this.handleLostEventsRequest.bind(this),\n };\n\n const handler = handlers[(parsed as BaseEvent<string, unknown>).type];\n\n if (handler) {\n handler(parsed);\n return;\n }\n\n if (isKnownConnection) {\n const isBatch = parsed.type === \"MESSENGER_EVENT_BATCH\";\n\n const transactionID = isBatch\n ? (parsed.payload as EventsBatchEvent<T>[\"payload\"]).events[0].id\n : parsed.id;\n\n const { messagesReceived, desync } = connection;\n\n // if we already received this message, ignore:\n if (transactionID > -1 && transactionID <= messagesReceived) {\n return;\n }\n\n // if we missed messages, request them, unless we already did:\n if (\n !desync &&\n transactionID > -1 &&\n transactionID > messagesReceived + 1\n ) {\n const message: RequestLostEventsEvent = {\n type: \"MESSENGER_REQUEST_LOST_EVENTS\",\n payload: {\n messagesReceived,\n },\n target: parsed.sender,\n };\n\n this.options.sendMessage(this.addTransactionMetadata(message));\n\n this.log(\n `requesting lost messages from ${parsed.context}:${parsed.sender}`\n );\n\n // set desync, so we don't request again:\n connection.desync = true;\n\n // don't process this message, since we requested missing ones:\n return;\n }\n\n if (isBatch) {\n // clear desync flag on event batch:\n connection.desync = false;\n connection.messagesReceived += (\n parsed.payload as EventsBatchEvent<T>[\"payload\"]\n ).events.length;\n } else {\n connection.messagesReceived += 1;\n }\n }\n\n this.options.messageCallback(parsed);\n\n this.log(\n `message received: ${(parsed as BaseEvent<string, unknown>).type}`\n );\n }\n\n private handleBeaconMessage(parsed: Transaction<T>) {\n if (this.getConnection(parsed.sender)) {\n return;\n }\n\n this.addConnection(parsed.sender);\n const events = this.getEvents();\n\n if (events.length > 0) {\n const message: EventsBatchEvent<T> = {\n type: \"MESSENGER_EVENT_BATCH\",\n payload: {\n events: events.map((event) => this.addTransactionMetadata(event)),\n },\n target: parsed.sender,\n };\n\n this.options.sendMessage(this.addTransactionMetadata(message));\n\n this.log(\n `messages [0 - ${events.length - 1}] sent to ${parsed.context}:${\n parsed.sender\n }`\n );\n\n const connection = this.getConnection(parsed.sender);\n connection.messagesSent = events.length;\n }\n\n this.log(`new connection added - ${parsed.context}:${parsed.sender}`);\n }\n\n private handleLostEventsRequest(parsed: Transaction<T>) {\n const connection = this.getConnection(parsed.sender);\n const events = this.getEvents();\n\n if (!connection || events.length === 0) {\n return;\n }\n\n const missingEvents = events.slice(connection.messagesSent, events.length);\n\n if (missingEvents.length === 0) {\n return;\n }\n\n const message: EventsBatchEvent<T> = {\n type: \"MESSENGER_EVENT_BATCH\",\n payload: {\n events: missingEvents.map((event) =>\n this.addTransactionMetadata(event)\n ),\n },\n target: parsed.sender,\n };\n\n this.options.sendMessage(this.addTransactionMetadata(message));\n\n connection.messagesSent = events.length;\n\n this.log(\n `messages [0 - ${events.length - 1}] sent to ${parsed.context}:${\n parsed.sender\n }`\n );\n }\n\n private handleDisconnectMessage(\n parsed: TransactionMetadata & MessengerEvent<T>\n ) {\n delete Messenger.connections[parsed.sender];\n\n this.log(`disconnected - ${parsed.context}:${parsed.sender}`);\n }\n\n public sendMessage(message: T | string) {\n const parsed: T =\n typeof message === \"string\" ? JSON.parse(message) : message;\n\n this.addEvent(parsed);\n\n const target = parsed.target || null;\n const msg = this.addTransactionMetadata(parsed);\n const connection = target ? this.getConnection(target) : null;\n\n if (connection) {\n connection.messagesSent += 1;\n }\n\n this.options.sendMessage(msg).catch(() => {\n this.options.handleFailedMessage?.(msg);\n\n this.log(\n `failed to send message: ${\n (parsed as BaseEvent<string, unknown>).type\n } from ${this.id} to ${target || \"all\"}`\n );\n });\n }\n\n public destroy() {\n if (this.beaconInterval) {\n clearInterval(this.beaconInterval);\n }\n\n this.options.removeListener(this.handleMessage);\n\n Object.keys(Messenger.connections).forEach((connection) => {\n const event: DisconnectEvent = {\n type: \"MESSENGER_DISCONNECT\",\n payload: null,\n target: connection,\n };\n const message = this.addTransactionMetadata(event);\n this.options.sendMessage(message);\n });\n\n Messenger.reset();\n this.log(\"destroyed\");\n }\n\n /** reset static records */\n static reset() {\n Messenger.events = {};\n Messenger.connections = {};\n }\n}\n"],"mappings":"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","names":["uid"]}
@@ -1,6 +1,11 @@
1
1
  // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/devtools/messenger/src/index.ts
2
2
  import uid from "tiny-uid";
3
- var counters = {};
3
+ var internalEvents = [
4
+ "MESSENGER_BEACON",
5
+ "MESSENGER_DISCONNECT",
6
+ "MESSENGER_REQUEST_LOST_EVENTS",
7
+ "MESSENGER_EVENT_BATCH"
8
+ ];
4
9
  var Messenger = class _Messenger {
5
10
  constructor(options) {
6
11
  this.options = options;
@@ -12,14 +17,11 @@ var Messenger = class _Messenger {
12
17
  );
13
18
  this.handleMessage = this._handleMessage.bind(this);
14
19
  this.options.addListener(this.handleMessage);
15
- if (!_Messenger.contextEvents[this.options.context]) {
16
- _Messenger.contextEvents[this.options.context] = [];
17
- }
18
20
  }
19
- /** static record of events per context */
20
- static contextEvents = {};
21
- /** connections record */
22
- connections = {};
21
+ /** static record of events by isntance ID */
22
+ static events = {};
23
+ /** static connections record by instance ID */
24
+ static connections = {};
23
25
  /** beacon interval */
24
26
  beaconInterval = null;
25
27
  /** time between beacons milliseconds */
@@ -35,30 +37,50 @@ var Messenger = class _Messenger {
35
37
  );
36
38
  }
37
39
  }
40
+ getConnection(id) {
41
+ if (!_Messenger.connections[this.id]) {
42
+ _Messenger.connections[this.id] = {};
43
+ }
44
+ return _Messenger.connections[this.id][id];
45
+ }
46
+ addConnection(id) {
47
+ _Messenger.connections[this.id][id] = {
48
+ id,
49
+ messagesReceived: 0,
50
+ messagesSent: 0,
51
+ desync: false
52
+ };
53
+ }
54
+ getEvents() {
55
+ if (!_Messenger.events[this.id]) {
56
+ _Messenger.events[this.id] = [];
57
+ }
58
+ return _Messenger.events[this.id];
59
+ }
60
+ addEvent(event) {
61
+ const events = this.getEvents();
62
+ events.push(event);
63
+ }
38
64
  /** generate a sequential id for each non-internal message */
39
- getTransactionID(message, target) {
40
- const internalEvents = [
41
- "MESSENGER_BEACON",
42
- "MESSENGER_DISCONNECT",
43
- "MESSENGER_REQUEST_LOST_EVENTS",
44
- "MESSENGER_EVENT_BATCH"
45
- ];
46
- if (!target || internalEvents.includes(message.type)) {
65
+ getTransactionID(message) {
66
+ if (!message.target || internalEvents.includes(message.type)) {
47
67
  return -1;
48
68
  }
49
- if (!counters[target]) {
50
- counters[target] = 0;
69
+ if (!this.getConnection(message.target)) {
70
+ this.addConnection(message.target);
51
71
  }
52
- return counters[target]++;
72
+ const connection = this.getConnection(message.target);
73
+ connection.messagesSent += 1;
74
+ return connection.messagesSent;
53
75
  }
54
- addTransactionMetadata(event, target) {
76
+ addTransactionMetadata(event) {
55
77
  const metadata = {
56
78
  _messenger_: true,
57
- id: this.getTransactionID(event, target),
79
+ id: this.getTransactionID(event),
58
80
  sender: this.id,
59
81
  timestamp: Date.now(),
60
82
  context: this.options.context,
61
- ...target && { target }
83
+ ...event.target && { target: event.target }
62
84
  };
63
85
  return {
64
86
  ...metadata,
@@ -80,8 +102,9 @@ var Messenger = class _Messenger {
80
102
  const isFromMessenger = parsed._messenger_;
81
103
  const isFromSelf = parsed.sender === this.id;
82
104
  const isFromSameContext = parsed.context === this.options.context;
83
- const isTargetingOthers = parsed.target && parsed.target !== this.id;
84
- const isKnownConnection = this.connections[parsed.sender];
105
+ const isTargetingOthers = parsed.target ? parsed.target !== this.id : false;
106
+ const connection = this.getConnection(parsed.sender);
107
+ const isKnownConnection = Boolean(connection);
85
108
  if (!isFromMessenger || isFromSelf || isFromSameContext || isTargetingOthers || isKnownConnection && parsed.type === "MESSENGER_BEACON") {
86
109
  return;
87
110
  }
@@ -98,29 +121,31 @@ var Messenger = class _Messenger {
98
121
  if (isKnownConnection) {
99
122
  const isBatch = parsed.type === "MESSENGER_EVENT_BATCH";
100
123
  const transactionID = isBatch ? parsed.payload.events[0].id : parsed.id;
101
- const { lastReceivedMessageId, desync } = this.connections[parsed.sender];
102
- if (transactionID <= lastReceivedMessageId) {
124
+ const { messagesReceived, desync } = connection;
125
+ if (transactionID > -1 && transactionID <= messagesReceived) {
103
126
  return;
104
127
  }
105
- if (!desync && transactionID > lastReceivedMessageId + 1) {
128
+ if (!desync && transactionID > -1 && transactionID > messagesReceived + 1) {
106
129
  const message = {
107
130
  type: "MESSENGER_REQUEST_LOST_EVENTS",
108
131
  payload: {
109
- lastReceivedMessageId
110
- }
132
+ messagesReceived
133
+ },
134
+ target: parsed.sender
111
135
  };
112
136
  this.options.sendMessage(this.addTransactionMetadata(message));
113
137
  this.log(
114
138
  `requesting lost messages from ${parsed.context}:${parsed.sender}`
115
139
  );
116
- this.connections[parsed.sender].desync = true;
140
+ connection.desync = true;
117
141
  return;
118
142
  }
119
143
  if (isBatch) {
120
- this.connections[parsed.sender].desync = false;
144
+ connection.desync = false;
145
+ connection.messagesReceived += parsed.payload.events.length;
146
+ } else {
147
+ connection.messagesReceived += 1;
121
148
  }
122
- const newLastMessageIdx = isBatch ? parsed.payload.events.slice(-1)[0].id : parsed.id;
123
- this.connections[parsed.sender].lastReceivedMessageId = newLastMessageIdx;
124
149
  }
125
150
  this.options.messageCallback(parsed);
126
151
  this.log(
@@ -128,67 +153,71 @@ var Messenger = class _Messenger {
128
153
  );
129
154
  }
130
155
  handleBeaconMessage(parsed) {
131
- if (_Messenger.contextEvents[this.options.context].length > 0) {
156
+ if (this.getConnection(parsed.sender)) {
157
+ return;
158
+ }
159
+ this.addConnection(parsed.sender);
160
+ const events = this.getEvents();
161
+ if (events.length > 0) {
132
162
  const message = {
133
163
  type: "MESSENGER_EVENT_BATCH",
134
164
  payload: {
135
- events: _Messenger.contextEvents[this.options.context].map(
136
- (event) => this.addTransactionMetadata(event, parsed.sender)
137
- )
138
- }
165
+ events: events.map((event) => this.addTransactionMetadata(event))
166
+ },
167
+ target: parsed.sender
139
168
  };
140
- this.options.sendMessage(
141
- this.addTransactionMetadata(message, parsed.sender)
142
- );
169
+ this.options.sendMessage(this.addTransactionMetadata(message));
143
170
  this.log(
144
- `messages [0 - ${_Messenger.contextEvents[this.options.context].length - 1}] sent to ${parsed.context}:${parsed.sender}`
171
+ `messages [0 - ${events.length - 1}] sent to ${parsed.context}:${parsed.sender}`
145
172
  );
173
+ const connection = this.getConnection(parsed.sender);
174
+ connection.messagesSent = events.length;
146
175
  }
147
- const newConnection = {
148
- id: parsed.sender,
149
- lastSentMessageId: _Messenger.contextEvents[this.options.context].length - 1,
150
- lastReceivedMessageId: -1,
151
- desync: false
152
- };
153
- this.connections[parsed.sender] = newConnection;
154
176
  this.log(`new connection added - ${parsed.context}:${parsed.sender}`);
155
177
  }
156
178
  handleLostEventsRequest(parsed) {
157
- const lastMessageIdx = parsed.payload.lastReceivedMessageId;
158
- const missingEvents = _Messenger.contextEvents[this.options.context].slice(
159
- lastMessageIdx + 1,
160
- _Messenger.contextEvents[this.options.context].length
161
- );
179
+ const connection = this.getConnection(parsed.sender);
180
+ const events = this.getEvents();
181
+ if (!connection || events.length === 0) {
182
+ return;
183
+ }
184
+ const missingEvents = events.slice(connection.messagesSent, events.length);
185
+ if (missingEvents.length === 0) {
186
+ return;
187
+ }
162
188
  const message = {
163
189
  type: "MESSENGER_EVENT_BATCH",
164
190
  payload: {
165
191
  events: missingEvents.map(
166
- (event) => this.addTransactionMetadata(event, parsed.sender)
192
+ (event) => this.addTransactionMetadata(event)
167
193
  )
168
- }
194
+ },
195
+ target: parsed.sender
169
196
  };
170
197
  this.options.sendMessage(this.addTransactionMetadata(message));
198
+ connection.messagesSent = events.length;
171
199
  this.log(
172
- `messages [0 - ${_Messenger.contextEvents[this.options.context].length - 1}] sent to ${parsed.context}:${parsed.sender}`
200
+ `messages [0 - ${events.length - 1}] sent to ${parsed.context}:${parsed.sender}`
173
201
  );
174
202
  }
175
203
  handleDisconnectMessage(parsed) {
176
- delete this.connections[parsed.sender];
204
+ delete _Messenger.connections[parsed.sender];
177
205
  this.log(`disconnected - ${parsed.context}:${parsed.sender}`);
178
206
  }
179
207
  sendMessage(message) {
180
208
  const parsed = typeof message === "string" ? JSON.parse(message) : message;
181
- _Messenger.contextEvents[this.options.context].push(parsed);
182
- Object.values(this.connections).forEach(({ id }) => {
183
- const msg = this.addTransactionMetadata(parsed, id);
184
- this.options.sendMessage(msg).then(() => {
185
- this.connections[id].lastSentMessageId = msg.id;
186
- }).catch(() => {
187
- this.options.handleFailedMessage?.(msg);
188
- this.log(
189
- `message failed: ${msg.context}:${id} - index: ${_Messenger.contextEvents[this.options.context].length}`
190
- );
191
- });
209
+ this.addEvent(parsed);
210
+ const target = parsed.target || null;
211
+ const msg = this.addTransactionMetadata(parsed);
212
+ const connection = target ? this.getConnection(target) : null;
213
+ if (connection) {
214
+ connection.messagesSent += 1;
215
+ }
216
+ this.options.sendMessage(msg).catch(() => {
217
+ this.options.handleFailedMessage?.(msg);
218
+ this.log(
219
+ `failed to send message: ${parsed.type} from ${this.id} to ${target || "all"}`
220
+ );
192
221
  });
193
222
  }
194
223
  destroy() {
@@ -196,18 +225,22 @@ var Messenger = class _Messenger {
196
225
  clearInterval(this.beaconInterval);
197
226
  }
198
227
  this.options.removeListener(this.handleMessage);
199
- Object.keys(this.connections).forEach((connection) => {
228
+ Object.keys(_Messenger.connections).forEach((connection) => {
200
229
  const event = {
201
230
  type: "MESSENGER_DISCONNECT",
202
- payload: null
231
+ payload: null,
232
+ target: connection
203
233
  };
204
- const message = this.addTransactionMetadata(event, connection);
234
+ const message = this.addTransactionMetadata(event);
205
235
  this.options.sendMessage(message);
206
236
  });
237
+ _Messenger.reset();
207
238
  this.log("destroyed");
208
239
  }
209
- static resetEvents() {
210
- _Messenger.contextEvents = {};
240
+ /** reset static records */
241
+ static reset() {
242
+ _Messenger.events = {};
243
+ _Messenger.connections = {};
211
244
  }
212
245
  };
213
246
  export {