@airframes/acars-decoder 1.8.14 → 1.9.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/dist/index.js CHANGED
@@ -131,11 +131,11 @@ var CoordinateUtils = class _CoordinateUtils {
131
131
  * @returns An object with latitude and longitude properties
132
132
  */
133
133
  static decodeStringCoordinates(stringCoords) {
134
- const firstChar = stringCoords.substring(0, 1);
135
- let middleChar = stringCoords.substring(6, 7);
134
+ const firstChar = stringCoords.charAt(0);
135
+ let middleChar = stringCoords.charAt(6);
136
136
  let longitudeChars = stringCoords.substring(7, 13);
137
- if (middleChar == " ") {
138
- middleChar = stringCoords.substring(7, 8);
137
+ if (middleChar === " ") {
138
+ middleChar = stringCoords.charAt(7);
139
139
  longitudeChars = stringCoords.substring(8, 14);
140
140
  }
141
141
  if ((firstChar === "N" || firstChar === "S") && (middleChar === "W" || middleChar === "E")) {
@@ -153,24 +153,26 @@ var CoordinateUtils = class _CoordinateUtils {
153
153
  * @returns An object with latitude and longitude properties
154
154
  */
155
155
  static decodeStringCoordinatesDecimalMinutes(stringCoords) {
156
- const firstChar = stringCoords.substring(0, 1);
157
- let middleChar = stringCoords.substring(6, 7);
156
+ const firstChar = stringCoords.charAt(0);
157
+ let middleChar = stringCoords.charAt(6);
158
158
  let longitudeChars = stringCoords.substring(7, 13);
159
- if (middleChar == " ") {
160
- middleChar = stringCoords.substring(7, 8);
159
+ if (middleChar === " ") {
160
+ middleChar = stringCoords.charAt(7);
161
161
  longitudeChars = stringCoords.substring(8, 14);
162
162
  }
163
- const latDeg = Math.trunc(Number(stringCoords.substring(1, 6)) / 1e3);
164
- const latMin = Number(stringCoords.substring(1, 6)) % 1e3 / 10;
165
- const lonDeg = Math.trunc(Number(longitudeChars) / 1e3);
166
- const lonMin = Number(longitudeChars) % 1e3 / 10;
167
- if ((firstChar === "N" || firstChar === "S") && (middleChar === "W" || middleChar === "E")) {
168
- return {
169
- latitude: (latDeg + latMin / 60) * _CoordinateUtils.getDirection(firstChar),
170
- longitude: (lonDeg + lonMin / 60) * _CoordinateUtils.getDirection(middleChar)
171
- };
163
+ if (firstChar !== "N" && firstChar !== "S" || middleChar !== "W" && middleChar !== "E") {
164
+ return void 0;
172
165
  }
173
- return void 0;
166
+ const latRaw = Number(stringCoords.substring(1, 6));
167
+ const lonRaw = Number(longitudeChars);
168
+ const latDeg = Math.trunc(latRaw / 1e3);
169
+ const latMin = latRaw % 1e3 / 10;
170
+ const lonDeg = Math.trunc(lonRaw / 1e3);
171
+ const lonMin = lonRaw % 1e3 / 10;
172
+ return {
173
+ latitude: (latDeg + latMin / 60) * _CoordinateUtils.getDirection(firstChar),
174
+ longitude: (lonDeg + lonMin / 60) * _CoordinateUtils.getDirection(middleChar)
175
+ };
174
176
  }
175
177
  static coordinateString(coords) {
176
178
  const latDir = coords.latitude > 0 ? "N" : "S";
@@ -201,25 +203,13 @@ var DateTimeUtils = class {
201
203
  // Expects a six digit date string and a four digit UTC time string
202
204
  // (DDMMYY) (HHMM)
203
205
  static UTCDateTimeToString(dateString, timeString) {
204
- let utcDate = /* @__PURE__ */ new Date();
205
- utcDate.setUTCDate(+dateString.substr(0, 2));
206
- utcDate.setUTCMonth(+dateString.substr(2, 2));
207
- if (dateString.length === 6) {
208
- utcDate.setUTCFullYear(2e3 + +dateString.substr(4, 2));
209
- }
210
- if (timeString.length === 6) {
211
- utcDate.setUTCHours(
212
- +timeString.substr(0, 2),
213
- +timeString.substr(2, 2),
214
- +timeString.substr(4, 2)
215
- );
216
- } else {
217
- utcDate.setUTCHours(
218
- +timeString.substr(0, 2),
219
- +timeString.substr(2, 2),
220
- 0
221
- );
222
- }
206
+ const day = +dateString.substr(0, 2);
207
+ const month = +dateString.substr(2, 2) - 1;
208
+ const year = dateString.length === 6 ? 2e3 + +dateString.substr(4, 2) : (/* @__PURE__ */ new Date()).getUTCFullYear();
209
+ const hours = +timeString.substr(0, 2);
210
+ const minutes = +timeString.substr(2, 2);
211
+ const seconds = timeString.length === 6 ? +timeString.substr(4, 2) : 0;
212
+ const utcDate = new Date(Date.UTC(year, month, day, hours, minutes, seconds));
223
213
  return utcDate.toUTCString();
224
214
  }
225
215
  /**
@@ -564,7 +554,7 @@ var ResultFormatter = class {
564
554
  decodeResult.raw.fuel_remaining = value;
565
555
  decodeResult.formatted.items.push({
566
556
  type: "fuel_remaining",
567
- code: " FUEL_REM",
557
+ code: "FUEL_REM",
568
558
  label: "Fuel Remaining",
569
559
  value: decodeResult.raw.fuel_remaining.toString()
570
560
  });
@@ -958,7 +948,7 @@ var ResultFormatter = class {
958
948
  type: "sequence",
959
949
  code: "SEQ",
960
950
  label: "Sequence Number",
961
- value: decodeResult.raw.sequence_number
951
+ value: decodeResult.raw.sequence_number.toString()
962
952
  });
963
953
  }
964
954
  static sequenceResponse(decodeResult, value) {
@@ -967,7 +957,7 @@ var ResultFormatter = class {
967
957
  type: "sequence",
968
958
  code: "SEQ_RESP",
969
959
  label: "Sequence Response",
970
- value: decodeResult.raw.sequence_response
960
+ value: decodeResult.raw.sequence_response.toString()
971
961
  });
972
962
  }
973
963
  static unknown(decodeResult, value, sep = ",") {
@@ -1073,6 +1063,7 @@ var DecoderPlugin = class {
1073
1063
  };
1074
1064
 
1075
1065
  // lib/plugins/CBand.ts
1066
+ var CBAND_HEADER = /^(?<msgno>[A-Z]\d{2}[A-Z])(?<airline>[A-Z0-9]{2})(?<number>[0-9]{4})/;
1076
1067
  var CBand = class extends DecoderPlugin {
1077
1068
  name = "c-band";
1078
1069
  qualifiers() {
@@ -1081,35 +1072,43 @@ var CBand = class extends DecoderPlugin {
1081
1072
  };
1082
1073
  }
1083
1074
  decode(message, options = {}) {
1084
- let decodeResult = this.defaultResult();
1075
+ const text = message.text;
1076
+ if (text.length < 10) {
1077
+ const result = this.defaultResult();
1078
+ result.decoder.name = this.name;
1079
+ result.message = message;
1080
+ return result;
1081
+ }
1082
+ const cband = CBAND_HEADER.exec(text);
1083
+ if (!cband?.groups) {
1084
+ const result = this.defaultResult();
1085
+ result.decoder.name = this.name;
1086
+ result.message = message;
1087
+ return result;
1088
+ }
1089
+ const decoded = this.decoder.decode(
1090
+ {
1091
+ label: message.label,
1092
+ sublabel: message.sublabel,
1093
+ text: text.substring(10)
1094
+ },
1095
+ options
1096
+ );
1097
+ const decodeResult = this.defaultResult();
1085
1098
  decodeResult.decoder.name = this.name;
1086
1099
  decodeResult.message = message;
1087
- let cband = message.text.match(
1088
- /^(?<msgno>[A-Z]\d{2}[A-Z])(?<airline>[A-Z0-9]{2})(?<number>[0-9]{4})/
1089
- );
1090
- if (cband?.groups) {
1091
- const messageText = message.text.substring(10);
1092
- const decoded = this.decoder.decode(
1093
- {
1094
- label: message.label,
1095
- sublabel: message.sublabel,
1096
- text: messageText
1097
- },
1098
- options
1100
+ if (decoded.decoded) {
1101
+ ResultFormatter.flightNumber(
1102
+ decodeResult,
1103
+ cband.groups.airline + Number(cband.groups.number)
1099
1104
  );
1100
- if (decoded.decoded) {
1101
- ResultFormatter.flightNumber(
1102
- decodeResult,
1103
- cband.groups.airline + Number(cband.groups.number)
1104
- );
1105
- decodeResult.decoded = true;
1106
- decodeResult.decoder.decodeLevel = decoded.decoder.decodeLevel;
1107
- decodeResult.decoder.name = this.name + "-" + decoded.decoder.name;
1108
- decodeResult.raw = { ...decodeResult.raw, ...decoded.raw };
1109
- decodeResult.formatted.description = decoded.formatted.description;
1110
- decodeResult.formatted.items.push(...decoded.formatted.items);
1111
- decodeResult.remaining = decoded.remaining;
1112
- }
1105
+ decodeResult.decoded = true;
1106
+ decodeResult.decoder.decodeLevel = decoded.decoder.decodeLevel;
1107
+ decodeResult.decoder.name = this.name + "-" + decoded.decoder.name;
1108
+ decodeResult.raw = { ...decodeResult.raw, ...decoded.raw };
1109
+ decodeResult.formatted.description = decoded.formatted.description;
1110
+ decodeResult.formatted.items.push(...decoded.formatted.items);
1111
+ decodeResult.remaining = decoded.remaining;
1113
1112
  }
1114
1113
  return decodeResult;
1115
1114
  }
@@ -1194,7 +1193,9 @@ var FlightPlanUtils = class _FlightPlanUtils {
1194
1193
  label: "Route Status",
1195
1194
  value: "Route Planned"
1196
1195
  });
1197
- decodeResult.raw.route_status = header;
1196
+ if (header.length > 2) {
1197
+ addRoute(decodeResult, header.substring(2));
1198
+ }
1198
1199
  } else if (header.startsWith("RI")) {
1199
1200
  decodeResult.raw.route_status = "RI";
1200
1201
  decodeResult.formatted.items.push({
@@ -1292,6 +1293,11 @@ function addCompanyRoute(decodeResult, value) {
1292
1293
  }
1293
1294
 
1294
1295
  // lib/utils/arinc_702_helper.ts
1296
+ function asciiStringToBytes(data) {
1297
+ const bytes = new Uint8Array(data.length);
1298
+ for (let i = 0; i < data.length; i++) bytes[i] = data.charCodeAt(i) & 255;
1299
+ return bytes;
1300
+ }
1295
1301
  var Arinc702Helper = class _Arinc702Helper {
1296
1302
  /**
1297
1303
  * Decodes an ARINC 702 message
@@ -1810,7 +1816,7 @@ function processSummary(decodeResult, data) {
1810
1816
  }
1811
1817
  function crc16IbmSdlcRev(data) {
1812
1818
  let crc = 65535;
1813
- const bytes = Buffer.from(data, "ascii");
1819
+ const bytes = asciiStringToBytes(data);
1814
1820
  for (const byte of bytes) {
1815
1821
  crc ^= byte;
1816
1822
  for (let i = 0; i < 8; i++) {
@@ -1831,7 +1837,7 @@ function crc16IbmSdlcRev(data) {
1831
1837
  function crc16Genibus(data) {
1832
1838
  let crc = 65535;
1833
1839
  const polynomial = 4129;
1834
- const bytes = Buffer.from(data, "ascii");
1840
+ const bytes = asciiStringToBytes(data);
1835
1841
  for (const byte of bytes) {
1836
1842
  crc ^= byte << 8;
1837
1843
  for (let i = 0; i < 8; i++) {
@@ -1856,6 +1862,7 @@ function processMessageReference(decodeResult, data) {
1856
1862
  }
1857
1863
 
1858
1864
  // lib/plugins/ARINC_702.ts
1865
+ var NEWLINE_REGEX = /[\n\r]/g;
1859
1866
  var Arinc702 = class extends DecoderPlugin {
1860
1867
  name = "arinc-702";
1861
1868
  qualifiers() {
@@ -1867,7 +1874,7 @@ var Arinc702 = class extends DecoderPlugin {
1867
1874
  let decodeResult = this.defaultResult();
1868
1875
  decodeResult.decoder.name = this.name;
1869
1876
  decodeResult.message = message;
1870
- const msg = message.text.replace(/\n|\r/g, "");
1877
+ const msg = message.text.replace(NEWLINE_REGEX, "");
1871
1878
  let decoded = false;
1872
1879
  decoded = Arinc702Helper.decodeH1Message(decodeResult, msg);
1873
1880
  if (decoded) {
@@ -3844,7 +3851,8 @@ var Label_4A = class extends DecoderPlugin {
3844
3851
  ResultFormatter.arrivalAirport(decodeResult, fields[5]);
3845
3852
  ResultFormatter.unknownArr(decodeResult, fields.slice(8));
3846
3853
  } else if (fields.length === 6) {
3847
- if (fields[0].match(/^[NS]/)) {
3854
+ const f0 = fields[0];
3855
+ if (f0.length > 0 && (f0[0] === "N" || f0[0] === "S")) {
3848
3856
  ResultFormatter.position(
3849
3857
  decodeResult,
3850
3858
  CoordinateUtils.decodeStringCoordinates(fields[0].substring(0, 13))
@@ -4225,6 +4233,9 @@ var Label_4T_ETA = class extends DecoderPlugin {
4225
4233
  };
4226
4234
 
4227
4235
  // lib/plugins/Label_80.ts
4236
+ var POS_REGEX = /^(?<latd>[NS])(?<lat>.+)(?<lngd>[EW])(?<lng>.+)/;
4237
+ var WS_REGEX = /\s+/;
4238
+ var NEWLINE_REGEX2 = /\r?\n/;
4228
4239
  var Label_80 = class extends DecoderPlugin {
4229
4240
  name = "label-80";
4230
4241
  qualifiers() {
@@ -4238,7 +4249,7 @@ var Label_80 = class extends DecoderPlugin {
4238
4249
  message,
4239
4250
  "Airline Defined Position Report"
4240
4251
  );
4241
- const lines = message.text.split(/\r?\n/);
4252
+ const lines = message.text.split(NEWLINE_REGEX2);
4242
4253
  if (lines.length === 1 && lines[0].includes(",")) {
4243
4254
  this.parseCsvFormat(lines[0], decodeResult);
4244
4255
  } else {
@@ -4273,7 +4284,7 @@ var Label_80 = class extends DecoderPlugin {
4273
4284
  ResultFormatter.unknown(results, header, "/");
4274
4285
  return;
4275
4286
  }
4276
- const msgInfo = fields[0].split(/\s+/);
4287
+ const msgInfo = fields[0].split(WS_REGEX);
4277
4288
  if (msgInfo.length === 3) {
4278
4289
  ResultFormatter.unknownArr(results, msgInfo.slice(0, 2), " ");
4279
4290
  ResultFormatter.flightNumber(results, msgInfo[2]);
@@ -4281,14 +4292,14 @@ var Label_80 = class extends DecoderPlugin {
4281
4292
  ResultFormatter.unknown(results, header, "/");
4282
4293
  return;
4283
4294
  }
4284
- const otherInfo1 = fields[1].split(/\s+/);
4295
+ const otherInfo1 = fields[1].split(WS_REGEX);
4285
4296
  if (otherInfo1.length === 2) {
4286
4297
  ResultFormatter.day(results, parseInt(otherInfo1[0], 10));
4287
4298
  ResultFormatter.departureAirport(results, otherInfo1[1]);
4288
4299
  } else {
4289
4300
  ResultFormatter.unknownArr(results, otherInfo1, " ");
4290
4301
  }
4291
- const otherInfo2 = fields[2].split(/\s+/);
4302
+ const otherInfo2 = fields[2].split(WS_REGEX);
4292
4303
  if (otherInfo2.length === 2) {
4293
4304
  ResultFormatter.arrivalAirport(results, otherInfo2[0]);
4294
4305
  ResultFormatter.tail(results, otherInfo2[1].replace(".", ""));
@@ -4300,7 +4311,7 @@ var Label_80 = class extends DecoderPlugin {
4300
4311
  }
4301
4312
  }
4302
4313
  parseTags(part, results) {
4303
- const kvPair = part.split(/\s+/);
4314
+ const kvPair = part.split(WS_REGEX);
4304
4315
  if (kvPair.length < 2) {
4305
4316
  ResultFormatter.unknown(results, part, "/");
4306
4317
  return;
@@ -4308,9 +4319,8 @@ var Label_80 = class extends DecoderPlugin {
4308
4319
  const tag = kvPair[0];
4309
4320
  const val = kvPair.slice(1).join(" ");
4310
4321
  switch (tag) {
4311
- case "POS":
4312
- const posRegex = /^(?<latd>[NS])(?<lat>.+)(?<lngd>[EW])(?<lng>.+)/;
4313
- const posResult = val.match(posRegex);
4322
+ case "POS": {
4323
+ const posResult = POS_REGEX.exec(val);
4314
4324
  const lat = Number(posResult?.groups?.lat) * (posResult?.groups?.latd === "S" ? -1 : 1);
4315
4325
  const lon = Number(posResult?.groups?.lng) * (posResult?.groups?.lngd === "W" ? -1 : 1);
4316
4326
  const position = {
@@ -4319,6 +4329,7 @@ var Label_80 = class extends DecoderPlugin {
4319
4329
  };
4320
4330
  ResultFormatter.position(results, position);
4321
4331
  break;
4332
+ }
4322
4333
  case "ALT":
4323
4334
  ResultFormatter.altitude(results, parseInt(val.replace("+", ""), 10));
4324
4335
  break;
@@ -4351,10 +4362,11 @@ var Label_80 = class extends DecoderPlugin {
4351
4362
  DateTimeUtils.convertHHMMSSToTod(val)
4352
4363
  );
4353
4364
  break;
4354
- case "ETA":
4365
+ case "ETA": {
4355
4366
  const hhmm = val.split(".")[0].replace(":", "");
4356
4367
  ResultFormatter.eta(results, DateTimeUtils.convertHHMMSSToTod(hhmm));
4357
4368
  break;
4369
+ }
4358
4370
  case "HDG":
4359
4371
  ResultFormatter.heading(results, parseInt(val, 10));
4360
4372
  break;
@@ -4378,7 +4390,7 @@ var Label_80 = class extends DecoderPlugin {
4378
4390
  if (csvParts.length !== 9) {
4379
4391
  return;
4380
4392
  }
4381
- const header = csvParts[0].trim().split(/\s+/);
4393
+ const header = csvParts[0].trim().split(WS_REGEX);
4382
4394
  ResultFormatter.unknown(results, header[0], " ");
4383
4395
  ResultFormatter.unknown(results, header[1], " ");
4384
4396
  ResultFormatter.position(
@@ -4401,6 +4413,9 @@ var Label_80 = class extends DecoderPlugin {
4401
4413
  };
4402
4414
 
4403
4415
  // lib/plugins/Label_83.ts
4416
+ var WS_REGEX2 = /\s+/;
4417
+ var ALL_WS_REGEX = /\s/g;
4418
+ var ALL_DOTS_REGEX = /\./g;
4404
4419
  var Label_83 = class extends DecoderPlugin {
4405
4420
  name = "label-83";
4406
4421
  qualifiers() {
@@ -4415,8 +4430,8 @@ var Label_83 = class extends DecoderPlugin {
4415
4430
  decodeResult.formatted.description = "Airline Defined";
4416
4431
  decodeResult.decoded = true;
4417
4432
  const text = message.text;
4418
- if (text.substring(0, 10) === "4DH3 ETAT2") {
4419
- const fields = text.split(/\s+/);
4433
+ if (text.startsWith("4DH3 ETAT2")) {
4434
+ const fields = text.split(WS_REGEX2);
4420
4435
  if (fields[2].length > 5) {
4421
4436
  decodeResult.raw.day = fields[2].substring(5);
4422
4437
  }
@@ -4424,15 +4439,15 @@ var Label_83 = class extends DecoderPlugin {
4424
4439
  const subfields = fields[3].split("/");
4425
4440
  ResultFormatter.departureAirport(decodeResult, subfields[0]);
4426
4441
  ResultFormatter.arrivalAirport(decodeResult, subfields[1]);
4427
- ResultFormatter.tail(decodeResult, fields[4].replace(/\./g, ""));
4442
+ ResultFormatter.tail(decodeResult, fields[4].replace(ALL_DOTS_REGEX, ""));
4428
4443
  ResultFormatter.eta(
4429
4444
  decodeResult,
4430
4445
  DateTimeUtils.convertHHMMSSToTod(fields[6] + "00")
4431
4446
  );
4432
- } else if (text.substring(0, 5) === "001PR") {
4447
+ } else if (text.startsWith("001PR")) {
4433
4448
  decodeResult.raw.day = text.substring(5, 7);
4434
4449
  const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(
4435
- text.substring(13, 28).replace(/\./g, "")
4450
+ text.substring(13, 28).replace(ALL_DOTS_REGEX, "")
4436
4451
  );
4437
4452
  if (position) {
4438
4453
  ResultFormatter.position(decodeResult, position);
@@ -4440,15 +4455,15 @@ var Label_83 = class extends DecoderPlugin {
4440
4455
  ResultFormatter.altitude(decodeResult, Number(text.substring(28, 33)));
4441
4456
  ResultFormatter.unknown(decodeResult, text.substring(33));
4442
4457
  } else {
4443
- const fields = text.replace(/\s/g, "").split(",");
4458
+ const fields = text.replace(ALL_WS_REGEX, "").split(",");
4444
4459
  if (fields.length === 9) {
4445
4460
  ResultFormatter.departureAirport(decodeResult, fields[0]);
4446
4461
  ResultFormatter.arrivalAirport(decodeResult, fields[1]);
4447
4462
  decodeResult.raw.day = fields[2].substring(0, 2);
4448
4463
  decodeResult.raw.time = fields[2].substring(2);
4449
4464
  ResultFormatter.position(decodeResult, {
4450
- latitude: Number(fields[3].replace(/\s/g, "")),
4451
- longitude: Number(fields[4].replace(/\s/g, ""))
4465
+ latitude: Number(fields[3]),
4466
+ longitude: Number(fields[4])
4452
4467
  });
4453
4468
  ResultFormatter.altitude(decodeResult, Number(fields[5]));
4454
4469
  ResultFormatter.groundspeed(decodeResult, Number(fields[6]));
@@ -4512,13 +4527,15 @@ var Label_B6_Forwardslash = class extends DecoderPlugin {
4512
4527
  };
4513
4528
  }
4514
4529
  decode(message, options = {}) {
4515
- const decodeResult = this.defaultResult();
4516
- decodeResult.decoder.name = this.name;
4517
- decodeResult.formatted.description = "CPDLC Message";
4518
- decodeResult.message = message;
4519
- if (options.debug) {
4520
- console.log("CPDLC: " + message);
4530
+ const decodeResult = this.initResult(message, "CPDLC Message");
4531
+ const body = message.text.substring(1);
4532
+ if (body.length > 0) {
4533
+ ResultFormatter.text(decodeResult, body);
4534
+ this.setDecodeLevel(decodeResult, true, "partial");
4535
+ } else {
4536
+ this.setDecodeLevel(decodeResult, false);
4521
4537
  }
4538
+ this.debug(options, "CPDLC body:", body);
4522
4539
  return decodeResult;
4523
4540
  }
4524
4541
  };
@@ -4877,9 +4894,39 @@ var Label_H1_M_POS = class extends DecoderPlugin {
4877
4894
  }
4878
4895
  };
4879
4896
 
4897
+ // lib/utils/compression.ts
4898
+ var pako = __toESM(require("pako"));
4899
+ function inflateData(data, raw) {
4900
+ const chunks = [];
4901
+ const inflator = new pako.Inflate({ windowBits: raw ? -15 : 15 });
4902
+ inflator.onData = (chunk) => {
4903
+ chunks.push(chunk);
4904
+ };
4905
+ inflator.push(data, 2);
4906
+ if (chunks.length === 0) return void 0;
4907
+ if (chunks.length === 1) return chunks[0];
4908
+ const totalLen = chunks.reduce((sum, c) => sum + c.length, 0);
4909
+ const result = new Uint8Array(totalLen);
4910
+ let offset = 0;
4911
+ for (const chunk of chunks) {
4912
+ result.set(chunk, offset);
4913
+ offset += chunk.length;
4914
+ }
4915
+ return result;
4916
+ }
4917
+ function base64ToUint8Array(base64) {
4918
+ const cleaned = base64.replace(/-/g, "+").replace(/_/g, "/").replace(/[^A-Za-z0-9+/]/g, "");
4919
+ const padded = cleaned + "=".repeat((4 - cleaned.length % 4) % 4);
4920
+ const binary = atob(padded);
4921
+ const bytes = new Uint8Array(binary.length);
4922
+ for (let i = 0; i < binary.length; i++) {
4923
+ bytes[i] = binary.charCodeAt(i);
4924
+ }
4925
+ return bytes;
4926
+ }
4927
+
4880
4928
  // lib/plugins/Label_H1_OHMA.ts
4881
- var zlib = __toESM(require("minizlib"));
4882
- var import_node_buffer = require("buffer");
4929
+ var textDecoder = new TextDecoder();
4883
4930
  var Label_H1_OHMA = class extends DecoderPlugin {
4884
4931
  name = "label-h1-ohma";
4885
4932
  qualifiers() {
@@ -4895,12 +4942,12 @@ var Label_H1_OHMA = class extends DecoderPlugin {
4895
4942
  decodeResult.message = message;
4896
4943
  const data = message.text.split("OHMA")[1];
4897
4944
  try {
4898
- const compressedBuffer = import_node_buffer.Buffer.from(data, "base64");
4899
- const decompress = new zlib.Inflate({});
4900
- decompress.write(compressedBuffer);
4901
- decompress.flush(zlib.constants.Z_SYNC_FLUSH);
4902
- const result = decompress.read();
4903
- const jsonText = result?.toString() || "";
4945
+ const compressedBuffer = base64ToUint8Array(data);
4946
+ const result = inflateData(compressedBuffer, false);
4947
+ if (!result || result.length === 0) {
4948
+ throw new Error("Decompression produced no output");
4949
+ }
4950
+ const jsonText = textDecoder.decode(result);
4904
4951
  let formattedMsg;
4905
4952
  let jsonMessage;
4906
4953
  try {
@@ -5305,10 +5352,47 @@ var Label_HX = class extends DecoderPlugin {
5305
5352
  }
5306
5353
  };
5307
5354
 
5355
+ // lib/utils/ascii85.ts
5356
+ function ascii85Decode(input) {
5357
+ let str = input;
5358
+ if (str.startsWith("<~")) str = str.slice(2);
5359
+ if (str.endsWith("~>")) str = str.slice(0, -2);
5360
+ str = str.replace(/\s/g, "");
5361
+ if (str.length === 0) return new Uint8Array(0);
5362
+ const output = [];
5363
+ let i = 0;
5364
+ while (i < str.length) {
5365
+ if (str[i] === "z") {
5366
+ output.push(0, 0, 0, 0);
5367
+ i++;
5368
+ continue;
5369
+ }
5370
+ const remaining = str.length - i;
5371
+ const chunkLen = Math.min(5, remaining);
5372
+ if (chunkLen === 1) break;
5373
+ let padded = str.slice(i, i + chunkLen);
5374
+ while (padded.length < 5) padded += "u";
5375
+ let value = 0;
5376
+ for (let j = 0; j < 5; j++) {
5377
+ const digit = padded.charCodeAt(j) - 33;
5378
+ if (digit < 0 || digit > 84) return null;
5379
+ value = value * 85 + digit;
5380
+ }
5381
+ if (chunkLen === 5 && value > 4294967295) return null;
5382
+ const v = value >>> 0;
5383
+ const numBytes = chunkLen === 5 ? 4 : chunkLen - 1;
5384
+ if (numBytes >= 1) output.push(v >>> 24 & 255);
5385
+ if (numBytes >= 2) output.push(v >>> 16 & 255);
5386
+ if (numBytes >= 3) output.push(v >>> 8 & 255);
5387
+ if (numBytes >= 4) output.push(v & 255);
5388
+ i += chunkLen;
5389
+ }
5390
+ return new Uint8Array(output);
5391
+ }
5392
+
5308
5393
  // lib/utils/miam.ts
5309
- var Base85 = __toESM(require("base85"));
5310
- var zlib2 = __toESM(require("minizlib"));
5311
- var import_node_buffer2 = require("buffer");
5394
+ var textDecoder2 = new TextDecoder();
5395
+ var textEncoder = new TextEncoder();
5312
5396
  var MIAMVersion = /* @__PURE__ */ ((MIAMVersion2) => {
5313
5397
  MIAMVersion2[MIAMVersion2["V1"] = 1] = "V1";
5314
5398
  MIAMVersion2[MIAMVersion2["V2"] = 2] = "V2";
@@ -5395,7 +5479,7 @@ var MIAMCoreUtils = class {
5395
5479
  error: "Empty MIAM message header"
5396
5480
  };
5397
5481
  }
5398
- let hdr = Base85.decode("<~" + rawHdr + "~>", "ascii85");
5482
+ let hdr = ascii85Decode("<~" + rawHdr + "~>");
5399
5483
  if (!hdr || hdr.length < hpad) {
5400
5484
  return {
5401
5485
  decoded: false,
@@ -5407,17 +5491,24 @@ var MIAMCoreUtils = class {
5407
5491
  if (rawBody.length > 0) {
5408
5492
  if ("0123".indexOf(bpad) >= 0) {
5409
5493
  const bpadValue = parseInt(bpad);
5410
- body = Base85.decode("<~" + rawBody + "~>", "ascii85") || void 0;
5411
- if (body && body.length >= bpadValue) {
5494
+ const decoded = ascii85Decode("<~" + rawBody + "~>");
5495
+ if (decoded === null) {
5496
+ return {
5497
+ decoded: false,
5498
+ error: "Ascii85 decode failed for MIAM message body"
5499
+ };
5500
+ }
5501
+ body = decoded;
5502
+ if (body.length >= bpadValue) {
5412
5503
  body = body.subarray(0, body.length - bpadValue);
5413
5504
  }
5414
5505
  } else if (bpad === "-") {
5415
- body = import_node_buffer2.Buffer.from(rawBody);
5506
+ body = textEncoder.encode(rawBody);
5416
5507
  }
5417
5508
  }
5418
5509
  hdr = hdr.subarray(0, hdr.length - hpad);
5419
- const version = hdr.readUInt8(0) & 15;
5420
- const pduType = hdr.readUInt8(0) >> 4 & 15;
5510
+ const version = hdr[0] & 15;
5511
+ const pduType = hdr[0] >> 4 & 15;
5421
5512
  if (isMIAMVersion(version) && isMIAMCorePdu(pduType)) {
5422
5513
  const versionPduHandler = this.VersionPduHandlerTable[version][pduType];
5423
5514
  return versionPduHandler(hdr, body);
@@ -5714,7 +5805,7 @@ var MIAMCoreUtils = class {
5714
5805
  ];
5715
5806
  let crc = (seed || 0) & 65535;
5716
5807
  for (let i = 0; i < buf.length; i++) {
5717
- crc = (crc << 8 >>> 0 ^ crctable[(crc >>> 8 ^ buf.readUInt8(i)) >>> 0 & 255]) >>> 0;
5808
+ crc = (crc << 8 >>> 0 ^ crctable[(crc >>> 8 ^ buf[i]) >>> 0 & 255]) >>> 0;
5718
5809
  }
5719
5810
  return crc & 65535;
5720
5811
  }
@@ -5979,7 +6070,7 @@ var MIAMCoreUtils = class {
5979
6070
  ];
5980
6071
  let crc = seed || 0;
5981
6072
  for (let i = 0; i < buf.length; i++) {
5982
- crc = (crc << 8 >>> 0 ^ crctable[(crc >>> 24 ^ buf.readUInt8(i)) >>> 0]) >>> 0;
6073
+ crc = (crc << 8 >>> 0 ^ crctable[(crc >>> 24 ^ buf[i]) >>> 0]) >>> 0;
5983
6074
  }
5984
6075
  return crc;
5985
6076
  }
@@ -6016,7 +6107,7 @@ var MIAMCoreUtils = class {
6016
6107
  let msgNum = 0;
6017
6108
  let ackOptions = 0;
6018
6109
  if (version === 1 /* V1 */) {
6019
- pduSize = hdr.readUInt8(1) << 16 | hdr.readUInt8(2) << 8 | hdr.readUInt8(3);
6110
+ pduSize = hdr[1] << 16 | hdr[2] << 8 | hdr[3];
6020
6111
  const msgSize = hdr.length + (body === void 0 ? 0 : body.length);
6021
6112
  if (pduSize > msgSize) {
6022
6113
  pduIsComplete = false;
@@ -6025,17 +6116,17 @@ var MIAMCoreUtils = class {
6025
6116
  );
6026
6117
  }
6027
6118
  hdr = hdr.subarray(4);
6028
- tail = hdr.subarray(0, 7).toString("ascii");
6119
+ tail = textDecoder2.decode(hdr.subarray(0, 7));
6029
6120
  hdr = hdr.subarray(7);
6030
6121
  } else if (version === 2 /* V2 */) {
6031
6122
  hdr = hdr.subarray(1);
6032
6123
  }
6033
- msgNum = hdr.readUInt8(0) >> 1 & 127;
6034
- ackOptions = hdr.readUInt8(0) & 1;
6124
+ msgNum = hdr[0] >> 1 & 127;
6125
+ ackOptions = hdr[0] & 1;
6035
6126
  hdr = hdr.subarray(1);
6036
- pduCompression = (hdr.readUInt8(0) << 2 | hdr.readUInt8(1) >> 6 & 3) & 7;
6037
- pduEncoding = hdr.readUInt8(1) >> 4 & 3;
6038
- pduAppType = hdr.readUInt8(1) & 15;
6127
+ pduCompression = (hdr[0] << 2 | hdr[1] >> 6 & 3) & 7;
6128
+ pduEncoding = hdr[1] >> 4 & 3;
6129
+ pduAppType = hdr[1] & 15;
6039
6130
  hdr = hdr.subarray(2);
6040
6131
  let appIdLen;
6041
6132
  if (isMIAMCoreApp(pduAppType)) {
@@ -6061,12 +6152,12 @@ var MIAMCoreUtils = class {
6061
6152
  error: "Header too short for v" + version + " appType: " + pduAppType
6062
6153
  };
6063
6154
  }
6064
- pduAppId = hdr.subarray(0, appIdLen).toString("ascii");
6155
+ pduAppId = textDecoder2.decode(hdr.subarray(0, appIdLen));
6065
6156
  hdr = hdr.subarray(appIdLen);
6066
6157
  if (crcLen === 4) {
6067
- pduCrc = hdr.readUInt8(0) << 24 | hdr.readUInt8(1) << 16 | hdr.readUInt8(2) << 8 | hdr.readUInt8(3);
6158
+ pduCrc = hdr[0] << 24 | hdr[1] << 16 | hdr[2] << 8 | hdr[3];
6068
6159
  } else if (crcLen === 2) {
6069
- pduCrc = hdr.readUInt8(0) << 8 | hdr.readUInt8(1);
6160
+ pduCrc = hdr[0] << 8 | hdr[1];
6070
6161
  }
6071
6162
  hdr = hdr.subarray(crcLen);
6072
6163
  if (body !== void 0 && body.length > 0) {
@@ -6074,10 +6165,12 @@ var MIAMCoreUtils = class {
6074
6165
  pduCompression
6075
6166
  ) >= 0) {
6076
6167
  try {
6077
- const decompress = new zlib2.InflateRaw({});
6078
- decompress.write(body);
6079
- decompress.flush(zlib2.constants.Z_SYNC_FLUSH);
6080
- pduData = decompress.read();
6168
+ const inflated = inflateData(body, true);
6169
+ if (inflated === void 0) {
6170
+ pduErrors.push("Inflation produced no output for body");
6171
+ } else {
6172
+ pduData = inflated;
6173
+ }
6081
6174
  } catch (e) {
6082
6175
  pduErrors.push("Inflation failed for body: " + e);
6083
6176
  }
@@ -6135,12 +6228,12 @@ var MIAMCoreUtils = class {
6135
6228
  label,
6136
6229
  ...sublabel ? { sublabel } : {},
6137
6230
  ...mfi ? { mfi } : {},
6138
- ...pduData ? { text: pduData.toString("ascii") } : {}
6231
+ ...pduData ? { text: textDecoder2.decode(pduData) } : {}
6139
6232
  };
6140
6233
  } else {
6141
6234
  pdu.non_acars = {
6142
6235
  appId: pduAppId,
6143
- ...pduData ? { text: pduData.toString("ascii") } : {}
6236
+ ...pduData ? { text: textDecoder2.decode(pduData) } : {}
6144
6237
  };
6145
6238
  }
6146
6239
  return {
@@ -6575,10 +6668,12 @@ var MessageDecoder = class {
6575
6668
  name;
6576
6669
  plugins;
6577
6670
  debug;
6578
- /** Maps a label string to the plugins registered for it, preserving registration order. */
6579
- labelIndex = /* @__PURE__ */ new Map();
6580
- /** Plugins that match all labels (qualifier label '*'). */
6581
- wildcardPlugins = [];
6671
+ /** Maps a label string to the candidate entries (wildcard + label-specific) in registration order. */
6672
+ candidatesByLabel = /* @__PURE__ */ new Map();
6673
+ /** Wildcard entries (plugins that register the '*' label). */
6674
+ wildcardEntries = [];
6675
+ /** Membership set for wildcard entries to dedupe when a plugin also registers a specific label. */
6676
+ wildcardSet = /* @__PURE__ */ new Set();
6582
6677
  constructor() {
6583
6678
  this.name = "acars-decoder-typescript";
6584
6679
  this.plugins = [];
@@ -6590,40 +6685,41 @@ var MessageDecoder = class {
6590
6685
  registerPlugin(plugin) {
6591
6686
  this.plugins.push(plugin);
6592
6687
  const qualifiers = plugin.qualifiers();
6688
+ const entry = {
6689
+ plugin,
6690
+ preambles: qualifiers.preambles && qualifiers.preambles.length > 0 ? qualifiers.preambles : void 0
6691
+ };
6593
6692
  for (const label of qualifiers.labels) {
6594
6693
  if (label === "*") {
6595
- this.wildcardPlugins.push(plugin);
6694
+ if (!this.wildcardSet.has(plugin)) {
6695
+ this.wildcardEntries.push(entry);
6696
+ this.wildcardSet.add(plugin);
6697
+ const wildcardInsertIndex = this.wildcardEntries.length - 1;
6698
+ for (const bucket of this.candidatesByLabel.values()) {
6699
+ bucket.splice(wildcardInsertIndex, 0, entry);
6700
+ }
6701
+ }
6596
6702
  } else {
6597
- let bucket = this.labelIndex.get(label);
6703
+ let bucket = this.candidatesByLabel.get(label);
6598
6704
  if (!bucket) {
6599
- bucket = [];
6600
- this.labelIndex.set(label, bucket);
6705
+ bucket = this.wildcardEntries.slice();
6706
+ this.candidatesByLabel.set(label, bucket);
6707
+ }
6708
+ if (!this.wildcardSet.has(plugin)) {
6709
+ bucket.push(entry);
6601
6710
  }
6602
- bucket.push(plugin);
6603
6711
  }
6604
6712
  }
6605
6713
  return true;
6606
6714
  }
6607
6715
  decode(message, options = {}) {
6608
- const labelPlugins = this.labelIndex.get(message.label) ?? [];
6609
- const seen = /* @__PURE__ */ new Set();
6610
- const candidates = [];
6611
- for (const plugin of [...this.wildcardPlugins, ...labelPlugins]) {
6612
- if (!seen.has(plugin)) {
6613
- seen.add(plugin);
6614
- candidates.push(plugin);
6615
- }
6616
- }
6617
- const usablePlugins = candidates.filter((plugin) => {
6618
- const preambles = plugin.qualifiers().preambles;
6619
- if (!preambles || preambles.length === 0) {
6620
- return true;
6621
- }
6622
- return preambles.some((p) => message.text.startsWith(p));
6623
- });
6716
+ const text = message.text;
6717
+ const candidates = this.candidatesByLabel.get(message.label) ?? this.wildcardEntries;
6624
6718
  if (options.debug) {
6625
6719
  console.log("Usable plugins");
6626
- console.log(usablePlugins);
6720
+ console.log(
6721
+ candidates.filter((e) => this.matchesPreambles(text, e.preambles)).map((e) => e.plugin)
6722
+ );
6627
6723
  }
6628
6724
  let result = {
6629
6725
  decoded: false,
@@ -6635,7 +6731,7 @@ var MessageDecoder = class {
6635
6731
  },
6636
6732
  message,
6637
6733
  remaining: {
6638
- text: message.text
6734
+ text
6639
6735
  },
6640
6736
  raw: {},
6641
6737
  formatted: {
@@ -6643,10 +6739,14 @@ var MessageDecoder = class {
6643
6739
  items: []
6644
6740
  }
6645
6741
  };
6646
- for (let i = 0; i < usablePlugins.length; i++) {
6647
- const plugin = usablePlugins[i];
6648
- result = plugin.decode(message, options);
6649
- if (result.decoded) {
6742
+ for (let i = 0; i < candidates.length; i++) {
6743
+ const entry = candidates[i];
6744
+ if (!this.matchesPreambles(text, entry.preambles)) {
6745
+ continue;
6746
+ }
6747
+ const pluginResult = entry.plugin.decode(message, options);
6748
+ if (pluginResult.decoded) {
6749
+ result = pluginResult;
6650
6750
  break;
6651
6751
  }
6652
6752
  }
@@ -6656,6 +6756,17 @@ var MessageDecoder = class {
6656
6756
  }
6657
6757
  return result;
6658
6758
  }
6759
+ matchesPreambles(text, preambles) {
6760
+ if (!preambles) {
6761
+ return true;
6762
+ }
6763
+ for (let i = 0; i < preambles.length; i++) {
6764
+ if (text.startsWith(preambles[i])) {
6765
+ return true;
6766
+ }
6767
+ }
6768
+ return false;
6769
+ }
6659
6770
  };
6660
6771
  // Annotate the CommonJS export names for ESM import in node:
6661
6772
  0 && (module.exports = {