@basmilius/apple-companion-link 0.3.2 → 0.4.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 (2) hide show
  1. package/dist/index.js +32 -17
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -189,33 +189,36 @@ import { randomInt } from "node:crypto";
189
189
  import { Chacha20, ENCRYPTION, EncryptionAwareConnection } from "@basmilius/apple-common";
190
190
  import { OPack } from "@basmilius/apple-encoding";
191
191
  var HEADER_SIZE = 4;
192
+ var PAIRING_QUEUE_IDENTIFIER = -1;
192
193
 
193
194
  class Stream extends EncryptionAwareConnection {
194
195
  get #encryptionState() {
195
196
  return this[ENCRYPTION];
196
197
  }
197
- #queue = {};
198
+ #queue = new Map;
198
199
  #buffer = Buffer.alloc(0);
199
200
  #xid;
200
201
  constructor(context, address, port) {
201
202
  super(context, address, port);
202
203
  this.#xid = randomInt(0, 2 ** 16);
204
+ this.on("close", this.#onClose.bind(this));
203
205
  this.on("data", this.#onData.bind(this));
206
+ this.on("error", this.#onError.bind(this));
204
207
  }
205
208
  async exchange(type, obj) {
206
209
  const _x = this.#xid;
207
210
  return new Promise((resolve, reject) => {
208
211
  if (PairFrameTypes.includes(type)) {
209
- this.#queue[-1] = resolve;
212
+ this.#queue.set(PAIRING_QUEUE_IDENTIFIER, [resolve, reject]);
210
213
  } else {
211
- this.#queue[_x] = resolve;
214
+ this.#queue.set(_x, [resolve, reject]);
212
215
  }
213
216
  this.send(type, obj).catch(reject);
214
217
  });
215
218
  }
216
219
  async send(type, obj) {
217
220
  const _x = this.#xid++;
218
- obj._x ??= OPack.sizedInt(_x, 8);
221
+ obj._x ??= OPack.sizedInteger(_x, 8);
219
222
  let payload = Buffer.from(OPack.encode(obj));
220
223
  let payloadLength = payload.byteLength;
221
224
  if (this.isEncrypted && payloadLength > 0) {
@@ -241,6 +244,13 @@ class Stream extends EncryptionAwareConnection {
241
244
  this.emit("error", err);
242
245
  }
243
246
  }
247
+ #onClose() {
248
+ const error = new Error("Connection closed while waiting for response");
249
+ for (const [, reject] of this.#queue.values()) {
250
+ reject(error);
251
+ }
252
+ this.#queue.clear();
253
+ }
244
254
  async#onData(data) {
245
255
  this.#buffer = Buffer.concat([this.#buffer, data]);
246
256
  try {
@@ -267,6 +277,12 @@ class Stream extends EncryptionAwareConnection {
267
277
  this.emit("error", err);
268
278
  }
269
279
  }
280
+ #onError(err) {
281
+ for (const [, reject] of this.#queue.values()) {
282
+ reject(err);
283
+ }
284
+ this.#queue.clear();
285
+ }
270
286
  #decrypt(data) {
271
287
  const header = data.subarray(0, 4);
272
288
  const payloadLength = header.readUintBE(1, 3);
@@ -287,25 +303,24 @@ class Stream extends EncryptionAwareConnection {
287
303
  payload = OPack.decode(payload);
288
304
  this.context.logger.raw("[companion-link]", "Decoded OPACK", { header, payload });
289
305
  if ("_x" in payload) {
290
- const _x = payload._x;
291
- if (_x in this.#queue) {
292
- const resolve = this.#queue[_x] ?? null;
293
- resolve?.([header, payload]);
294
- delete this.#queue[_x];
306
+ const _x = Number(payload["_x"]);
307
+ if (this.#queue.has(_x)) {
308
+ const [resolve] = this.#queue.get(_x);
309
+ resolve([header, payload]);
310
+ this.#queue.delete(_x);
295
311
  } else if ("_i" in payload) {
296
312
  this.emit(payload["_i"], payload["_c"]);
297
313
  } else {
298
314
  const content = payload["_c"];
299
- const keys = Object.keys(content).map((k) => k.substring(0, -3));
315
+ const keys = Object.keys(content).map((k) => k.slice(0, -3));
300
316
  for (const key of keys) {
301
317
  this.emit(key, content[key]);
302
318
  }
303
319
  }
304
- } else if (this.#queue[-1]) {
305
- const _x = -1;
306
- const resolve = this.#queue[_x] ?? null;
307
- resolve?.([header, payload]);
308
- delete this.#queue[_x];
320
+ } else if (this.#queue.has(PAIRING_QUEUE_IDENTIFIER)) {
321
+ const [resolve] = this.#queue.get(PAIRING_QUEUE_IDENTIFIER);
322
+ resolve([header, payload]);
323
+ this.#queue.delete(PAIRING_QUEUE_IDENTIFIER);
309
324
  } else {
310
325
  this.context.logger.warn("[companion-link]", "No handler for message", [header, payload]);
311
326
  }
@@ -601,10 +616,10 @@ class Protocol {
601
616
  }
602
617
  }
603
618
  function objectOrFail(obj) {
604
- if (typeof obj === "object") {
619
+ if (obj !== null && typeof obj === "object") {
605
620
  return obj;
606
621
  }
607
- throw new Error("Expected an object.");
622
+ throw new TypeError("Expected an object.");
608
623
  }
609
624
  export {
610
625
  convertAttentionState,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@basmilius/apple-companion-link",
3
3
  "description": "Implementation of Apple's Companion Link in Node.js.",
4
- "version": "0.3.2",
4
+ "version": "0.4.0",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "author": {
@@ -40,8 +40,8 @@
40
40
  }
41
41
  },
42
42
  "dependencies": {
43
- "@basmilius/apple-common": "0.3.2",
44
- "@basmilius/apple-encoding": "0.3.2"
43
+ "@basmilius/apple-common": "0.4.0",
44
+ "@basmilius/apple-encoding": "0.4.0"
45
45
  },
46
46
  "devDependencies": {
47
47
  "@basmilius/tools": "^2.23.0",