@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.mjs CHANGED
@@ -94,11 +94,11 @@ var CoordinateUtils = class _CoordinateUtils {
94
94
  * @returns An object with latitude and longitude properties
95
95
  */
96
96
  static decodeStringCoordinates(stringCoords) {
97
- const firstChar = stringCoords.substring(0, 1);
98
- let middleChar = stringCoords.substring(6, 7);
97
+ const firstChar = stringCoords.charAt(0);
98
+ let middleChar = stringCoords.charAt(6);
99
99
  let longitudeChars = stringCoords.substring(7, 13);
100
- if (middleChar == " ") {
101
- middleChar = stringCoords.substring(7, 8);
100
+ if (middleChar === " ") {
101
+ middleChar = stringCoords.charAt(7);
102
102
  longitudeChars = stringCoords.substring(8, 14);
103
103
  }
104
104
  if ((firstChar === "N" || firstChar === "S") && (middleChar === "W" || middleChar === "E")) {
@@ -116,24 +116,26 @@ var CoordinateUtils = class _CoordinateUtils {
116
116
  * @returns An object with latitude and longitude properties
117
117
  */
118
118
  static decodeStringCoordinatesDecimalMinutes(stringCoords) {
119
- const firstChar = stringCoords.substring(0, 1);
120
- let middleChar = stringCoords.substring(6, 7);
119
+ const firstChar = stringCoords.charAt(0);
120
+ let middleChar = stringCoords.charAt(6);
121
121
  let longitudeChars = stringCoords.substring(7, 13);
122
- if (middleChar == " ") {
123
- middleChar = stringCoords.substring(7, 8);
122
+ if (middleChar === " ") {
123
+ middleChar = stringCoords.charAt(7);
124
124
  longitudeChars = stringCoords.substring(8, 14);
125
125
  }
126
- const latDeg = Math.trunc(Number(stringCoords.substring(1, 6)) / 1e3);
127
- const latMin = Number(stringCoords.substring(1, 6)) % 1e3 / 10;
128
- const lonDeg = Math.trunc(Number(longitudeChars) / 1e3);
129
- const lonMin = Number(longitudeChars) % 1e3 / 10;
130
- if ((firstChar === "N" || firstChar === "S") && (middleChar === "W" || middleChar === "E")) {
131
- return {
132
- latitude: (latDeg + latMin / 60) * _CoordinateUtils.getDirection(firstChar),
133
- longitude: (lonDeg + lonMin / 60) * _CoordinateUtils.getDirection(middleChar)
134
- };
126
+ if (firstChar !== "N" && firstChar !== "S" || middleChar !== "W" && middleChar !== "E") {
127
+ return void 0;
135
128
  }
136
- return void 0;
129
+ const latRaw = Number(stringCoords.substring(1, 6));
130
+ const lonRaw = Number(longitudeChars);
131
+ const latDeg = Math.trunc(latRaw / 1e3);
132
+ const latMin = latRaw % 1e3 / 10;
133
+ const lonDeg = Math.trunc(lonRaw / 1e3);
134
+ const lonMin = lonRaw % 1e3 / 10;
135
+ return {
136
+ latitude: (latDeg + latMin / 60) * _CoordinateUtils.getDirection(firstChar),
137
+ longitude: (lonDeg + lonMin / 60) * _CoordinateUtils.getDirection(middleChar)
138
+ };
137
139
  }
138
140
  static coordinateString(coords) {
139
141
  const latDir = coords.latitude > 0 ? "N" : "S";
@@ -164,25 +166,13 @@ var DateTimeUtils = class {
164
166
  // Expects a six digit date string and a four digit UTC time string
165
167
  // (DDMMYY) (HHMM)
166
168
  static UTCDateTimeToString(dateString, timeString) {
167
- let utcDate = /* @__PURE__ */ new Date();
168
- utcDate.setUTCDate(+dateString.substr(0, 2));
169
- utcDate.setUTCMonth(+dateString.substr(2, 2));
170
- if (dateString.length === 6) {
171
- utcDate.setUTCFullYear(2e3 + +dateString.substr(4, 2));
172
- }
173
- if (timeString.length === 6) {
174
- utcDate.setUTCHours(
175
- +timeString.substr(0, 2),
176
- +timeString.substr(2, 2),
177
- +timeString.substr(4, 2)
178
- );
179
- } else {
180
- utcDate.setUTCHours(
181
- +timeString.substr(0, 2),
182
- +timeString.substr(2, 2),
183
- 0
184
- );
185
- }
169
+ const day = +dateString.substr(0, 2);
170
+ const month = +dateString.substr(2, 2) - 1;
171
+ const year = dateString.length === 6 ? 2e3 + +dateString.substr(4, 2) : (/* @__PURE__ */ new Date()).getUTCFullYear();
172
+ const hours = +timeString.substr(0, 2);
173
+ const minutes = +timeString.substr(2, 2);
174
+ const seconds = timeString.length === 6 ? +timeString.substr(4, 2) : 0;
175
+ const utcDate = new Date(Date.UTC(year, month, day, hours, minutes, seconds));
186
176
  return utcDate.toUTCString();
187
177
  }
188
178
  /**
@@ -527,7 +517,7 @@ var ResultFormatter = class {
527
517
  decodeResult.raw.fuel_remaining = value;
528
518
  decodeResult.formatted.items.push({
529
519
  type: "fuel_remaining",
530
- code: " FUEL_REM",
520
+ code: "FUEL_REM",
531
521
  label: "Fuel Remaining",
532
522
  value: decodeResult.raw.fuel_remaining.toString()
533
523
  });
@@ -921,7 +911,7 @@ var ResultFormatter = class {
921
911
  type: "sequence",
922
912
  code: "SEQ",
923
913
  label: "Sequence Number",
924
- value: decodeResult.raw.sequence_number
914
+ value: decodeResult.raw.sequence_number.toString()
925
915
  });
926
916
  }
927
917
  static sequenceResponse(decodeResult, value) {
@@ -930,7 +920,7 @@ var ResultFormatter = class {
930
920
  type: "sequence",
931
921
  code: "SEQ_RESP",
932
922
  label: "Sequence Response",
933
- value: decodeResult.raw.sequence_response
923
+ value: decodeResult.raw.sequence_response.toString()
934
924
  });
935
925
  }
936
926
  static unknown(decodeResult, value, sep = ",") {
@@ -1036,6 +1026,7 @@ var DecoderPlugin = class {
1036
1026
  };
1037
1027
 
1038
1028
  // lib/plugins/CBand.ts
1029
+ var CBAND_HEADER = /^(?<msgno>[A-Z]\d{2}[A-Z])(?<airline>[A-Z0-9]{2})(?<number>[0-9]{4})/;
1039
1030
  var CBand = class extends DecoderPlugin {
1040
1031
  name = "c-band";
1041
1032
  qualifiers() {
@@ -1044,35 +1035,43 @@ var CBand = class extends DecoderPlugin {
1044
1035
  };
1045
1036
  }
1046
1037
  decode(message, options = {}) {
1047
- let decodeResult = this.defaultResult();
1038
+ const text = message.text;
1039
+ if (text.length < 10) {
1040
+ const result = this.defaultResult();
1041
+ result.decoder.name = this.name;
1042
+ result.message = message;
1043
+ return result;
1044
+ }
1045
+ const cband = CBAND_HEADER.exec(text);
1046
+ if (!cband?.groups) {
1047
+ const result = this.defaultResult();
1048
+ result.decoder.name = this.name;
1049
+ result.message = message;
1050
+ return result;
1051
+ }
1052
+ const decoded = this.decoder.decode(
1053
+ {
1054
+ label: message.label,
1055
+ sublabel: message.sublabel,
1056
+ text: text.substring(10)
1057
+ },
1058
+ options
1059
+ );
1060
+ const decodeResult = this.defaultResult();
1048
1061
  decodeResult.decoder.name = this.name;
1049
1062
  decodeResult.message = message;
1050
- let cband = message.text.match(
1051
- /^(?<msgno>[A-Z]\d{2}[A-Z])(?<airline>[A-Z0-9]{2})(?<number>[0-9]{4})/
1052
- );
1053
- if (cband?.groups) {
1054
- const messageText = message.text.substring(10);
1055
- const decoded = this.decoder.decode(
1056
- {
1057
- label: message.label,
1058
- sublabel: message.sublabel,
1059
- text: messageText
1060
- },
1061
- options
1063
+ if (decoded.decoded) {
1064
+ ResultFormatter.flightNumber(
1065
+ decodeResult,
1066
+ cband.groups.airline + Number(cband.groups.number)
1062
1067
  );
1063
- if (decoded.decoded) {
1064
- ResultFormatter.flightNumber(
1065
- decodeResult,
1066
- cband.groups.airline + Number(cband.groups.number)
1067
- );
1068
- decodeResult.decoded = true;
1069
- decodeResult.decoder.decodeLevel = decoded.decoder.decodeLevel;
1070
- decodeResult.decoder.name = this.name + "-" + decoded.decoder.name;
1071
- decodeResult.raw = { ...decodeResult.raw, ...decoded.raw };
1072
- decodeResult.formatted.description = decoded.formatted.description;
1073
- decodeResult.formatted.items.push(...decoded.formatted.items);
1074
- decodeResult.remaining = decoded.remaining;
1075
- }
1068
+ decodeResult.decoded = true;
1069
+ decodeResult.decoder.decodeLevel = decoded.decoder.decodeLevel;
1070
+ decodeResult.decoder.name = this.name + "-" + decoded.decoder.name;
1071
+ decodeResult.raw = { ...decodeResult.raw, ...decoded.raw };
1072
+ decodeResult.formatted.description = decoded.formatted.description;
1073
+ decodeResult.formatted.items.push(...decoded.formatted.items);
1074
+ decodeResult.remaining = decoded.remaining;
1076
1075
  }
1077
1076
  return decodeResult;
1078
1077
  }
@@ -1157,7 +1156,9 @@ var FlightPlanUtils = class _FlightPlanUtils {
1157
1156
  label: "Route Status",
1158
1157
  value: "Route Planned"
1159
1158
  });
1160
- decodeResult.raw.route_status = header;
1159
+ if (header.length > 2) {
1160
+ addRoute(decodeResult, header.substring(2));
1161
+ }
1161
1162
  } else if (header.startsWith("RI")) {
1162
1163
  decodeResult.raw.route_status = "RI";
1163
1164
  decodeResult.formatted.items.push({
@@ -1255,6 +1256,11 @@ function addCompanyRoute(decodeResult, value) {
1255
1256
  }
1256
1257
 
1257
1258
  // lib/utils/arinc_702_helper.ts
1259
+ function asciiStringToBytes(data) {
1260
+ const bytes = new Uint8Array(data.length);
1261
+ for (let i = 0; i < data.length; i++) bytes[i] = data.charCodeAt(i) & 255;
1262
+ return bytes;
1263
+ }
1258
1264
  var Arinc702Helper = class _Arinc702Helper {
1259
1265
  /**
1260
1266
  * Decodes an ARINC 702 message
@@ -1773,7 +1779,7 @@ function processSummary(decodeResult, data) {
1773
1779
  }
1774
1780
  function crc16IbmSdlcRev(data) {
1775
1781
  let crc = 65535;
1776
- const bytes = Buffer.from(data, "ascii");
1782
+ const bytes = asciiStringToBytes(data);
1777
1783
  for (const byte of bytes) {
1778
1784
  crc ^= byte;
1779
1785
  for (let i = 0; i < 8; i++) {
@@ -1794,7 +1800,7 @@ function crc16IbmSdlcRev(data) {
1794
1800
  function crc16Genibus(data) {
1795
1801
  let crc = 65535;
1796
1802
  const polynomial = 4129;
1797
- const bytes = Buffer.from(data, "ascii");
1803
+ const bytes = asciiStringToBytes(data);
1798
1804
  for (const byte of bytes) {
1799
1805
  crc ^= byte << 8;
1800
1806
  for (let i = 0; i < 8; i++) {
@@ -1819,6 +1825,7 @@ function processMessageReference(decodeResult, data) {
1819
1825
  }
1820
1826
 
1821
1827
  // lib/plugins/ARINC_702.ts
1828
+ var NEWLINE_REGEX = /[\n\r]/g;
1822
1829
  var Arinc702 = class extends DecoderPlugin {
1823
1830
  name = "arinc-702";
1824
1831
  qualifiers() {
@@ -1830,7 +1837,7 @@ var Arinc702 = class extends DecoderPlugin {
1830
1837
  let decodeResult = this.defaultResult();
1831
1838
  decodeResult.decoder.name = this.name;
1832
1839
  decodeResult.message = message;
1833
- const msg = message.text.replace(/\n|\r/g, "");
1840
+ const msg = message.text.replace(NEWLINE_REGEX, "");
1834
1841
  let decoded = false;
1835
1842
  decoded = Arinc702Helper.decodeH1Message(decodeResult, msg);
1836
1843
  if (decoded) {
@@ -3807,7 +3814,8 @@ var Label_4A = class extends DecoderPlugin {
3807
3814
  ResultFormatter.arrivalAirport(decodeResult, fields[5]);
3808
3815
  ResultFormatter.unknownArr(decodeResult, fields.slice(8));
3809
3816
  } else if (fields.length === 6) {
3810
- if (fields[0].match(/^[NS]/)) {
3817
+ const f0 = fields[0];
3818
+ if (f0.length > 0 && (f0[0] === "N" || f0[0] === "S")) {
3811
3819
  ResultFormatter.position(
3812
3820
  decodeResult,
3813
3821
  CoordinateUtils.decodeStringCoordinates(fields[0].substring(0, 13))
@@ -4188,6 +4196,9 @@ var Label_4T_ETA = class extends DecoderPlugin {
4188
4196
  };
4189
4197
 
4190
4198
  // lib/plugins/Label_80.ts
4199
+ var POS_REGEX = /^(?<latd>[NS])(?<lat>.+)(?<lngd>[EW])(?<lng>.+)/;
4200
+ var WS_REGEX = /\s+/;
4201
+ var NEWLINE_REGEX2 = /\r?\n/;
4191
4202
  var Label_80 = class extends DecoderPlugin {
4192
4203
  name = "label-80";
4193
4204
  qualifiers() {
@@ -4201,7 +4212,7 @@ var Label_80 = class extends DecoderPlugin {
4201
4212
  message,
4202
4213
  "Airline Defined Position Report"
4203
4214
  );
4204
- const lines = message.text.split(/\r?\n/);
4215
+ const lines = message.text.split(NEWLINE_REGEX2);
4205
4216
  if (lines.length === 1 && lines[0].includes(",")) {
4206
4217
  this.parseCsvFormat(lines[0], decodeResult);
4207
4218
  } else {
@@ -4236,7 +4247,7 @@ var Label_80 = class extends DecoderPlugin {
4236
4247
  ResultFormatter.unknown(results, header, "/");
4237
4248
  return;
4238
4249
  }
4239
- const msgInfo = fields[0].split(/\s+/);
4250
+ const msgInfo = fields[0].split(WS_REGEX);
4240
4251
  if (msgInfo.length === 3) {
4241
4252
  ResultFormatter.unknownArr(results, msgInfo.slice(0, 2), " ");
4242
4253
  ResultFormatter.flightNumber(results, msgInfo[2]);
@@ -4244,14 +4255,14 @@ var Label_80 = class extends DecoderPlugin {
4244
4255
  ResultFormatter.unknown(results, header, "/");
4245
4256
  return;
4246
4257
  }
4247
- const otherInfo1 = fields[1].split(/\s+/);
4258
+ const otherInfo1 = fields[1].split(WS_REGEX);
4248
4259
  if (otherInfo1.length === 2) {
4249
4260
  ResultFormatter.day(results, parseInt(otherInfo1[0], 10));
4250
4261
  ResultFormatter.departureAirport(results, otherInfo1[1]);
4251
4262
  } else {
4252
4263
  ResultFormatter.unknownArr(results, otherInfo1, " ");
4253
4264
  }
4254
- const otherInfo2 = fields[2].split(/\s+/);
4265
+ const otherInfo2 = fields[2].split(WS_REGEX);
4255
4266
  if (otherInfo2.length === 2) {
4256
4267
  ResultFormatter.arrivalAirport(results, otherInfo2[0]);
4257
4268
  ResultFormatter.tail(results, otherInfo2[1].replace(".", ""));
@@ -4263,7 +4274,7 @@ var Label_80 = class extends DecoderPlugin {
4263
4274
  }
4264
4275
  }
4265
4276
  parseTags(part, results) {
4266
- const kvPair = part.split(/\s+/);
4277
+ const kvPair = part.split(WS_REGEX);
4267
4278
  if (kvPair.length < 2) {
4268
4279
  ResultFormatter.unknown(results, part, "/");
4269
4280
  return;
@@ -4271,9 +4282,8 @@ var Label_80 = class extends DecoderPlugin {
4271
4282
  const tag = kvPair[0];
4272
4283
  const val = kvPair.slice(1).join(" ");
4273
4284
  switch (tag) {
4274
- case "POS":
4275
- const posRegex = /^(?<latd>[NS])(?<lat>.+)(?<lngd>[EW])(?<lng>.+)/;
4276
- const posResult = val.match(posRegex);
4285
+ case "POS": {
4286
+ const posResult = POS_REGEX.exec(val);
4277
4287
  const lat = Number(posResult?.groups?.lat) * (posResult?.groups?.latd === "S" ? -1 : 1);
4278
4288
  const lon = Number(posResult?.groups?.lng) * (posResult?.groups?.lngd === "W" ? -1 : 1);
4279
4289
  const position = {
@@ -4282,6 +4292,7 @@ var Label_80 = class extends DecoderPlugin {
4282
4292
  };
4283
4293
  ResultFormatter.position(results, position);
4284
4294
  break;
4295
+ }
4285
4296
  case "ALT":
4286
4297
  ResultFormatter.altitude(results, parseInt(val.replace("+", ""), 10));
4287
4298
  break;
@@ -4314,10 +4325,11 @@ var Label_80 = class extends DecoderPlugin {
4314
4325
  DateTimeUtils.convertHHMMSSToTod(val)
4315
4326
  );
4316
4327
  break;
4317
- case "ETA":
4328
+ case "ETA": {
4318
4329
  const hhmm = val.split(".")[0].replace(":", "");
4319
4330
  ResultFormatter.eta(results, DateTimeUtils.convertHHMMSSToTod(hhmm));
4320
4331
  break;
4332
+ }
4321
4333
  case "HDG":
4322
4334
  ResultFormatter.heading(results, parseInt(val, 10));
4323
4335
  break;
@@ -4341,7 +4353,7 @@ var Label_80 = class extends DecoderPlugin {
4341
4353
  if (csvParts.length !== 9) {
4342
4354
  return;
4343
4355
  }
4344
- const header = csvParts[0].trim().split(/\s+/);
4356
+ const header = csvParts[0].trim().split(WS_REGEX);
4345
4357
  ResultFormatter.unknown(results, header[0], " ");
4346
4358
  ResultFormatter.unknown(results, header[1], " ");
4347
4359
  ResultFormatter.position(
@@ -4364,6 +4376,9 @@ var Label_80 = class extends DecoderPlugin {
4364
4376
  };
4365
4377
 
4366
4378
  // lib/plugins/Label_83.ts
4379
+ var WS_REGEX2 = /\s+/;
4380
+ var ALL_WS_REGEX = /\s/g;
4381
+ var ALL_DOTS_REGEX = /\./g;
4367
4382
  var Label_83 = class extends DecoderPlugin {
4368
4383
  name = "label-83";
4369
4384
  qualifiers() {
@@ -4378,8 +4393,8 @@ var Label_83 = class extends DecoderPlugin {
4378
4393
  decodeResult.formatted.description = "Airline Defined";
4379
4394
  decodeResult.decoded = true;
4380
4395
  const text = message.text;
4381
- if (text.substring(0, 10) === "4DH3 ETAT2") {
4382
- const fields = text.split(/\s+/);
4396
+ if (text.startsWith("4DH3 ETAT2")) {
4397
+ const fields = text.split(WS_REGEX2);
4383
4398
  if (fields[2].length > 5) {
4384
4399
  decodeResult.raw.day = fields[2].substring(5);
4385
4400
  }
@@ -4387,15 +4402,15 @@ var Label_83 = class extends DecoderPlugin {
4387
4402
  const subfields = fields[3].split("/");
4388
4403
  ResultFormatter.departureAirport(decodeResult, subfields[0]);
4389
4404
  ResultFormatter.arrivalAirport(decodeResult, subfields[1]);
4390
- ResultFormatter.tail(decodeResult, fields[4].replace(/\./g, ""));
4405
+ ResultFormatter.tail(decodeResult, fields[4].replace(ALL_DOTS_REGEX, ""));
4391
4406
  ResultFormatter.eta(
4392
4407
  decodeResult,
4393
4408
  DateTimeUtils.convertHHMMSSToTod(fields[6] + "00")
4394
4409
  );
4395
- } else if (text.substring(0, 5) === "001PR") {
4410
+ } else if (text.startsWith("001PR")) {
4396
4411
  decodeResult.raw.day = text.substring(5, 7);
4397
4412
  const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(
4398
- text.substring(13, 28).replace(/\./g, "")
4413
+ text.substring(13, 28).replace(ALL_DOTS_REGEX, "")
4399
4414
  );
4400
4415
  if (position) {
4401
4416
  ResultFormatter.position(decodeResult, position);
@@ -4403,15 +4418,15 @@ var Label_83 = class extends DecoderPlugin {
4403
4418
  ResultFormatter.altitude(decodeResult, Number(text.substring(28, 33)));
4404
4419
  ResultFormatter.unknown(decodeResult, text.substring(33));
4405
4420
  } else {
4406
- const fields = text.replace(/\s/g, "").split(",");
4421
+ const fields = text.replace(ALL_WS_REGEX, "").split(",");
4407
4422
  if (fields.length === 9) {
4408
4423
  ResultFormatter.departureAirport(decodeResult, fields[0]);
4409
4424
  ResultFormatter.arrivalAirport(decodeResult, fields[1]);
4410
4425
  decodeResult.raw.day = fields[2].substring(0, 2);
4411
4426
  decodeResult.raw.time = fields[2].substring(2);
4412
4427
  ResultFormatter.position(decodeResult, {
4413
- latitude: Number(fields[3].replace(/\s/g, "")),
4414
- longitude: Number(fields[4].replace(/\s/g, ""))
4428
+ latitude: Number(fields[3]),
4429
+ longitude: Number(fields[4])
4415
4430
  });
4416
4431
  ResultFormatter.altitude(decodeResult, Number(fields[5]));
4417
4432
  ResultFormatter.groundspeed(decodeResult, Number(fields[6]));
@@ -4475,13 +4490,15 @@ var Label_B6_Forwardslash = class extends DecoderPlugin {
4475
4490
  };
4476
4491
  }
4477
4492
  decode(message, options = {}) {
4478
- const decodeResult = this.defaultResult();
4479
- decodeResult.decoder.name = this.name;
4480
- decodeResult.formatted.description = "CPDLC Message";
4481
- decodeResult.message = message;
4482
- if (options.debug) {
4483
- console.log("CPDLC: " + message);
4493
+ const decodeResult = this.initResult(message, "CPDLC Message");
4494
+ const body = message.text.substring(1);
4495
+ if (body.length > 0) {
4496
+ ResultFormatter.text(decodeResult, body);
4497
+ this.setDecodeLevel(decodeResult, true, "partial");
4498
+ } else {
4499
+ this.setDecodeLevel(decodeResult, false);
4484
4500
  }
4501
+ this.debug(options, "CPDLC body:", body);
4485
4502
  return decodeResult;
4486
4503
  }
4487
4504
  };
@@ -4840,9 +4857,39 @@ var Label_H1_M_POS = class extends DecoderPlugin {
4840
4857
  }
4841
4858
  };
4842
4859
 
4860
+ // lib/utils/compression.ts
4861
+ import * as pako from "pako";
4862
+ function inflateData(data, raw) {
4863
+ const chunks = [];
4864
+ const inflator = new pako.Inflate({ windowBits: raw ? -15 : 15 });
4865
+ inflator.onData = (chunk) => {
4866
+ chunks.push(chunk);
4867
+ };
4868
+ inflator.push(data, 2);
4869
+ if (chunks.length === 0) return void 0;
4870
+ if (chunks.length === 1) return chunks[0];
4871
+ const totalLen = chunks.reduce((sum, c) => sum + c.length, 0);
4872
+ const result = new Uint8Array(totalLen);
4873
+ let offset = 0;
4874
+ for (const chunk of chunks) {
4875
+ result.set(chunk, offset);
4876
+ offset += chunk.length;
4877
+ }
4878
+ return result;
4879
+ }
4880
+ function base64ToUint8Array(base64) {
4881
+ const cleaned = base64.replace(/-/g, "+").replace(/_/g, "/").replace(/[^A-Za-z0-9+/]/g, "");
4882
+ const padded = cleaned + "=".repeat((4 - cleaned.length % 4) % 4);
4883
+ const binary = atob(padded);
4884
+ const bytes = new Uint8Array(binary.length);
4885
+ for (let i = 0; i < binary.length; i++) {
4886
+ bytes[i] = binary.charCodeAt(i);
4887
+ }
4888
+ return bytes;
4889
+ }
4890
+
4843
4891
  // lib/plugins/Label_H1_OHMA.ts
4844
- import * as zlib from "minizlib";
4845
- import { Buffer as Buffer2 } from "buffer";
4892
+ var textDecoder = new TextDecoder();
4846
4893
  var Label_H1_OHMA = class extends DecoderPlugin {
4847
4894
  name = "label-h1-ohma";
4848
4895
  qualifiers() {
@@ -4858,12 +4905,12 @@ var Label_H1_OHMA = class extends DecoderPlugin {
4858
4905
  decodeResult.message = message;
4859
4906
  const data = message.text.split("OHMA")[1];
4860
4907
  try {
4861
- const compressedBuffer = Buffer2.from(data, "base64");
4862
- const decompress = new zlib.Inflate({});
4863
- decompress.write(compressedBuffer);
4864
- decompress.flush(zlib.constants.Z_SYNC_FLUSH);
4865
- const result = decompress.read();
4866
- const jsonText = result?.toString() || "";
4908
+ const compressedBuffer = base64ToUint8Array(data);
4909
+ const result = inflateData(compressedBuffer, false);
4910
+ if (!result || result.length === 0) {
4911
+ throw new Error("Decompression produced no output");
4912
+ }
4913
+ const jsonText = textDecoder.decode(result);
4867
4914
  let formattedMsg;
4868
4915
  let jsonMessage;
4869
4916
  try {
@@ -5268,10 +5315,47 @@ var Label_HX = class extends DecoderPlugin {
5268
5315
  }
5269
5316
  };
5270
5317
 
5318
+ // lib/utils/ascii85.ts
5319
+ function ascii85Decode(input) {
5320
+ let str = input;
5321
+ if (str.startsWith("<~")) str = str.slice(2);
5322
+ if (str.endsWith("~>")) str = str.slice(0, -2);
5323
+ str = str.replace(/\s/g, "");
5324
+ if (str.length === 0) return new Uint8Array(0);
5325
+ const output = [];
5326
+ let i = 0;
5327
+ while (i < str.length) {
5328
+ if (str[i] === "z") {
5329
+ output.push(0, 0, 0, 0);
5330
+ i++;
5331
+ continue;
5332
+ }
5333
+ const remaining = str.length - i;
5334
+ const chunkLen = Math.min(5, remaining);
5335
+ if (chunkLen === 1) break;
5336
+ let padded = str.slice(i, i + chunkLen);
5337
+ while (padded.length < 5) padded += "u";
5338
+ let value = 0;
5339
+ for (let j = 0; j < 5; j++) {
5340
+ const digit = padded.charCodeAt(j) - 33;
5341
+ if (digit < 0 || digit > 84) return null;
5342
+ value = value * 85 + digit;
5343
+ }
5344
+ if (chunkLen === 5 && value > 4294967295) return null;
5345
+ const v = value >>> 0;
5346
+ const numBytes = chunkLen === 5 ? 4 : chunkLen - 1;
5347
+ if (numBytes >= 1) output.push(v >>> 24 & 255);
5348
+ if (numBytes >= 2) output.push(v >>> 16 & 255);
5349
+ if (numBytes >= 3) output.push(v >>> 8 & 255);
5350
+ if (numBytes >= 4) output.push(v & 255);
5351
+ i += chunkLen;
5352
+ }
5353
+ return new Uint8Array(output);
5354
+ }
5355
+
5271
5356
  // lib/utils/miam.ts
5272
- import * as Base85 from "base85";
5273
- import * as zlib2 from "minizlib";
5274
- import { Buffer as Buffer3 } from "buffer";
5357
+ var textDecoder2 = new TextDecoder();
5358
+ var textEncoder = new TextEncoder();
5275
5359
  var MIAMVersion = /* @__PURE__ */ ((MIAMVersion2) => {
5276
5360
  MIAMVersion2[MIAMVersion2["V1"] = 1] = "V1";
5277
5361
  MIAMVersion2[MIAMVersion2["V2"] = 2] = "V2";
@@ -5358,7 +5442,7 @@ var MIAMCoreUtils = class {
5358
5442
  error: "Empty MIAM message header"
5359
5443
  };
5360
5444
  }
5361
- let hdr = Base85.decode("<~" + rawHdr + "~>", "ascii85");
5445
+ let hdr = ascii85Decode("<~" + rawHdr + "~>");
5362
5446
  if (!hdr || hdr.length < hpad) {
5363
5447
  return {
5364
5448
  decoded: false,
@@ -5370,17 +5454,24 @@ var MIAMCoreUtils = class {
5370
5454
  if (rawBody.length > 0) {
5371
5455
  if ("0123".indexOf(bpad) >= 0) {
5372
5456
  const bpadValue = parseInt(bpad);
5373
- body = Base85.decode("<~" + rawBody + "~>", "ascii85") || void 0;
5374
- if (body && body.length >= bpadValue) {
5457
+ const decoded = ascii85Decode("<~" + rawBody + "~>");
5458
+ if (decoded === null) {
5459
+ return {
5460
+ decoded: false,
5461
+ error: "Ascii85 decode failed for MIAM message body"
5462
+ };
5463
+ }
5464
+ body = decoded;
5465
+ if (body.length >= bpadValue) {
5375
5466
  body = body.subarray(0, body.length - bpadValue);
5376
5467
  }
5377
5468
  } else if (bpad === "-") {
5378
- body = Buffer3.from(rawBody);
5469
+ body = textEncoder.encode(rawBody);
5379
5470
  }
5380
5471
  }
5381
5472
  hdr = hdr.subarray(0, hdr.length - hpad);
5382
- const version = hdr.readUInt8(0) & 15;
5383
- const pduType = hdr.readUInt8(0) >> 4 & 15;
5473
+ const version = hdr[0] & 15;
5474
+ const pduType = hdr[0] >> 4 & 15;
5384
5475
  if (isMIAMVersion(version) && isMIAMCorePdu(pduType)) {
5385
5476
  const versionPduHandler = this.VersionPduHandlerTable[version][pduType];
5386
5477
  return versionPduHandler(hdr, body);
@@ -5677,7 +5768,7 @@ var MIAMCoreUtils = class {
5677
5768
  ];
5678
5769
  let crc = (seed || 0) & 65535;
5679
5770
  for (let i = 0; i < buf.length; i++) {
5680
- crc = (crc << 8 >>> 0 ^ crctable[(crc >>> 8 ^ buf.readUInt8(i)) >>> 0 & 255]) >>> 0;
5771
+ crc = (crc << 8 >>> 0 ^ crctable[(crc >>> 8 ^ buf[i]) >>> 0 & 255]) >>> 0;
5681
5772
  }
5682
5773
  return crc & 65535;
5683
5774
  }
@@ -5942,7 +6033,7 @@ var MIAMCoreUtils = class {
5942
6033
  ];
5943
6034
  let crc = seed || 0;
5944
6035
  for (let i = 0; i < buf.length; i++) {
5945
- crc = (crc << 8 >>> 0 ^ crctable[(crc >>> 24 ^ buf.readUInt8(i)) >>> 0]) >>> 0;
6036
+ crc = (crc << 8 >>> 0 ^ crctable[(crc >>> 24 ^ buf[i]) >>> 0]) >>> 0;
5946
6037
  }
5947
6038
  return crc;
5948
6039
  }
@@ -5979,7 +6070,7 @@ var MIAMCoreUtils = class {
5979
6070
  let msgNum = 0;
5980
6071
  let ackOptions = 0;
5981
6072
  if (version === 1 /* V1 */) {
5982
- pduSize = hdr.readUInt8(1) << 16 | hdr.readUInt8(2) << 8 | hdr.readUInt8(3);
6073
+ pduSize = hdr[1] << 16 | hdr[2] << 8 | hdr[3];
5983
6074
  const msgSize = hdr.length + (body === void 0 ? 0 : body.length);
5984
6075
  if (pduSize > msgSize) {
5985
6076
  pduIsComplete = false;
@@ -5988,17 +6079,17 @@ var MIAMCoreUtils = class {
5988
6079
  );
5989
6080
  }
5990
6081
  hdr = hdr.subarray(4);
5991
- tail = hdr.subarray(0, 7).toString("ascii");
6082
+ tail = textDecoder2.decode(hdr.subarray(0, 7));
5992
6083
  hdr = hdr.subarray(7);
5993
6084
  } else if (version === 2 /* V2 */) {
5994
6085
  hdr = hdr.subarray(1);
5995
6086
  }
5996
- msgNum = hdr.readUInt8(0) >> 1 & 127;
5997
- ackOptions = hdr.readUInt8(0) & 1;
6087
+ msgNum = hdr[0] >> 1 & 127;
6088
+ ackOptions = hdr[0] & 1;
5998
6089
  hdr = hdr.subarray(1);
5999
- pduCompression = (hdr.readUInt8(0) << 2 | hdr.readUInt8(1) >> 6 & 3) & 7;
6000
- pduEncoding = hdr.readUInt8(1) >> 4 & 3;
6001
- pduAppType = hdr.readUInt8(1) & 15;
6090
+ pduCompression = (hdr[0] << 2 | hdr[1] >> 6 & 3) & 7;
6091
+ pduEncoding = hdr[1] >> 4 & 3;
6092
+ pduAppType = hdr[1] & 15;
6002
6093
  hdr = hdr.subarray(2);
6003
6094
  let appIdLen;
6004
6095
  if (isMIAMCoreApp(pduAppType)) {
@@ -6024,12 +6115,12 @@ var MIAMCoreUtils = class {
6024
6115
  error: "Header too short for v" + version + " appType: " + pduAppType
6025
6116
  };
6026
6117
  }
6027
- pduAppId = hdr.subarray(0, appIdLen).toString("ascii");
6118
+ pduAppId = textDecoder2.decode(hdr.subarray(0, appIdLen));
6028
6119
  hdr = hdr.subarray(appIdLen);
6029
6120
  if (crcLen === 4) {
6030
- pduCrc = hdr.readUInt8(0) << 24 | hdr.readUInt8(1) << 16 | hdr.readUInt8(2) << 8 | hdr.readUInt8(3);
6121
+ pduCrc = hdr[0] << 24 | hdr[1] << 16 | hdr[2] << 8 | hdr[3];
6031
6122
  } else if (crcLen === 2) {
6032
- pduCrc = hdr.readUInt8(0) << 8 | hdr.readUInt8(1);
6123
+ pduCrc = hdr[0] << 8 | hdr[1];
6033
6124
  }
6034
6125
  hdr = hdr.subarray(crcLen);
6035
6126
  if (body !== void 0 && body.length > 0) {
@@ -6037,10 +6128,12 @@ var MIAMCoreUtils = class {
6037
6128
  pduCompression
6038
6129
  ) >= 0) {
6039
6130
  try {
6040
- const decompress = new zlib2.InflateRaw({});
6041
- decompress.write(body);
6042
- decompress.flush(zlib2.constants.Z_SYNC_FLUSH);
6043
- pduData = decompress.read();
6131
+ const inflated = inflateData(body, true);
6132
+ if (inflated === void 0) {
6133
+ pduErrors.push("Inflation produced no output for body");
6134
+ } else {
6135
+ pduData = inflated;
6136
+ }
6044
6137
  } catch (e) {
6045
6138
  pduErrors.push("Inflation failed for body: " + e);
6046
6139
  }
@@ -6098,12 +6191,12 @@ var MIAMCoreUtils = class {
6098
6191
  label,
6099
6192
  ...sublabel ? { sublabel } : {},
6100
6193
  ...mfi ? { mfi } : {},
6101
- ...pduData ? { text: pduData.toString("ascii") } : {}
6194
+ ...pduData ? { text: textDecoder2.decode(pduData) } : {}
6102
6195
  };
6103
6196
  } else {
6104
6197
  pdu.non_acars = {
6105
6198
  appId: pduAppId,
6106
- ...pduData ? { text: pduData.toString("ascii") } : {}
6199
+ ...pduData ? { text: textDecoder2.decode(pduData) } : {}
6107
6200
  };
6108
6201
  }
6109
6202
  return {
@@ -6538,10 +6631,12 @@ var MessageDecoder = class {
6538
6631
  name;
6539
6632
  plugins;
6540
6633
  debug;
6541
- /** Maps a label string to the plugins registered for it, preserving registration order. */
6542
- labelIndex = /* @__PURE__ */ new Map();
6543
- /** Plugins that match all labels (qualifier label '*'). */
6544
- wildcardPlugins = [];
6634
+ /** Maps a label string to the candidate entries (wildcard + label-specific) in registration order. */
6635
+ candidatesByLabel = /* @__PURE__ */ new Map();
6636
+ /** Wildcard entries (plugins that register the '*' label). */
6637
+ wildcardEntries = [];
6638
+ /** Membership set for wildcard entries to dedupe when a plugin also registers a specific label. */
6639
+ wildcardSet = /* @__PURE__ */ new Set();
6545
6640
  constructor() {
6546
6641
  this.name = "acars-decoder-typescript";
6547
6642
  this.plugins = [];
@@ -6553,40 +6648,41 @@ var MessageDecoder = class {
6553
6648
  registerPlugin(plugin) {
6554
6649
  this.plugins.push(plugin);
6555
6650
  const qualifiers = plugin.qualifiers();
6651
+ const entry = {
6652
+ plugin,
6653
+ preambles: qualifiers.preambles && qualifiers.preambles.length > 0 ? qualifiers.preambles : void 0
6654
+ };
6556
6655
  for (const label of qualifiers.labels) {
6557
6656
  if (label === "*") {
6558
- this.wildcardPlugins.push(plugin);
6657
+ if (!this.wildcardSet.has(plugin)) {
6658
+ this.wildcardEntries.push(entry);
6659
+ this.wildcardSet.add(plugin);
6660
+ const wildcardInsertIndex = this.wildcardEntries.length - 1;
6661
+ for (const bucket of this.candidatesByLabel.values()) {
6662
+ bucket.splice(wildcardInsertIndex, 0, entry);
6663
+ }
6664
+ }
6559
6665
  } else {
6560
- let bucket = this.labelIndex.get(label);
6666
+ let bucket = this.candidatesByLabel.get(label);
6561
6667
  if (!bucket) {
6562
- bucket = [];
6563
- this.labelIndex.set(label, bucket);
6668
+ bucket = this.wildcardEntries.slice();
6669
+ this.candidatesByLabel.set(label, bucket);
6670
+ }
6671
+ if (!this.wildcardSet.has(plugin)) {
6672
+ bucket.push(entry);
6564
6673
  }
6565
- bucket.push(plugin);
6566
6674
  }
6567
6675
  }
6568
6676
  return true;
6569
6677
  }
6570
6678
  decode(message, options = {}) {
6571
- const labelPlugins = this.labelIndex.get(message.label) ?? [];
6572
- const seen = /* @__PURE__ */ new Set();
6573
- const candidates = [];
6574
- for (const plugin of [...this.wildcardPlugins, ...labelPlugins]) {
6575
- if (!seen.has(plugin)) {
6576
- seen.add(plugin);
6577
- candidates.push(plugin);
6578
- }
6579
- }
6580
- const usablePlugins = candidates.filter((plugin) => {
6581
- const preambles = plugin.qualifiers().preambles;
6582
- if (!preambles || preambles.length === 0) {
6583
- return true;
6584
- }
6585
- return preambles.some((p) => message.text.startsWith(p));
6586
- });
6679
+ const text = message.text;
6680
+ const candidates = this.candidatesByLabel.get(message.label) ?? this.wildcardEntries;
6587
6681
  if (options.debug) {
6588
6682
  console.log("Usable plugins");
6589
- console.log(usablePlugins);
6683
+ console.log(
6684
+ candidates.filter((e) => this.matchesPreambles(text, e.preambles)).map((e) => e.plugin)
6685
+ );
6590
6686
  }
6591
6687
  let result = {
6592
6688
  decoded: false,
@@ -6598,7 +6694,7 @@ var MessageDecoder = class {
6598
6694
  },
6599
6695
  message,
6600
6696
  remaining: {
6601
- text: message.text
6697
+ text
6602
6698
  },
6603
6699
  raw: {},
6604
6700
  formatted: {
@@ -6606,10 +6702,14 @@ var MessageDecoder = class {
6606
6702
  items: []
6607
6703
  }
6608
6704
  };
6609
- for (let i = 0; i < usablePlugins.length; i++) {
6610
- const plugin = usablePlugins[i];
6611
- result = plugin.decode(message, options);
6612
- if (result.decoded) {
6705
+ for (let i = 0; i < candidates.length; i++) {
6706
+ const entry = candidates[i];
6707
+ if (!this.matchesPreambles(text, entry.preambles)) {
6708
+ continue;
6709
+ }
6710
+ const pluginResult = entry.plugin.decode(message, options);
6711
+ if (pluginResult.decoded) {
6712
+ result = pluginResult;
6613
6713
  break;
6614
6714
  }
6615
6715
  }
@@ -6619,6 +6719,17 @@ var MessageDecoder = class {
6619
6719
  }
6620
6720
  return result;
6621
6721
  }
6722
+ matchesPreambles(text, preambles) {
6723
+ if (!preambles) {
6724
+ return true;
6725
+ }
6726
+ for (let i = 0; i < preambles.length; i++) {
6727
+ if (text.startsWith(preambles[i])) {
6728
+ return true;
6729
+ }
6730
+ }
6731
+ return false;
6732
+ }
6622
6733
  };
6623
6734
  export {
6624
6735
  IcaoDecoder,