From 517fab0427c25cbd0868a1f721ca282af3824520 Mon Sep 17 00:00:00 2001 From: Mark Bumiller Date: Fri, 6 Feb 2026 08:36:50 -0500 Subject: [PATCH] Added and ran linter --- .github/FUNDING.yml | 2 +- .github/dependabot.yml | 12 +- .github/workflows/ci.yml | 2 +- .github/workflows/publish.yml | 4 +- .prettierrc.json | 6 + README.md | 14 +- babel.config.js | 2 +- eslint.config.mts | 42 + jest.config.ts | 4 +- lib/DateTimeUtils.ts | 43 +- lib/DecoderPlugin.ts | 61 +- lib/DecoderPluginInterface.ts | 53 +- lib/IcaoDecoder.test.ts | 324 +- lib/IcaoDecoder.ts | 4 +- lib/MessageDecoder.test.ts | 18 +- lib/MessageDecoder.ts | 14 +- lib/bin/acars-decoder-test.ts | 68 +- lib/bin/acars-decoder.ts | 6 +- lib/plugins/Label_10_LDR.test.ts | 138 +- lib/plugins/Label_10_LDR.ts | 19 +- lib/plugins/Label_10_POS.test.ts | 78 +- lib/plugins/Label_10_POS.ts | 18 +- lib/plugins/Label_10_Slash.test.ts | 144 +- lib/plugins/Label_10_Slash.ts | 48 +- lib/plugins/Label_12_N_Space.test.ts | 6 +- lib/plugins/Label_12_N_Space.ts | 31 +- lib/plugins/Label_12_POS.test.ts | 13 +- lib/plugins/Label_12_POS.ts | 33 +- lib/plugins/Label_13Through18_Slash.test.ts | 130 +- lib/plugins/Label_13Through18_Slash.ts | 54 +- lib/plugins/Label_15.test.ts | 227 +- lib/plugins/Label_15.ts | 39 +- lib/plugins/Label_15_FST.test.ts | 97 +- lib/plugins/Label_15_FST.ts | 31 +- lib/plugins/Label_16_Honeywell.test.ts | 56 +- lib/plugins/Label_16_Honeywell.ts | 33 +- lib/plugins/Label_16_N_Space.test.ts | 5 +- lib/plugins/Label_16_N_Space.ts | 65 +- lib/plugins/Label_16_POSA1.test.ts | 19 +- lib/plugins/Label_16_POSA1.ts | 24 +- lib/plugins/Label_16_TOD.test.ts | 18 +- lib/plugins/Label_16_TOD.ts | 24 +- lib/plugins/Label_1J_2J_FTX.test.ts | 17 +- lib/plugins/Label_1J_2J_FTX.ts | 20 +- lib/plugins/Label_1L_070.test.ts | 7 +- lib/plugins/Label_1L_070.ts | 23 +- lib/plugins/Label_1L_3-line.test.ts | 37 +- lib/plugins/Label_1L_3-line.ts | 14 +- lib/plugins/Label_1L_660.test.ts | 93 +- lib/plugins/Label_1L_660.ts | 14 +- lib/plugins/Label_1L_Slash.test.ts | 10 +- lib/plugins/Label_1L_Slash.ts | 11 +- lib/plugins/Label_1M_Slash.test.ts | 88 +- lib/plugins/Label_1M_Slash.ts | 25 +- lib/plugins/Label_20_CFB.01.ts | 38 +- lib/plugins/Label_20_POS.test.ts | 20 +- lib/plugins/Label_20_POS.ts | 11 +- lib/plugins/Label_21_POS.test.ts | 121 +- lib/plugins/Label_21_POS.ts | 44 +- lib/plugins/Label_22_OFF.test.ts | 13 +- lib/plugins/Label_22_OFF.ts | 69 +- lib/plugins/Label_22_POS.test.ts | 10 +- lib/plugins/Label_22_POS.ts | 11 +- lib/plugins/Label_24_Slash.test.ts | 8 +- lib/plugins/Label_24_Slash.ts | 29 +- lib/plugins/Label_2P_FM3.test.ts | 15 +- lib/plugins/Label_2P_FM3.ts | 61 +- lib/plugins/Label_2P_FM4.test.ts | 16 +- lib/plugins/Label_2P_FM4.ts | 36 +- lib/plugins/Label_2P_FM5.test.ts | 9 +- lib/plugins/Label_2P_FM5.ts | 30 +- lib/plugins/Label_2P_POS.test.ts | 5 +- lib/plugins/Label_2P_POS.ts | 56 +- lib/plugins/Label_30_Slash_EA.test.ts | 60 +- lib/plugins/Label_30_Slash_EA.ts | 22 +- lib/plugins/Label_44_ETA.test.ts | 7 +- lib/plugins/Label_44_ETA.ts | 20 +- lib/plugins/Label_44_IN.test.ts | 7 +- lib/plugins/Label_44_IN.ts | 15 +- lib/plugins/Label_44_OFF.test.ts | 9 +- lib/plugins/Label_44_OFF.ts | 21 +- lib/plugins/Label_44_ON.test.ts | 7 +- lib/plugins/Label_44_ON.ts | 15 +- lib/plugins/Label_44_POS.test.ts | 114 +- lib/plugins/Label_44_POS.ts | 53 +- lib/plugins/Label_44_Slash.test.ts | 53 +- lib/plugins/Label_44_Slash.ts | 45 +- lib/plugins/Label_4A.test.ts | 252 +- lib/plugins/Label_4A.ts | 120 +- lib/plugins/Label_4A_01.test.ts | 98 +- lib/plugins/Label_4A_01.ts | 40 +- lib/plugins/Label_4A_DIS.test.ts | 78 +- lib/plugins/Label_4A_DIS.ts | 22 +- lib/plugins/Label_4A_DOOR.test.ts | 72 +- lib/plugins/Label_4A_DOOR.ts | 30 +- lib/plugins/Label_4A_Slash_01.test.ts | 72 +- lib/plugins/Label_4A_Slash_01.ts | 22 +- lib/plugins/Label_4J_POS.test.ts | 24 +- lib/plugins/Label_4J_POS.ts | 10 +- lib/plugins/Label_4N.test.ts | 242 +- lib/plugins/Label_4N.ts | 90 +- lib/plugins/Label_4T_AGFSR.test.ts | 22 +- lib/plugins/Label_4T_AGFSR.ts | 31 +- lib/plugins/Label_4T_ETA.test.ts | 11 +- lib/plugins/Label_4T_ETA.ts | 12 +- lib/plugins/Label_58.test.ts | 95 +- lib/plugins/Label_58.ts | 13 +- lib/plugins/Label_5Z_Slash.test.ts | 32 +- lib/plugins/Label_5Z_Slash.ts | 57 +- lib/plugins/Label_80.test.ts | 304 +- lib/plugins/Label_80.ts | 103 +- lib/plugins/Label_83.test.ts | 321 +- lib/plugins/Label_83.ts | 106 +- lib/plugins/Label_8E.test.ts | 60 +- lib/plugins/Label_8E.ts | 13 +- lib/plugins/Label_B6.ts | 6 +- lib/plugins/Label_ColonComma.ts | 9 +- lib/plugins/Label_H1.test.ts | 4 +- lib/plugins/Label_H1.ts | 36 +- lib/plugins/Label_H1_FLR.test.ts | 229 +- lib/plugins/Label_H1_FLR.ts | 28 +- lib/plugins/Label_H1_FPN.test.ts | 118 +- lib/plugins/Label_H1_FTX.test.ts | 18 +- lib/plugins/Label_H1_INI.test.ts | 26 +- lib/plugins/Label_H1_OHMA.test.ts | 190 +- lib/plugins/Label_H1_OHMA.ts | 16 +- lib/plugins/Label_H1_PER.test.ts | 18 +- lib/plugins/Label_H1_POS.test.ts | 229 +- lib/plugins/Label_H1_PRG.test.ts | 42 +- lib/plugins/Label_H1_PWI.test.ts | 93 +- lib/plugins/Label_H1_Slash.test.ts | 36 +- lib/plugins/Label_H1_Slash.ts | 51 +- lib/plugins/Label_H1_StarPOS.test.ts | 10 +- lib/plugins/Label_H1_StarPOS.ts | 20 +- lib/plugins/Label_H1_WRN.test.ts | 225 +- lib/plugins/Label_H1_WRN.ts | 24 +- lib/plugins/Label_H2_02E.test.ts | 146 +- lib/plugins/Label_H2_02E.ts | 45 +- lib/plugins/Label_HX.test.ts | 109 +- lib/plugins/Label_HX.ts | 48 +- lib/plugins/Label_MA.test.ts | 16 +- lib/plugins/Label_MA.ts | 54 +- lib/plugins/Label_QP.ts | 20 +- lib/plugins/Label_QQ.test.ts | 228 +- lib/plugins/Label_QQ.ts | 75 +- lib/plugins/Label_QR.ts | 20 +- lib/plugins/Label_QS.ts | 20 +- lib/plugins/Label_SQ.test.ts | 35 +- lib/plugins/Label_SQ.ts | 28 +- lib/types/route.ts | 18 +- lib/types/waypoint.ts | 71 +- lib/types/wind.ts | 18 +- lib/utils/coordinate_utils.ts | 61 +- lib/utils/flight_plan_utils.ts | 79 +- lib/utils/h1_helper.ts | 761 ++-- lib/utils/miam.test.ts | 79 +- lib/utils/miam.ts | 554 +-- lib/utils/result_formatter.ts | 929 ++--- lib/utils/route_utils.ts | 172 +- package-lock.json | 3901 +++++++++++++++++-- package.json | 14 +- tsconfig.json | 9 +- tsup.config.ts | 8 +- 163 files changed, 9513 insertions(+), 5223 deletions(-) create mode 100644 .prettierrc.json create mode 100644 eslint.config.mts diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 6accfe2..4280cb2 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,7 +1,7 @@ # These are supported funding model platforms github: [kevinelliott] -patreon: Airframes +patreon: Airframes open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 3deee26..0955e54 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,10 +1,10 @@ version: 2 updates: - - package-ecosystem: "github-actions" - directory: "/" + - package-ecosystem: 'github-actions' + directory: '/' schedule: - interval: "weekly" - - package-ecosystem: "npm" - directory: "/" + interval: 'weekly' + - package-ecosystem: 'npm' + directory: '/' schedule: - interval: "weekly" + interval: 'weekly' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5f6f034..ba645ea 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: id: scan uses: anchore/scan-action@v7 with: - path: "." + path: '.' - name: upload Anchore scan SARIF report uses: github/codeql-action/upload-sarif@v4 with: diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 5f5b5c9..411c9ce 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -4,8 +4,8 @@ on: types: [published] permissions: - id-token: write # Required for OIDC - contents: read + id-token: write # Required for OIDC + contents: read jobs: build: diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..120fa49 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,6 @@ +{ + "semi": true, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "all" +} diff --git a/README.md b/README.md index 20d495e..e361a7a 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,10 @@ - TypeScript development uses the latest stable Node types (currently v24) without forcing consumers to use a specific Node types version, since `@types/node` is a devDependency. - The published builds are compiled targeting Node 18 runtime via `tsup` so they remain compatible across supported Node versions. - # @airframes/acars-decoder This is a no-op documentation tweak to verify repository access, build/lint, tests, and PR workflow. No functional code changes are included. + # acars-decoder-typescript [![NPM Version](https://badge.fury.io/js/@airframes%2Facars-decoder.svg)](https://badge.fury.io/js/@airframes%2Facars-decoder) @@ -32,11 +32,13 @@ You are welcome to contribute (please see https://github.com/airframesio/acars-m Add the `@airframes/acars-decoder` library to your JavaScript or TypeScript project. With `yarn`: + ``` yarn add @airframes/acars-decoder ``` With `npm`: + ``` npm install @airframes/acars-decoder ``` @@ -51,8 +53,8 @@ Contributions are welcome! Please follow the [ACARS Message Documentation](https # Contributors -| Contributor | Description | -| ----------- | ----------- | -| [Kevin Elliott](https://github.com/kevinelliott) | Primary Airframes contributor | -| [Michael Johnson](https://github.com/johnsom) | Decoder plugins, testing framework | -| [Mark Bumiller](https://github.com/makrsmark) | Decoder plugins, tests, utilities | +| Contributor | Description | +| ------------------------------------------------ | ---------------------------------- | +| [Kevin Elliott](https://github.com/kevinelliott) | Primary Airframes contributor | +| [Michael Johnson](https://github.com/johnsom) | Decoder plugins, testing framework | +| [Mark Bumiller](https://github.com/makrsmark) | Decoder plugins, tests, utilities | diff --git a/babel.config.js b/babel.config.js index 1bca1aa..8165fe4 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,6 +1,6 @@ module.exports = { presets: [ - ['@babel/preset-env', {targets: {node: 'current'}}], + ['@babel/preset-env', { targets: { node: 'current' } }], '@babel/preset-typescript', ], }; diff --git a/eslint.config.mts b/eslint.config.mts new file mode 100644 index 0000000..18d73b7 --- /dev/null +++ b/eslint.config.mts @@ -0,0 +1,42 @@ +import tseslint from '@typescript-eslint/eslint-plugin'; +import tsparser from '@typescript-eslint/parser'; +import prettierPlugin from 'eslint-plugin-prettier'; +import prettierConfig from 'eslint-config-prettier'; +import importPlugin from 'eslint-plugin-import'; +import stylistic from '@stylistic/eslint-plugin'; + +export default [ + { + files: ['**/*.ts'], + + languageOptions: { + parser: tsparser, + sourceType: 'module', + }, + + plugins: { + '@typescript-eslint': tseslint, + prettier: prettierPlugin, + import: importPlugin, + '@stylistic': stylistic + }, + + rules: { + ...tseslint.configs.recommended.rules, + ...prettierConfig.rules, + '@typescript-eslint/no-unused-vars': 'warn', + 'no-console': 'warn', + semi: ['error', 'always'], + '@stylistic/indent': ['error', 2], + 'prettier/prettier': 'error', + 'import/no-cycle': [ + 'error', + { + maxDepth: Infinity, + ignoreExternal: true, + allowUnsafeDynamicCyclicDependency: false, + }, + ], + }, + }, +]; diff --git a/jest.config.ts b/jest.config.ts index a7d70d1..2fcf2f6 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -23,7 +23,7 @@ export default { // collectCoverageFrom: undefined, // The directory where Jest should output its coverage files - coverageDirectory: "coverage", + coverageDirectory: 'coverage', // An array of regexp pattern strings used to skip coverage collection // coveragePathIgnorePatterns: [ @@ -31,7 +31,7 @@ export default { // ], // Indicates which provider should be used to instrument code for coverage - coverageProvider: "v8", + coverageProvider: 'v8', // A list of reporter names that Jest uses when writing coverage reports // coverageReporters: [ diff --git a/lib/DateTimeUtils.ts b/lib/DateTimeUtils.ts index e171a4b..2cf5540 100644 --- a/lib/DateTimeUtils.ts +++ b/lib/DateTimeUtils.ts @@ -1,5 +1,4 @@ export class DateTimeUtils { - // Expects a four digit UTC time string (HHMM) public static UTCToString(UTCString: string) { let utcDate = new Date(); @@ -17,31 +16,39 @@ export class DateTimeUtils { utcDate.setUTCFullYear(2000 + +dateString.substr(4, 2)); } if (timeString.length === 6) { - utcDate.setUTCHours(+timeString.substr(0, 2), +timeString.substr(2, 2), +timeString.substr(4, 2)); + utcDate.setUTCHours( + +timeString.substr(0, 2), + +timeString.substr(2, 2), + +timeString.substr(4, 2), + ); } else { - utcDate.setUTCHours(+timeString.substr(0, 2), +timeString.substr(2, 2), 0); + utcDate.setUTCHours( + +timeString.substr(0, 2), + +timeString.substr(2, 2), + 0, + ); } return utcDate.toUTCString(); } /** - * + * * @param time HHMMSS or HHMM * @returns seconds since midnight */ public static convertHHMMSSToTod(time: string): number { - if(time.length === 4) { + if (time.length === 4) { time += '00'; } const h = Number(time.substring(0, 2)); const m = Number(time.substring(2, 4)); const s = Number(time.substring(4, 6)); - const tod = (h * 3600) + (m * 60) + s; + const tod = h * 3600 + m * 60 + s; return tod; } /** - * + * * @param time HHMMSS * @param date MMDDYY or MMDDYYYY * @returns seconds since epoch @@ -51,25 +58,29 @@ export class DateTimeUtils { if (date.length === 6) { date = date.substring(0, 4) + `20${date.substring(4, 6)}`; } - const timestamp = `${date.substring(4, 8)}-${date.substring(0, 2)}-${date.substring(2, 4)}T${time.substring(0, 2)}:${time.substring(2, 4)}:${time.substring(4, 6)}.000Z` + const timestamp = `${date.substring(4, 8)}-${date.substring(0, 2)}-${date.substring(2, 4)}T${time.substring(0, 2)}:${time.substring(2, 4)}:${time.substring(4, 6)}.000Z`; const millis = Date.parse(timestamp); return millis / 1000; } /** * Converts a timestamp to a string - * + * * ISO-8601 format for 'epoch' * HH:MM:SS for 'tod' - * @param time - * @param format - * @returns + * @param time + * @param format + * @returns */ - public static timestampToString(time: number, format: 'tod' | 'epoch'): string { - const date = new Date(time * 1000); if (format == 'tod') { + public static timestampToString( + time: number, + format: 'tod' | 'epoch', + ): string { + const date = new Date(time * 1000); + if (format == 'tod') { return date.toISOString().slice(11, 19); } //strip off millis - return date.toISOString().slice(0, -5) + "Z"; + return date.toISOString().slice(0, -5) + 'Z'; } -} \ No newline at end of file +} diff --git a/lib/DecoderPlugin.ts b/lib/DecoderPlugin.ts index dd2c461..f1f80bc 100644 --- a/lib/DecoderPlugin.ts +++ b/lib/DecoderPlugin.ts @@ -1,4 +1,10 @@ -import { DecodeResult, DecoderPluginInterface, Message, Options } from './DecoderPluginInterface'; +import { + DecodeResult, + DecoderPluginInterface, + Message, + Options, + Qualifiers, +} from './DecoderPluginInterface'; import { MessageDecoder } from './MessageDecoder'; export abstract class DecoderPlugin implements DecoderPluginInterface { @@ -7,35 +13,37 @@ export abstract class DecoderPlugin implements DecoderPluginInterface { name: string = 'unknown'; defaultResult(): DecodeResult { - return { - decoded: false, - decoder: { - name: 'unknown', - type: 'pattern-match', - decodeLevel: 'none', - }, - formatted: { - description: 'Unknown', - items: [], - }, - raw: {}, - remaining: {}, - }; - }; - - options: Object; - - constructor(decoder : MessageDecoder, options : Options = {}) { + return { + decoded: false, + decoder: { + name: 'unknown', + type: 'pattern-match', + decodeLevel: 'none', + }, + formatted: { + description: 'Unknown', + items: [], + }, + raw: {}, + remaining: {}, + }; + } + + options: object; + + constructor(decoder: MessageDecoder, options: Options = {}) { this.decoder = decoder; this.options = options; } - id() : string { // eslint-disable-line class-methods-use-this - console.log('DecoderPlugin subclass has not overriden id() to provide a unique ID for this plugin!'); + id(): string { + console.log( + 'DecoderPlugin subclass has not overriden id() to provide a unique ID for this plugin!', + ); return 'abstract_decoder_plugin'; } - meetsStateRequirements() : boolean { // eslint-disable-line class-methods-use-this + meetsStateRequirements(): boolean { return true; } @@ -43,18 +51,17 @@ export abstract class DecoderPlugin implements DecoderPluginInterface { // this.store = store; // } - qualifiers() : any { // eslint-disable-line class-methods-use-this - const labels : Array = []; + qualifiers(): Qualifiers { + const labels: Array = []; return { labels, }; } - decode(message: Message) : DecodeResult { // eslint-disable-line class-methods-use-this + decode(message: Message): DecodeResult { const decodeResult = this.defaultResult(); decodeResult.remaining.text = message.text; return decodeResult; } } - diff --git a/lib/DecoderPluginInterface.ts b/lib/DecoderPluginInterface.ts index 382cb0a..1dc2a4e 100644 --- a/lib/DecoderPluginInterface.ts +++ b/lib/DecoderPluginInterface.ts @@ -2,16 +2,16 @@ * Representation of a Message */ export interface Message { - label: string, - sublabel?: string, - text: string, + label: string; + sublabel?: string; + text: string; } /** * Decoder Options */ export interface Options { - debug?:boolean, + debug?: boolean; } /** @@ -20,30 +20,35 @@ export interface Options { export interface DecodeResult { decoded: boolean; decoder: { - name: string, - type: 'pattern-match' | 'none', - decodeLevel: 'none' | 'partial' | 'full', - }, - error?: string, + name: string; + type: 'pattern-match' | 'none'; + decodeLevel: 'none' | 'partial' | 'full'; + }; + error?: string; formatted: { - description: string, - items: { - type: string, - code: string, - label: string, - value: string, - }[] - }, - message?: Message, - raw: any, + description: string; + items: { + type: string; + code: string; + label: string; + value: string; + }[]; + }; + message?: Message; + raw: any; // eslint-disable-line @typescript-eslint/no-explicit-any remaining: { - text?: string - } + text?: string; + }; +} + +export interface Qualifiers { + labels: string[]; + preambles?: string[]; } export interface DecoderPluginInterface { - decode(message: Message) : DecodeResult; - meetsStateRequirements() : boolean; + decode(message: Message): DecodeResult; + meetsStateRequirements(): boolean; // onRegister(store: Store) : void; - qualifiers() : any; + qualifiers(): Qualifiers; } diff --git a/lib/IcaoDecoder.test.ts b/lib/IcaoDecoder.test.ts index 7efc8ea..19b5215 100644 --- a/lib/IcaoDecoder.test.ts +++ b/lib/IcaoDecoder.test.ts @@ -8,167 +8,167 @@ describe('IcaoDecoder', () => { }); describe('isMilitary', () => { - it('should match Egypt', () => { - expect(new IcaoDecoder('010070').isMilitary()).toBe(true); - expect(new IcaoDecoder('01008f').isMilitary()).toBe(true); - }); - - it('should match Algeria', () => { - expect(new IcaoDecoder('0a4000').isMilitary()).toBe(true); - expect(new IcaoDecoder('0a4fff').isMilitary()).toBe(true); - }); - - it('should match Italy', () => { - expect(new IcaoDecoder('33ff00').isMilitary()).toBe(true); - expect(new IcaoDecoder('33ffff').isMilitary()).toBe(true); - }); - - it('should match Spain', () => { - expect(new IcaoDecoder('350000').isMilitary()).toBe(true); - expect(new IcaoDecoder('37ffff').isMilitary()).toBe(true); - }); - - it('should match France', () => { - // 1 - expect(new IcaoDecoder('3aa000').isMilitary()).toBe(true); - expect(new IcaoDecoder('3affff').isMilitary()).toBe(true); - // 2 - expect(new IcaoDecoder('3b7000').isMilitary()).toBe(true); - expect(new IcaoDecoder('3bffff').isMilitary()).toBe(true); - }); - - it('should match Germany', () => { - // 1 - expect(new IcaoDecoder('3ea000').isMilitary()).toBe(true); - expect(new IcaoDecoder('3ebfff').isMilitary()).toBe(true); - // 2 - expect(new IcaoDecoder('3f4000').isMilitary()).toBe(true); - expect(new IcaoDecoder('3fbfff').isMilitary()).toBe(true); - }); - - it('should match UK', () => { - // 1 - expect(new IcaoDecoder('400000').isMilitary()).toBe(true); - expect(new IcaoDecoder('40003f').isMilitary()).toBe(true); - // 2 - expect(new IcaoDecoder('43c000').isMilitary()).toBe(true); - expect(new IcaoDecoder('43cfff').isMilitary()).toBe(true); - }); - - it('should match Austria', () => { - expect(new IcaoDecoder('444000').isMilitary()).toBe(true); - expect(new IcaoDecoder('446fff').isMilitary()).toBe(true); - }); - - it('should match Belgium', () => { - expect(new IcaoDecoder('44f000').isMilitary()).toBe(true); - expect(new IcaoDecoder('44ffff').isMilitary()).toBe(true); - }); - - it('should match Bulgaria', () => { - expect(new IcaoDecoder('457000').isMilitary()).toBe(true); - expect(new IcaoDecoder('457fff').isMilitary()).toBe(true); - }); - - it('should match Denmark', () => { - expect(new IcaoDecoder('45f400').isMilitary()).toBe(true); - expect(new IcaoDecoder('45f4ff').isMilitary()).toBe(true); - }); - - it('should match Greece', () => { - expect(new IcaoDecoder('468000').isMilitary()).toBe(true); - expect(new IcaoDecoder('4683ff').isMilitary()).toBe(true); - }); - - it('should match Hungary', () => { - expect(new IcaoDecoder('473c00').isMilitary()).toBe(true); - expect(new IcaoDecoder('473c0f').isMilitary()).toBe(true); - }); - - it('should match Norway', () => { - expect(new IcaoDecoder('478100').isMilitary()).toBe(true); - expect(new IcaoDecoder('4781ff').isMilitary()).toBe(true); - }); - - it('should match Netherlands', () => { - expect(new IcaoDecoder('480000').isMilitary()).toBe(true); - expect(new IcaoDecoder('480fff').isMilitary()).toBe(true); - }); - - it('should match Poland', () => { - expect(new IcaoDecoder('48d800').isMilitary()).toBe(true); - expect(new IcaoDecoder('48d87f').isMilitary()).toBe(true); - }); - - it('should match Portugal', () => { - expect(new IcaoDecoder('497c00').isMilitary()).toBe(true); - expect(new IcaoDecoder('497cff').isMilitary()).toBe(true); - }); - - it('should match Czech', () => { - expect(new IcaoDecoder('498420').isMilitary()).toBe(true); - expect(new IcaoDecoder('49842f').isMilitary()).toBe(true); - }); - - it('should match Switzerland', () => { - expect(new IcaoDecoder('4b7000').isMilitary()).toBe(true); - expect(new IcaoDecoder('4b7fff').isMilitary()).toBe(true); - }); - - it('should match Turkey', () => { - expect(new IcaoDecoder('4b8200').isMilitary()).toBe(true); - expect(new IcaoDecoder('4b82ff').isMilitary()).toBe(true); - }); - - it('should match Oman', () => { - expect(new IcaoDecoder('70c070').isMilitary()).toBe(true); - expect(new IcaoDecoder('70c07f').isMilitary()).toBe(true); - }); - - it('should match Saudi Arabia', () => { - // 1 - expect(new IcaoDecoder('710258').isMilitary()).toBe(true); - expect(new IcaoDecoder('71028f').isMilitary()).toBe(true); - // 2 - expect(new IcaoDecoder('710380').isMilitary()).toBe(true); - expect(new IcaoDecoder('71039f').isMilitary()).toBe(true); - }); - - it('should match Israel', () => { - expect(new IcaoDecoder('738a00').isMilitary()).toBe(true); - expect(new IcaoDecoder('738aff').isMilitary()).toBe(true); - }); - - it('should match Australia', () => { - expect(new IcaoDecoder('7cf800').isMilitary()).toBe(true); - expect(new IcaoDecoder('7cfaff').isMilitary()).toBe(true); - }); - - it('should match India', () => { - expect(new IcaoDecoder('800200').isMilitary()).toBe(true); - expect(new IcaoDecoder('8002ff').isMilitary()).toBe(true); - }); - - it('should match USA', () => { - expect(new IcaoDecoder('adf7c8').isMilitary()).toBe(true); - expect(new IcaoDecoder('afffff').isMilitary()).toBe(true); - }); - - it('should match Canada', () => { - expect(new IcaoDecoder('c20000').isMilitary()).toBe(true); - expect(new IcaoDecoder('c3ffff').isMilitary()).toBe(true); - }); - - it('should match Brazil', () => { - expect(new IcaoDecoder('e40000').isMilitary()).toBe(true); - expect(new IcaoDecoder('e41fff').isMilitary()).toBe(true); - }); - - it('should return false for non-military ICAO', () => { - expect(new IcaoDecoder('a00000').isMilitary()).toBe(false); - expect(new IcaoDecoder('123456').isMilitary()).toBe(false); - expect(new IcaoDecoder('000000').isMilitary()).toBe(false); - expect(new IcaoDecoder('ffffff').isMilitary()).toBe(false); - }); + it('should match Egypt', () => { + expect(new IcaoDecoder('010070').isMilitary()).toBe(true); + expect(new IcaoDecoder('01008f').isMilitary()).toBe(true); + }); + + it('should match Algeria', () => { + expect(new IcaoDecoder('0a4000').isMilitary()).toBe(true); + expect(new IcaoDecoder('0a4fff').isMilitary()).toBe(true); + }); + + it('should match Italy', () => { + expect(new IcaoDecoder('33ff00').isMilitary()).toBe(true); + expect(new IcaoDecoder('33ffff').isMilitary()).toBe(true); + }); + + it('should match Spain', () => { + expect(new IcaoDecoder('350000').isMilitary()).toBe(true); + expect(new IcaoDecoder('37ffff').isMilitary()).toBe(true); + }); + + it('should match France', () => { + // 1 + expect(new IcaoDecoder('3aa000').isMilitary()).toBe(true); + expect(new IcaoDecoder('3affff').isMilitary()).toBe(true); + // 2 + expect(new IcaoDecoder('3b7000').isMilitary()).toBe(true); + expect(new IcaoDecoder('3bffff').isMilitary()).toBe(true); + }); + + it('should match Germany', () => { + // 1 + expect(new IcaoDecoder('3ea000').isMilitary()).toBe(true); + expect(new IcaoDecoder('3ebfff').isMilitary()).toBe(true); + // 2 + expect(new IcaoDecoder('3f4000').isMilitary()).toBe(true); + expect(new IcaoDecoder('3fbfff').isMilitary()).toBe(true); + }); + + it('should match UK', () => { + // 1 + expect(new IcaoDecoder('400000').isMilitary()).toBe(true); + expect(new IcaoDecoder('40003f').isMilitary()).toBe(true); + // 2 + expect(new IcaoDecoder('43c000').isMilitary()).toBe(true); + expect(new IcaoDecoder('43cfff').isMilitary()).toBe(true); + }); + + it('should match Austria', () => { + expect(new IcaoDecoder('444000').isMilitary()).toBe(true); + expect(new IcaoDecoder('446fff').isMilitary()).toBe(true); + }); + + it('should match Belgium', () => { + expect(new IcaoDecoder('44f000').isMilitary()).toBe(true); + expect(new IcaoDecoder('44ffff').isMilitary()).toBe(true); + }); + + it('should match Bulgaria', () => { + expect(new IcaoDecoder('457000').isMilitary()).toBe(true); + expect(new IcaoDecoder('457fff').isMilitary()).toBe(true); + }); + + it('should match Denmark', () => { + expect(new IcaoDecoder('45f400').isMilitary()).toBe(true); + expect(new IcaoDecoder('45f4ff').isMilitary()).toBe(true); + }); + + it('should match Greece', () => { + expect(new IcaoDecoder('468000').isMilitary()).toBe(true); + expect(new IcaoDecoder('4683ff').isMilitary()).toBe(true); + }); + + it('should match Hungary', () => { + expect(new IcaoDecoder('473c00').isMilitary()).toBe(true); + expect(new IcaoDecoder('473c0f').isMilitary()).toBe(true); + }); + + it('should match Norway', () => { + expect(new IcaoDecoder('478100').isMilitary()).toBe(true); + expect(new IcaoDecoder('4781ff').isMilitary()).toBe(true); + }); + + it('should match Netherlands', () => { + expect(new IcaoDecoder('480000').isMilitary()).toBe(true); + expect(new IcaoDecoder('480fff').isMilitary()).toBe(true); + }); + + it('should match Poland', () => { + expect(new IcaoDecoder('48d800').isMilitary()).toBe(true); + expect(new IcaoDecoder('48d87f').isMilitary()).toBe(true); + }); + + it('should match Portugal', () => { + expect(new IcaoDecoder('497c00').isMilitary()).toBe(true); + expect(new IcaoDecoder('497cff').isMilitary()).toBe(true); + }); + + it('should match Czech', () => { + expect(new IcaoDecoder('498420').isMilitary()).toBe(true); + expect(new IcaoDecoder('49842f').isMilitary()).toBe(true); + }); + + it('should match Switzerland', () => { + expect(new IcaoDecoder('4b7000').isMilitary()).toBe(true); + expect(new IcaoDecoder('4b7fff').isMilitary()).toBe(true); + }); + + it('should match Turkey', () => { + expect(new IcaoDecoder('4b8200').isMilitary()).toBe(true); + expect(new IcaoDecoder('4b82ff').isMilitary()).toBe(true); + }); + + it('should match Oman', () => { + expect(new IcaoDecoder('70c070').isMilitary()).toBe(true); + expect(new IcaoDecoder('70c07f').isMilitary()).toBe(true); + }); + + it('should match Saudi Arabia', () => { + // 1 + expect(new IcaoDecoder('710258').isMilitary()).toBe(true); + expect(new IcaoDecoder('71028f').isMilitary()).toBe(true); + // 2 + expect(new IcaoDecoder('710380').isMilitary()).toBe(true); + expect(new IcaoDecoder('71039f').isMilitary()).toBe(true); + }); + + it('should match Israel', () => { + expect(new IcaoDecoder('738a00').isMilitary()).toBe(true); + expect(new IcaoDecoder('738aff').isMilitary()).toBe(true); + }); + + it('should match Australia', () => { + expect(new IcaoDecoder('7cf800').isMilitary()).toBe(true); + expect(new IcaoDecoder('7cfaff').isMilitary()).toBe(true); + }); + + it('should match India', () => { + expect(new IcaoDecoder('800200').isMilitary()).toBe(true); + expect(new IcaoDecoder('8002ff').isMilitary()).toBe(true); + }); + + it('should match USA', () => { + expect(new IcaoDecoder('adf7c8').isMilitary()).toBe(true); + expect(new IcaoDecoder('afffff').isMilitary()).toBe(true); + }); + + it('should match Canada', () => { + expect(new IcaoDecoder('c20000').isMilitary()).toBe(true); + expect(new IcaoDecoder('c3ffff').isMilitary()).toBe(true); + }); + + it('should match Brazil', () => { + expect(new IcaoDecoder('e40000').isMilitary()).toBe(true); + expect(new IcaoDecoder('e41fff').isMilitary()).toBe(true); + }); + + it('should return false for non-military ICAO', () => { + expect(new IcaoDecoder('a00000').isMilitary()).toBe(false); + expect(new IcaoDecoder('123456').isMilitary()).toBe(false); + expect(new IcaoDecoder('000000').isMilitary()).toBe(false); + expect(new IcaoDecoder('ffffff').isMilitary()).toBe(false); + }); }); }); diff --git a/lib/IcaoDecoder.ts b/lib/IcaoDecoder.ts index 13893e4..5c7b931 100644 --- a/lib/IcaoDecoder.ts +++ b/lib/IcaoDecoder.ts @@ -1,6 +1,6 @@ export class IcaoDecoder { - name : string; - icao : string; + name: string; + icao: string; // Military ICAO address ranges (as [start, end] tuples, inclusive) private static readonly MILITARY_RANGES: Array<[number, number]> = [ diff --git a/lib/MessageDecoder.test.ts b/lib/MessageDecoder.test.ts index 9c86b40..aefb7ce 100644 --- a/lib/MessageDecoder.test.ts +++ b/lib/MessageDecoder.test.ts @@ -1,4 +1,4 @@ -import { MessageDecoder } from "./MessageDecoder" +import { MessageDecoder } from './MessageDecoder'; describe('MessageDecoder', () => { let decoder: MessageDecoder; @@ -10,35 +10,35 @@ describe('MessageDecoder', () => { test('MIAM core seamless decode', () => { const message = { label: 'MA', - text: 'T02!<<,:/k.E`;FOV@!\'s.16q6R+p(RK,|D2ujNJhRah?_qrNftWiI-V,@*RQs,tn,FYN$/V1!gNIc6CO;$D,1:.4?dF952;>XP$"B"Ok-Fr\'0^k?rP]3&UGoPX;\\XP$\"B\"Ok-Fr'0^k?rP]3&UGoPX;\\ { const message = { label: 'H1', - text: 'POSN43312W123174,EASON,215754,370,EBINY,220601,ELENN,M48,02216,185/TS215754,0921227A40' + text: 'POSN43312W123174,EASON,215754,370,EBINY,220601,ELENN,M48,02216,185/TS215754,0921227A40', }; decoder.decode(message); const decodeResult = decoder.decode(message); - if(!decodeResult.message) { + if (!decodeResult.message) { expect(decodeResult.message).toBeDefined(); - return; + return; } expect(decodeResult.message.label).toBe('H1'); expect(decodeResult.formatted.items.length).toBe(5); }); -}); \ No newline at end of file +}); diff --git a/lib/MessageDecoder.ts b/lib/MessageDecoder.ts index 1f81b1a..1b1199a 100644 --- a/lib/MessageDecoder.ts +++ b/lib/MessageDecoder.ts @@ -1,8 +1,11 @@ -import { DecodeResult, DecoderPluginInterface, Message, Options } from './DecoderPluginInterface'; // eslint-disable-line import/no-cycle +import { + DecodeResult, + DecoderPluginInterface, + Message, + Options, +} from './DecoderPluginInterface'; import * as Plugins from './plugins/official'; -import { MIAMCoreUtils } from './utils/miam'; - export class MessageDecoder { name: string; plugins: Array; @@ -80,7 +83,6 @@ export class MessageDecoder { } registerPlugin(plugin: DecoderPluginInterface): boolean { - const pluginInstance = plugin; // plugin.onRegister(this.store); this.plugins.push(plugin); return true; @@ -94,14 +96,14 @@ export class MessageDecoder { if (qualifiers.labels.includes(message.label)) { if (qualifiers.preambles && qualifiers.preambles.length > 0) { - const matching = qualifiers.preambles.filter((preamble: string) => { // eslint-disable-line arrow-body-style,max-len + const matching = qualifiers.preambles.filter((preamble: string) => { // console.log(message.text.substring(0, preamble.length)); // console.log(preamble); return message.text.substring(0, preamble.length) === preamble; }); // console.log(matching); return matching.length >= 1; - } else { // eslint-disable-line no-else-return + } else { return true; } } diff --git a/lib/bin/acars-decoder-test.ts b/lib/bin/acars-decoder-test.ts index 410d64f..e4d249a 100644 --- a/lib/bin/acars-decoder-test.ts +++ b/lib/bin/acars-decoder-test.ts @@ -4,11 +4,11 @@ import { DecodeResult, Message } from '../DecoderPluginInterface'; import { MessageDecoder } from '../MessageDecoder'; function debugMessage(message: Message, decoding: DecodeResult) { - console.log("ORIGINAL MESSAGE"); - console.log("Label:", message.label); - console.log("Text:", message.text); + console.log('ORIGINAL MESSAGE'); + console.log('Label:', message.label); + console.log('Text:', message.text); console.log(); - console.log("Decoded Message:"); + console.log('Decoded Message:'); console.log(decoding.formatted.description); if (decoding.formatted.items && decoding.formatted.items.length > 0) { decoding.formatted.items.forEach((item) => { @@ -21,7 +21,7 @@ function debugMessage(message: Message, decoding: DecodeResult) { function decode(label: string, text: string) { const message = { label: label, - text: text + text: text, }; const decoder = new MessageDecoder(); const decoding = decoder.decode(message); @@ -49,51 +49,61 @@ decode('5Z', '/R3 SNAORD 2205-14 SNA'); // Label 80 + 3N01 POSRPT decode( '80', - "3N01 POSRPT 0570/13 MSLP/KJFK .N603AV/04F 00:59\n\ + '3N01 POSRPT 0570/13 MSLP/KJFK .N603AV/04F 00:59\n\ /NWYP ORF /HDG 074/MCH 779\n\ /POS N3630.7W07701.8/FL 350/TAS 453/SAT -054\n\ -/SWND 079/DWND 216/FOB N006857/ETA 01:40.3" +/SWND 079/DWND 216/FOB N006857/ETA 01:40.3', ); // Label H1 + M1BPOS decode( 'H1', - '#M1BPOSN32492W117178,N33492W118335,195835,350,N32463W117143,195907,N32359W117014,M51,25872,249/TS195835,1214195C27' + '#M1BPOSN32492W117178,N33492W118335,195835,350,N32463W117143,195907,N32359W117014,M51,25872,249/TS195835,1214195C27', ); // Label H1 with POS decode( 'H1', - 'POSN43312W123174,EASON,215754,370,EBINY,220601,ELENN,M48,02216,185/TS215754,0921227A40') + 'POSN43312W123174,EASON,215754,370,EBINY,220601,ELENN,M48,02216,185/TS215754,0921227A40', +); decode( 'H1', - 'POSN45209W122550,PEGTY,220309,134,MINNE,220424,HISKU,M6,060013,269,366,355K,292K,730A5B') + 'POSN45209W122550,PEGTY,220309,134,MINNE,220424,HISKU,M6,060013,269,366,355K,292K,730A5B', +); decode( 'H1', - 'POSN43030W122406,IBALL,220516,380,AARON,220816,MOXEE,M47,0047,86/TS220516,092122BF64') + 'POSN43030W122406,IBALL,220516,380,AARON,220816,MOXEE,M47,0047,86/TS220516,092122BF64', +); decode( 'H1', - 'POSN33225W079428,SCOOB,232933,340,ENEME,235712,FETAL,M42,003051,15857F6') + 'POSN33225W079428,SCOOB,232933,340,ENEME,235712,FETAL,M42,003051,15857F6', +); -// Label 8E -decode("8E", "EGSS,1618"); +// Label 8E +decode('8E', 'EGSS,1618'); // Label 1M with slash -decode("1M", `/BA0843/ETA01/230822/LDSP/EGLL/EGSS/2JK0 -1940/EGLL27L/10 `); -decode("1M", `/AY1416/ETA02/230822/EDDF/EFHK/EETN/2JK0 +decode( + '1M', + `/BA0843/ETA01/230822/LDSP/EGLL/EGSS/2JK0 +1940/EGLL27L/10 `, +); +decode( + '1M', + `/AY1416/ETA02/230822/EDDF/EFHK/EETN/2JK0 1956/EFHK04L/50 - / / / / / /`); + / / / / / /`, +); // some Positions messages -decode( '15', '(2N38111W 82211266 76400-64(Z'); -decode( '15', '(2N46315E 40252 40300-45(Z'); -decode( '15', 'FST01KMCOEGKKN505552W00118021'); -decode( '20', 'POSN38160W077075,,211733,360,OTT,212041,,N42,19689,40,544'); -decode( '44', '00ETA02,N38338W121179,KMHR,KPDX,0806,2355,005.1'); -decode( '44', 'IN02,N38338W121179,KMHR,KPDX,0806,2355,005.1'); -decode( '44', 'OFF02,N38334W121176,KMHR,KPDX,0807,0014,0123,004.9'); -decode( '44', 'ON02,N38333W121178,KRNO,KMHR,0806,2350,005.2'); -decode( '44', 'POS02,N38338W121179,GRD,KMHR,KPDX,0807,0003,0112,005.1'); -decode( '44', 'POS02,N51112 E000516,390,EGCC,LYBE,0608,1851,2053,00091'); -decode( 'H1', 'POSN50378W002590,ELRIP,091226'); +decode('15', '(2N38111W 82211266 76400-64(Z'); +decode('15', '(2N46315E 40252 40300-45(Z'); +decode('15', 'FST01KMCOEGKKN505552W00118021'); +decode('20', 'POSN38160W077075,,211733,360,OTT,212041,,N42,19689,40,544'); +decode('44', '00ETA02,N38338W121179,KMHR,KPDX,0806,2355,005.1'); +decode('44', 'IN02,N38338W121179,KMHR,KPDX,0806,2355,005.1'); +decode('44', 'OFF02,N38334W121176,KMHR,KPDX,0807,0014,0123,004.9'); +decode('44', 'ON02,N38333W121178,KRNO,KMHR,0806,2350,005.2'); +decode('44', 'POS02,N38338W121179,GRD,KMHR,KPDX,0807,0003,0112,005.1'); +decode('44', 'POS02,N51112 E000516,390,EGCC,LYBE,0608,1851,2053,00091'); +decode('H1', 'POSN50378W002590,ELRIP,091226'); diff --git a/lib/bin/acars-decoder.ts b/lib/bin/acars-decoder.ts index c54e320..69d8447 100644 --- a/lib/bin/acars-decoder.ts +++ b/lib/bin/acars-decoder.ts @@ -5,16 +5,16 @@ import { MessageDecoder } from '../MessageDecoder'; const decoder = new MessageDecoder(); const message = { label: process.argv[2], - text: process.argv[3] + text: process.argv[3], }; -console.log("Original Message:"); +console.log('Original Message:'); console.log(message.text); console.log(); const result = decoder.decode(message, { debug: true }); -console.log("Decoded Message:"); +console.log('Decoded Message:'); console.log(result.formatted.description); if (result.formatted.items && result.formatted.items.length > 0) { result.formatted.items.forEach((item) => { diff --git a/lib/plugins/Label_10_LDR.test.ts b/lib/plugins/Label_10_LDR.test.ts index 4772810..6d47780 100644 --- a/lib/plugins/Label_10_LDR.test.ts +++ b/lib/plugins/Label_10_LDR.test.ts @@ -2,80 +2,84 @@ import { MessageDecoder } from '../MessageDecoder'; import { Label_10_LDR } from './Label_10_LDR'; describe('Label_10_LDR', () => { - let plugin: Label_10_LDR; - const message = {label: '10', text: ''}; + let plugin: Label_10_LDR; + const message = { label: '10', text: '' }; - beforeEach(() => { - const decoder = new MessageDecoder(); - plugin = new Label_10_LDR(decoder); - }); + beforeEach(() => { + const decoder = new MessageDecoder(); + plugin = new Label_10_LDR(decoder); + }); - test('matches Label 10 Preamble ldr qualifiers', () => { - expect(plugin.decode).toBeDefined(); - expect(plugin.name).toBe('label-10-ldr'); - expect(plugin.qualifiers).toBeDefined(); - expect(plugin.qualifiers()).toEqual({ - labels: ['10'], - preambles: ['LDR'], - }); - }); + test('matches Label 10 Preamble ldr qualifiers', () => { + expect(plugin.decode).toBeDefined(); + expect(plugin.name).toBe('label-10-ldr'); + expect(plugin.qualifiers).toBeDefined(); + expect(plugin.qualifiers()).toEqual({ + labels: ['10'], + preambles: ['LDR'], + }); + }); - test('decodes Label 10 Preamble LDR variant 1', () => { - message.text = 'LDR01,189,C,SWA-2600-016,0,N 38.151,W 76.623,37003, 10.2,KATL,KLGA,KLGA,22/,/,/,0,0,,,,,,,0,0,0,00,,135.1,08.6,143.7,,,'; - const decodeResult = plugin.decode(message); + test('decodes Label 10 Preamble LDR variant 1', () => { + message.text = + 'LDR01,189,C,SWA-2600-016,0,N 38.151,W 76.623,37003, 10.2,KATL,KLGA,KLGA,22/,/,/,0,0,,,,,,,0,0,0,00,,135.1,08.6,143.7,,,'; + const decodeResult = plugin.decode(message); - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.formatted.items.length).toBe(6); - expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); - expect(decodeResult.formatted.items[0].value).toBe('38.151 N, 76.623 W'); - expect(decodeResult.formatted.items[1].label).toBe('Altitude'); - expect(decodeResult.formatted.items[1].value).toBe('37003 feet'); - expect(decodeResult.formatted.items[2].label).toBe('Origin'); - expect(decodeResult.formatted.items[2].value).toBe('KATL'); - expect(decodeResult.formatted.items[3].label).toBe('Destination'); - expect(decodeResult.formatted.items[3].value).toBe('KLGA'); - expect(decodeResult.formatted.items[4].label).toBe('Alternate Destination'); - expect(decodeResult.formatted.items[4].value).toBe('KLGA'); - expect(decodeResult.formatted.items[5].label).toBe('Arrival Runway'); - expect(decodeResult.formatted.items[5].value).toBe('22'); - expect(decodeResult.remaining.text).toBe('LDR01,189,C,SWA-2600-016,0,0,0,,,,,,,0,0,0,00,,135.1,08.6,143.7,,,'); - }); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.formatted.items.length).toBe(6); + expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); + expect(decodeResult.formatted.items[0].value).toBe('38.151 N, 76.623 W'); + expect(decodeResult.formatted.items[1].label).toBe('Altitude'); + expect(decodeResult.formatted.items[1].value).toBe('37003 feet'); + expect(decodeResult.formatted.items[2].label).toBe('Origin'); + expect(decodeResult.formatted.items[2].value).toBe('KATL'); + expect(decodeResult.formatted.items[3].label).toBe('Destination'); + expect(decodeResult.formatted.items[3].value).toBe('KLGA'); + expect(decodeResult.formatted.items[4].label).toBe('Alternate Destination'); + expect(decodeResult.formatted.items[4].value).toBe('KLGA'); + expect(decodeResult.formatted.items[5].label).toBe('Arrival Runway'); + expect(decodeResult.formatted.items[5].value).toBe('22'); + expect(decodeResult.remaining.text).toBe( + 'LDR01,189,C,SWA-2600-016,0,0,0,,,,,,,0,0,0,00,,135.1,08.6,143.7,,,', + ); + }); - test('decodes Label 10 Preamble LDR variant 2', () => { - message.text = 'LDR01,189,C,SWA-2600-016,0,N 37.873,W 79.541,30998, 16.6,KBNA,KBOS,KBOS,27/,33L/,22L/,0,1,,,,,,,0,0,0,00,,131.2,11.4,142.6,,,'; - const decodeResult = plugin.decode(message); + test('decodes Label 10 Preamble LDR variant 2', () => { + message.text = + 'LDR01,189,C,SWA-2600-016,0,N 37.873,W 79.541,30998, 16.6,KBNA,KBOS,KBOS,27/,33L/,22L/,0,1,,,,,,,0,0,0,00,,131.2,11.4,142.6,,,'; + const decodeResult = plugin.decode(message); - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.formatted.items.length).toBe(7); - expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); - expect(decodeResult.formatted.items[0].value).toBe('37.873 N, 79.541 W'); - expect(decodeResult.formatted.items[1].label).toBe('Altitude'); - expect(decodeResult.formatted.items[1].value).toBe('30998 feet'); - expect(decodeResult.formatted.items[2].label).toBe('Origin'); - expect(decodeResult.formatted.items[2].value).toBe('KBNA'); - expect(decodeResult.formatted.items[3].label).toBe('Destination'); - expect(decodeResult.formatted.items[3].value).toBe('KBOS'); - expect(decodeResult.formatted.items[4].label).toBe('Alternate Destination'); - expect(decodeResult.formatted.items[4].value).toBe('KBOS'); - expect(decodeResult.formatted.items[5].label).toBe('Arrival Runway'); - expect(decodeResult.formatted.items[5].value).toBe('27'); - expect(decodeResult.formatted.items[6].label).toBe('Alternate Runway'); - expect(decodeResult.formatted.items[6].value).toBe('33L,22L'); - expect(decodeResult.remaining.text).toBe('LDR01,189,C,SWA-2600-016,0,0,1,,,,,,,0,0,0,00,,131.2,11.4,142.6,,,'); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.formatted.items.length).toBe(7); + expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); + expect(decodeResult.formatted.items[0].value).toBe('37.873 N, 79.541 W'); + expect(decodeResult.formatted.items[1].label).toBe('Altitude'); + expect(decodeResult.formatted.items[1].value).toBe('30998 feet'); + expect(decodeResult.formatted.items[2].label).toBe('Origin'); + expect(decodeResult.formatted.items[2].value).toBe('KBNA'); + expect(decodeResult.formatted.items[3].label).toBe('Destination'); + expect(decodeResult.formatted.items[3].value).toBe('KBOS'); + expect(decodeResult.formatted.items[4].label).toBe('Alternate Destination'); + expect(decodeResult.formatted.items[4].value).toBe('KBOS'); + expect(decodeResult.formatted.items[5].label).toBe('Arrival Runway'); + expect(decodeResult.formatted.items[5].value).toBe('27'); + expect(decodeResult.formatted.items[6].label).toBe('Alternate Runway'); + expect(decodeResult.formatted.items[6].value).toBe('33L,22L'); + expect(decodeResult.remaining.text).toBe( + 'LDR01,189,C,SWA-2600-016,0,0,1,,,,,,,0,0,0,00,,131.2,11.4,142.6,,,', + ); }); + test('decodes Label 10 Preamble LDR ', () => { + message.text = 'LDR Bogus Message'; + const decodeResult = plugin.decode(message); - test('decodes Label 10 Preamble LDR ', () => { - - message.text = 'LDR Bogus Message'; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(false); - expect(decodeResult.decoder.decodeLevel).toBe('none'); - expect(decodeResult.decoder.name).toBe('label-10-ldr'); - expect(decodeResult.formatted.description).toBe('Position Report'); - expect(decodeResult.message).toBe(message); - }); + expect(decodeResult.decoded).toBe(false); + expect(decodeResult.decoder.decodeLevel).toBe('none'); + expect(decodeResult.decoder.name).toBe('label-10-ldr'); + expect(decodeResult.formatted.description).toBe('Position Report'); + expect(decodeResult.message).toBe(message); + }); }); diff --git a/lib/plugins/Label_10_LDR.ts b/lib/plugins/Label_10_LDR.ts index 45e181c..85f77c3 100644 --- a/lib/plugins/Label_10_LDR.ts +++ b/lib/plugins/Label_10_LDR.ts @@ -2,10 +2,10 @@ import { DecoderPlugin } from '../DecoderPlugin'; import { DecodeResult, Message, Options } from '../DecoderPluginInterface'; import { ResultFormatter } from '../utils/result_formatter'; -export class Label_10_LDR extends DecoderPlugin { // eslint-disable-line camelcase +export class Label_10_LDR extends DecoderPlugin { name = 'label-10-ldr'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['10'], preambles: ['LDR'], @@ -34,18 +34,23 @@ export class Label_10_LDR extends DecoderPlugin { // eslint-disable-line camelca const position = { latitude: (lat[0] === 'N' ? 1 : -1) * Number(lat.substring(1).trim()), longitude: (lon[0] === 'E' ? 1 : -1) * Number(lon.substring(1).trim()), - } + }; ResultFormatter.position(decodeResult, position); ResultFormatter.altitude(decodeResult, Number(parts[7])); ResultFormatter.departureAirport(decodeResult, parts[9]); ResultFormatter.arrivalAirport(decodeResult, parts[10]); ResultFormatter.alternateAirport(decodeResult, parts[11]); ResultFormatter.arrivalRunway(decodeResult, parts[12].split('/')[0]); // TODO: find out if anything comes after `/` sometimes - const altRwy = [parts[13].split('/')[0], parts[14].split('/')[0]].filter((r) => r != "").join(","); - if (altRwy != "") { - ResultFormatter.alternateRunway(decodeResult, altRwy); // TODO: find out if anything comes after `/` sometimes + const altRwy = [parts[13].split('/')[0], parts[14].split('/')[0]] + .filter((r) => r != '') + .join(','); + if (altRwy != '') { + ResultFormatter.alternateRunway(decodeResult, altRwy); // TODO: find out if anything comes after `/` sometimes } - ResultFormatter.unknownArr(decodeResult, [...parts.slice(0,5), ...parts.slice(15)]); + ResultFormatter.unknownArr(decodeResult, [ + ...parts.slice(0, 5), + ...parts.slice(15), + ]); decodeResult.decoded = true; decodeResult.decoder.decodeLevel = 'partial'; diff --git a/lib/plugins/Label_10_POS.test.ts b/lib/plugins/Label_10_POS.test.ts index 3e7c185..a3f6f7d 100644 --- a/lib/plugins/Label_10_POS.test.ts +++ b/lib/plugins/Label_10_POS.test.ts @@ -2,47 +2,49 @@ import { MessageDecoder } from '../MessageDecoder'; import { Label_10_POS } from './Label_10_POS'; describe('Label_10_POS', () => { - let plugin: Label_10_POS; - const message = {label: '10', text: ''}; + let plugin: Label_10_POS; + const message = { label: '10', text: '' }; - beforeEach(() => { - const decoder = new MessageDecoder(); - plugin = new Label_10_POS(decoder); + beforeEach(() => { + const decoder = new MessageDecoder(); + plugin = new Label_10_POS(decoder); + }); + + test('matches Label 10 Preamble pos qualifiers', () => { + expect(plugin.decode).toBeDefined(); + expect(plugin.name).toBe('label-10-pos'); + expect(plugin.qualifiers).toBeDefined(); + expect(plugin.qualifiers()).toEqual({ + labels: ['10'], + preambles: ['POS'], }); + }); - test('matches Label 10 Preamble pos qualifiers', () => { - expect(plugin.decode).toBeDefined(); - expect(plugin.name).toBe('label-10-pos'); - expect(plugin.qualifiers).toBeDefined(); - expect(plugin.qualifiers()).toEqual({ - labels: ['10'], - preambles: ['POS'], - }); - }); + test('decodes Label 10 Preamble POS variant 1', () => { + message.text = + 'POS082150, N 3885,W 7841,---,308,26922, 51,22290, 529, 19,-225,6'; + const decodeResult = plugin.decode(message); - test('decodes Label 10 Preamble POS variant 1', () => { - message.text = 'POS082150, N 3885,W 7841,---,308,26922, 51,22290, 529, 19,-225,6' - const decodeResult = plugin.decode(message); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.formatted.items.length).toBe(2); + expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); + expect(decodeResult.formatted.items[0].value).toBe('38.850 N, 78.410 W'); + expect(decodeResult.formatted.items[1].label).toBe('Altitude'); + expect(decodeResult.formatted.items[1].value).toBe('22290 feet'); + expect(decodeResult.remaining.text).toBe( + 'POS082150,---,308,26922, 51, 529, 19,-225,6', + ); + }); - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.formatted.items.length).toBe(2); - expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); - expect(decodeResult.formatted.items[0].value).toBe('38.850 N, 78.410 W'); - expect(decodeResult.formatted.items[1].label).toBe('Altitude'); - expect(decodeResult.formatted.items[1].value).toBe('22290 feet'); - expect(decodeResult.remaining.text).toBe('POS082150,---,308,26922, 51, 529, 19,-225,6'); - }); + test('decodes Label 10 Preamble POS ', () => { + message.text = 'POS Bogus Message'; + const decodeResult = plugin.decode(message); - test('decodes Label 10 Preamble POS ', () => { - - message.text = 'POS Bogus Message'; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(false); - expect(decodeResult.decoder.decodeLevel).toBe('none'); - expect(decodeResult.decoder.name).toBe('label-10-pos'); - expect(decodeResult.formatted.description).toBe('Position Report'); - expect(decodeResult.message).toBe(message); - }); -}); \ No newline at end of file + expect(decodeResult.decoded).toBe(false); + expect(decodeResult.decoder.decodeLevel).toBe('none'); + expect(decodeResult.decoder.name).toBe('label-10-pos'); + expect(decodeResult.formatted.description).toBe('Position Report'); + expect(decodeResult.message).toBe(message); + }); +}); diff --git a/lib/plugins/Label_10_POS.ts b/lib/plugins/Label_10_POS.ts index 9a1a1fb..93b9cd0 100644 --- a/lib/plugins/Label_10_POS.ts +++ b/lib/plugins/Label_10_POS.ts @@ -2,10 +2,10 @@ import { DecoderPlugin } from '../DecoderPlugin'; import { DecodeResult, Message, Options } from '../DecoderPluginInterface'; import { ResultFormatter } from '../utils/result_formatter'; -export class Label_10_POS extends DecoderPlugin { // eslint-disable-line camelcase +export class Label_10_POS extends DecoderPlugin { name = 'label-10-pos'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['10'], preambles: ['POS'], @@ -33,12 +33,18 @@ export class Label_10_POS extends DecoderPlugin { // eslint-disable-line camelca const lat = parts[1].trim(); const lon = parts[2].trim(); const position = { - latitude: (lat[0] === 'N' ? 1 : -1) * Number(lat.substring(1).trim())/100, - longitude: (lon[0] === 'E' ? 1 : -1) * Number(lon.substring(1).trim())/100, - } + latitude: + ((lat[0] === 'N' ? 1 : -1) * Number(lat.substring(1).trim())) / 100, + longitude: + ((lon[0] === 'E' ? 1 : -1) * Number(lon.substring(1).trim())) / 100, + }; ResultFormatter.position(decodeResult, position); ResultFormatter.altitude(decodeResult, Number(parts[7])); - ResultFormatter.unknownArr(decodeResult, [parts[0], ...parts.slice(3,7), ...parts.slice(8)]); + ResultFormatter.unknownArr(decodeResult, [ + parts[0], + ...parts.slice(3, 7), + ...parts.slice(8), + ]); decodeResult.decoded = true; decodeResult.decoder.decodeLevel = 'partial'; diff --git a/lib/plugins/Label_10_Slash.test.ts b/lib/plugins/Label_10_Slash.test.ts index 13cb55b..37d4278 100644 --- a/lib/plugins/Label_10_Slash.test.ts +++ b/lib/plugins/Label_10_Slash.test.ts @@ -2,80 +2,88 @@ import { MessageDecoder } from '../MessageDecoder'; import { Label_10_Slash } from './Label_10_Slash'; describe('Label_10_Slash', () => { - let plugin: Label_10_Slash; - const message = {label: '10', text: ''}; + let plugin: Label_10_Slash; + const message = { label: '10', text: '' }; - beforeEach(() => { - const decoder = new MessageDecoder(); - plugin = new Label_10_Slash(decoder); - }); + beforeEach(() => { + const decoder = new MessageDecoder(); + plugin = new Label_10_Slash(decoder); + }); - test('matches Label 10 Preamble slash qualifiers', () => { - expect(plugin.decode).toBeDefined(); - expect(plugin.name).toBe('label-10-slash'); - expect(plugin.qualifiers).toBeDefined(); - expect(plugin.qualifiers()).toEqual({ - labels: ['10'], - preambles: ['/'], - }); - }); + test('matches Label 10 Preamble slash qualifiers', () => { + expect(plugin.decode).toBeDefined(); + expect(plugin.name).toBe('label-10-slash'); + expect(plugin.qualifiers).toBeDefined(); + expect(plugin.qualifiers()).toEqual({ + labels: ['10'], + preambles: ['/'], + }); + }); - test('decodes Label 10 Preamble / variant 1', () => { - message.text = '/N39.182/W077.217/10/0.42/180/055/KIAD/0004/0028/00015/MOWAT/HUSEL/2349/YACKK/2352/' - const decodeResult = plugin.decode(message); + test('decodes Label 10 Preamble / variant 1', () => { + message.text = + '/N39.182/W077.217/10/0.42/180/055/KIAD/0004/0028/00015/MOWAT/HUSEL/2349/YACKK/2352/'; + const decodeResult = plugin.decode(message); - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.formatted.items.length).toBe(6); - expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); - expect(decodeResult.formatted.items[0].value).toBe('39.182 N, 77.217 W'); - expect(decodeResult.formatted.items[1].label).toBe('Heading'); - expect(decodeResult.formatted.items[1].value).toBe('180'); - expect(decodeResult.formatted.items[2].label).toBe('Altitude'); - expect(decodeResult.formatted.items[2].value).toBe('5500 feet'); - expect(decodeResult.formatted.items[3].label).toBe('Destination'); - expect(decodeResult.formatted.items[3].value).toBe('KIAD'); - expect(decodeResult.formatted.items[4].label).toBe('Estimated Time of Arrival'); - expect(decodeResult.formatted.items[4].value).toBe('00:04:00'); - expect(decodeResult.formatted.items[5].label).toBe('Aircraft Route'); - expect(decodeResult.formatted.items[5].value).toBe('MOWAT > HUSEL@23:49:00 > YACKK@23:52:00'); - expect(decodeResult.remaining.text).toBe('10/0.42/0028/00015'); - }); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.formatted.items.length).toBe(6); + expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); + expect(decodeResult.formatted.items[0].value).toBe('39.182 N, 77.217 W'); + expect(decodeResult.formatted.items[1].label).toBe('Heading'); + expect(decodeResult.formatted.items[1].value).toBe('180'); + expect(decodeResult.formatted.items[2].label).toBe('Altitude'); + expect(decodeResult.formatted.items[2].value).toBe('5500 feet'); + expect(decodeResult.formatted.items[3].label).toBe('Destination'); + expect(decodeResult.formatted.items[3].value).toBe('KIAD'); + expect(decodeResult.formatted.items[4].label).toBe( + 'Estimated Time of Arrival', + ); + expect(decodeResult.formatted.items[4].value).toBe('00:04:00'); + expect(decodeResult.formatted.items[5].label).toBe('Aircraft Route'); + expect(decodeResult.formatted.items[5].value).toBe( + 'MOWAT > HUSEL@23:49:00 > YACKK@23:52:00', + ); + expect(decodeResult.remaining.text).toBe('10/0.42/0028/00015'); + }); - test('decodes Label 10 Preamble / variant 2', () => { - message.text = '/N39.019/W078.468/10/0.83/246/400/KCVG/0155/0073/00018/COLNS/STEVY/0120/FAIIR/0126/KTEB/' - const decodeResult = plugin.decode(message); + test('decodes Label 10 Preamble / variant 2', () => { + message.text = + '/N39.019/W078.468/10/0.83/246/400/KCVG/0155/0073/00018/COLNS/STEVY/0120/FAIIR/0126/KTEB/'; + const decodeResult = plugin.decode(message); - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.formatted.items.length).toBe(7); - expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); - expect(decodeResult.formatted.items[0].value).toBe('39.019 N, 78.468 W'); - expect(decodeResult.formatted.items[1].label).toBe('Heading'); - expect(decodeResult.formatted.items[1].value).toBe('246'); - expect(decodeResult.formatted.items[2].label).toBe('Altitude'); - expect(decodeResult.formatted.items[2].value).toBe('40000 feet'); - expect(decodeResult.formatted.items[3].label).toBe('Destination'); - expect(decodeResult.formatted.items[3].value).toBe('KCVG'); - expect(decodeResult.formatted.items[4].label).toBe('Estimated Time of Arrival'); - expect(decodeResult.formatted.items[4].value).toBe('01:55:00'); - expect(decodeResult.formatted.items[5].label).toBe('Aircraft Route'); - expect(decodeResult.formatted.items[5].value).toBe('COLNS > STEVY@01:20:00 > FAIIR@01:26:00'); - expect(decodeResult.formatted.items[6].label).toBe('Origin'); - expect(decodeResult.formatted.items[6].value).toBe('KTEB'); - expect(decodeResult.remaining.text).toBe('10/0.83/0073/00018/'); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.formatted.items.length).toBe(7); + expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); + expect(decodeResult.formatted.items[0].value).toBe('39.019 N, 78.468 W'); + expect(decodeResult.formatted.items[1].label).toBe('Heading'); + expect(decodeResult.formatted.items[1].value).toBe('246'); + expect(decodeResult.formatted.items[2].label).toBe('Altitude'); + expect(decodeResult.formatted.items[2].value).toBe('40000 feet'); + expect(decodeResult.formatted.items[3].label).toBe('Destination'); + expect(decodeResult.formatted.items[3].value).toBe('KCVG'); + expect(decodeResult.formatted.items[4].label).toBe( + 'Estimated Time of Arrival', + ); + expect(decodeResult.formatted.items[4].value).toBe('01:55:00'); + expect(decodeResult.formatted.items[5].label).toBe('Aircraft Route'); + expect(decodeResult.formatted.items[5].value).toBe( + 'COLNS > STEVY@01:20:00 > FAIIR@01:26:00', + ); + expect(decodeResult.formatted.items[6].label).toBe('Origin'); + expect(decodeResult.formatted.items[6].value).toBe('KTEB'); + expect(decodeResult.remaining.text).toBe('10/0.83/0073/00018/'); }); + test('decodes Label 10 Preamble / ', () => { + message.text = '/Bogus Message/'; + const decodeResult = plugin.decode(message); - test('decodes Label 10 Preamble / ', () => { - - message.text = '/Bogus Message/'; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(false); - expect(decodeResult.decoder.decodeLevel).toBe('none'); - expect(decodeResult.decoder.name).toBe('label-10-slash'); - expect(decodeResult.formatted.description).toBe('Position Report'); - expect(decodeResult.message).toBe(message); - }); -}); \ No newline at end of file + expect(decodeResult.decoded).toBe(false); + expect(decodeResult.decoder.decodeLevel).toBe('none'); + expect(decodeResult.decoder.name).toBe('label-10-slash'); + expect(decodeResult.formatted.description).toBe('Position Report'); + expect(decodeResult.message).toBe(message); + }); +}); diff --git a/lib/plugins/Label_10_Slash.ts b/lib/plugins/Label_10_Slash.ts index c512515..9dc2256 100644 --- a/lib/plugins/Label_10_Slash.ts +++ b/lib/plugins/Label_10_Slash.ts @@ -3,12 +3,11 @@ import { DecoderPlugin } from '../DecoderPlugin'; import { DecodeResult, Message, Options } from '../DecoderPluginInterface'; import { Waypoint } from '../types/waypoint'; import { ResultFormatter } from '../utils/result_formatter'; -import { RouteUtils } from '../utils/route_utils'; -export class Label_10_Slash extends DecoderPlugin { // eslint-disable-line camelcase +export class Label_10_Slash extends DecoderPlugin { name = 'label-10-slash'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['10'], preambles: ['/'], @@ -37,30 +36,41 @@ export class Label_10_Slash extends DecoderPlugin { // eslint-disable-line camel const position = { latitude: (lat[0] === 'N' ? 1 : -1) * Number(lat.substring(1)), longitude: (lon[0] === 'E' ? 1 : -1) * Number(lon.substring(1)), - } + }; ResultFormatter.position(decodeResult, position); ResultFormatter.heading(decodeResult, Number(parts[5])); - ResultFormatter.altitude(decodeResult, 100*Number(parts[6])); + ResultFormatter.altitude(decodeResult, 100 * Number(parts[6])); ResultFormatter.arrivalAirport(decodeResult, parts[7]); - ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(parts[8])); - const waypoints: Waypoint[] = [{ - name: parts[11], - },{ - name: parts[12], - time: DateTimeUtils.convertHHMMSSToTod(parts[13]), - timeFormat: 'tod', - },{ - name: parts[14], - time: DateTimeUtils.convertHHMMSSToTod(parts[15]), - timeFormat: 'tod', - },]; + ResultFormatter.eta( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(parts[8]), + ); + const waypoints: Waypoint[] = [ + { + name: parts[11], + }, + { + name: parts[12], + time: DateTimeUtils.convertHHMMSSToTod(parts[13]), + timeFormat: 'tod', + }, + { + name: parts[14], + time: DateTimeUtils.convertHHMMSSToTod(parts[15]), + timeFormat: 'tod', + }, + ]; ResultFormatter.route(decodeResult, { waypoints: waypoints }); - if(parts[16]) { + if (parts[16]) { ResultFormatter.departureAirport(decodeResult, parts[16]); } - ResultFormatter.unknownArr(decodeResult, [parts[3], parts[4], ...parts.slice(9,11), ...parts.slice(17)], '/'); + ResultFormatter.unknownArr( + decodeResult, + [parts[3], parts[4], ...parts.slice(9, 11), ...parts.slice(17)], + '/', + ); decodeResult.decoded = true; decodeResult.decoder.decodeLevel = 'partial'; diff --git a/lib/plugins/Label_12_N_Space.test.ts b/lib/plugins/Label_12_N_Space.test.ts index 50f6c43..e3e9397 100644 --- a/lib/plugins/Label_12_N_Space.test.ts +++ b/lib/plugins/Label_12_N_Space.test.ts @@ -3,7 +3,7 @@ import { Label_12_N_Space } from './Label_12_N_Space'; describe('Label_12_N_Space', () => { let plugin: Label_12_N_Space; - const message = {label: '12', text: ''}; + const message = { label: '12', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); @@ -29,7 +29,7 @@ describe('Label_12_N_Space', () => { expect(decodeResult.decoder.name).toBe('label-12-n-space'); expect(decodeResult.formatted.description).toBe('Position Report'); expect(decodeResult.message).toBe(message); - expect(decodeResult.raw.position.latitude).toBe(42.150); + expect(decodeResult.raw.position.latitude).toBe(42.15); expect(decodeResult.raw.position.longitude).toBe(-121.187); expect(decodeResult.raw.altitude).toBe(39000); expect(decodeResult.formatted.items.length).toBe(2); @@ -76,4 +76,4 @@ describe('Label_12_N_Space', () => { expect(decodeResult.formatted.description).toBe('Position Report'); expect(decodeResult.message).toBe(message); }); -}); \ No newline at end of file +}); diff --git a/lib/plugins/Label_12_N_Space.ts b/lib/plugins/Label_12_N_Space.ts index a73e61a..3e397bd 100644 --- a/lib/plugins/Label_12_N_Space.ts +++ b/lib/plugins/Label_12_N_Space.ts @@ -1,14 +1,13 @@ import { DecoderPlugin } from '../DecoderPlugin'; import { DecodeResult, Message, Options } from '../DecoderPluginInterface'; -import { CoordinateUtils } from '../utils/coordinate_utils'; import { ResultFormatter } from '../utils/result_formatter'; export class Label_12_N_Space extends DecoderPlugin { name = 'label-12-n-space'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { - labels: ["12"], + labels: ['12'], preambles: ['N ', 'S '], }; } @@ -19,24 +18,36 @@ export class Label_12_N_Space extends DecoderPlugin { decodeResult.formatted.description = 'Position Report'; decodeResult.message = message; - const variant1Regex = /^(?[NS])\s(?.*),(?[EW])\s*(?.*),(?.*),(?.*),\s*(?.*),.(?.*),(?.*)$/; + const variant1Regex = + /^(?[NS])\s(?.*),(?[EW])\s*(?.*),(?.*),(?.*),\s*(?.*),.(?.*),(?.*)$/; - let results = message.text.match(variant1Regex) + let results = message.text.match(variant1Regex); if (results?.groups) { if (options.debug) { - console.log(`Label 12 N : results`); + console.log('Label 12 N : results'); console.log(results); } ResultFormatter.position(decodeResult, { - latitude: Number(results.groups.lat_coord) * (results.groups.lat == 'N' ? 1 : -1), - longitude: Number(results.groups.long_coord) * (results.groups.long == 'E' ? 1 : -1) + latitude: + Number(results.groups.lat_coord) * + (results.groups.lat == 'N' ? 1 : -1), + longitude: + Number(results.groups.long_coord) * + (results.groups.long == 'E' ? 1 : -1), }); - const altitude = results.groups.alt == 'GRD' || results.groups.alt == '***' ? 0 : Number(results.groups.alt); + const altitude = + results.groups.alt == 'GRD' || results.groups.alt == '***' + ? 0 + : Number(results.groups.alt); ResultFormatter.altitude(decodeResult, altitude); - ResultFormatter.unknownArr(decodeResult, [results.groups.unkwn1, results.groups.unkwn2, results.groups.unkwn3]); + ResultFormatter.unknownArr(decodeResult, [ + results.groups.unkwn1, + results.groups.unkwn2, + results.groups.unkwn3, + ]); decodeResult.decoded = true; decodeResult.decoder.decodeLevel = 'partial'; return decodeResult; diff --git a/lib/plugins/Label_12_POS.test.ts b/lib/plugins/Label_12_POS.test.ts index 5d75a87..2c9540b 100644 --- a/lib/plugins/Label_12_POS.test.ts +++ b/lib/plugins/Label_12_POS.test.ts @@ -2,16 +2,14 @@ import { MessageDecoder } from '../MessageDecoder'; import { Label_12_POS } from './Label_12_POS'; describe('Label 12 POS', () => { - let plugin: Label_12_POS; - const message = {label: '12', text: ''}; + const message = { label: '12', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); plugin = new Label_12_POS(decoder); }); - test('matches qualifiers', () => { expect(plugin.decode).toBeDefined(); expect(plugin.name).toBe('label-12-pos'); @@ -22,9 +20,9 @@ describe('Label 12 POS', () => { }); }); - test('decodes msg 1', () => { - message.text = 'POSN 390104W 754601,-------,1244,1446,,- 4,23249 12,FOB 73,ETA 1303,KATL,KPHL,'; + message.text = + 'POSN 390104W 754601,-------,1244,1446,,- 4,23249 12,FOB 73,ETA 1303,KATL,KPHL,'; const decodeResult = plugin.decode(message); @@ -40,7 +38,9 @@ describe('Label 12 POS', () => { expect(decodeResult.formatted.items[2].value).toBe('14460 feet'); expect(decodeResult.formatted.items[3].label).toBe('Fuel On Board'); expect(decodeResult.formatted.items[3].value).toBe('73'); - expect(decodeResult.formatted.items[4].label).toBe('Estimated Time of Arrival'); + expect(decodeResult.formatted.items[4].label).toBe( + 'Estimated Time of Arrival', + ); expect(decodeResult.formatted.items[4].value).toBe('13:03:00'); expect(decodeResult.formatted.items[5].label).toBe('Origin'); expect(decodeResult.formatted.items[5].value).toBe('KATL'); @@ -50,7 +50,6 @@ describe('Label 12 POS', () => { }); test('decodes ', () => { - message.text = 'POS Bogus message'; const decodeResult = plugin.decode(message); diff --git a/lib/plugins/Label_12_POS.ts b/lib/plugins/Label_12_POS.ts index c373949..9c9fa3b 100644 --- a/lib/plugins/Label_12_POS.ts +++ b/lib/plugins/Label_12_POS.ts @@ -8,7 +8,7 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_12_POS extends DecoderPlugin { name = 'label-12-pos'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['12'], preambles: ['POS'], @@ -35,15 +35,36 @@ export class Label_12_POS extends DecoderPlugin { const lat = data[0].substring(0, 8); const lon = data[0].substring(8); ResultFormatter.position(decodeResult, { - latitude: CoordinateUtils.getDirection(lat[0]) * CoordinateUtils.dmsToDecimalDegrees(Number(lat.substring(1, 4)), Number(lat.substring(4, 6)), Number(lat.substring(6, 8))), - longitude: CoordinateUtils.getDirection(lon[0]) * CoordinateUtils.dmsToDecimalDegrees(Number(lon.substring(1, 4)), Number(lon.substring(4, 6)), Number(lon.substring(6, 8))), + latitude: + CoordinateUtils.getDirection(lat[0]) * + CoordinateUtils.dmsToDecimalDegrees( + Number(lat.substring(1, 4)), + Number(lat.substring(4, 6)), + Number(lat.substring(6, 8)), + ), + longitude: + CoordinateUtils.getDirection(lon[0]) * + CoordinateUtils.dmsToDecimalDegrees( + Number(lon.substring(1, 4)), + Number(lon.substring(4, 6)), + Number(lon.substring(6, 8)), + ), }); ResultFormatter.unknown(decodeResult, data[1]); - ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(data[2])); + ResultFormatter.time_of_day( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(data[2]), + ); ResultFormatter.altitude(decodeResult, 10 * Number(data[3])); ResultFormatter.unknownArr(decodeResult, data.slice(4, 7)); - ResultFormatter.currentFuel(decodeResult, Number(data[7].substring(3).trim())); // strip FOB - ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(data[8].substring(3).trim())); // strip ETA + ResultFormatter.currentFuel( + decodeResult, + Number(data[7].substring(3).trim()), + ); // strip FOB + ResultFormatter.eta( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(data[8].substring(3).trim()), + ); // strip ETA ResultFormatter.departureAirport(decodeResult, data[9]); ResultFormatter.arrivalAirport(decodeResult, data[10]); ResultFormatter.unknown(decodeResult, data[11]); diff --git a/lib/plugins/Label_13Through18_Slash.test.ts b/lib/plugins/Label_13Through18_Slash.test.ts index 6660d57..6012f61 100644 --- a/lib/plugins/Label_13Through18_Slash.test.ts +++ b/lib/plugins/Label_13Through18_Slash.test.ts @@ -20,10 +20,10 @@ describe('Label_13Through18_Slash', () => { }); describe('Label 13', () => { - const message = {label: '13', text: ''}; + const message = { label: '13', text: '' }; test('decodes 1-line', () => { // https://app.airframes.io/messages/3423349335 - message.text = '/13 OUT EVENT / KSFO KEWR 12 231445/TIME 2314' + message.text = '/13 OUT EVENT / KSFO KEWR 12 231445/TIME 2314'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -41,8 +41,10 @@ describe('Label_13Through18_Slash', () => { }); test('decodes 2-line', () => { // https://app.airframes.io/messages/3424584522 - message.text = '/13 OUT EVENT / KSFO KMSP 12 024518/TIME 0245' + '\r\n' + - '/LOC N373703,W1222251' + message.text = + '/13 OUT EVENT / KSFO KMSP 12 024518/TIME 0245' + + '\r\n' + + '/LOC N373703,W1222251'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -62,9 +64,9 @@ describe('Label_13Through18_Slash', () => { }); describe('Label 14', () => { - const message = {label: '14', text: ''}; + const message = { label: '14', text: '' }; test('decodes 1-line', () => { - message.text = '/14 OFF EVENT / KIAD KDEN 08 124438/TIME 1244' + message.text = '/14 OFF EVENT / KIAD KDEN 08 124438/TIME 1244'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -81,8 +83,10 @@ describe('Label_13Through18_Slash', () => { }); test('decodes 2-line', () => { - message.text = '/14 OFF EVENT / KIAD EDDF 09 025055/TIME 0250' + '\r\n' + - '/LOC +38.9603,-077.4595' + message.text = + '/14 OFF EVENT / KIAD EDDF 09 025055/TIME 0250' + + '\r\n' + + '/LOC +38.9603,-077.4595'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -100,9 +104,12 @@ describe('Label_13Through18_Slash', () => { }); test('decodes 3-line', () => { - message.text = '/14 OFF EVENT / KIAD KATL 09 123630/TIME 1236' + '\r\n' + - '/AU 08752167/AON 08716165/AIN 08717165/AOT 08744166' + '\r\n' + - '/LOC N385618,W0772823' + message.text = + '/14 OFF EVENT / KIAD KATL 09 123630/TIME 1236' + + '\r\n' + + '/AU 08752167/AON 08716165/AIN 08717165/AOT 08744166' + + '\r\n' + + '/LOC N385618,W0772823'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -117,18 +124,23 @@ describe('Label_13Through18_Slash', () => { expect(decodeResult.formatted.items[2].value).toBe('12:36:30'); expect(decodeResult.formatted.items[3].label).toBe('Aircraft Position'); expect(decodeResult.formatted.items[3].value).toBe('38.938 N, 77.473 W'); - expect(decodeResult.remaining.text).toBe('/AU 08752167/AON 08716165/AIN 08717165/AOT 08744166'); + expect(decodeResult.remaining.text).toBe( + '/AU 08752167/AON 08716165/AIN 08717165/AOT 08744166', + ); }); // Ignoring this test because the position is definteley invalid // The position is in the middle of the pacific ocean for a domestic flight - // ADS-B confirms it took off from IAD at the correct time + // ADS-B confirms it took off from IAD at the correct time // Despite all this, it shows that positions may be in decimal minutes instead of DMS when using NSEW instead of +/- // so i'm putting it here for reference test.skip('decodes position invalid', () => { - message.text = '/14 OFF EVENT / KIAD KSAT 10 122555/TIME 1225' + '\r\n' + - '/AU 55808910/AON 55729908/AIN 55731908/AOT 55804909' + '\r\n' + - '/LOC N169380,E1334348' + message.text = + '/14 OFF EVENT / KIAD KSAT 10 122555/TIME 1225' + + '\r\n' + + '/AU 55808910/AON 55729908/AIN 55731908/AOT 55804909' + + '\r\n' + + '/LOC N169380,E1334348'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -143,15 +155,17 @@ describe('Label_13Through18_Slash', () => { expect(decodeResult.formatted.items[2].value).toBe('12:25:55'); expect(decodeResult.formatted.items[3].label).toBe('Aircraft Position'); expect(decodeResult.formatted.items[3].value).toBe('17.572 N, 133.730 W'); - expect(decodeResult.remaining.text).toBe('/AU 55808910/AON 55729908/AIN 55731908/AOT 55804909'); + expect(decodeResult.remaining.text).toBe( + '/AU 55808910/AON 55729908/AIN 55731908/AOT 55804909', + ); }); }); describe('Label 15', () => { - const message = {label: '15', text: ''}; + const message = { label: '15', text: '' }; test('decodes 1-line', () => { // https://app.airframes.io/messages/3424704396 - message.text = '/15 ON EVENT / PHOG KSFO 12 064852/TIME 0648' + message.text = '/15 ON EVENT / PHOG KSFO 12 064852/TIME 0648'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -169,8 +183,10 @@ describe('Label_13Through18_Slash', () => { }); test('decodes 2-line', () => { // https://app.airframes.io/messages/3425113893 - message.text = '/15 ON EVENT / KCMH KSFO 12 044401/TIME 0444' + '\r\n' + - '/LOC N373705,W1222208' + message.text = + '/15 ON EVENT / KCMH KSFO 12 044401/TIME 0444' + + '\r\n' + + '/LOC N373705,W1222208'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -189,14 +205,16 @@ describe('Label_13Through18_Slash', () => { }); }); - describe('Label 16', () => { - const message = {label: '16', text: ''}; + const message = { label: '16', text: '' }; test('decodes 3-line', () => { // https://app.airframes.io/messages/3424584522 - message.text = '/16 1ST PDOOR OPEN / KLAX KSEA 12 053340/TIME 0533' + '\r\n' + - '/BR SET 0529' + '\r\n' + - '/LOC S101043,W1530624' + message.text = + '/16 1ST PDOOR OPEN / KLAX KSEA 12 053340/TIME 0533' + + '\r\n' + + '/BR SET 0529' + + '\r\n' + + '/LOC S101043,W1530624'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -216,14 +234,16 @@ describe('Label_13Through18_Slash', () => { }); }); - describe('Label 17', () => { - const message = {label: '17', text: ''}; + const message = { label: '17', text: '' }; test('decodes 3-line', () => { // https://app.airframes.io/messages/3423349335 - message.text = '/17 POST RPT / KIAD KSFO 12 034537/TIME 0343' + '\r\n' + - '/FOB 0215/LP U246680/OP /CAT NR' + '\r\n' + - '/AL NN/SUC /SAV /BR ACT NO REPORT/RWY 28R /PDOOR 0343 -----/CDOOR 0343/BR SET 0341' + message.text = + '/17 POST RPT / KIAD KSFO 12 034537/TIME 0343' + + '\r\n' + + '/FOB 0215/LP U246680/OP /CAT NR' + + '\r\n' + + '/AL NN/SUC /SAV /BR ACT NO REPORT/RWY 28R /PDOOR 0343 -----/CDOOR 0343/BR SET 0341'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -236,15 +256,22 @@ describe('Label_13Through18_Slash', () => { expect(decodeResult.formatted.items[0].value).toBe('KIAD'); expect(decodeResult.formatted.items[1].label).toBe('Destination'); expect(decodeResult.formatted.items[1].value).toBe('KSFO'); - expect(decodeResult.remaining.text).toBe('/FOB 0215/LP U246680/OP /CAT NR\r\n/AL NN/SUC /SAV /BR ACT NO REPORT/RWY 28R /PDOOR 0343 -----/CDOOR 0343/BR SET 0341'); + expect(decodeResult.remaining.text).toBe( + '/FOB 0215/LP U246680/OP /CAT NR\r\n/AL NN/SUC /SAV /BR ACT NO REPORT/RWY 28R /PDOOR 0343 -----/CDOOR 0343/BR SET 0341', + ); }); test('decodes 5-line', () => { // https://app.airframes.io/messages/3424453568 - message.text = '/17 POST RPT / KLAX KSEA 12 053340/TIME 0530' + '\r\n' + - '/FOB 0074/LP U423108/MP U332185/CAT NR' + '\r\n' + - '/AL N-/SUC -/SAV -/BR ACT NO REPORT/RWY ---' + '\r\n' + - '/PDOOR 0533 MCD1 /CDOOR 0530/BR SET 0529' + '\r\n' + - '/LOC S101043,W1530624' + message.text = + '/17 POST RPT / KLAX KSEA 12 053340/TIME 0530' + + '\r\n' + + '/FOB 0074/LP U423108/MP U332185/CAT NR' + + '\r\n' + + '/AL N-/SUC -/SAV -/BR ACT NO REPORT/RWY ---' + + '\r\n' + + '/PDOOR 0533 MCD1 /CDOOR 0530/BR SET 0529' + + '\r\n' + + '/LOC S101043,W1530624'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -260,17 +287,20 @@ describe('Label_13Through18_Slash', () => { //expect(decodeResult.formatted.items[2].value).toBe('02:45:18'); expect(decodeResult.formatted.items[2].label).toBe('Aircraft Position'); expect(decodeResult.formatted.items[2].value).toBe('10.179 S, 153.107 W'); - expect(decodeResult.remaining.text).toBe('/FOB 0074/LP U423108/MP U332185/CAT NR\r\n/AL N-/SUC -/SAV -/BR ACT NO REPORT/RWY ---\r\n/PDOOR 0533 MCD1 /CDOOR 0530/BR SET 0529'); + expect(decodeResult.remaining.text).toBe( + '/FOB 0074/LP U423108/MP U332185/CAT NR\r\n/AL N-/SUC -/SAV -/BR ACT NO REPORT/RWY ---\r\n/PDOOR 0533 MCD1 /CDOOR 0530/BR SET 0529', + ); }); }); - describe('Label 18', () => { - const message = {label: '18', text: ''}; + const message = { label: '18', text: '' }; test('decodes location', () => { // https://app.airframes.io/messages/3424453669 - message.text = '/18 POST TIMES SECS/ KLAX KSEA 12 053340/IN TIME 0530/PDOOR 053340/CDOOR 053024/BR SET 052942' + '\r\n' + - '/LOC S101043,W1530624' + message.text = + '/18 POST TIMES SECS/ KLAX KSEA 12 053340/IN TIME 0530/PDOOR 053340/CDOOR 053024/BR SET 052942' + + '\r\n' + + '/LOC S101043,W1530624'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -284,12 +314,16 @@ describe('Label_13Through18_Slash', () => { expect(decodeResult.formatted.items[1].value).toBe('KSEA'); expect(decodeResult.formatted.items[2].label).toBe('Aircraft Position'); expect(decodeResult.formatted.items[2].value).toBe('10.179 S, 153.107 W'); - expect(decodeResult.remaining.text).toBe('PDOOR 053340/CDOOR 053024/BR SET 052942'); + expect(decodeResult.remaining.text).toBe( + 'PDOOR 053340/CDOOR 053024/BR SET 052942', + ); }); test('decodes no location', () => { // https://app.airframes.io/messages/3424453669 - message.text = '/18 POST TIMES SECS/ KSFO CYVR 12 040416/IN TIME0353/PDOOR 035414/CDOOR 035315/BR SET 035223' + '\r\n' + - '/LOC , ' + message.text = + '/18 POST TIMES SECS/ KSFO CYVR 12 040416/IN TIME0353/PDOOR 035414/CDOOR 035315/BR SET 035223' + + '\r\n' + + '/LOC , '; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -301,12 +335,14 @@ describe('Label_13Through18_Slash', () => { expect(decodeResult.formatted.items[0].value).toBe('KSFO'); expect(decodeResult.formatted.items[1].label).toBe('Destination'); expect(decodeResult.formatted.items[1].value).toBe('CYVR'); - expect(decodeResult.remaining.text).toBe('PDOOR 035414/CDOOR 035315/BR SET 035223'); + expect(decodeResult.remaining.text).toBe( + 'PDOOR 035414/CDOOR 035315/BR SET 035223', + ); }); }); test('does not decode ', () => { - const message = {label: '14', text: '/14 Bogus Message'}; + const message = { label: '14', text: '/14 Bogus Message' }; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(false); diff --git a/lib/plugins/Label_13Through18_Slash.ts b/lib/plugins/Label_13Through18_Slash.ts index 0cb5829..06873a8 100644 --- a/lib/plugins/Label_13Through18_Slash.ts +++ b/lib/plugins/Label_13Through18_Slash.ts @@ -1,15 +1,13 @@ import { DateTimeUtils } from '../DateTimeUtils'; import { DecoderPlugin } from '../DecoderPlugin'; import { DecodeResult, Message, Options } from '../DecoderPluginInterface'; -import { Waypoint } from '../types/waypoint'; import { CoordinateUtils } from '../utils/coordinate_utils'; import { ResultFormatter } from '../utils/result_formatter'; -import { RouteUtils } from '../utils/route_utils'; -export class Label_13Through18_Slash extends DecoderPlugin { // eslint-disable-line camelcase +export class Label_13Through18_Slash extends DecoderPlugin { name = 'label-13-18-slash'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['13', '14', '15', '16', '17', '18'], preambles: ['/'], @@ -27,7 +25,10 @@ export class Label_13Through18_Slash extends DecoderPlugin { // eslint-disable-l decodeResult.formatted.description = getMsgType(labelNumber); // labels 13-17 have 4 parts, label 18 has 7 parts - if ((labelNumber !== 18 && parts.length !== 4) || (labelNumber === 18 && parts.length !== 7)) { + if ( + (labelNumber !== 18 && parts.length !== 4) || + (labelNumber === 18 && parts.length !== 7) + ) { if (options?.debug) { console.log(`Decoder: Unknown OOOI message: ${message.text}`); } @@ -42,7 +43,7 @@ export class Label_13Through18_Slash extends DecoderPlugin { // eslint-disable-l ResultFormatter.departureAirport(decodeResult, data[1]); ResultFormatter.arrivalAirport(decodeResult, data[2]); decodeResult.raw.day = Number(data[3]); - const time = DateTimeUtils.convertHHMMSSToTod(data[4]) + const time = DateTimeUtils.convertHHMMSSToTod(data[4]); if (labelNumber === 13) { ResultFormatter.out(decodeResult, time); } else if (labelNumber === 14) { @@ -62,43 +63,60 @@ export class Label_13Through18_Slash extends DecoderPlugin { // eslint-disable-l const location = lines[i].substring(5).split(','); // strip off '/LOC' let position; if (location[0].startsWith('+') || location[0].startsWith('-')) { - position = { latitude: Number(location[0]), longitude: Number(location[1]) }; + position = { + latitude: Number(location[0]), + longitude: Number(location[1]), + }; } else { position = { - latitude: CoordinateUtils.getDirection(location[0][0]) * CoordinateUtils.dmsToDecimalDegrees(Number(location[0].substring(1, 3)), Number(location[0].substring(3, 5)), Number(location[0].substring(5, 7))), - longitude: CoordinateUtils.getDirection(location[1][0]) * CoordinateUtils.dmsToDecimalDegrees(Number(location[1].substring(1, 4)), Number(location[1].substring(4, 6)), Number(location[1].substring(6, 8))), - } + latitude: + CoordinateUtils.getDirection(location[0][0]) * + CoordinateUtils.dmsToDecimalDegrees( + Number(location[0].substring(1, 3)), + Number(location[0].substring(3, 5)), + Number(location[0].substring(5, 7)), + ), + longitude: + CoordinateUtils.getDirection(location[1][0]) * + CoordinateUtils.dmsToDecimalDegrees( + Number(location[1].substring(1, 4)), + Number(location[1].substring(4, 6)), + Number(location[1].substring(6, 8)), + ), + }; } ResultFormatter.position(decodeResult, position); } else { - ResultFormatter.unknown(decodeResult, lines[i], "\r\n"); + ResultFormatter.unknown(decodeResult, lines[i], '\r\n'); } } decodeResult.decoded = true; - decodeResult.decoder.decodeLevel = decodeResult.remaining.text ? 'partial' : 'full'; + decodeResult.decoder.decodeLevel = decodeResult.remaining.text + ? 'partial' + : 'full'; return decodeResult; } } function getMsgType(labelNumber: number): string { if (labelNumber === 13) { - return 'Out of Gate Report' + return 'Out of Gate Report'; } if (labelNumber === 14) { - return 'Takeoff Report' + return 'Takeoff Report'; } if (labelNumber === 15) { - return 'On Ground Report' + return 'On Ground Report'; } if (labelNumber === 16) { - return 'In Gate Report' + return 'In Gate Report'; } if (labelNumber === 17) { - return 'Post Report' + return 'Post Report'; } if (labelNumber === 18) { - return 'Post Times Report' + return 'Post Times Report'; } return 'Unknown'; diff --git a/lib/plugins/Label_15.test.ts b/lib/plugins/Label_15.test.ts index b7abfce..b7c8bcf 100644 --- a/lib/plugins/Label_15.test.ts +++ b/lib/plugins/Label_15.test.ts @@ -2,123 +2,128 @@ import { MessageDecoder } from '../MessageDecoder'; import { Label_15 } from './Label_15'; describe('Label_15', () => { - let plugin: Label_15; - const message = {label: '15', text: ''}; + let plugin: Label_15; + const message = { label: '15', text: '' }; - beforeEach(() => { - const decoder = new MessageDecoder(); - plugin = new Label_15(decoder); - }); + beforeEach(() => { + const decoder = new MessageDecoder(); + plugin = new Label_15(decoder); + }); - test('matches Label 15s qualifiers', () => { - expect(plugin.decode).toBeDefined(); - expect(plugin.name).toBe('label-15'); - expect(plugin.qualifiers).toBeDefined(); - expect(plugin.qualifiers()).toEqual({ - labels: ['15'], - preambles: ['(2'], - }); + test('matches Label 15s qualifiers', () => { + expect(plugin.decode).toBeDefined(); + expect(plugin.name).toBe('label-15'); + expect(plugin.qualifiers).toBeDefined(); + expect(plugin.qualifiers()).toEqual({ + labels: ['15'], + preambles: ['(2'], }); + }); - test('decodes short variant missing unkown', () => { - message.text = '(2N38448W 77216--- 28 20 7(Z' - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.formatted.items.length).toBe(3); - expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); - expect(decodeResult.formatted.items[0].value).toBe('38.747 N, 77.360 W'); - expect(decodeResult.formatted.items[1].label).toBe('Altitude'); - expect(decodeResult.formatted.items[1].value).toBe('2000 feet'); - expect(decodeResult.formatted.items[2].label).toBe('Outside Air Temperature (C)'); - expect(decodeResult.formatted.items[2].value).toBe('7 degrees'); - expect(decodeResult.remaining.text).toBe('--- 28'); - }); + test('decodes short variant missing unkown', () => { + message.text = '(2N38448W 77216--- 28 20 7(Z'; + const decodeResult = plugin.decode(message); - test('decodes short variant all fields', () => { - message.text = '(2N40492W 77179248 99380-53(Z' - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.formatted.items.length).toBe(3); - expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); - expect(decodeResult.formatted.items[0].value).toBe('40.820 N, 77.298 W'); - expect(decodeResult.formatted.items[1].label).toBe('Altitude'); - expect(decodeResult.formatted.items[1].value).toBe('38000 feet'); - expect(decodeResult.formatted.items[2].label).toBe('Outside Air Temperature (C)'); - expect(decodeResult.formatted.items[2].value).toBe('-53 degrees'); - expect(decodeResult.remaining.text).toBe('248 99'); - }); - - test('decodes short variant missing alt', () => { - message.text = '(2N39269W 77374--- 42---- 5(Z' - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.formatted.items.length).toBe(2); - expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); - expect(decodeResult.formatted.items[0].value).toBe('39.448 N, 77.623 W'); - expect(decodeResult.formatted.items[1].label).toBe('Outside Air Temperature (C)'); - expect(decodeResult.formatted.items[1].value).toBe('-5 degrees'); - expect(decodeResult.remaining.text).toBe('--- 42'); - }); - - test('decodes off variant no unkown', () => { - message.text = '(2N39018W 77284OFF11112418101313--------(Z' - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.formatted.items.length).toBe(2); - expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); - expect(decodeResult.formatted.items[0].value).toBe('39.030 N, 77.473 W'); - expect(decodeResult.formatted.items[1].label).toBe('Takeoff Time'); - expect(decodeResult.formatted.items[1].value).toBe('2024-11-11T18:10:00Z'); - expect(decodeResult.remaining.text).toBe('1313--------'); - }); - - test('decodes off variant no date and unknown', () => { - // https://app.airframes.io/messages/3593342701 - message.text = '(2N42589W 83520OFF------13280606--------(Z' - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.formatted.items.length).toBe(2); - expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); - expect(decodeResult.formatted.items[0].value).toBe('42.982 N, 83.867 W'); - expect(decodeResult.formatted.items[1].label).toBe('Takeoff Time'); - expect(decodeResult.formatted.items[1].value).toBe('13:28:00'); - expect(decodeResult.remaining.text).toBe('0606--------'); - }); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.formatted.items.length).toBe(3); + expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); + expect(decodeResult.formatted.items[0].value).toBe('38.747 N, 77.360 W'); + expect(decodeResult.formatted.items[1].label).toBe('Altitude'); + expect(decodeResult.formatted.items[1].value).toBe('2000 feet'); + expect(decodeResult.formatted.items[2].label).toBe( + 'Outside Air Temperature (C)', + ); + expect(decodeResult.formatted.items[2].value).toBe('7 degrees'); + expect(decodeResult.remaining.text).toBe('--- 28'); + }); - test('decodes off variant all fields', () => { - // https://app.airframes.io/messages/3603048708 - message.text = '(2N39042W 77308OFF1311240327B1818 015(Z' - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.formatted.items.length).toBe(2); - expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); - expect(decodeResult.formatted.items[0].value).toBe('39.070 N, 77.513 W'); - expect(decodeResult.formatted.items[1].label).toBe('Takeoff Time'); - expect(decodeResult.formatted.items[1].value).toBe('2024-11-13T03:27:00Z'); - expect(decodeResult.remaining.text).toBe('B1818 015'); - }); + test('decodes short variant all fields', () => { + message.text = '(2N40492W 77179248 99380-53(Z'; + const decodeResult = plugin.decode(message); - test('does not decode Label 15 ', () => { + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.formatted.items.length).toBe(3); + expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); + expect(decodeResult.formatted.items[0].value).toBe('40.820 N, 77.298 W'); + expect(decodeResult.formatted.items[1].label).toBe('Altitude'); + expect(decodeResult.formatted.items[1].value).toBe('38000 feet'); + expect(decodeResult.formatted.items[2].label).toBe( + 'Outside Air Temperature (C)', + ); + expect(decodeResult.formatted.items[2].value).toBe('-53 degrees'); + expect(decodeResult.remaining.text).toBe('248 99'); + }); - message.text = '(2 Bogus message'; - const decodeResult = plugin.decode(message); + test('decodes short variant missing alt', () => { + message.text = '(2N39269W 77374--- 42---- 5(Z'; + const decodeResult = plugin.decode(message); - expect(decodeResult.decoded).toBe(false); - expect(decodeResult.decoder.decodeLevel).toBe('none'); - expect(decodeResult.decoder.name).toBe('label-15'); - expect(decodeResult.formatted.description).toBe('Position Report'); - expect(decodeResult.message).toBe(message); - }); -}); \ No newline at end of file + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.formatted.items.length).toBe(2); + expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); + expect(decodeResult.formatted.items[0].value).toBe('39.448 N, 77.623 W'); + expect(decodeResult.formatted.items[1].label).toBe( + 'Outside Air Temperature (C)', + ); + expect(decodeResult.formatted.items[1].value).toBe('-5 degrees'); + expect(decodeResult.remaining.text).toBe('--- 42'); + }); + + test('decodes off variant no unkown', () => { + message.text = '(2N39018W 77284OFF11112418101313--------(Z'; + const decodeResult = plugin.decode(message); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.formatted.items.length).toBe(2); + expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); + expect(decodeResult.formatted.items[0].value).toBe('39.030 N, 77.473 W'); + expect(decodeResult.formatted.items[1].label).toBe('Takeoff Time'); + expect(decodeResult.formatted.items[1].value).toBe('2024-11-11T18:10:00Z'); + expect(decodeResult.remaining.text).toBe('1313--------'); + }); + + test('decodes off variant no date and unknown', () => { + // https://app.airframes.io/messages/3593342701 + message.text = '(2N42589W 83520OFF------13280606--------(Z'; + const decodeResult = plugin.decode(message); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.formatted.items.length).toBe(2); + expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); + expect(decodeResult.formatted.items[0].value).toBe('42.982 N, 83.867 W'); + expect(decodeResult.formatted.items[1].label).toBe('Takeoff Time'); + expect(decodeResult.formatted.items[1].value).toBe('13:28:00'); + expect(decodeResult.remaining.text).toBe('0606--------'); + }); + + test('decodes off variant all fields', () => { + // https://app.airframes.io/messages/3603048708 + message.text = '(2N39042W 77308OFF1311240327B1818 015(Z'; + const decodeResult = plugin.decode(message); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.formatted.items.length).toBe(2); + expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); + expect(decodeResult.formatted.items[0].value).toBe('39.070 N, 77.513 W'); + expect(decodeResult.formatted.items[1].label).toBe('Takeoff Time'); + expect(decodeResult.formatted.items[1].value).toBe('2024-11-13T03:27:00Z'); + expect(decodeResult.remaining.text).toBe('B1818 015'); + }); + + test('does not decode Label 15 ', () => { + message.text = '(2 Bogus message'; + const decodeResult = plugin.decode(message); + + expect(decodeResult.decoded).toBe(false); + expect(decodeResult.decoder.decodeLevel).toBe('none'); + expect(decodeResult.decoder.name).toBe('label-15'); + expect(decodeResult.formatted.description).toBe('Position Report'); + expect(decodeResult.message).toBe(message); + }); +}); diff --git a/lib/plugins/Label_15.ts b/lib/plugins/Label_15.ts index df9df4f..dc9bd59 100644 --- a/lib/plugins/Label_15.ts +++ b/lib/plugins/Label_15.ts @@ -8,7 +8,7 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_15 extends DecoderPlugin { name = 'label-15'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['15'], preambles: ['(2'], @@ -23,22 +23,43 @@ export class Label_15 extends DecoderPlugin { if (message.text.startsWith('(2') && message.text.endsWith('(Z')) { const between = message.text.substring(2, message.text.length - 2); - ResultFormatter.position(decodeResult, CoordinateUtils.decodeStringCoordinatesDecimalMinutes(between.substring(0, 13))); - if (between.length === 25) { // short variant + ResultFormatter.position( + decodeResult, + CoordinateUtils.decodeStringCoordinatesDecimalMinutes( + between.substring(0, 13), + ), + ); + if (between.length === 25) { + // short variant ResultFormatter.unknown(decodeResult, between.substring(13, 19)); const alt = between.substring(19, 22); if (alt != '---') { ResultFormatter.altitude(decodeResult, 100 * Number(alt)); } - ResultFormatter.temperature(decodeResult, between.substring(22).replaceAll(" ", "0")); - } else if(between.substring(13,16) === 'OFF') { // off variant + ResultFormatter.temperature( + decodeResult, + between.substring(22).replaceAll(' ', '0'), + ); + } else if (between.substring(13, 16) === 'OFF') { + // off variant const ddmmyy = between.substring(16, 22); const hhmm = between.substring(22, 26); - if(ddmmyy != '------') { - const mmddyy = ddmmyy.substring(2, 4) + ddmmyy.substring(0, 2) + ddmmyy.substring(4); - ResultFormatter.off(decodeResult, DateTimeUtils.convertDateTimeToEpoch(hhmm+'00', mmddyy), 'epoch'); + if (ddmmyy != '------') { + const mmddyy = + ddmmyy.substring(2, 4) + + ddmmyy.substring(0, 2) + + ddmmyy.substring(4); + ResultFormatter.off( + decodeResult, + DateTimeUtils.convertDateTimeToEpoch(hhmm + '00', mmddyy), + 'epoch', + ); } else { - ResultFormatter.off(decodeResult, DateTimeUtils.convertHHMMSSToTod(hhmm), 'tod'); + ResultFormatter.off( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(hhmm), + 'tod', + ); } ResultFormatter.unknown(decodeResult, between.substring(26)); } else { diff --git a/lib/plugins/Label_15_FST.test.ts b/lib/plugins/Label_15_FST.test.ts index cb4a22e..89bc3bf 100644 --- a/lib/plugins/Label_15_FST.test.ts +++ b/lib/plugins/Label_15_FST.test.ts @@ -2,52 +2,53 @@ import { MessageDecoder } from '../MessageDecoder'; import { Label_15_FST } from './Label_15_FST'; describe('Label_15_FST', () => { - let plugin: Label_15_FST; - const message = {label: '15', text: ''}; - - beforeEach(() => { - const decoder = new MessageDecoder(); - plugin = new Label_15_FST(decoder); - }); - - test('matches Label 15 Preamble FST qualifiers', () => { - expect(plugin.decode).toBeDefined(); - expect(plugin.name).toBe('label-15-fst'); - expect(plugin.qualifiers).toBeDefined(); - expect(plugin.qualifiers()).toEqual({ - labels: ['15'], - preambles: ['FST01'], - }); - }); - - - test('decodes Label 15 Preamble FST variant 1', () => { - message.text = 'FST01EGKKKMCON373488W0756927380 156 495 M53C 4427422721045313002518521710' - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.formatted.items.length).toBe(4); - expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); - expect(decodeResult.formatted.items[0].value).toBe('37.349 N, 75.693 W'); - expect(decodeResult.formatted.items[1].label).toBe('Altitude'); - expect(decodeResult.formatted.items[1].value).toBe('38000 feet'); - expect(decodeResult.formatted.items[2].label).toBe('Origin'); - expect(decodeResult.formatted.items[2].value).toBe('EGKK'); - expect(decodeResult.formatted.items[3].label).toBe('Destination'); - expect(decodeResult.formatted.items[3].value).toBe('KMCO'); - expect(decodeResult.remaining.text).toBe('156 495 M53C 4427422721045313002518521710'); - }); - - test('decodes Label 15 Preamble FST ', () => { - - message.text = 'INI Bogus message'; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(false); - expect(decodeResult.decoder.decodeLevel).toBe('none'); - expect(decodeResult.decoder.name).toBe('label-15-fst'); - expect(decodeResult.formatted.description).toBe('Position Report'); - expect(decodeResult.message).toBe(message); + let plugin: Label_15_FST; + const message = { label: '15', text: '' }; + + beforeEach(() => { + const decoder = new MessageDecoder(); + plugin = new Label_15_FST(decoder); + }); + + test('matches Label 15 Preamble FST qualifiers', () => { + expect(plugin.decode).toBeDefined(); + expect(plugin.name).toBe('label-15-fst'); + expect(plugin.qualifiers).toBeDefined(); + expect(plugin.qualifiers()).toEqual({ + labels: ['15'], + preambles: ['FST01'], }); -}); \ No newline at end of file + }); + + test('decodes Label 15 Preamble FST variant 1', () => { + message.text = + 'FST01EGKKKMCON373488W0756927380 156 495 M53C 4427422721045313002518521710'; + const decodeResult = plugin.decode(message); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.formatted.items.length).toBe(4); + expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); + expect(decodeResult.formatted.items[0].value).toBe('37.349 N, 75.693 W'); + expect(decodeResult.formatted.items[1].label).toBe('Altitude'); + expect(decodeResult.formatted.items[1].value).toBe('38000 feet'); + expect(decodeResult.formatted.items[2].label).toBe('Origin'); + expect(decodeResult.formatted.items[2].value).toBe('EGKK'); + expect(decodeResult.formatted.items[3].label).toBe('Destination'); + expect(decodeResult.formatted.items[3].value).toBe('KMCO'); + expect(decodeResult.remaining.text).toBe( + '156 495 M53C 4427422721045313002518521710', + ); + }); + + test('decodes Label 15 Preamble FST ', () => { + message.text = 'INI Bogus message'; + const decodeResult = plugin.decode(message); + + expect(decodeResult.decoded).toBe(false); + expect(decodeResult.decoder.decodeLevel).toBe('none'); + expect(decodeResult.decoder.name).toBe('label-15-fst'); + expect(decodeResult.formatted.description).toBe('Position Report'); + expect(decodeResult.message).toBe(message); + }); +}); diff --git a/lib/plugins/Label_15_FST.ts b/lib/plugins/Label_15_FST.ts index e86eb36..b3066b6 100644 --- a/lib/plugins/Label_15_FST.ts +++ b/lib/plugins/Label_15_FST.ts @@ -1,13 +1,12 @@ import { DecoderPlugin } from '../DecoderPlugin'; import { DecodeResult, Message, Options } from '../DecoderPluginInterface'; -import { CoordinateUtils } from '../utils/coordinate_utils'; import { ResultFormatter } from '../utils/result_formatter'; // Position Report export class Label_15_FST extends DecoderPlugin { name = 'label-15-fst'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['15'], preambles: ['FST01'], @@ -24,26 +23,36 @@ export class Label_15_FST extends DecoderPlugin { // FST01KMCOEGKKN505552W00118021 const header = parts[0]; - const stringCoords = header.substring(13) + const stringCoords = header.substring(13); // Don't use decodeStringCoordinates here, because of different pos format const firstChar = stringCoords.substring(0, 1); - const middleChar= stringCoords.substring(7, 8); + const middleChar = stringCoords.substring(7, 8); decodeResult.raw.position = {}; - if ((firstChar === 'N' || firstChar === 'S') && (middleChar === 'W' || middleChar === 'E')) { - const lat = (Number(stringCoords.substring(1, 7)) / 10000) * (firstChar === 'S' ? -1 : 1); - const lon = (Number(stringCoords.substring(8, 15)) / 10000) * (middleChar === 'W' ? -1 : 1); - ResultFormatter.position(decodeResult, {latitude: lat, longitude: lon}); - ResultFormatter.altitude(decodeResult, Number(stringCoords.substring(15)) * 100); + if ( + (firstChar === 'N' || firstChar === 'S') && + (middleChar === 'W' || middleChar === 'E') + ) { + const lat = + (Number(stringCoords.substring(1, 7)) / 10000) * + (firstChar === 'S' ? -1 : 1); + const lon = + (Number(stringCoords.substring(8, 15)) / 10000) * + (middleChar === 'W' ? -1 : 1); + ResultFormatter.position(decodeResult, { latitude: lat, longitude: lon }); + ResultFormatter.altitude( + decodeResult, + Number(stringCoords.substring(15)) * 100, + ); } else { decodeResult.decoded = false; decodeResult.decoder.decodeLevel = 'none'; return decodeResult; } - ResultFormatter.departureAirport(decodeResult, header.substring(5,9)); - ResultFormatter.arrivalAirport(decodeResult, header.substring(9,13)); + ResultFormatter.departureAirport(decodeResult, header.substring(5, 9)); + ResultFormatter.arrivalAirport(decodeResult, header.substring(9, 13)); ResultFormatter.unknownArr(decodeResult, parts.slice(1), ' '); diff --git a/lib/plugins/Label_16_Honeywell.test.ts b/lib/plugins/Label_16_Honeywell.test.ts index 736c70d..90c299d 100644 --- a/lib/plugins/Label_16_Honeywell.test.ts +++ b/lib/plugins/Label_16_Honeywell.test.ts @@ -1,72 +1,72 @@ -import { MessageDecoder } from "../MessageDecoder"; -import { Label_16_Honeywell } from "./Label_16_Honeywell"; +import { MessageDecoder } from '../MessageDecoder'; +import { Label_16_Honeywell } from './Label_16_Honeywell'; -describe("Label_16_Honeywell", () => { +describe('Label_16_Honeywell', () => { let plugin: Label_16_Honeywell; - const message = { label: "16", text: "" }; + const message = { label: '16', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); plugin = new Label_16_Honeywell(decoder); }); - test("matches qualifiers", () => { + test('matches qualifiers', () => { expect(plugin.decode).toBeDefined(); - expect(plugin.name).toBe("label-16-honeywell"); + expect(plugin.name).toBe('label-16-honeywell'); expect(plugin.qualifiers).toBeDefined(); expect(plugin.qualifiers()).toEqual({ - labels: ["16"], - preambles: ["(2"], + labels: ['16'], + preambles: ['(2'], }); }); - test("decodes variant 1", () => { - message.text = "(2AAABN39211W 77144KTEBMMTO-/A(Z"; + test('decodes variant 1', () => { + message.text = '(2AAABN39211W 77144KTEBMMTO-/A(Z'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe("partial"); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); expect(decodeResult.raw.position.latitude).toBeCloseTo(39.352, 3); expect(decodeResult.raw.position.longitude).toBeCloseTo(-77.24, 3); - expect(decodeResult.raw.departure_icao).toBe("KTEB"); - expect(decodeResult.raw.arrival_icao).toBe("MMTO"); + expect(decodeResult.raw.departure_icao).toBe('KTEB'); + expect(decodeResult.raw.arrival_icao).toBe('MMTO'); expect(decodeResult.formatted.items.length).toBe(3); - expect(decodeResult.remaining.text).toBe("AAAB/A"); + expect(decodeResult.remaining.text).toBe('AAAB/A'); }); - test("decodes variant 2", () => { - message.text = "(2AAAAN37265W 78334-SSI /O(Z"; + test('decodes variant 2', () => { + message.text = '(2AAAAN37265W 78334-SSI /O(Z'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe("partial"); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); expect(decodeResult.raw.position.latitude).toBeCloseTo(37.442, 3); expect(decodeResult.raw.position.longitude).toBeCloseTo(-78.557, 3); - expect(decodeResult.raw.route.waypoints[0].name).toBe("SSI"); + expect(decodeResult.raw.route.waypoints[0].name).toBe('SSI'); expect(decodeResult.formatted.items.length).toBe(2); - expect(decodeResult.remaining.text).toBe("AAAA/O"); + expect(decodeResult.remaining.text).toBe('AAAA/O'); }); - test("decodes variant 3", () => { - message.text = "(2AAABN37197W 78404-SLOJOGRONK/O(Z"; + test('decodes variant 3', () => { + message.text = '(2AAABN37197W 78404-SLOJOGRONK/O(Z'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe("partial"); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); expect(decodeResult.raw.position.latitude).toBeCloseTo(37.328, 3); expect(decodeResult.raw.position.longitude).toBeCloseTo(-78.673, 3); - expect(decodeResult.raw.route.waypoints[0].name).toBe("SLOJO"); - expect(decodeResult.raw.route.waypoints[1].name).toBe("GRONK"); + expect(decodeResult.raw.route.waypoints[0].name).toBe('SLOJO'); + expect(decodeResult.raw.route.waypoints[1].name).toBe('GRONK'); expect(decodeResult.formatted.items.length).toBe(2); - expect(decodeResult.remaining.text).toBe("AAAB/O"); + expect(decodeResult.remaining.text).toBe('AAAB/O'); }); - test("does not decode ", () => { - message.text = "(2 Bogus message"; + test('does not decode ', () => { + message.text = '(2 Bogus message'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(false); - expect(decodeResult.decoder.decodeLevel).toBe("none"); + expect(decodeResult.decoder.decodeLevel).toBe('none'); expect(decodeResult.message).toBe(message); }); }); diff --git a/lib/plugins/Label_16_Honeywell.ts b/lib/plugins/Label_16_Honeywell.ts index f9f70c2..28f6bc1 100644 --- a/lib/plugins/Label_16_Honeywell.ts +++ b/lib/plugins/Label_16_Honeywell.ts @@ -1,36 +1,39 @@ -import { DecoderPlugin } from "../DecoderPlugin"; -import { DecodeResult, Message, Options } from "../DecoderPluginInterface"; -import { CoordinateUtils } from "../utils/coordinate_utils"; -import { ResultFormatter } from "../utils/result_formatter"; +import { DecoderPlugin } from '../DecoderPlugin'; +import { DecodeResult, Message, Options } from '../DecoderPluginInterface'; +import { CoordinateUtils } from '../utils/coordinate_utils'; +import { ResultFormatter } from '../utils/result_formatter'; // Honeywell Label 16 Position Report export class Label_16_Honeywell extends DecoderPlugin { - name = "label-16-honeywell"; + name = 'label-16-honeywell'; qualifiers() { - // eslint-disable-line class-methods-use-this return { - labels: ["16"], - preambles: ["(2"], + labels: ['16'], + preambles: ['(2'], }; } decode(message: Message, options: Options = {}): DecodeResult { const decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; - decodeResult.formatted.description = "Position Report"; + decodeResult.formatted.description = 'Position Report'; decodeResult.message = message; - if (message.text.startsWith("(2") && message.text.endsWith("(Z") && message.text.length >= 29) { + if ( + message.text.startsWith('(2') && + message.text.endsWith('(Z') && + message.text.length >= 29 + ) { const between = message.text.substring(2, message.text.length - 2); - ResultFormatter.unknown(decodeResult, between.substring(0, 4), ""); // Session ID + ResultFormatter.unknown(decodeResult, between.substring(0, 4), ''); // Session ID ResultFormatter.position( decodeResult, CoordinateUtils.decodeStringCoordinatesDecimalMinutes( between.substring(4, 17), ), ); - if (between.charAt(17) === "-") { + if (between.charAt(17) === '-') { // Waypoint mode const waypoint1 = between.substring(18, 23).trim(); const waypoint2 = between.substring(23, 28).trim(); @@ -64,7 +67,7 @@ export class Label_16_Honeywell extends DecoderPlugin { ResultFormatter.unknown( decodeResult, between.substring(between.length - 2), - "", + '', ); } else { if (options.debug) { @@ -72,12 +75,12 @@ export class Label_16_Honeywell extends DecoderPlugin { } ResultFormatter.unknown(decodeResult, message.text); decodeResult.decoded = false; - decodeResult.decoder.decodeLevel = "none"; + decodeResult.decoder.decodeLevel = 'none'; return decodeResult; } decodeResult.decoded = true; - decodeResult.decoder.decodeLevel = "partial"; + decodeResult.decoder.decodeLevel = 'partial'; return decodeResult; } } diff --git a/lib/plugins/Label_16_N_Space.test.ts b/lib/plugins/Label_16_N_Space.test.ts index 171169b..d8e79ef 100644 --- a/lib/plugins/Label_16_N_Space.test.ts +++ b/lib/plugins/Label_16_N_Space.test.ts @@ -2,9 +2,8 @@ import { MessageDecoder } from '../MessageDecoder'; import { Label_16_N_Space } from './Label_16_N_Space'; describe('Label_16_N_Space', () => { - let plugin: Label_16_N_Space; - const message = {label: '16', text: ''}; + const message = { label: '16', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); @@ -94,4 +93,4 @@ describe('Label_16_N_Space', () => { expect(decodeResult.formatted.description).toBe('Position Report'); expect(decodeResult.message).toBe(message); }); -}); \ No newline at end of file +}); diff --git a/lib/plugins/Label_16_N_Space.ts b/lib/plugins/Label_16_N_Space.ts index 2537db6..5acc6dc 100644 --- a/lib/plugins/Label_16_N_Space.ts +++ b/lib/plugins/Label_16_N_Space.ts @@ -1,63 +1,78 @@ import { DecoderPlugin } from '../DecoderPlugin'; import { DecodeResult, Message, Options } from '../DecoderPluginInterface'; -import { CoordinateUtils } from '../utils/coordinate_utils'; import { ResultFormatter } from '../utils/result_formatter'; export class Label_16_N_Space extends DecoderPlugin { name = 'label-16-n-space'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { - labels: ["16"], + labels: ['16'], preambles: ['N ', 'S '], }; } - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { const decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; decodeResult.formatted.description = 'Position Report'; decodeResult.message = message; // Style: N 44.203,W 86.546,31965,6, 290 - let variant1Regex = /^(?[NS])\s(?.*),(?[EW])\s*(?.*),(?.*),(?.*),\s*(?.*)$/; + let variant1Regex = + /^(?[NS])\s(?.*),(?[EW])\s*(?.*),(?.*),(?.*),\s*(?.*)$/; // Style: N 28.177/W 96.055 - let variant2Regex = /^(?[NS])\s(?.*)\/(?[EW])\s*(?.*)$/; + let variant2Regex = + /^(?[NS])\s(?.*)\/(?[EW])\s*(?.*)$/; let results = message.text.match(variant1Regex); if (results?.groups) { if (options.debug) { - console.log(`Label 16 N : results`); + console.log('Label 16 N : results'); console.log(results); } let pos = { - latitude: Number(results.groups.lat_coord) * (results.groups.lat == 'N' ? 1 : -1), - longitude: Number(results.groups.long_coord) * (results.groups.long == 'E' ? 1 : -1), + latitude: + Number(results.groups.lat_coord) * + (results.groups.lat == 'N' ? 1 : -1), + longitude: + Number(results.groups.long_coord) * + (results.groups.long == 'E' ? 1 : -1), }; - const altitude = results.groups.alt == 'GRD' || results.groups.alt == '***' ? 0 : Number(results.groups.alt); + const altitude = + results.groups.alt == 'GRD' || results.groups.alt == '***' + ? 0 + : Number(results.groups.alt); ResultFormatter.position(decodeResult, pos); - ResultFormatter.altitude(decodeResult, altitude) + ResultFormatter.altitude(decodeResult, altitude); - ResultFormatter.unknownArr(decodeResult, [results.groups.unkwn1, results.groups.unkwn2]); + ResultFormatter.unknownArr(decodeResult, [ + results.groups.unkwn1, + results.groups.unkwn2, + ]); decodeResult.decoded = true; decodeResult.decoder.decodeLevel = 'partial'; - return decodeResult + return decodeResult; } - - results = message.text.match(variant2Regex) + + results = message.text.match(variant2Regex); if (results?.groups) { if (options.debug) { - console.log(`Label 16 N : results`); + console.log('Label 16 N : results'); console.log(results); } let pos = { - latitude: Number(results.groups.lat_coord) * (results.groups.lat == 'N' ? 1 : -1), - longitude: Number(results.groups.long_coord) * (results.groups.long == 'E' ? 1 : -1) + latitude: + Number(results.groups.lat_coord) * + (results.groups.lat == 'N' ? 1 : -1), + longitude: + Number(results.groups.long_coord) * + (results.groups.long == 'E' ? 1 : -1), }; ResultFormatter.position(decodeResult, pos); @@ -65,15 +80,15 @@ export class Label_16_N_Space extends DecoderPlugin { decodeResult.decoded = true; decodeResult.decoder.decodeLevel = 'full'; return decodeResult; - } + } // Unknown - if (options.debug) { - console.log(`Decoder: Unknown 16 message: ${message.text}`); - } - ResultFormatter.unknown(decodeResult, message.text); - decodeResult.decoded = false; - decodeResult.decoder.decodeLevel = 'none'; + if (options.debug) { + console.log(`Decoder: Unknown 16 message: ${message.text}`); + } + ResultFormatter.unknown(decodeResult, message.text); + decodeResult.decoded = false; + decodeResult.decoder.decodeLevel = 'none'; return decodeResult; } diff --git a/lib/plugins/Label_16_POSA1.test.ts b/lib/plugins/Label_16_POSA1.test.ts index 8ad2fef..d8b852a 100644 --- a/lib/plugins/Label_16_POSA1.test.ts +++ b/lib/plugins/Label_16_POSA1.test.ts @@ -2,9 +2,8 @@ import { MessageDecoder } from '../MessageDecoder'; import { Label_16_POSA1 } from './Label_16_POSA1'; describe('Label 16 POSA1', () => { - let plugin: Label_16_POSA1; - const message = {label: '16', text: ''}; + const message = { label: '16', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); @@ -21,7 +20,8 @@ describe('Label 16 POSA1', () => { }); }); test('decodes variant 1', () => { - message.text = 'POSA1N37358W 77279,GEARS ,221626,370,BBOBO ,222053,,-61,139,1174,829'; + message.text = + 'POSA1N37358W 77279,GEARS ,221626,370,BBOBO ,222053,,-61,139,1174,829'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -35,12 +35,15 @@ describe('Label 16 POSA1', () => { expect(decodeResult.formatted.items[1].label).toBe('Altitude'); expect(decodeResult.formatted.items[1].value).toBe('37000 feet'); expect(decodeResult.formatted.items[2].label).toBe('Aircraft Route'); - expect(decodeResult.formatted.items[2].value).toBe('GEARS@22:16:26 > BBOBO@22:20:53'); + expect(decodeResult.formatted.items[2].value).toBe( + 'GEARS@22:16:26 > BBOBO@22:20:53', + ); expect(decodeResult.remaining.text).toBe(',-61,139,1174,829'); }); test('decodes redacted', () => { - message.text = 'POSA1N38843W 78790,RONZZ ,005159,390,RAMAY ,010055,,*****,*****, 744, 0'; + message.text = + 'POSA1N38843W 78790,RONZZ ,005159,390,RAMAY ,010055,,*****,*****, 744, 0'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -54,7 +57,9 @@ describe('Label 16 POSA1', () => { expect(decodeResult.formatted.items[1].label).toBe('Altitude'); expect(decodeResult.formatted.items[1].value).toBe('39000 feet'); expect(decodeResult.formatted.items[2].label).toBe('Aircraft Route'); - expect(decodeResult.formatted.items[2].value).toBe('RONZZ@00:51:59 > RAMAY@01:00:55'); + expect(decodeResult.formatted.items[2].value).toBe( + 'RONZZ@00:51:59 > RAMAY@01:00:55', + ); expect(decodeResult.remaining.text).toBe(',*****,*****, 744, 0'); }); @@ -68,4 +73,4 @@ describe('Label 16 POSA1', () => { expect(decodeResult.formatted.description).toBe('Position Report'); expect(decodeResult.message).toBe(message); }); -}); \ No newline at end of file +}); diff --git a/lib/plugins/Label_16_POSA1.ts b/lib/plugins/Label_16_POSA1.ts index 0fc4ee9..b30ec8d 100644 --- a/lib/plugins/Label_16_POSA1.ts +++ b/lib/plugins/Label_16_POSA1.ts @@ -7,14 +7,14 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_16_POSA1 extends DecoderPlugin { name = 'label-16-posa1'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { - labels: ["16"], + labels: ['16'], preambles: ['POSA1'], }; } - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { const decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; decodeResult.formatted.description = 'Position Report'; @@ -31,21 +31,25 @@ export class Label_16_POSA1 extends DecoderPlugin { return decodeResult; } - ResultFormatter.position(decodeResult, CoordinateUtils.decodeStringCoordinates(fields[0].substring(5))); // strip 'POSA1' + ResultFormatter.position( + decodeResult, + CoordinateUtils.decodeStringCoordinates(fields[0].substring(5)), + ); // strip 'POSA1' const waypoint = fields[1].trim(); const time = DateTimeUtils.convertHHMMSSToTod(fields[2]); - ResultFormatter.altitude(decodeResult, Number(fields[3])*100); + ResultFormatter.altitude(decodeResult, Number(fields[3]) * 100); const nextWaypoint = fields[4].trim(); const nextTime = DateTimeUtils.convertHHMMSSToTod(fields[5]); ResultFormatter.unknownArr(decodeResult, fields.slice(6), ','); - ResultFormatter.route(decodeResult, {waypoints: [ - {name: waypoint, time: time, timeFormat: 'tod'}, - {name: nextWaypoint, time: nextTime, timeFormat: 'tod'} - ]}); + ResultFormatter.route(decodeResult, { + waypoints: [ + { name: waypoint, time: time, timeFormat: 'tod' }, + { name: nextWaypoint, time: nextTime, timeFormat: 'tod' }, + ], + }); decodeResult.decoded = true; decodeResult.decoder.decodeLevel = 'partial'; - return decodeResult; } } diff --git a/lib/plugins/Label_16_TOD.test.ts b/lib/plugins/Label_16_TOD.test.ts index 3886243..de796d3 100644 --- a/lib/plugins/Label_16_TOD.test.ts +++ b/lib/plugins/Label_16_TOD.test.ts @@ -2,9 +2,8 @@ import { MessageDecoder } from '../MessageDecoder'; import { Label_16_TOD } from './Label_16_TOD'; describe('Label 16 Time of Day', () => { - let plugin: Label_16_TOD; - const message = {label: '16', text: ''}; + const message = { label: '16', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); @@ -34,7 +33,9 @@ describe('Label 16 Time of Day', () => { expect(decodeResult.formatted.items[0].value).toBe('00:52:36'); expect(decodeResult.formatted.items[1].label).toBe('Altitude'); expect(decodeResult.formatted.items[1].value).toBe('36787 feet'); - expect(decodeResult.formatted.items[2].label).toBe('Estimated Time of Arrival'); + expect(decodeResult.formatted.items[2].label).toBe( + 'Estimated Time of Arrival', + ); expect(decodeResult.formatted.items[2].value).toBe('01:35:00'); expect(decodeResult.formatted.items[3].label).toBe('Aircraft Position'); expect(decodeResult.formatted.items[3].value).toBe('38.364 N, 75.226 W'); @@ -56,7 +57,9 @@ describe('Label 16 Time of Day', () => { expect(decodeResult.formatted.items[0].value).toBe('11:01:12'); expect(decodeResult.formatted.items[1].label).toBe('Altitude'); expect(decodeResult.formatted.items[1].value).toBe('36000 feet'); - expect(decodeResult.formatted.items[2].label).toBe('Estimated Time of Arrival'); + expect(decodeResult.formatted.items[2].label).toBe( + 'Estimated Time of Arrival', + ); expect(decodeResult.formatted.items[2].value).toBe('12:06:00'); expect(decodeResult.formatted.items[3].label).toBe('Aircraft Position'); expect(decodeResult.formatted.items[3].value).toBe('45.140 N, 16.341 E'); @@ -65,7 +68,6 @@ describe('Label 16 Time of Day', () => { expect(decodeResult.remaining.text).toBe(' 51'); }); - test('decodes no position', () => { // https://app.airframes.io/messages/4260590899 message.text = '110122,,1206, 92,N . MMMM.MMM'; @@ -79,7 +81,9 @@ describe('Label 16 Time of Day', () => { expect(decodeResult.formatted.items.length).toBe(2); expect(decodeResult.formatted.items[0].label).toBe('Message Timestamp'); expect(decodeResult.formatted.items[0].value).toBe('11:01:22'); - expect(decodeResult.formatted.items[1].label).toBe('Estimated Time of Arrival'); + expect(decodeResult.formatted.items[1].label).toBe( + 'Estimated Time of Arrival', + ); expect(decodeResult.formatted.items[1].value).toBe('12:06:00'); expect(decodeResult.remaining.text).toBe(' 92'); }); @@ -94,4 +98,4 @@ describe('Label 16 Time of Day', () => { expect(decodeResult.formatted.description).toBe('Position Report'); expect(decodeResult.message).toBe(message); }); -}); \ No newline at end of file +}); diff --git a/lib/plugins/Label_16_TOD.ts b/lib/plugins/Label_16_TOD.ts index 5c4e0fb..7bca1e9 100644 --- a/lib/plugins/Label_16_TOD.ts +++ b/lib/plugins/Label_16_TOD.ts @@ -7,20 +7,20 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_16_TOD extends DecoderPlugin { name = 'label-16-tod'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { - labels: ["16"], + labels: ['16'], }; } - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { const decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; decodeResult.formatted.description = 'Position Report'; decodeResult.message = message; const fields = message.text.split(','); - const time = DateTimeUtils.convertHHMMSSToTod(fields[0]) + const time = DateTimeUtils.convertHHMMSSToTod(fields[0]); if (fields.length !== 5 || Number.isNaN(time)) { if (options.debug) { console.log(`Decoder: Unknown 16 message: ${message.text}`); @@ -32,25 +32,29 @@ export class Label_16_TOD extends DecoderPlugin { } ResultFormatter.time_of_day(decodeResult, time); - if(fields[1] !== '') { + if (fields[1] !== '') { ResultFormatter.altitude(decodeResult, Number(fields[1])); } - ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[2])); + ResultFormatter.eta( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(fields[2]), + ); ResultFormatter.unknown(decodeResult, fields[3]); const temp = fields[4].split('/'); const posFields = temp[0].split(' '); ResultFormatter.position(decodeResult, { - latitude: CoordinateUtils.getDirection(posFields[0]) * Number(posFields[1]), - longitude: CoordinateUtils.getDirection(posFields[2]) * Number(posFields[3]), + latitude: + CoordinateUtils.getDirection(posFields[0]) * Number(posFields[1]), + longitude: + CoordinateUtils.getDirection(posFields[2]) * Number(posFields[3]), }); - if(temp.length > 1) { + if (temp.length > 1) { ResultFormatter.flightNumber(decodeResult, temp[1]); } decodeResult.decoded = true; decodeResult.decoder.decodeLevel = 'partial'; - return decodeResult; } } diff --git a/lib/plugins/Label_1J_2J_FTX.test.ts b/lib/plugins/Label_1J_2J_FTX.test.ts index d2dafec..d059dba 100644 --- a/lib/plugins/Label_1J_2J_FTX.test.ts +++ b/lib/plugins/Label_1J_2J_FTX.test.ts @@ -2,19 +2,18 @@ import { MessageDecoder } from '../MessageDecoder'; import { Label_1J_2J_FTX } from './Label_1J_2J_FTX'; describe('Label 1J/2J FTX', () => { - let plugin: Label_1J_2J_FTX; - let message = { label: '1J', text: '' }; + let message = { label: '1J', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); plugin = new Label_1J_2J_FTX(decoder); }); - test('decodes Label 1J', () => { // https://app.airframes.io/messages/4178692503 - message.text = 'FTX/ID50007B,RCH4086,ABB02R70E037/MR6,/FX4 QTR PHILLY UP 37-6307A' + message.text = + 'FTX/ID50007B,RCH4086,ABB02R70E037/MR6,/FX4 QTR PHILLY UP 37-6307A'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -35,7 +34,8 @@ describe('Label 1J/2J FTX', () => { // Disabled due to checksum mismatch. Possibly copy-paste issue due to non-ascii characters in message? test('decodes Label 2J', () => { // https://app.airframes.io/messages/4178362466 - message.text = 'M74AMC4086FTX/ID50007B,RCH4086,ABB02R70E037/DC10022025,011728/MR049,/FXGOOD EVENING PLEASE PASS US THE SUPER BOWL SCORE WHEN ABLE. THANK YOU/FB1791/VR0328D70' + message.text = + 'M74AMC4086FTX/ID50007B,RCH4086,ABB02R70E037/DC10022025,011728/MR049,/FXGOOD EVENING PLEASE PASS US THE SUPER BOWL SCORE WHEN ABLE. THANK YOU/FB1791/VR0328D70'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -45,15 +45,16 @@ describe('Label 1J/2J FTX', () => { expect(decodeResult.raw.mission_number).toBe('ABB02R70E037'); expect(decodeResult.raw.message_timestamp).toBe(1759367848); expect(decodeResult.raw.fuel_on_board).toBe(1791); - expect(decodeResult.raw.freetext).toBe('GOOD EVENING PLEASE PASS US THE SUPER BOWL SCORE WHEN ABLE. THANK YOU'); + expect(decodeResult.raw.freetext).toBe( + 'GOOD EVENING PLEASE PASS US THE SUPER BOWL SCORE WHEN ABLE. THANK YOU', + ); expect(decodeResult.raw.version).toBe(3.2); - expect(decodeResult.raw.checksum).toBe(0x8D70); + expect(decodeResult.raw.checksum).toBe(0x8d70); expect(decodeResult.formatted.items.length).toBe(6); expect(decodeResult.remaining.text).toBe('M74/MR049,'); }); test('decodes ', () => { - message.text = 'FTX Bogus message'; const decodeResult = plugin.decode(message); diff --git a/lib/plugins/Label_1J_2J_FTX.ts b/lib/plugins/Label_1J_2J_FTX.ts index 7b6176b..10c0fcc 100644 --- a/lib/plugins/Label_1J_2J_FTX.ts +++ b/lib/plugins/Label_1J_2J_FTX.ts @@ -5,30 +5,36 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_1J_2J_FTX extends DecoderPlugin { name = 'label-1j-2j-ftx'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['1J', '2J'], }; } - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { let decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; decodeResult.message = message; - const msg = message.text.replace(/\n|\r/g, ""); + const msg = message.text.replace(/\n|\r/g, ''); const parts = msg.split('/'); let decoded = false; if (parts[0].length > 3) { - decoded = H1Helper.decodeH1Message(decodeResult, msg.slice(parts[0].length - 3)); + decoded = H1Helper.decodeH1Message( + decodeResult, + msg.slice(parts[0].length - 3), + ); // flight number is already decoded in other fields - decodeResult.remaining.text = parts[0].slice(0,3) + '/' + decodeResult.remaining.text; + decodeResult.remaining.text = + parts[0].slice(0, 3) + '/' + decodeResult.remaining.text; } else { - decoded = H1Helper.decodeH1Message(decodeResult, msg); + decoded = H1Helper.decodeH1Message(decodeResult, msg); } decodeResult.decoded = decoded; - decodeResult.decoder.decodeLevel = !decodeResult.remaining.text ? 'full' : 'partial'; + decodeResult.decoder.decodeLevel = !decodeResult.remaining.text + ? 'full' + : 'partial'; if (decodeResult.formatted.items.length === 0) { if (options.debug) { console.log(`Decoder: Unknown 1J/2J message: ${message.text}`); diff --git a/lib/plugins/Label_1L_070.test.ts b/lib/plugins/Label_1L_070.test.ts index 0bb4dca..a4cc9cf 100644 --- a/lib/plugins/Label_1L_070.test.ts +++ b/lib/plugins/Label_1L_070.test.ts @@ -40,7 +40,9 @@ describe('Label_1L 070', () => { expect(decodeResult.formatted.items[1].value).toBe('KEWR'); expect(decodeResult.formatted.items[2].label).toBe('Message Timestamp'); expect(decodeResult.formatted.items[2].value).toBe('09:32:00'); - expect(decodeResult.formatted.items[3].label).toBe('Estimated Time of Arrival'); + expect(decodeResult.formatted.items[3].label).toBe( + 'Estimated Time of Arrival', + ); expect(decodeResult.formatted.items[3].value).toBe('17:44:00'); expect(decodeResult.formatted.items[4].label).toBe('Aircraft Position'); expect(decodeResult.formatted.items[4].value).toBe('49.223 N, 12.038 E'); @@ -48,7 +50,6 @@ describe('Label_1L 070', () => { }); test('does not decode ', () => { - message.text = 'POS Bogus Message'; const decodeResult = plugin.decode(message); @@ -57,4 +58,4 @@ describe('Label_1L 070', () => { expect(decodeResult.decoder.name).toBe('label-1l-070'); expect(decodeResult.formatted.description).toBe('Position Report'); }); -}); \ No newline at end of file +}); diff --git a/lib/plugins/Label_1L_070.ts b/lib/plugins/Label_1L_070.ts index 0b6d44f..eb1633c 100644 --- a/lib/plugins/Label_1L_070.ts +++ b/lib/plugins/Label_1L_070.ts @@ -4,10 +4,10 @@ import { DecodeResult, Message, Options } from '../DecoderPluginInterface'; import { CoordinateUtils } from '../utils/coordinate_utils'; import { ResultFormatter } from '../utils/result_formatter'; -export class Label_1L_070 extends DecoderPlugin { // eslint-disable-line camelcase +export class Label_1L_070 extends DecoderPlugin { name = 'label-1l-070'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['1L'], preambles: ['000000070'], @@ -44,14 +44,23 @@ export class Label_1L_070 extends DecoderPlugin { // eslint-disable-line camelca ResultFormatter.departureAirport(decodeResult, parts[0]); ResultFormatter.arrivalAirport(decodeResult, parts[1]); - ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(parts[2])); - ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(parts[3])); + ResultFormatter.time_of_day( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(parts[2]), + ); + ResultFormatter.eta( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(parts[3]), + ); ResultFormatter.position(decodeResult, { - latitude: CoordinateUtils.getDirection(parts[4][0]) * Number(parts[4].substring(1)), - longitude: CoordinateUtils.getDirection(parts[5][0]) * Number(parts[5].substring(1)), + latitude: + CoordinateUtils.getDirection(parts[4][0]) * + Number(parts[4].substring(1)), + longitude: + CoordinateUtils.getDirection(parts[5][0]) * + Number(parts[5].substring(1)), }); - decodeResult.remaining.text = parts[6]; decodeResult.decoded = true; diff --git a/lib/plugins/Label_1L_3-line.test.ts b/lib/plugins/Label_1L_3-line.test.ts index 84ae0d7..56ae9a8 100644 --- a/lib/plugins/Label_1L_3-line.test.ts +++ b/lib/plugins/Label_1L_3-line.test.ts @@ -20,9 +20,12 @@ describe('Label_1L 3-line', () => { }); test('decodes variant 1', () => { - message.text = '00018213200/GS 411500/DEP MDPC/DES CYYZ/ETA 0120/GW 479/ALT 39002' + '\r\n' + - 'CAS 229/SAT - 59.0/FN SWG9040/TFQ 48/DAY 22OCT24/UTC 002714' + '\r\n' + - 'LON W 78.289/LAT N 39.556/WD 20/WS 13' + message.text = + '00018213200/GS 411500/DEP MDPC/DES CYYZ/ETA 0120/GW 479/ALT 39002' + + '\r\n' + + 'CAS 229/SAT - 59.0/FN SWG9040/TFQ 48/DAY 22OCT24/UTC 002714' + + '\r\n' + + 'LON W 78.289/LAT N 39.556/WD 20/WS 13'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -40,7 +43,9 @@ describe('Label_1L 3-line', () => { expect(decodeResult.formatted.items[0].value).toBe('MDPC'); expect(decodeResult.formatted.items[1].label).toBe('Destination'); expect(decodeResult.formatted.items[1].value).toBe('CYYZ'); - expect(decodeResult.formatted.items[2].label).toBe('Estimated Time of Arrival'); + expect(decodeResult.formatted.items[2].label).toBe( + 'Estimated Time of Arrival', + ); expect(decodeResult.formatted.items[2].value).toBe('01:20:00'); expect(decodeResult.formatted.items[3].label).toBe('Altitude'); expect(decodeResult.formatted.items[3].value).toBe('39002 feet'); @@ -48,13 +53,18 @@ describe('Label_1L 3-line', () => { expect(decodeResult.formatted.items[4].value).toBe('SWG9040'); expect(decodeResult.formatted.items[5].label).toBe('Aircraft Position'); expect(decodeResult.formatted.items[5].value).toBe('39.556 N, 78.289 W'); - expect(decodeResult.remaining.text).toBe('00018213200/GS 411500/GW 479/CAS 229/SAT - 59.0/TFQ 48/WD 20/WS 13'); + expect(decodeResult.remaining.text).toBe( + '00018213200/GS 411500/GW 479/CAS 229/SAT - 59.0/TFQ 48/WD 20/WS 13', + ); }); test('decodes no position', () => { - message.text = '00086213200/GS 497000/DEP CYUL/DES MDPC/ETA 1705/GW 700/ALT 37002' + '\r\n' + - 'CAS/SAT 15.0/FN SWG4426/TFQ 111/DAY 26SEP24/UTC 135139' + '\r\n' + - 'LON MMMM.MMM/LAT MMMM.MMM/WD ---/WS ---' + message.text = + '00086213200/GS 497000/DEP CYUL/DES MDPC/ETA 1705/GW 700/ALT 37002' + + '\r\n' + + 'CAS/SAT 15.0/FN SWG4426/TFQ 111/DAY 26SEP24/UTC 135139' + + '\r\n' + + 'LON MMMM.MMM/LAT MMMM.MMM/WD ---/WS ---'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -71,17 +81,20 @@ describe('Label_1L 3-line', () => { expect(decodeResult.formatted.items[0].value).toBe('CYUL'); expect(decodeResult.formatted.items[1].label).toBe('Destination'); expect(decodeResult.formatted.items[1].value).toBe('MDPC'); - expect(decodeResult.formatted.items[2].label).toBe('Estimated Time of Arrival'); + expect(decodeResult.formatted.items[2].label).toBe( + 'Estimated Time of Arrival', + ); expect(decodeResult.formatted.items[2].value).toBe('17:05:00'); expect(decodeResult.formatted.items[3].label).toBe('Altitude'); expect(decodeResult.formatted.items[3].value).toBe('37002 feet'); expect(decodeResult.formatted.items[4].label).toBe('Flight Number'); expect(decodeResult.formatted.items[4].value).toBe('SWG4426'); - expect(decodeResult.remaining.text).toBe('00086213200/GS 497000/GW 700/CAS /SAT 15.0/TFQ 111/WD ---/WS ---'); + expect(decodeResult.remaining.text).toBe( + '00086213200/GS 497000/GW 700/CAS /SAT 15.0/TFQ 111/WD ---/WS ---', + ); }); test('does not decode ', () => { - message.text = 'POS Bogus Message'; const decodeResult = plugin.decode(message); @@ -90,4 +103,4 @@ describe('Label_1L 3-line', () => { expect(decodeResult.decoder.name).toBe('label-1l-3-line'); expect(decodeResult.formatted.description).toBe('Position Report'); }); -}); \ No newline at end of file +}); diff --git a/lib/plugins/Label_1L_3-line.ts b/lib/plugins/Label_1L_3-line.ts index ef5d4f9..16d5200 100644 --- a/lib/plugins/Label_1L_3-line.ts +++ b/lib/plugins/Label_1L_3-line.ts @@ -4,10 +4,10 @@ import { DecodeResult, Message, Options } from '../DecoderPluginInterface'; import { CoordinateUtils } from '../utils/coordinate_utils'; import { ResultFormatter } from '../utils/result_formatter'; -export class Label_1L_3Line extends DecoderPlugin { // eslint-disable-line camelcase +export class Label_1L_3Line extends DecoderPlugin { name = 'label-1l-3-line'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['1L'], }; @@ -65,7 +65,9 @@ export class Label_1L_3Line extends DecoderPlugin { // eslint-disable-line camel const day = data.get('DAY'); const utc = data.get('UTC'); if (day && utc) { - decodeResult.raw.message_timestamp = (Date.parse(day + ' GMT+0000') / 1000) + DateTimeUtils.convertHHMMSSToTod(utc); + decodeResult.raw.message_timestamp = + Date.parse(day + ' GMT+0000') / 1000 + + DateTimeUtils.convertHHMMSSToTod(utc); data.delete('DAY'); data.delete('UTC'); } @@ -74,8 +76,10 @@ export class Label_1L_3Line extends DecoderPlugin { // eslint-disable-line camel const lon = data.get('LON'); if (lat && lon) { ResultFormatter.position(decodeResult, { - latitude: CoordinateUtils.getDirection(lat[0]) * Number(lat.substring(1)), - longitude: CoordinateUtils.getDirection(lon[0]) * Number(lon.substring(1)), + latitude: + CoordinateUtils.getDirection(lat[0]) * Number(lat.substring(1)), + longitude: + CoordinateUtils.getDirection(lon[0]) * Number(lon.substring(1)), }); data.delete('LAT'); data.delete('LON'); diff --git a/lib/plugins/Label_1L_660.test.ts b/lib/plugins/Label_1L_660.test.ts index 86d9fa4..cfb3390 100644 --- a/lib/plugins/Label_1L_660.test.ts +++ b/lib/plugins/Label_1L_660.test.ts @@ -2,56 +2,55 @@ import { MessageDecoder } from '../MessageDecoder'; import { Label_1L_660 } from './Label_1L_660'; describe('Label_1L 660', () => { - let plugin: Label_1L_660; - const message = { label: '1L', text: '' }; + let plugin: Label_1L_660; + const message = { label: '1L', text: '' }; - beforeEach(() => { - const decoder = new MessageDecoder(); - plugin = new Label_1L_660(decoder); + beforeEach(() => { + const decoder = new MessageDecoder(); + plugin = new Label_1L_660(decoder); + }); + + test('matches qualifiers', () => { + expect(plugin.decode).toBeDefined(); + expect(plugin.name).toBe('label-1l-660'); + expect(plugin.qualifiers).toBeDefined(); + expect(plugin.qualifiers()).toEqual({ + labels: ['1L'], + preambles: ['000000660'], }); + }); - test('matches qualifiers', () => { - expect(plugin.decode).toBeDefined(); - expect(plugin.name).toBe('label-1l-660'); - expect(plugin.qualifiers).toBeDefined(); - expect(plugin.qualifiers()).toEqual({ - labels: ['1L'], - preambles: ['000000660'], - }); - }); + test('decodes variant 1', () => { + // https://app.airframes.io/messages/3492135103 + message.text = '000000660N50442E005566,100444359SOG-06 ,,--- 21-,83617441'; + const decodeResult = plugin.decode(message); - test('decodes variant 1', () => { - // https://app.airframes.io/messages/3492135103 - message.text = '000000660N50442E005566,100444359SOG-06 ,,--- 21-,83617441' - const decodeResult = plugin.decode(message); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.raw.position.latitude).toBe(50.736666666666665); + expect(decodeResult.raw.position.longitude).toBe(5.943333333333333); + expect(decodeResult.raw.time_of_day).toBe(36284); + expect(decodeResult.raw.altitude).toBe(35900); + expect(decodeResult.formatted.items.length).toBe(4); + expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); + expect(decodeResult.formatted.items[0].value).toBe('50.737 N, 5.943 E'); + expect(decodeResult.formatted.items[1].label).toBe('Message Timestamp'); + expect(decodeResult.formatted.items[1].value).toBe('10:04:44'); + expect(decodeResult.formatted.items[2].label).toBe('Altitude'); + expect(decodeResult.formatted.items[2].value).toBe('35900 feet'); + expect(decodeResult.formatted.items[3].label).toBe('Aircraft Route'); + expect(decodeResult.formatted.items[3].value).toBe('SOG-06'); + expect(decodeResult.remaining.text).toBe(',--- 21-,83617441'); + }); - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.raw.position.latitude).toBe(50.736666666666665); - expect(decodeResult.raw.position.longitude).toBe(5.943333333333333); - expect(decodeResult.raw.time_of_day).toBe(36284); - expect(decodeResult.raw.altitude).toBe(35900); - expect(decodeResult.formatted.items.length).toBe(4); - expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); - expect(decodeResult.formatted.items[0].value).toBe('50.737 N, 5.943 E'); - expect(decodeResult.formatted.items[1].label).toBe('Message Timestamp'); - expect(decodeResult.formatted.items[1].value).toBe('10:04:44'); - expect(decodeResult.formatted.items[2].label).toBe('Altitude'); - expect(decodeResult.formatted.items[2].value).toBe('35900 feet'); - expect(decodeResult.formatted.items[3].label).toBe('Aircraft Route'); - expect(decodeResult.formatted.items[3].value).toBe('SOG-06'); - expect(decodeResult.remaining.text).toBe(',--- 21-,83617441'); - }); + test('does not decode ', () => { + message.text = 'POS Bogus Message'; + const decodeResult = plugin.decode(message); - test('does not decode ', () => { - - message.text = 'POS Bogus Message'; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(false); - expect(decodeResult.decoder.decodeLevel).toBe('none'); - expect(decodeResult.decoder.name).toBe('label-1l-660'); - expect(decodeResult.formatted.description).toBe('Position Report'); - expect(decodeResult.message).toBe(message); - }); -}); \ No newline at end of file + expect(decodeResult.decoded).toBe(false); + expect(decodeResult.decoder.decodeLevel).toBe('none'); + expect(decodeResult.decoder.name).toBe('label-1l-660'); + expect(decodeResult.formatted.description).toBe('Position Report'); + expect(decodeResult.message).toBe(message); + }); +}); diff --git a/lib/plugins/Label_1L_660.ts b/lib/plugins/Label_1L_660.ts index bd7b628..469b3ed 100644 --- a/lib/plugins/Label_1L_660.ts +++ b/lib/plugins/Label_1L_660.ts @@ -4,10 +4,10 @@ import { DecodeResult, Message, Options } from '../DecoderPluginInterface'; import { CoordinateUtils } from '../utils/coordinate_utils'; import { ResultFormatter } from '../utils/result_formatter'; -export class Label_1L_660 extends DecoderPlugin { // eslint-disable-line camelcase +export class Label_1L_660 extends DecoderPlugin { name = 'label-1l-660'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['1L'], preambles: ['000000660'], @@ -30,7 +30,6 @@ export class Label_1L_660 extends DecoderPlugin { // eslint-disable-line camelca return decodeResult; } - const parts = message.text.substring(9).split(','); if (parts.length !== 5) { @@ -43,12 +42,17 @@ export class Label_1L_660 extends DecoderPlugin { // eslint-disable-line camelca return decodeResult; } - const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(parts[0]); + const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes( + parts[0], + ); if (position) { ResultFormatter.position(decodeResult, position); } const hhmmss = parts[1].substring(0, 6); - ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(hhmmss)); + ResultFormatter.time_of_day( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(hhmmss), + ); const fl = parts[1].substring(6, 9); ResultFormatter.altitude(decodeResult, Number(fl) * 100); const next = parts[1].substring(9); diff --git a/lib/plugins/Label_1L_Slash.test.ts b/lib/plugins/Label_1L_Slash.test.ts index 56d39fa..1226458 100644 --- a/lib/plugins/Label_1L_Slash.test.ts +++ b/lib/plugins/Label_1L_Slash.test.ts @@ -21,7 +21,8 @@ describe('Label_1L Slash', () => { }); test('decodes variant 1', () => { - message.text = '+ 39.126/- 77.358/UTC 085208/FOB 8.2/ALT 3997/CAS 239/ETA 0903' + message.text = + '+ 39.126/- 77.358/UTC 085208/FOB 8.2/ALT 3997/CAS 239/ETA 0903'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -42,13 +43,14 @@ describe('Label_1L Slash', () => { expect(decodeResult.formatted.items[2].value).toBe('3997 feet'); expect(decodeResult.formatted.items[3].label).toBe('Fuel On Board'); expect(decodeResult.formatted.items[3].value).toBe('8.2'); // tons? - expect(decodeResult.formatted.items[4].label).toBe('Estimated Time of Arrival'); + expect(decodeResult.formatted.items[4].label).toBe( + 'Estimated Time of Arrival', + ); expect(decodeResult.formatted.items[4].value).toBe('09:03:00'); expect(decodeResult.remaining.text).toBe('/CAS 239'); }); test('does not decode ', () => { - message.text = 'POS Bogus Message'; const decodeResult = plugin.decode(message); @@ -58,4 +60,4 @@ describe('Label_1L Slash', () => { expect(decodeResult.formatted.description).toBe('Position Report'); expect(decodeResult.message).toBe(message); }); -}); \ No newline at end of file +}); diff --git a/lib/plugins/Label_1L_Slash.ts b/lib/plugins/Label_1L_Slash.ts index d6f3071..92da318 100644 --- a/lib/plugins/Label_1L_Slash.ts +++ b/lib/plugins/Label_1L_Slash.ts @@ -3,10 +3,10 @@ import { DecoderPlugin } from '../DecoderPlugin'; import { DecodeResult, Message, Options } from '../DecoderPluginInterface'; import { ResultFormatter } from '../utils/result_formatter'; -export class Label_1L_Slash extends DecoderPlugin { // eslint-disable-line camelcase +export class Label_1L_Slash extends DecoderPlugin { name = 'label-1l-1-line'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['1L'], preambles: ['+', '-'], @@ -42,14 +42,17 @@ export class Label_1L_Slash extends DecoderPlugin { // eslint-disable-line camel const position = { latitude: Number(data.get('LAT')), longitude: Number(data.get('LON')), - } + }; data.delete('LAT'); data.delete('LON'); ResultFormatter.position(decodeResult, position); const utc = data.get('UTC'); if (utc) { - ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(utc)); + ResultFormatter.time_of_day( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(utc), + ); data.delete('UTC'); } const alt = data.get('ALT'); diff --git a/lib/plugins/Label_1M_Slash.test.ts b/lib/plugins/Label_1M_Slash.test.ts index 17fe779..042073c 100644 --- a/lib/plugins/Label_1M_Slash.test.ts +++ b/lib/plugins/Label_1M_Slash.test.ts @@ -10,50 +10,52 @@ describe('Label 1M Slash', () => { plugin = new Label_1M_Slash(decoder); }); -test('matches qualifiers', () => { - expect(plugin.decode).toBeDefined(); - expect(plugin.name).toBe('label-1m-slash'); - expect(plugin.qualifiers).toBeDefined(); - expect(plugin.qualifiers()).toEqual({ - labels: ['1M'], - preambles: ['/'], + test('matches qualifiers', () => { + expect(plugin.decode).toBeDefined(); + expect(plugin.name).toBe('label-1m-slash'); + expect(plugin.qualifiers).toBeDefined(); + expect(plugin.qualifiers()).toEqual({ + labels: ['1M'], + preambles: ['/'], + }); }); -}); -test('decodes variant 1', () => { - message.text = '/BA0843/ETA01/230822/LDSP/EGLL/EGSS/2JK0\n1940/EGLL27L/10'; - const decodeResult = plugin.decode(message); + test('decodes variant 1', () => { + message.text = '/BA0843/ETA01/230822/LDSP/EGLL/EGSS/2JK0\n1940/EGLL27L/10'; + const decodeResult = plugin.decode(message); - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-1m-slash'); - expect(decodeResult.formatted.description).toBe('ETA Report'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.raw.alternate_icao).toBe('EGSS'); - expect(decodeResult.raw.arrival_icao).toBe('EGLL'); - expect(decodeResult.raw.arrival_runway).toBe('27L'); - expect(decodeResult.raw.departure_icao).toBe('LDSP'); - expect(decodeResult.raw.flight_number).toBe('BA0843'); - expect(decodeResult.formatted.items.length).toBe(5); - expect(decodeResult.formatted.items[0].type).toBe('icao'); - expect(decodeResult.formatted.items[0].code).toBe('ORG'); - expect(decodeResult.formatted.items[0].label).toBe('Origin'); - expect(decodeResult.formatted.items[0].value).toBe('LDSP'); - expect(decodeResult.formatted.items[1].type).toBe('icao'); - expect(decodeResult.formatted.items[1].code).toBe('DST'); - expect(decodeResult.formatted.items[1].label).toBe('Destination'); - expect(decodeResult.formatted.items[1].value).toBe('EGLL'); - expect(decodeResult.formatted.items[2].type).toBe('icao'); - expect(decodeResult.formatted.items[2].code).toBe('ALT_DST'); - expect(decodeResult.formatted.items[2].label).toBe('Alternate Destination'); - expect(decodeResult.formatted.items[2].value).toBe('EGSS'); - expect(decodeResult.formatted.items[3].type).toBe('runway'); - expect(decodeResult.formatted.items[3].code).toBe('ARWY'); - expect(decodeResult.formatted.items[3].label).toBe('Arrival Runway'); - expect(decodeResult.formatted.items[3].value).toBe('27L'); - expect(decodeResult.formatted.items[4].type).toBe('epoch'); - expect(decodeResult.formatted.items[4].code).toBe('ETA'); - expect(decodeResult.formatted.items[4].label).toBe('Estimated Time of Arrival'); - expect(decodeResult.formatted.items[4].value).toBe('2023-08-22T19:40:00Z'); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-1m-slash'); + expect(decodeResult.formatted.description).toBe('ETA Report'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.raw.alternate_icao).toBe('EGSS'); + expect(decodeResult.raw.arrival_icao).toBe('EGLL'); + expect(decodeResult.raw.arrival_runway).toBe('27L'); + expect(decodeResult.raw.departure_icao).toBe('LDSP'); + expect(decodeResult.raw.flight_number).toBe('BA0843'); + expect(decodeResult.formatted.items.length).toBe(5); + expect(decodeResult.formatted.items[0].type).toBe('icao'); + expect(decodeResult.formatted.items[0].code).toBe('ORG'); + expect(decodeResult.formatted.items[0].label).toBe('Origin'); + expect(decodeResult.formatted.items[0].value).toBe('LDSP'); + expect(decodeResult.formatted.items[1].type).toBe('icao'); + expect(decodeResult.formatted.items[1].code).toBe('DST'); + expect(decodeResult.formatted.items[1].label).toBe('Destination'); + expect(decodeResult.formatted.items[1].value).toBe('EGLL'); + expect(decodeResult.formatted.items[2].type).toBe('icao'); + expect(decodeResult.formatted.items[2].code).toBe('ALT_DST'); + expect(decodeResult.formatted.items[2].label).toBe('Alternate Destination'); + expect(decodeResult.formatted.items[2].value).toBe('EGSS'); + expect(decodeResult.formatted.items[3].type).toBe('runway'); + expect(decodeResult.formatted.items[3].code).toBe('ARWY'); + expect(decodeResult.formatted.items[3].label).toBe('Arrival Runway'); + expect(decodeResult.formatted.items[3].value).toBe('27L'); + expect(decodeResult.formatted.items[4].type).toBe('epoch'); + expect(decodeResult.formatted.items[4].code).toBe('ETA'); + expect(decodeResult.formatted.items[4].label).toBe( + 'Estimated Time of Arrival', + ); + expect(decodeResult.formatted.items[4].value).toBe('2023-08-22T19:40:00Z'); + }); }); -}); \ No newline at end of file diff --git a/lib/plugins/Label_1M_Slash.ts b/lib/plugins/Label_1M_Slash.ts index a795935..7d972a3 100644 --- a/lib/plugins/Label_1M_Slash.ts +++ b/lib/plugins/Label_1M_Slash.ts @@ -6,14 +6,14 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_1M_Slash extends DecoderPlugin { name = 'label-1m-slash'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { - labels: ["1M"], + labels: ['1M'], preambles: ['/'], }; } - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { const decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; decodeResult.formatted.description = 'ETA Report'; @@ -24,7 +24,7 @@ export class Label_1M_Slash extends DecoderPlugin { if (results) { if (options.debug) { - console.log(`Label 1M ETA: results`); + console.log('Label 1M ETA: results'); console.log(results); } @@ -36,12 +36,23 @@ export class Label_1M_Slash extends DecoderPlugin { ResultFormatter.alternateAirport(decodeResult, results[5]); // results[6]: 2JK0 (???) // results[7] 1940 - UTC eta - ResultFormatter.arrivalRunway(decodeResult, results[8].replace(results[4], "")); // results[8] EGLL27L + ResultFormatter.arrivalRunway( + decodeResult, + results[8].replace(results[4], ''), + ); // results[8] EGLL27L // results[9]: 10(space) (???) const yymmdd = results[2]; - ResultFormatter.eta(decodeResult, DateTimeUtils.convertDateTimeToEpoch(results[7]+'00', yymmdd.substring(2,4)+yymmdd.substring(4,6)+yymmdd.substring(0,2)), 'epoch') - + ResultFormatter.eta( + decodeResult, + DateTimeUtils.convertDateTimeToEpoch( + results[7] + '00', + yymmdd.substring(2, 4) + + yymmdd.substring(4, 6) + + yymmdd.substring(0, 2), + ), + 'epoch', + ); } decodeResult.decoded = true; diff --git a/lib/plugins/Label_20_CFB.01.ts b/lib/plugins/Label_20_CFB.01.ts index 7cbbe86..8418d94 100644 --- a/lib/plugins/Label_20_CFB.01.ts +++ b/lib/plugins/Label_20_CFB.01.ts @@ -7,7 +7,7 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_20_CFB01 extends DecoderPlugin { name = 'label-20-cfb01'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['20'], preambles: ['#CFB.01'], @@ -22,30 +22,44 @@ export class Label_20_CFB01 extends DecoderPlugin { // Style: IN02,N38338W121179,KMHR,KPDX,0806,2355,005.1 // Match: IN02,coords,departure_icao,arrival_icao,current_date,current_time,fuel_in_tons - const regex = /^IN02,(?.*),(?.*),(?.*),(?.*),(?.*),(?.*)$/; + const regex = + /^IN02,(?.*),(?.*),(?.*),(?.*),(?.*),(?.*)$/; const results = message.text.match(regex); if (results?.groups) { if (options.debug) { - console.log(`Label 44 ETA Report: groups`); + console.log('Label 44 ETA Report: groups'); console.log(results.groups); } - ResultFormatter.position(decodeResult, CoordinateUtils.decodeStringCoordinates(results.groups.unsplit_coords)); - ResultFormatter.departureAirport(decodeResult, results.groups.departure_icao); + ResultFormatter.position( + decodeResult, + CoordinateUtils.decodeStringCoordinates(results.groups.unsplit_coords), + ); + ResultFormatter.departureAirport( + decodeResult, + results.groups.departure_icao, + ); ResultFormatter.arrivalAirport(decodeResult, results.groups.arrival_icao); decodeResult.raw.current_time = Date.parse( - new Date().getFullYear() + "-" + - results.groups.current_date.substr(0, 2) + "-" + - results.groups.current_date.substr(2, 2) + "T" + - results.groups.current_time.substr(0, 2) + ":" + - results.groups.current_time.substr(2, 2) + ":00Z" + new Date().getFullYear() + + '-' + + results.groups.current_date.substr(0, 2) + + '-' + + results.groups.current_date.substr(2, 2) + + 'T' + + results.groups.current_time.substr(0, 2) + + ':' + + results.groups.current_time.substr(2, 2) + + ':00Z', ); - if (results.groups.fuel_in_tons != '***' && results.groups.fuel_in_tons != '****') { + if ( + results.groups.fuel_in_tons != '***' && + results.groups.fuel_in_tons != '****' + ) { decodeResult.raw.fuel_in_tons = Number(results.groups.fuel_in_tons); } - } decodeResult.decoded = true; diff --git a/lib/plugins/Label_20_POS.test.ts b/lib/plugins/Label_20_POS.test.ts index d3a3faa..c91484c 100644 --- a/lib/plugins/Label_20_POS.test.ts +++ b/lib/plugins/Label_20_POS.test.ts @@ -3,7 +3,7 @@ import { Label_20_POS } from './Label_20_POS'; describe('Label_20_POS', () => { let plugin: Label_20_POS; - const message = {label: '20', text: ''}; + const message = { label: '20', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); @@ -21,11 +21,14 @@ describe('Label_20_POS', () => { expect(res.raw.preamble).toBe('POS'); expect(res.raw.position).toBeDefined(); - expect(res.raw.position.latitude).toBeCloseTo(38.160, 3); + expect(res.raw.position.latitude).toBeCloseTo(38.16, 3); expect(res.raw.position.longitude).toBeCloseTo(-77.075, 3); - const posItem = res.formatted.items.find(i => i.code === 'POS'); - if(!posItem) {expect(posItem).toBeDefined(); return;} + const posItem = res.formatted.items.find((i) => i.code === 'POS'); + if (!posItem) { + expect(posItem).toBeDefined(); + return; + } expect(posItem.value).toContain('38.160'); expect(posItem.value).toContain('77.075'); }); @@ -41,11 +44,14 @@ describe('Label_20_POS', () => { expect(res.raw.preamble).toBe('POS'); expect(res.raw.position).toBeDefined(); - expect(res.raw.position.latitude).toBeCloseTo(38.160, 3); + expect(res.raw.position.latitude).toBeCloseTo(38.16, 3); expect(res.raw.position.longitude).toBeCloseTo(-77.075, 3); - const posItem = res.formatted.items.find(i => i.code === 'POS'); - if(!posItem) {expect(posItem).toBeDefined(); return;} + const posItem = res.formatted.items.find((i) => i.code === 'POS'); + if (!posItem) { + expect(posItem).toBeDefined(); + return; + } expect(posItem.value).toContain('38.160'); expect(posItem.value).toContain('77.075'); }); diff --git a/lib/plugins/Label_20_POS.ts b/lib/plugins/Label_20_POS.ts index 362f0e3..620665f 100644 --- a/lib/plugins/Label_20_POS.ts +++ b/lib/plugins/Label_20_POS.ts @@ -7,7 +7,7 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_20_POS extends DecoderPlugin { name = 'label-20-pos'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['20'], preambles: ['POS'], @@ -32,7 +32,10 @@ export class Label_20_POS extends DecoderPlugin { } // Field 1: Coordinates const rawCoords = fields[0]; - ResultFormatter.position(decodeResult, CoordinateUtils.decodeStringCoordinates(rawCoords)); + ResultFormatter.position( + decodeResult, + CoordinateUtils.decodeStringCoordinates(rawCoords), + ); decodeResult.decoded = true; decodeResult.decoder.decodeLevel = 'full'; @@ -51,7 +54,9 @@ export class Label_20_POS extends DecoderPlugin { } else { // Unknown! if (options.debug) { - console.log(`DEBUG: ${this.name}: Unknown variation. Field count: ${fields.length}, content: ${content}`); + console.log( + `DEBUG: ${this.name}: Unknown variation. Field count: ${fields.length}, content: ${content}`, + ); } decodeResult.decoded = false; decodeResult.decoder.decodeLevel = 'none'; diff --git a/lib/plugins/Label_21_POS.test.ts b/lib/plugins/Label_21_POS.test.ts index 436bf39..6d02220 100644 --- a/lib/plugins/Label_21_POS.test.ts +++ b/lib/plugins/Label_21_POS.test.ts @@ -2,67 +2,72 @@ import { MessageDecoder } from '../MessageDecoder'; import { Label_21_POS } from './Label_21_POS'; describe('Label_21_POS', () => { - let plugin: Label_21_POS; - const message = {label: '21', text: ''}; + let plugin: Label_21_POS; + const message = { label: '21', text: '' }; - beforeEach(() => { - const decoder = new MessageDecoder(); - plugin = new Label_21_POS(decoder); + beforeEach(() => { + const decoder = new MessageDecoder(); + plugin = new Label_21_POS(decoder); + }); + + test('matches qualifiers', () => { + expect(plugin.decode).toBeDefined(); + expect(plugin.name).toBe('label-21-pos'); + expect(plugin.qualifiers).toBeDefined(); + expect(plugin.qualifiers()).toEqual({ + labels: ['21'], + preambles: ['POS'], }); + }); - test('matches qualifiers', () => { - expect(plugin.decode).toBeDefined(); - expect(plugin.name).toBe('label-21-pos'); - expect(plugin.qualifiers).toBeDefined(); - expect(plugin.qualifiers()).toEqual({ - labels: ['21'], - preambles: ['POS'], - }); - }); - + test('decodes valid', () => { + message.text = + 'POSN 39.841W 75.790, 220,184218,17222,22051, 34,- 4,204748,KTPA'; + const decodeResult = plugin.decode(message); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.formatted.items.length).toBe(6); + expect(decodeResult.formatted.items[0].type).toBe('aircraft_position'); + expect(decodeResult.formatted.items[0].code).toBe('POS'); + expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); + expect(decodeResult.formatted.items[0].value).toBe('39.840 N, 75.790 W'); + expect(decodeResult.formatted.items[1].type).toBe('time_of_day'); + expect(decodeResult.formatted.items[1].code).toBe('MSG_TOD'); + expect(decodeResult.formatted.items[1].label).toBe('Message Timestamp'); + expect(decodeResult.formatted.items[1].value).toBe('18:42:18'); + expect(decodeResult.formatted.items[2].type).toBe('altitude'); + expect(decodeResult.formatted.items[2].code).toBe('ALT'); + expect(decodeResult.formatted.items[2].label).toBe('Altitude'); + expect(decodeResult.formatted.items[2].value).toBe('17222 feet'); + expect(decodeResult.formatted.items[3].type).toBe( + 'outside_air_temperature', + ); + expect(decodeResult.formatted.items[3].code).toBe('OATEMP'); + expect(decodeResult.formatted.items[3].label).toBe( + 'Outside Air Temperature (C)', + ); + expect(decodeResult.formatted.items[3].value).toBe('-4 degrees'); + expect(decodeResult.formatted.items[4].type).toBe('time_of_day'); + expect(decodeResult.formatted.items[4].code).toBe('ETA'); + expect(decodeResult.formatted.items[4].label).toBe( + 'Estimated Time of Arrival', + ); + expect(decodeResult.formatted.items[4].value).toBe('20:47:48'); + expect(decodeResult.formatted.items[5].type).toBe('icao'); + expect(decodeResult.formatted.items[5].code).toBe('DST'); + expect(decodeResult.formatted.items[5].label).toBe('Destination'); + expect(decodeResult.formatted.items[5].value).toBe('KTPA'); + expect(decodeResult.remaining.text).toBe(' 220,22051, 34'); + }); - test('decodes valid', () => { - message.text = 'POSN 39.841W 75.790, 220,184218,17222,22051, 34,- 4,204748,KTPA' - const decodeResult = plugin.decode(message); - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.formatted.items.length).toBe(6); - expect(decodeResult.formatted.items[0].type).toBe('aircraft_position'); - expect(decodeResult.formatted.items[0].code).toBe('POS'); - expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); - expect(decodeResult.formatted.items[0].value).toBe('39.840 N, 75.790 W'); - expect(decodeResult.formatted.items[1].type).toBe('time_of_day'); - expect(decodeResult.formatted.items[1].code).toBe('MSG_TOD'); - expect(decodeResult.formatted.items[1].label).toBe('Message Timestamp'); - expect(decodeResult.formatted.items[1].value).toBe('18:42:18'); - expect(decodeResult.formatted.items[2].type).toBe('altitude'); - expect(decodeResult.formatted.items[2].code).toBe('ALT'); - expect(decodeResult.formatted.items[2].label).toBe('Altitude'); - expect(decodeResult.formatted.items[2].value).toBe('17222 feet'); - expect(decodeResult.formatted.items[3].type).toBe('outside_air_temperature'); - expect(decodeResult.formatted.items[3].code).toBe('OATEMP'); - expect(decodeResult.formatted.items[3].label).toBe('Outside Air Temperature (C)'); - expect(decodeResult.formatted.items[3].value).toBe('-4 degrees'); - expect(decodeResult.formatted.items[4].type).toBe('time_of_day'); - expect(decodeResult.formatted.items[4].code).toBe('ETA'); - expect(decodeResult.formatted.items[4].label).toBe('Estimated Time of Arrival'); - expect(decodeResult.formatted.items[4].value).toBe('20:47:48'); - expect(decodeResult.formatted.items[5].type).toBe('icao'); - expect(decodeResult.formatted.items[5].code).toBe('DST'); - expect(decodeResult.formatted.items[5].label).toBe('Destination'); - expect(decodeResult.formatted.items[5].value).toBe('KTPA'); - expect(decodeResult.remaining.text).toBe(' 220,22051, 34'); - }); + test('does not decode invalid', () => { + message.text = 'POS Bogus message'; + const decodeResult = plugin.decode(message); - test('does not decode invalid', () => { - - message.text = 'POS Bogus message'; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(false); - expect(decodeResult.decoder.decodeLevel).toBe('none'); - expect(decodeResult.decoder.name).toBe('label-21-pos'); - expect(decodeResult.formatted.description).toBe('Position Report'); - expect(decodeResult.message).toBe(message); - }); + expect(decodeResult.decoded).toBe(false); + expect(decodeResult.decoder.decodeLevel).toBe('none'); + expect(decodeResult.decoder.name).toBe('label-21-pos'); + expect(decodeResult.formatted.description).toBe('Position Report'); + expect(decodeResult.message).toBe(message); + }); }); diff --git a/lib/plugins/Label_21_POS.ts b/lib/plugins/Label_21_POS.ts index 642e3f9..33caab9 100644 --- a/lib/plugins/Label_21_POS.ts +++ b/lib/plugins/Label_21_POS.ts @@ -1,21 +1,20 @@ import { DateTimeUtils } from '../DateTimeUtils'; import { DecoderPlugin } from '../DecoderPlugin'; import { DecodeResult, Message, Options } from '../DecoderPluginInterface'; -import { CoordinateUtils } from '../utils/coordinate_utils'; import { ResultFormatter } from '../utils/result_formatter'; // Position Report export class Label_21_POS extends DecoderPlugin { name = 'label-21-pos'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['21'], preambles: ['POS'], }; } - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { const decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; decodeResult.formatted.description = 'Position Report'; @@ -29,20 +28,32 @@ export class Label_21_POS extends DecoderPlugin { if (fields.length == 9) { // POSN 37.550W 76.436, 98,110800,23961,25820, 65,-23,114212,KRDU processPosition(decodeResult, fields[0].trim()); - ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[2])); - ResultFormatter.altitude( decodeResult, Number(fields[3])); - ResultFormatter.temperature(decodeResult, fields[6].replace(/ /g, "")); - ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[7])); + ResultFormatter.time_of_day( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(fields[2]), + ); + ResultFormatter.altitude(decodeResult, Number(fields[3])); + ResultFormatter.temperature(decodeResult, fields[6].replace(/ /g, '')); + ResultFormatter.eta( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(fields[7]), + ); ResultFormatter.arrivalAirport(decodeResult, fields[8]); - ResultFormatter.unknownArr(decodeResult, [fields[1], fields[4], fields[5]]); + ResultFormatter.unknownArr(decodeResult, [ + fields[1], + fields[4], + fields[5], + ]); decodeResult.decoded = true; decodeResult.decoder.decodeLevel = 'partial'; } else { // Unknown! - if(options.debug) { - console.log(`DEBUG: ${this.name}: Unknown variation. Field count: ${fields.length}, content: ${content}`); + if (options.debug) { + console.log( + `DEBUG: ${this.name}: Unknown variation. Field count: ${fields.length}, content: ${content}`, + ); } decodeResult.decoded = false; decodeResult.decoder.decodeLevel = 'none'; @@ -52,16 +63,21 @@ export class Label_21_POS extends DecoderPlugin { } function processPosition(decodeResult: DecodeResult, value: string) { // N 39.841W 75.790 - if(value.length !== 16 && value[0] !== 'N' && value[0] !== 'S' && value[8] !== 'W' && value[8] !== 'E') { + if ( + value.length !== 16 && + value[0] !== 'N' && + value[0] !== 'S' && + value[8] !== 'W' && + value[8] !== 'E' + ) { return; } const latDir = value[0] === 'N' ? 1 : -1; const lonDir = value[8] === 'E' ? 1 : -1; - const position ={ + const position = { latitude: latDir * Number(value.substring(1, 7)), longitude: lonDir * Number(value.substring(9, 15)), }; - + ResultFormatter.position(decodeResult, position); } - diff --git a/lib/plugins/Label_22_OFF.test.ts b/lib/plugins/Label_22_OFF.test.ts index ca806bc..806c7de 100644 --- a/lib/plugins/Label_22_OFF.test.ts +++ b/lib/plugins/Label_22_OFF.test.ts @@ -3,7 +3,7 @@ import { Label_22_OFF } from './Label_22_OFF'; describe('Label 22 OFF', () => { let plugin: Label_22_OFF; - const message = {label: '22', text: ''}; + const message = { label: '22', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); @@ -21,7 +21,7 @@ describe('Label 22 OFF', () => { }); test('decodes variant 1', () => { - message.text = 'OFF01YX3661/25251712KIADKPWM171207 92' + message.text = 'OFF01YX3661/25251712KIADKPWM171207 92'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); expect(decodeResult.decoder.decodeLevel).toBe('partial'); @@ -51,7 +51,7 @@ describe('Label 22 OFF', () => { }); test('decodes variant 2', () => { - message.text = 'OFF02XA0000/N38568 W077261251152KIADEPRZ1152****1958' + message.text = 'OFF02XA0000/N38568 W077261251152KIADEPRZ1152****1958'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); expect(decodeResult.decoder.decodeLevel).toBe('partial'); @@ -77,13 +77,15 @@ describe('Label 22 OFF', () => { expect(decodeResult.formatted.items[5].value).toBe('EPRZ'); expect(decodeResult.formatted.items[6].label).toBe('Takeoff Time'); expect(decodeResult.formatted.items[6].value).toBe('11:52:00'); - expect(decodeResult.formatted.items[7].label).toBe('Estimated Time of Arrival'); + expect(decodeResult.formatted.items[7].label).toBe( + 'Estimated Time of Arrival', + ); expect(decodeResult.formatted.items[7].value).toBe('19:58:00'); expect(decodeResult.remaining.text).toBe('****'); }); test('decodes variant 3', () => { - message.text = 'OFF02\r\nKBWI,KIND,1237,18.4' + message.text = 'OFF02\r\nKBWI,KIND,1237,18.4'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); expect(decodeResult.decoder.decodeLevel).toBe('partial'); @@ -101,7 +103,6 @@ describe('Label 22 OFF', () => { }); test('does not decode invalid', () => { - message.text = 'POS Bogus message'; const decodeResult = plugin.decode(message); diff --git a/lib/plugins/Label_22_OFF.ts b/lib/plugins/Label_22_OFF.ts index d6c11a5..c144d4d 100644 --- a/lib/plugins/Label_22_OFF.ts +++ b/lib/plugins/Label_22_OFF.ts @@ -8,7 +8,7 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_22_OFF extends DecoderPlugin { name = 'label-22-off'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['22'], preambles: ['OFF'], @@ -21,7 +21,8 @@ export class Label_22_OFF extends DecoderPlugin { decodeResult.formatted.description = 'Takeoff Report'; decodeResult.message = message; - if (message.text.startsWith('OFF01')) { // variant 1 + if (message.text.startsWith('OFF01')) { + // variant 1 const fields = message.text.substring(5).split('/'); if (fields.length != 2) { @@ -31,18 +32,33 @@ export class Label_22_OFF extends DecoderPlugin { } ResultFormatter.flightNumber(decodeResult, fields[0]); - ResultFormatter.departureDay(decodeResult, Number(fields[1].substring(0, 2))); // departure day - ResultFormatter.arrivalDay(decodeResult, Number(fields[1].substring(2,4))); // arrival day - ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[1].substring(4, 8))); - ResultFormatter.departureAirport(decodeResult, fields[1].substring(8, 12)); + ResultFormatter.departureDay( + decodeResult, + Number(fields[1].substring(0, 2)), + ); // departure day + ResultFormatter.arrivalDay( + decodeResult, + Number(fields[1].substring(2, 4)), + ); // arrival day + ResultFormatter.time_of_day( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(fields[1].substring(4, 8)), + ); + ResultFormatter.departureAirport( + decodeResult, + fields[1].substring(8, 12), + ); ResultFormatter.arrivalAirport(decodeResult, fields[1].substring(12, 16)); - ResultFormatter.off(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[1].substring(16,22))); + ResultFormatter.off( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(fields[1].substring(16, 22)), + ); ResultFormatter.unknown(decodeResult, fields[1].substring(22)); decodeResult.decoded = true; decodeResult.decoder.decodeLevel = 'partial'; - - } else if (message.text.startsWith('OFF02\r\n')) { // varaint 3 + } else if (message.text.startsWith('OFF02\r\n')) { + // varaint 3 const fields = message.text.substring(7).split(','); if (fields.length != 4) { decodeResult.decoded = false; @@ -52,13 +68,16 @@ export class Label_22_OFF extends DecoderPlugin { ResultFormatter.departureAirport(decodeResult, fields[0]); ResultFormatter.arrivalAirport(decodeResult, fields[1]); - ResultFormatter.off(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[2])); + ResultFormatter.off( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(fields[2]), + ); ResultFormatter.unknown(decodeResult, fields[3]); decodeResult.decoded = true; decodeResult.decoder.decodeLevel = 'partial'; - - } else if (message.text.startsWith('OFF02')) { // varaint 2 + } else if (message.text.startsWith('OFF02')) { + // varaint 2 const fields = message.text.substring(5).split('/'); if (fields.length != 2) { decodeResult.decoded = false; @@ -67,21 +86,33 @@ export class Label_22_OFF extends DecoderPlugin { } ResultFormatter.flightNumber(decodeResult, fields[0]); - const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(fields[1].substring(0, 14)); + const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes( + fields[1].substring(0, 14), + ); if (position) { ResultFormatter.position(decodeResult, position); } ResultFormatter.day(decodeResult, Number(fields[1].substring(14, 16))); - ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[1].substring(16, 20) )); - ResultFormatter.departureAirport(decodeResult, fields[1].substring(20, 24)); + ResultFormatter.time_of_day( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(fields[1].substring(16, 20)), + ); + ResultFormatter.departureAirport( + decodeResult, + fields[1].substring(20, 24), + ); ResultFormatter.arrivalAirport(decodeResult, fields[1].substring(24, 28)); - ResultFormatter.off(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[1].substring(28, 32))); + ResultFormatter.off( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(fields[1].substring(28, 32)), + ); ResultFormatter.unknown(decodeResult, fields[1].substring(32, 36)); - ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[1].substring(36,40))); + ResultFormatter.eta( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(fields[1].substring(36, 40)), + ); decodeResult.decoded = true; decodeResult.decoder.decodeLevel = 'partial'; - - } else { if (options.debug) { console.log(`DEBUG: ${this.name}: Unknown variation.`); diff --git a/lib/plugins/Label_22_POS.test.ts b/lib/plugins/Label_22_POS.test.ts index 893ecd2..45b6c18 100644 --- a/lib/plugins/Label_22_POS.test.ts +++ b/lib/plugins/Label_22_POS.test.ts @@ -3,7 +3,7 @@ import { Label_22_POS } from './Label_22_POS'; describe('Label 22', () => { let plugin: Label_22_POS; - const message = {label: '22', text: ''}; + const message = { label: '22', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); @@ -21,7 +21,8 @@ describe('Label 22', () => { }); test('decodes valid', () => { - message.text = 'N 370824W 760010,-------,194936,30418, , , ,M 42,27335 42, 107,' + message.text = + 'N 370824W 760010,-------,194936,30418, , , ,M 42,27335 42, 107,'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); expect(decodeResult.decoder.decodeLevel).toBe('partial'); @@ -36,11 +37,12 @@ describe('Label 22', () => { expect(decodeResult.formatted.items[1].value).toBe('19:49:36'); expect(decodeResult.formatted.items[2].label).toBe('Altitude'); expect(decodeResult.formatted.items[2].value).toBe('30418 feet'); - expect(decodeResult.remaining.text).toBe('-------, , , ,M 42,27335 42, 107,'); + expect(decodeResult.remaining.text).toBe( + '-------, , , ,M 42,27335 42, 107,', + ); }); test('does not decode invalid', () => { - message.text = 'POS Bogus message'; const decodeResult = plugin.decode(message); diff --git a/lib/plugins/Label_22_POS.ts b/lib/plugins/Label_22_POS.ts index b0841bd..c6b12e8 100644 --- a/lib/plugins/Label_22_POS.ts +++ b/lib/plugins/Label_22_POS.ts @@ -8,7 +8,7 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_22_POS extends DecoderPlugin { name = 'label-22-pos'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['22'], preambles: ['N', 'S'], @@ -25,7 +25,9 @@ export class Label_22_POS extends DecoderPlugin { if (fields.length !== 11) { if (options.debug) { - console.log(`DEBUG: ${this.name}: Unknown variation. Field count: ${fields.length}, content: ${fields.join(',')}`); + console.log( + `DEBUG: ${this.name}: Unknown variation. Field count: ${fields.length}, content: ${fields.join(',')}`, + ); } decodeResult.decoded = false; decodeResult.decoder.decodeLevel = 'none'; @@ -41,7 +43,10 @@ export class Label_22_POS extends DecoderPlugin { longitude: CoordinateUtils.getDirection(fields[0][8]) * lon, }); - ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[2])); + ResultFormatter.time_of_day( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(fields[2]), + ); ResultFormatter.altitude(decodeResult, Number(fields[3])); ResultFormatter.unknownArr(decodeResult, [fields[1], ...fields.slice(4)]); diff --git a/lib/plugins/Label_24_Slash.test.ts b/lib/plugins/Label_24_Slash.test.ts index 60749d8..1ab4f11 100644 --- a/lib/plugins/Label_24_Slash.test.ts +++ b/lib/plugins/Label_24_Slash.test.ts @@ -3,7 +3,7 @@ import { Label_24_Slash } from './Label_24_Slash'; describe('Label_24_Slash', () => { let plugin: Label_24_Slash; - const message = {label: '24', text: ''}; + const message = { label: '24', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); @@ -20,7 +20,6 @@ describe('Label_24_Slash', () => { }); }); - test('valid', () => { // https://app.airframes.io/messages/3439806391 message.text = '/241710/1021/04WM/34962/N53.13/E001.33/3374/1056/'; @@ -40,14 +39,15 @@ describe('Label_24_Slash', () => { expect(decodeResult.formatted.items[1].value).toBe('34962 feet'); expect(decodeResult.formatted.items[2].label).toBe('Aircraft Position'); expect(decodeResult.formatted.items[2].value).toBe('53.130 N, 1.330 E'); - expect(decodeResult.formatted.items[3].label).toBe('Estimated Time of Arrival'); + expect(decodeResult.formatted.items[3].label).toBe( + 'Estimated Time of Arrival', + ); expect(decodeResult.formatted.items[3].value).toBe('10:56:00'); expect(decodeResult.remaining.text).toBe('3374'); }); test('does not decode invalid', () => { - message.text = '/ Bogus message'; const decodeResult = plugin.decode(message); diff --git a/lib/plugins/Label_24_Slash.ts b/lib/plugins/Label_24_Slash.ts index 89a4f63..0e5bc1f 100644 --- a/lib/plugins/Label_24_Slash.ts +++ b/lib/plugins/Label_24_Slash.ts @@ -7,14 +7,14 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_24_Slash extends DecoderPlugin { name = 'label-24-slash'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['24'], preambles: ['/'], }; } - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { const decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; decodeResult.formatted.description = 'Position Report'; @@ -22,10 +22,17 @@ export class Label_24_Slash extends DecoderPlugin { const fields = message.text.split('/'); - if (fields.length == 10 && fields[0] == '' && fields[9] == '') { // begin and ends with `/` - const mmddyy = fields[1].substring(4,6) + fields[1].substring(2,4) + fields[1].substring(0,2); // YYDDMM + if (fields.length == 10 && fields[0] == '' && fields[9] == '') { + // begin and ends with `/` + const mmddyy = + fields[1].substring(4, 6) + + fields[1].substring(2, 4) + + fields[1].substring(0, 2); // YYDDMM const hhmmss = fields[2] + '00'; - decodeResult.raw.message_timestamp = DateTimeUtils.convertDateTimeToEpoch(hhmmss,mmddyy); + decodeResult.raw.message_timestamp = DateTimeUtils.convertDateTimeToEpoch( + hhmmss, + mmddyy, + ); ResultFormatter.flightNumber(decodeResult, fields[3]); ResultFormatter.altitude(decodeResult, Number(fields[4])); const lat = fields[5]; @@ -35,15 +42,20 @@ export class Label_24_Slash extends DecoderPlugin { longitude: (lon[0] === 'E' ? 1 : -1) * Number(lon.substring(1)), }; ResultFormatter.position(decodeResult, position); - ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[8])); + ResultFormatter.eta( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(fields[8]), + ); ResultFormatter.unknown(decodeResult, fields[7]); decodeResult.decoded = true; decodeResult.decoder.decodeLevel = 'partial'; } else { // Unknown! - if(options.debug) { - console.log(`DEBUG: ${this.name}: Unknown variation. Field count: ${fields.length}. Message: ${message.text}`); + if (options.debug) { + console.log( + `DEBUG: ${this.name}: Unknown variation. Field count: ${fields.length}. Message: ${message.text}`, + ); } decodeResult.decoded = false; decodeResult.decoder.decodeLevel = 'none'; @@ -51,4 +63,3 @@ export class Label_24_Slash extends DecoderPlugin { return decodeResult; } } - diff --git a/lib/plugins/Label_2P_FM3.test.ts b/lib/plugins/Label_2P_FM3.test.ts index 6d088d7..921e4bd 100644 --- a/lib/plugins/Label_2P_FM3.test.ts +++ b/lib/plugins/Label_2P_FM3.test.ts @@ -2,7 +2,6 @@ import { MessageDecoder } from '../MessageDecoder'; import { Label_2P_FM3 } from './Label_2P_FM3'; describe('Label 2P Preamble FM3', () => { - let plugin: Label_2P_FM3; const message = { label: '2P', text: '' }; @@ -22,7 +21,9 @@ describe('Label 2P Preamble FM3', () => { expect(decodeResult.formatted.items.length).toBe(4); expect(decodeResult.formatted.items[0].label).toBe('Message Timestamp'); expect(decodeResult.formatted.items[0].value).toBe('12:17:00'); - expect(decodeResult.formatted.items[1].label).toBe('Estimated Time of Arrival'); + expect(decodeResult.formatted.items[1].label).toBe( + 'Estimated Time of Arrival', + ); expect(decodeResult.formatted.items[1].value).toBe('13:12:00'); expect(decodeResult.formatted.items[2].label).toBe('Aircraft Position'); expect(decodeResult.formatted.items[2].value).toBe('43.770 N, 70.180 W'); @@ -44,7 +45,9 @@ describe('Label 2P Preamble FM3', () => { expect(decodeResult.formatted.items[0].value).toBe('EY093C'); expect(decodeResult.formatted.items[1].label).toBe('Message Timestamp'); expect(decodeResult.formatted.items[1].value).toBe('12:16:00'); - expect(decodeResult.formatted.items[2].label).toBe('Estimated Time of Arrival'); + expect(decodeResult.formatted.items[2].label).toBe( + 'Estimated Time of Arrival', + ); expect(decodeResult.formatted.items[2].value).toBe('14:54:00'); expect(decodeResult.formatted.items[3].label).toBe('Aircraft Position'); expect(decodeResult.formatted.items[3].value).toBe('57.310 N, 75.580 W'); @@ -58,7 +61,6 @@ describe('Label 2P Preamble FM3', () => { message.text = 'FM3 133818,1607,N 45.206,E 17.726,34030, 440,98'; const decodeResult = plugin.decode(message); - expect(decodeResult.decoded).toBe(true); expect(decodeResult.decoder.decodeLevel).toBe('partial'); expect(decodeResult.formatted.description).toBe('Flight Report'); @@ -66,7 +68,9 @@ describe('Label 2P Preamble FM3', () => { expect(decodeResult.formatted.items.length).toBe(4); expect(decodeResult.formatted.items[0].label).toBe('Message Timestamp'); expect(decodeResult.formatted.items[0].value).toBe('13:38:18'); - expect(decodeResult.formatted.items[1].label).toBe('Estimated Time of Arrival'); + expect(decodeResult.formatted.items[1].label).toBe( + 'Estimated Time of Arrival', + ); expect(decodeResult.formatted.items[1].value).toBe('16:07:00'); expect(decodeResult.formatted.items[2].label).toBe('Aircraft Position'); expect(decodeResult.formatted.items[2].value).toBe('45.206 N, 17.726 E'); @@ -76,7 +80,6 @@ describe('Label 2P Preamble FM3', () => { }); test('', () => { - message.text = 'FM4 Bogus message'; const decodeResult = plugin.decode(message); diff --git a/lib/plugins/Label_2P_FM3.ts b/lib/plugins/Label_2P_FM3.ts index f4d8a16..55ebd3d 100644 --- a/lib/plugins/Label_2P_FM3.ts +++ b/lib/plugins/Label_2P_FM3.ts @@ -7,13 +7,13 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_2P_FM3 extends DecoderPlugin { name = 'label-2p-fm3'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { - labels: ["2P"], + labels: ['2P'], }; } - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { let decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; decodeResult.formatted.description = 'Flight Report'; @@ -21,10 +21,9 @@ export class Label_2P_FM3 extends DecoderPlugin { const parts = message.text.split(','); - if(parts.length === 7){ - + if (parts.length === 7) { const header = parts[0].split('FM3 '); - if(header.length == 0) { + if (header.length == 0) { // can't use preambles, as there can be info before `FM4` // so let's check if we want to decode it here ResultFormatter.unknown(decodeResult, message.text); @@ -33,27 +32,41 @@ export class Label_2P_FM3 extends DecoderPlugin { return decodeResult; } - if(header[0].length > 0) { - ResultFormatter.unknown(decodeResult, header[0].substring(0,4)); + if (header[0].length > 0) { + ResultFormatter.unknown(decodeResult, header[0].substring(0, 4)); ResultFormatter.flightNumber(decodeResult, header[0].substring(4)); } - if(header[1].length === 4) { - ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(header[1])); + if (header[1].length === 4) { + ResultFormatter.time_of_day( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(header[1]), + ); } else { - ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(header[1])); + ResultFormatter.time_of_day( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(header[1]), + ); + } + ResultFormatter.eta( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(parts[1]), + ); + const lat = parts[2].replaceAll(' ', ''); + const lon = parts[3].replaceAll(' ', ''); + if (lat[0] === 'N' || lat[0] === 'S') { + ResultFormatter.position(decodeResult, { + latitude: + CoordinateUtils.getDirection(lat[0]) * Number(lat.substring(1)), + longitude: + CoordinateUtils.getDirection(lon[0]) * Number(lon.substring(1)), + }); + } else { + ResultFormatter.position(decodeResult, { + latitude: Number(lat), + longitude: Number(lon), + }); } - ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(parts[1])); - const lat = parts[2].replaceAll(' ',''); - const lon = parts[3].replaceAll(' ',''); - if(lat[0] === 'N' || lat[0] === 'S') { - ResultFormatter.position(decodeResult, { - latitude: CoordinateUtils.getDirection(lat[0]) * Number(lat.substring(1)), - longitude: CoordinateUtils.getDirection(lon[0]) * Number(lon.substring(1)), - }); - } else { - ResultFormatter.position(decodeResult, {latitude: Number(lat), longitude: Number(lon)}); - } ResultFormatter.altitude(decodeResult, Number(parts[4])); // TODO: decode further ResultFormatter.unknown(decodeResult, parts[5]); @@ -61,7 +74,7 @@ export class Label_2P_FM3 extends DecoderPlugin { decodeResult.decoded = true; decodeResult.decoder.decodeLevel = 'partial'; - } else { + } else { // Unknown if (options.debug) { console.log(`Decoder: Unknown H1 message: ${message.text}`); @@ -71,6 +84,6 @@ export class Label_2P_FM3 extends DecoderPlugin { decodeResult.decoder.decodeLevel = 'none'; } - return decodeResult; + return decodeResult; } } diff --git a/lib/plugins/Label_2P_FM4.test.ts b/lib/plugins/Label_2P_FM4.test.ts index c3d0104..b6382c8 100644 --- a/lib/plugins/Label_2P_FM4.test.ts +++ b/lib/plugins/Label_2P_FM4.test.ts @@ -2,7 +2,6 @@ import { MessageDecoder } from '../MessageDecoder'; import { Label_2P_FM4 } from './Label_2P_FM4'; describe('Label_2P Preamble FM4', () => { - let plugin: Label_2P_FM4; const message = { label: '2P', text: '' }; @@ -13,7 +12,8 @@ describe('Label_2P Preamble FM4', () => { test('variant 1', () => { // https://app.airframes.io/messages/4206449201 - message.text = 'FM4KIAD,OMAA,140256,1448, 39.43,- 75.62,23228,328, 43.5, 72500'; + message.text = + 'FM4KIAD,OMAA,140256,1448, 39.43,- 75.62,23228,328, 43.5, 72500'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -29,7 +29,9 @@ describe('Label_2P Preamble FM4', () => { expect(decodeResult.formatted.items[2].value).toBe('14'); expect(decodeResult.formatted.items[3].label).toBe('Message Timestamp'); expect(decodeResult.formatted.items[3].value).toBe('02:56:00'); - expect(decodeResult.formatted.items[4].label).toBe('Estimated Time of Arrival'); + expect(decodeResult.formatted.items[4].label).toBe( + 'Estimated Time of Arrival', + ); expect(decodeResult.formatted.items[4].value).toBe('14:48:00'); expect(decodeResult.formatted.items[5].label).toBe('Aircraft Position'); expect(decodeResult.formatted.items[5].value).toBe('39.430 N, 75.620 W'); @@ -42,7 +44,8 @@ describe('Label_2P Preamble FM4', () => { test('variant 2', () => { // https://app.airframes.io/messages/4209103135 - message.text = 'M58AEY0801FM4RJAA,OMAA,141234,2105, 38.92, 115.44,34099,296,-105.5, 52800'; + message.text = + 'M58AEY0801FM4RJAA,OMAA,141234,2105, 38.92, 115.44,34099,296,-105.5, 52800'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -60,7 +63,9 @@ describe('Label_2P Preamble FM4', () => { expect(decodeResult.formatted.items[3].value).toBe('14'); expect(decodeResult.formatted.items[4].label).toBe('Message Timestamp'); expect(decodeResult.formatted.items[4].value).toBe('12:34:00'); - expect(decodeResult.formatted.items[5].label).toBe('Estimated Time of Arrival'); + expect(decodeResult.formatted.items[5].label).toBe( + 'Estimated Time of Arrival', + ); expect(decodeResult.formatted.items[5].value).toBe('21:05:00'); expect(decodeResult.formatted.items[6].label).toBe('Aircraft Position'); expect(decodeResult.formatted.items[6].value).toBe('38.920 N, 115.440 E'); @@ -72,7 +77,6 @@ describe('Label_2P Preamble FM4', () => { }); test('', () => { - message.text = 'FM4 Bogus message'; const decodeResult = plugin.decode(message); diff --git a/lib/plugins/Label_2P_FM4.ts b/lib/plugins/Label_2P_FM4.ts index aa11feb..1ff5c18 100644 --- a/lib/plugins/Label_2P_FM4.ts +++ b/lib/plugins/Label_2P_FM4.ts @@ -6,13 +6,13 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_2P_FM4 extends DecoderPlugin { name = 'label-2p-fm4'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { - labels: ["2P"], + labels: ['2P'], }; } - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { let decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; decodeResult.formatted.description = 'Flight Report'; @@ -20,9 +20,9 @@ export class Label_2P_FM4 extends DecoderPlugin { const parts = message.text.split(','); - if(parts.length === 10){ + if (parts.length === 10) { const header = parts[0].split('FM4'); - if(header.length == 0) { + if (header.length == 0) { // can't use preambles, as there can be info before `FM4` // so let's check if we want to decode it here ResultFormatter.unknown(decodeResult, message.text); @@ -30,26 +30,34 @@ export class Label_2P_FM4 extends DecoderPlugin { decodeResult.decoder.decodeLevel = 'none'; return decodeResult; } - if(header[0].length > 0) { - ResultFormatter.unknown(decodeResult, header[0].substring(0,4)); + if (header[0].length > 0) { + ResultFormatter.unknown(decodeResult, header[0].substring(0, 4)); ResultFormatter.flightNumber(decodeResult, header[0].substring(4)); } ResultFormatter.departureAirport(decodeResult, header[1]); ResultFormatter.arrivalAirport(decodeResult, parts[1]); - ResultFormatter.day(decodeResult, Number(parts[2].substring(0,2))); - ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(parts[2].substring(2))); - ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(parts[3])); - ResultFormatter.position(decodeResult, {latitude: Number(parts[4].replaceAll(' ','')), longitude: Number(parts[5].replaceAll(' ',''))}); + ResultFormatter.day(decodeResult, Number(parts[2].substring(0, 2))); + ResultFormatter.time_of_day( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(parts[2].substring(2)), + ); + ResultFormatter.eta( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(parts[3]), + ); + ResultFormatter.position(decodeResult, { + latitude: Number(parts[4].replaceAll(' ', '')), + longitude: Number(parts[5].replaceAll(' ', '')), + }); ResultFormatter.altitude(decodeResult, Number(parts[6])); ResultFormatter.heading(decodeResult, Number(parts[7])); // TODO: decode further ResultFormatter.unknown(decodeResult, parts[8]); ResultFormatter.unknown(decodeResult, parts[9]); - decodeResult.decoded = true; decodeResult.decoder.decodeLevel = 'partial'; - } else { + } else { // Unknown if (options.debug) { console.log(`Decoder: Unknown H1 message: ${message.text}`); @@ -59,6 +67,6 @@ export class Label_2P_FM4 extends DecoderPlugin { decodeResult.decoder.decodeLevel = 'none'; } - return decodeResult; + return decodeResult; } } diff --git a/lib/plugins/Label_2P_FM5.test.ts b/lib/plugins/Label_2P_FM5.test.ts index 634b91f..a337dac 100644 --- a/lib/plugins/Label_2P_FM5.test.ts +++ b/lib/plugins/Label_2P_FM5.test.ts @@ -2,7 +2,6 @@ import { MessageDecoder } from '../MessageDecoder'; import { Label_2P_FM5 } from './Label_2P_FM5'; describe('Label_2P Preamble FM5', () => { - let plugin: Label_2P_FM5; const message = { label: '2P', text: '' }; @@ -13,7 +12,8 @@ describe('Label_2P Preamble FM5', () => { test('variant 1', () => { // https://app.airframes.io/messages/4208768180 - message.text = 'FM5 EIDW,OMAA,113522,1540,+45.147, +23.384,35002,116.24,502 ,36900,ETD23N ,'; + message.text = + 'FM5 EIDW,OMAA,113522,1540,+45.147, +23.384,35002,116.24,502 ,36900,ETD23N ,'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -27,7 +27,9 @@ describe('Label_2P Preamble FM5', () => { expect(decodeResult.formatted.items[1].value).toBe('OMAA'); expect(decodeResult.formatted.items[2].label).toBe('Message Timestamp'); expect(decodeResult.formatted.items[2].value).toBe('11:35:22'); - expect(decodeResult.formatted.items[3].label).toBe('Estimated Time of Arrival'); + expect(decodeResult.formatted.items[3].label).toBe( + 'Estimated Time of Arrival', + ); expect(decodeResult.formatted.items[3].value).toBe('15:40:00'); expect(decodeResult.formatted.items[4].label).toBe('Aircraft Position'); expect(decodeResult.formatted.items[4].value).toBe('45.147 N, 23.384 E'); @@ -39,7 +41,6 @@ describe('Label_2P Preamble FM5', () => { }); test('', () => { - message.text = 'FM4 Bogus message'; const decodeResult = plugin.decode(message); diff --git a/lib/plugins/Label_2P_FM5.ts b/lib/plugins/Label_2P_FM5.ts index 9f2bd5c..7f2e5d3 100644 --- a/lib/plugins/Label_2P_FM5.ts +++ b/lib/plugins/Label_2P_FM5.ts @@ -6,13 +6,13 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_2P_FM5 extends DecoderPlugin { name = 'label-2p-fm5'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { - labels: ["2P"], + labels: ['2P'], }; } - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { let decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; decodeResult.formatted.description = 'Flight Report'; @@ -20,10 +20,9 @@ export class Label_2P_FM5 extends DecoderPlugin { const parts = message.text.split(','); - if(parts.length === 12){ - + if (parts.length === 12) { const header = parts[0].split('FM5 '); - if(header.length == 0) { + if (header.length == 0) { // can't use preambles, as there can be info before `FM4` // so let's check if we want to decode it here ResultFormatter.unknown(decodeResult, message.text); @@ -34,9 +33,18 @@ export class Label_2P_FM5 extends DecoderPlugin { ResultFormatter.departureAirport(decodeResult, header[1]); ResultFormatter.arrivalAirport(decodeResult, parts[1]); - ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(parts[2])); - ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(parts[3])); - ResultFormatter.position(decodeResult, {latitude: Number(parts[4].replaceAll(' ','')), longitude: Number(parts[5].replaceAll(' ',''))}); + ResultFormatter.time_of_day( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(parts[2]), + ); + ResultFormatter.eta( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(parts[3]), + ); + ResultFormatter.position(decodeResult, { + latitude: Number(parts[4].replaceAll(' ', '')), + longitude: Number(parts[5].replaceAll(' ', '')), + }); ResultFormatter.altitude(decodeResult, Number(parts[6])); // TODO: decode further ResultFormatter.unknown(decodeResult, parts[7]); @@ -47,7 +55,7 @@ export class Label_2P_FM5 extends DecoderPlugin { decodeResult.decoded = true; decodeResult.decoder.decodeLevel = 'partial'; - } else { + } else { // Unknown if (options.debug) { console.log(`Decoder: Unknown H1 message: ${message.text}`); @@ -57,6 +65,6 @@ export class Label_2P_FM5 extends DecoderPlugin { decodeResult.decoder.decodeLevel = 'none'; } - return decodeResult; + return decodeResult; } } diff --git a/lib/plugins/Label_2P_POS.test.ts b/lib/plugins/Label_2P_POS.test.ts index 6586e49..d3e1c5d 100644 --- a/lib/plugins/Label_2P_POS.test.ts +++ b/lib/plugins/Label_2P_POS.test.ts @@ -2,7 +2,6 @@ import { MessageDecoder } from '../MessageDecoder'; import { Label_2P_POS } from './Label_2P_POS'; describe('Label_2P Preamble POS', () => { - let plugin: Label_2P_POS; const message = { label: '2P', text: '' }; @@ -13,7 +12,8 @@ describe('Label_2P Preamble POS', () => { test('variant 1', () => { // https://app.airframes.io/messages/4179262958 - message.text = 'M80AMC4086POS/ID50007B,RCH4086,ABB02R70E037/DC10022025,051804/MR103,/ET090738/PSN56012W013273,051804,350,,,,,084081,/CG,,/FB0857/VR0322B89'; + message.text = + 'M80AMC4086POS/ID50007B,RCH4086,ABB02R70E037/DC10022025,051804/MR103,/ET090738/PSN56012W013273,051804,350,,,,,084081,/CG,,/FB0857/VR0322B89'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -39,7 +39,6 @@ describe('Label_2P Preamble POS', () => { }); test('', () => { - message.text = 'M01AFN1234POS Bogus message'; const decodeResult = plugin.decode(message); diff --git a/lib/plugins/Label_2P_POS.ts b/lib/plugins/Label_2P_POS.ts index 8213c24..535ff25 100644 --- a/lib/plugins/Label_2P_POS.ts +++ b/lib/plugins/Label_2P_POS.ts @@ -6,38 +6,44 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_2P_POS extends DecoderPlugin { name = 'label-2p-pos'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { - labels: ["2P"], + labels: ['2P'], }; } - decode(message: Message, options: Options = {}) : DecodeResult { - let decodeResult = this.defaultResult(); - decodeResult.decoder.name = this.name; - decodeResult.message = message; + decode(message: Message, options: Options = {}): DecodeResult { + let decodeResult = this.defaultResult(); + decodeResult.decoder.name = this.name; + decodeResult.message = message; - const msg = message.text.replace(/\n|\r/g, ""); - const parts = msg.split('/'); - let decoded = false; - if (parts[0].length > 3) { - decoded = H1Helper.decodeH1Message(decodeResult, msg.slice(parts[0].length - 3)); - // flight number is already decoded in other fields - decodeResult.remaining.text = parts[0].slice(0,3) + '/' + decodeResult.remaining.text; - } else { + const msg = message.text.replace(/\n|\r/g, ''); + const parts = msg.split('/'); + let decoded = false; + if (parts[0].length > 3) { + decoded = H1Helper.decodeH1Message( + decodeResult, + msg.slice(parts[0].length - 3), + ); + // flight number is already decoded in other fields + decodeResult.remaining.text = + parts[0].slice(0, 3) + '/' + decodeResult.remaining.text; + } else { decoded = H1Helper.decodeH1Message(decodeResult, msg); - } - decodeResult.decoded = decoded; + } + decodeResult.decoded = decoded; - decodeResult.decoder.decodeLevel = !decodeResult.remaining.text ? 'full' : 'partial'; - if (decodeResult.formatted.items.length === 0) { - if (options.debug) { - console.log(`Decoder: Unknown H1 message: ${message.text}`); - } - ResultFormatter.unknown(decodeResult, message.text); - decodeResult.decoded = false; - decodeResult.decoder.decodeLevel = 'none'; + decodeResult.decoder.decodeLevel = !decodeResult.remaining.text + ? 'full' + : 'partial'; + if (decodeResult.formatted.items.length === 0) { + if (options.debug) { + console.log(`Decoder: Unknown H1 message: ${message.text}`); } - return decodeResult; + ResultFormatter.unknown(decodeResult, message.text); + decodeResult.decoded = false; + decodeResult.decoder.decodeLevel = 'none'; } + return decodeResult; + } } diff --git a/lib/plugins/Label_30_Slash_EA.test.ts b/lib/plugins/Label_30_Slash_EA.test.ts index dce50b7..1da7dba 100644 --- a/lib/plugins/Label_30_Slash_EA.test.ts +++ b/lib/plugins/Label_30_Slash_EA.test.ts @@ -3,40 +3,42 @@ import { Label_30_Slash_EA } from './Label_30_Slash_EA'; describe('Label 30 preamble /EA', () => { let plugin: Label_30_Slash_EA; - const message = {label: '30', text: ''}; + const message = { label: '30', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); plugin = new Label_30_Slash_EA(decoder); }); -test('matches qualifiers', () => { - expect(plugin.decode).toBeDefined(); - expect(plugin.name).toBe('label-30-slash-ea'); - expect(plugin.qualifiers).toBeDefined(); - expect(plugin.qualifiers()).toEqual({ - labels: ['30'], - preambles: ['/EA'], + test('matches qualifiers', () => { + expect(plugin.decode).toBeDefined(); + expect(plugin.name).toBe('label-30-slash-ea'); + expect(plugin.qualifiers).toBeDefined(); + expect(plugin.qualifiers()).toEqual({ + labels: ['30'], + preambles: ['/EA'], + }); + }); + test('decodes variant 1', () => { + message.text = '/EA1719/DSKSFO/SK23'; + const decodeResult = plugin.decode(message); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-30-slash-ea'); + expect(decodeResult.formatted.description).toBe('ETA Report'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.raw.arrival_icao).toBe('KSFO'); + expect(decodeResult.formatted.items.length).toBe(2); + expect(decodeResult.formatted.items[0].type).toBe('time_of_day'); + expect(decodeResult.formatted.items[0].code).toBe('ETA'); + expect(decodeResult.formatted.items[0].label).toBe( + 'Estimated Time of Arrival', + ); + expect(decodeResult.formatted.items[0].value).toBe('17:19:00'); + expect(decodeResult.formatted.items[1].type).toBe('icao'); + expect(decodeResult.formatted.items[1].code).toBe('DST'); + expect(decodeResult.formatted.items[1].label).toBe('Destination'); + expect(decodeResult.formatted.items[1].value).toBe('KSFO'); }); }); -test('decodes variant 1', () => { - message.text = '/EA1719/DSKSFO/SK23'; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-30-slash-ea'); - expect(decodeResult.formatted.description).toBe('ETA Report'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.raw.arrival_icao).toBe('KSFO'); - expect(decodeResult.formatted.items.length).toBe(2); - expect(decodeResult.formatted.items[0].type).toBe('time_of_day'); - expect(decodeResult.formatted.items[0].code).toBe('ETA'); - expect(decodeResult.formatted.items[0].label).toBe('Estimated Time of Arrival'); - expect(decodeResult.formatted.items[0].value).toBe('17:19:00'); - expect(decodeResult.formatted.items[1].type).toBe('icao'); - expect(decodeResult.formatted.items[1].code).toBe('DST'); - expect(decodeResult.formatted.items[1].label).toBe('Destination'); - expect(decodeResult.formatted.items[1].value).toBe('KSFO'); -}); -}); \ No newline at end of file diff --git a/lib/plugins/Label_30_Slash_EA.ts b/lib/plugins/Label_30_Slash_EA.ts index 1b1f25b..92c90dc 100644 --- a/lib/plugins/Label_30_Slash_EA.ts +++ b/lib/plugins/Label_30_Slash_EA.ts @@ -6,14 +6,14 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_30_Slash_EA extends DecoderPlugin { name = 'label-30-slash-ea'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { - labels: ["30"], + labels: ['30'], preambles: ['/EA'], }; } - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { const decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; decodeResult.formatted.description = 'ETA Report'; @@ -24,18 +24,24 @@ export class Label_30_Slash_EA extends DecoderPlugin { if (results) { if (options.debug) { - console.log(`Label 30 EA: results`); + console.log('Label 30 EA: results'); console.log(results); } } - ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(results[0].substr(2, 4))); + ResultFormatter.eta( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(results[0].substr(2, 4)), + ); - if (results[1].substring(0,2) === "DS") { + if (results[1].substring(0, 2) === 'DS') { ResultFormatter.arrivalAirport(decodeResult, results[1].substring(2, 6)); - ResultFormatter.unknown(decodeResult, "/".concat(results[2])); + ResultFormatter.unknown(decodeResult, '/'.concat(results[2])); } else { - ResultFormatter.unknown(decodeResult, "/".concat(results[1], "/", results[2])); + ResultFormatter.unknown( + decodeResult, + '/'.concat(results[1], '/', results[2]), + ); } decodeResult.decoded = true; diff --git a/lib/plugins/Label_44_ETA.test.ts b/lib/plugins/Label_44_ETA.test.ts index d6d711d..83d792d 100644 --- a/lib/plugins/Label_44_ETA.test.ts +++ b/lib/plugins/Label_44_ETA.test.ts @@ -3,7 +3,7 @@ import { Label_44_ETA } from './Label_44_ETA'; describe('Label 44 Preamble ETA', () => { let plugin: Label_44_ETA; - const message = {label: '44', text: ''}; + const message = { label: '44', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); @@ -50,14 +50,15 @@ describe('Label 44 Preamble ETA', () => { expect(decodeResult.formatted.items[5].value).toBe('7'); expect(decodeResult.formatted.items[6].label).toBe('Message Timestamp'); expect(decodeResult.formatted.items[6].value).toBe('01:23:00'); - expect(decodeResult.formatted.items[7].label).toBe('Estimated Time of Arrival'); + expect(decodeResult.formatted.items[7].label).toBe( + 'Estimated Time of Arrival', + ); expect(decodeResult.formatted.items[7].value).toBe('02:08:00'); expect(decodeResult.formatted.items[8].label).toBe('Fuel Remaining'); expect(decodeResult.formatted.items[8].value).toBe('8.1'); }); test('does not decode invalid', () => { - message.text = '00OFF01 Bogus message'; const decodeResult = plugin.decode(message); diff --git a/lib/plugins/Label_44_ETA.ts b/lib/plugins/Label_44_ETA.ts index f8f5ad6..6274e75 100644 --- a/lib/plugins/Label_44_ETA.ts +++ b/lib/plugins/Label_44_ETA.ts @@ -8,7 +8,7 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_44_ETA extends DecoderPlugin { name = 'label-44-eta'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['44'], preambles: ['00ETA01', '00ETA02', '00ETA03', 'ETA01', 'ETA02', 'ETA03'], @@ -24,19 +24,28 @@ export class Label_44_ETA extends DecoderPlugin { const data = message.text.split(','); if (data.length >= 9) { if (options.debug) { - console.log(`Label 44 ETA Report: groups`); + console.log('Label 44 ETA Report: groups'); console.log(data); } - ResultFormatter.position(decodeResult, CoordinateUtils.decodeStringCoordinatesDecimalMinutes(data[1])); + ResultFormatter.position( + decodeResult, + CoordinateUtils.decodeStringCoordinatesDecimalMinutes(data[1]), + ); ResultFormatter.altitude(decodeResult, 100 * Number(data[2])); ResultFormatter.departureAirport(decodeResult, data[3]); ResultFormatter.arrivalAirport(decodeResult, data[4]); ResultFormatter.month(decodeResult, Number(data[5].substring(0, 2))); ResultFormatter.day(decodeResult, Number(data[5].substring(2, 4))); - ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(data[6])); - ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(data[7])); + ResultFormatter.time_of_day( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(data[6]), + ); + ResultFormatter.eta( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(data[7]), + ); const fuel = Number(data[8]); if (!isNaN(fuel)) { ResultFormatter.remainingFuel(decodeResult, Number(fuel)); @@ -45,7 +54,6 @@ export class Label_44_ETA extends DecoderPlugin { if (data.length > 9) { ResultFormatter.unknownArr(decodeResult, data.slice(9)); } - } else { if (options.debug) { console.log(`Decoder: Unknown 44 message: ${message.text}`); diff --git a/lib/plugins/Label_44_IN.test.ts b/lib/plugins/Label_44_IN.test.ts index 85b7191..d79fd7f 100644 --- a/lib/plugins/Label_44_IN.test.ts +++ b/lib/plugins/Label_44_IN.test.ts @@ -3,7 +3,7 @@ import { Label_44_IN } from './Label_44_IN'; describe('Label 44 IN', () => { let plugin: Label_44_IN; - const message = {label: '44', text: ''}; + const message = { label: '44', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); @@ -22,7 +22,7 @@ describe('Label 44 IN', () => { test('decodes variant 1', () => { // https://app.airframes.io/messages/3563679070 - message.text = 'IN01,N33528W084181,KCLT,KPDK,1106,0045,---.-' + message.text = 'IN01,N33528W084181,KCLT,KPDK,1106,0045,---.-'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); expect(decodeResult.decoder.decodeLevel).toBe('full'); @@ -49,7 +49,7 @@ describe('Label 44 IN', () => { }); test('decodes variant 2', () => { - message.text = 'IN02,N38338W121179,KMHR,KPDX,0806,2355,005.1' + message.text = 'IN02,N38338W121179,KMHR,KPDX,0806,2355,005.1'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); expect(decodeResult.decoder.decodeLevel).toBe('full'); @@ -78,7 +78,6 @@ describe('Label 44 IN', () => { }); test('does not decode invalid', () => { - message.text = '00OFF01 Bogus message'; const decodeResult = plugin.decode(message); diff --git a/lib/plugins/Label_44_IN.ts b/lib/plugins/Label_44_IN.ts index 05d0ddd..cb1c03b 100644 --- a/lib/plugins/Label_44_IN.ts +++ b/lib/plugins/Label_44_IN.ts @@ -8,7 +8,7 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_44_IN extends DecoderPlugin { name = 'label-44-in'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['44'], preambles: ['00IN01', '00IN02', '00IN03', 'IN01', 'IN02', 'IN03'], @@ -24,16 +24,22 @@ export class Label_44_IN extends DecoderPlugin { const data = message.text.split(','); if (data.length >= 7) { if (options.debug) { - console.log(`Label 44 In Air Report: groups`); + console.log('Label 44 In Air Report: groups'); console.log(data); } - ResultFormatter.position(decodeResult, CoordinateUtils.decodeStringCoordinatesDecimalMinutes(data[1])); + ResultFormatter.position( + decodeResult, + CoordinateUtils.decodeStringCoordinatesDecimalMinutes(data[1]), + ); ResultFormatter.departureAirport(decodeResult, data[2]); ResultFormatter.arrivalAirport(decodeResult, data[3]); ResultFormatter.month(decodeResult, Number(data[4].substring(0, 2))); ResultFormatter.day(decodeResult, Number(data[4].substring(2, 4))); - ResultFormatter.in(decodeResult, DateTimeUtils.convertHHMMSSToTod(data[5])); + ResultFormatter.in( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(data[5]), + ); const fuel = Number(data[6]); if (!isNaN(fuel)) { ResultFormatter.remainingFuel(decodeResult, Number(fuel)); @@ -42,7 +48,6 @@ export class Label_44_IN extends DecoderPlugin { if (data.length > 7) { ResultFormatter.unknownArr(decodeResult, data.slice(7)); } - } else { if (options.debug) { console.log(`Decoder: Unknown 44 message: ${message.text}`); diff --git a/lib/plugins/Label_44_OFF.test.ts b/lib/plugins/Label_44_OFF.test.ts index c01c30a..31d44fa 100644 --- a/lib/plugins/Label_44_OFF.test.ts +++ b/lib/plugins/Label_44_OFF.test.ts @@ -3,7 +3,7 @@ import { Label_44_OFF } from './Label_44_OFF'; describe('Label 44 OFF', () => { let plugin: Label_44_OFF; - const message = {label: '44', text: ''}; + const message = { label: '44', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); @@ -21,7 +21,7 @@ describe('Label 44 OFF', () => { }); test('decodes variant 1', () => { - message.text = 'OFF02,N39247W077226,KFDK,KSNA,1106,2124,0248,011.1' + message.text = 'OFF02,N39247W077226,KFDK,KSNA,1106,2124,0248,011.1'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); expect(decodeResult.decoder.decodeLevel).toBe('full'); @@ -47,14 +47,15 @@ describe('Label 44 OFF', () => { expect(decodeResult.formatted.items[4].value).toBe('6'); expect(decodeResult.formatted.items[5].label).toBe('Takeoff Time'); expect(decodeResult.formatted.items[5].value).toBe('21:24:00'); - expect(decodeResult.formatted.items[6].label).toBe('Estimated Time of Arrival'); + expect(decodeResult.formatted.items[6].label).toBe( + 'Estimated Time of Arrival', + ); expect(decodeResult.formatted.items[6].value).toBe('02:48:00'); expect(decodeResult.formatted.items[7].label).toBe('Fuel Remaining'); expect(decodeResult.formatted.items[7].value).toBe('11.1'); }); test('does not decode invalid', () => { - message.text = '00OFF01 Bogus message'; const decodeResult = plugin.decode(message); diff --git a/lib/plugins/Label_44_OFF.ts b/lib/plugins/Label_44_OFF.ts index 29dc8e1..66a5a4e 100644 --- a/lib/plugins/Label_44_OFF.ts +++ b/lib/plugins/Label_44_OFF.ts @@ -8,7 +8,7 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_44_OFF extends DecoderPlugin { name = 'label-44-off'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['44'], preambles: ['00OFF01', '00OFF02', '00OFF03', 'OFF01', 'OFF02', 'OFF03'], @@ -21,21 +21,29 @@ export class Label_44_OFF extends DecoderPlugin { decodeResult.formatted.description = 'Off Runway Report'; decodeResult.message = message; - const data = message.text.split(','); if (data.length >= 8) { if (options.debug) { - console.log(`Label 44 Off Runway Report: groups`); + console.log('Label 44 Off Runway Report: groups'); console.log(data); } - ResultFormatter.position(decodeResult, CoordinateUtils.decodeStringCoordinatesDecimalMinutes(data[1])); + ResultFormatter.position( + decodeResult, + CoordinateUtils.decodeStringCoordinatesDecimalMinutes(data[1]), + ); ResultFormatter.departureAirport(decodeResult, data[2]); ResultFormatter.arrivalAirport(decodeResult, data[3]); ResultFormatter.month(decodeResult, Number(data[4].substring(0, 2))); ResultFormatter.day(decodeResult, Number(data[4].substring(2, 4))); - ResultFormatter.off(decodeResult, DateTimeUtils.convertHHMMSSToTod(data[5])); - ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(data[6])); + ResultFormatter.off( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(data[5]), + ); + ResultFormatter.eta( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(data[6]), + ); const fuel = Number(data[7]); if (!isNaN(fuel)) { ResultFormatter.remainingFuel(decodeResult, Number(fuel)); @@ -44,7 +52,6 @@ export class Label_44_OFF extends DecoderPlugin { if (data.length > 8) { ResultFormatter.unknownArr(decodeResult, data.slice(8)); } - } else { if (options.debug) { console.log(`Decoder: Unknown 44 message: ${message.text}`); diff --git a/lib/plugins/Label_44_ON.test.ts b/lib/plugins/Label_44_ON.test.ts index 9d1a18f..57333b8 100644 --- a/lib/plugins/Label_44_ON.test.ts +++ b/lib/plugins/Label_44_ON.test.ts @@ -3,7 +3,7 @@ import { Label_44_ON } from './Label_44_ON'; describe('Label 44 ON', () => { let plugin: Label_44_ON; - const message = {label: '44', text: ''}; + const message = { label: '44', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); @@ -22,7 +22,7 @@ describe('Label 44 ON', () => { test('decodes variant 1', () => { // https://app.airframes.io/messages/3563679058 - message.text = 'ON01,N33522W084181,KCLT,KPDK,1106,004023,---.-,' + message.text = 'ON01,N33522W084181,KCLT,KPDK,1106,004023,---.-,'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); expect(decodeResult.decoder.decodeLevel).toBe('full'); @@ -49,7 +49,7 @@ describe('Label 44 ON', () => { }); test('decodes variant 2', () => { - message.text = 'ON02,N38333W121178,KRNO,KMHR,0806,2350,005.2' + message.text = 'ON02,N38333W121178,KRNO,KMHR,0806,2350,005.2'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); expect(decodeResult.decoder.decodeLevel).toBe('full'); @@ -78,7 +78,6 @@ describe('Label 44 ON', () => { }); test('does not decode invalid', () => { - message.text = '00OFF01 Bogus message'; const decodeResult = plugin.decode(message); diff --git a/lib/plugins/Label_44_ON.ts b/lib/plugins/Label_44_ON.ts index b5aa972..00ab3c6 100644 --- a/lib/plugins/Label_44_ON.ts +++ b/lib/plugins/Label_44_ON.ts @@ -8,7 +8,7 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_44_ON extends DecoderPlugin { name = 'label-44-on'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['44'], preambles: ['00ON01', '00ON02', '00ON03', 'ON01', 'ON02', 'ON03'], @@ -24,16 +24,22 @@ export class Label_44_ON extends DecoderPlugin { const data = message.text.split(','); if (data.length >= 7) { if (options.debug) { - console.log(`Label 44 On Runway Report: groups`); + console.log('Label 44 On Runway Report: groups'); console.log(data); } - ResultFormatter.position(decodeResult, CoordinateUtils.decodeStringCoordinatesDecimalMinutes(data[1])); + ResultFormatter.position( + decodeResult, + CoordinateUtils.decodeStringCoordinatesDecimalMinutes(data[1]), + ); ResultFormatter.departureAirport(decodeResult, data[2]); ResultFormatter.arrivalAirport(decodeResult, data[3]); ResultFormatter.month(decodeResult, Number(data[4].substring(0, 2))); ResultFormatter.day(decodeResult, Number(data[4].substring(2, 4))); - ResultFormatter.on(decodeResult, DateTimeUtils.convertHHMMSSToTod(data[5])); + ResultFormatter.on( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(data[5]), + ); const fuel = Number(data[6]); if (!isNaN(fuel)) { ResultFormatter.remainingFuel(decodeResult, Number(fuel)); @@ -42,7 +48,6 @@ export class Label_44_ON extends DecoderPlugin { if (data.length > 7) { ResultFormatter.unknownArr(decodeResult, data.slice(7)); } - } else { if (options.debug) { console.log(`Decoder: Unknown 44 message: ${message.text}`); diff --git a/lib/plugins/Label_44_POS.test.ts b/lib/plugins/Label_44_POS.test.ts index 0bc9513..ce5995a 100644 --- a/lib/plugins/Label_44_POS.test.ts +++ b/lib/plugins/Label_44_POS.test.ts @@ -3,67 +3,69 @@ import { Label_44_POS } from './Label_44_POS'; describe('Label 44 Preamble POS', () => { let plugin: Label_44_POS; - const message = {label: '44', text: ''}; + const message = { label: '44', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); plugin = new Label_44_POS(decoder); }); -test('matches Label 44 Preamble POS qualifiers', () => { - expect(plugin.decode).toBeDefined(); - expect(plugin.name).toBe('label-44-pos'); - expect(plugin.qualifiers).toBeDefined(); - expect(plugin.qualifiers()).toEqual({ - labels: ['44'], - preambles: ['00POS01', '00POS02', '00POS03', 'POS01', 'POS02', 'POS03'], + test('matches Label 44 Preamble POS qualifiers', () => { + expect(plugin.decode).toBeDefined(); + expect(plugin.name).toBe('label-44-pos'); + expect(plugin.qualifiers).toBeDefined(); + expect(plugin.qualifiers()).toEqual({ + labels: ['44'], + preambles: ['00POS01', '00POS02', '00POS03', 'POS01', 'POS02', 'POS03'], + }); }); -}); -test('decodes Label 44 Preamble POS02 variant 1', () => { - // https://app.airframes.io/messages/3389060301 - message.text = 'POS02,N38171W077507,319,KJFK,KUZA,0926,0245,0327,004.6'; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('full'); - expect(decodeResult.decoder.name).toBe('label-44-pos'); - expect(decodeResult.formatted.description).toBe('Position Report'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.raw.position.latitude).toBe(38.285); - expect(decodeResult.raw.position.longitude).toBe(-77.845); - expect(decodeResult.raw.altitude).toBe(31900); - expect(decodeResult.raw.departure_icao).toBe('KJFK'); - expect(decodeResult.raw.arrival_icao).toBe('KUZA'); - expect(decodeResult.raw.time_of_day).toBe(9900); - expect(decodeResult.raw.eta_time).toBe(12420); - expect(decodeResult.raw.fuel_in_tons).toBe(4.6); - expect(decodeResult.formatted.items.length).toBe(8); - expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); - expect(decodeResult.formatted.items[0].value).toBe('38.285 N, 77.845 W') - expect(decodeResult.formatted.items[1].label).toBe('Month of Year'); - expect(decodeResult.formatted.items[1].value).toBe('9'); - expect(decodeResult.formatted.items[2].label).toBe('Day of Month'); - expect(decodeResult.formatted.items[2].value).toBe('26'); - expect(decodeResult.formatted.items[3].label).toBe('Message Timestamp'); - expect(decodeResult.formatted.items[3].value).toBe('02:45:00'); - expect(decodeResult.formatted.items[4].label).toBe('Estimated Time of Arrival'); - expect(decodeResult.formatted.items[4].value).toBe('03:27:00'); - expect(decodeResult.formatted.items[5].label).toBe('Origin'); - expect(decodeResult.formatted.items[5].value).toBe('KJFK'); - expect(decodeResult.formatted.items[6].label).toBe('Destination'); - expect(decodeResult.formatted.items[6].value).toBe('KUZA'); - expect(decodeResult.formatted.items[7].label).toBe('Altitude'); - expect(decodeResult.formatted.items[7].value).toBe('31900 feet'); -}); + test('decodes Label 44 Preamble POS02 variant 1', () => { + // https://app.airframes.io/messages/3389060301 + message.text = 'POS02,N38171W077507,319,KJFK,KUZA,0926,0245,0327,004.6'; + const decodeResult = plugin.decode(message); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('full'); + expect(decodeResult.decoder.name).toBe('label-44-pos'); + expect(decodeResult.formatted.description).toBe('Position Report'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.raw.position.latitude).toBe(38.285); + expect(decodeResult.raw.position.longitude).toBe(-77.845); + expect(decodeResult.raw.altitude).toBe(31900); + expect(decodeResult.raw.departure_icao).toBe('KJFK'); + expect(decodeResult.raw.arrival_icao).toBe('KUZA'); + expect(decodeResult.raw.time_of_day).toBe(9900); + expect(decodeResult.raw.eta_time).toBe(12420); + expect(decodeResult.raw.fuel_in_tons).toBe(4.6); + expect(decodeResult.formatted.items.length).toBe(8); + expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); + expect(decodeResult.formatted.items[0].value).toBe('38.285 N, 77.845 W'); + expect(decodeResult.formatted.items[1].label).toBe('Month of Year'); + expect(decodeResult.formatted.items[1].value).toBe('9'); + expect(decodeResult.formatted.items[2].label).toBe('Day of Month'); + expect(decodeResult.formatted.items[2].value).toBe('26'); + expect(decodeResult.formatted.items[3].label).toBe('Message Timestamp'); + expect(decodeResult.formatted.items[3].value).toBe('02:45:00'); + expect(decodeResult.formatted.items[4].label).toBe( + 'Estimated Time of Arrival', + ); + expect(decodeResult.formatted.items[4].value).toBe('03:27:00'); + expect(decodeResult.formatted.items[5].label).toBe('Origin'); + expect(decodeResult.formatted.items[5].value).toBe('KJFK'); + expect(decodeResult.formatted.items[6].label).toBe('Destination'); + expect(decodeResult.formatted.items[6].value).toBe('KUZA'); + expect(decodeResult.formatted.items[7].label).toBe('Altitude'); + expect(decodeResult.formatted.items[7].value).toBe('31900 feet'); + }); + + // disabled because current parser decodes 'full' + test.skip('decodes Label 44 Preamble POS02 ', () => { + message.text = 'POS02 Bogus message'; + const decodeResult = plugin.decode(message); -// disabled because current parser decodes 'full' -test.skip('decodes Label 44 Preamble POS02 ', () => { - message.text = 'POS02 Bogus message'; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(false); - expect(decodeResult.decoder.decodeLevel).toBe('none'); - expect(decodeResult.decoder.name).toBe('label-44-pos'); - expect(decodeResult.formatted.description).toBe('Position Report'); - expect(decodeResult.formatted.items.length).toBe(0); + expect(decodeResult.decoded).toBe(false); + expect(decodeResult.decoder.decodeLevel).toBe('none'); + expect(decodeResult.decoder.name).toBe('label-44-pos'); + expect(decodeResult.formatted.description).toBe('Position Report'); + expect(decodeResult.formatted.items.length).toBe(0); + }); }); -}); \ No newline at end of file diff --git a/lib/plugins/Label_44_POS.ts b/lib/plugins/Label_44_POS.ts index a812cd3..f263c25 100644 --- a/lib/plugins/Label_44_POS.ts +++ b/lib/plugins/Label_44_POS.ts @@ -8,14 +8,14 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_44_POS extends DecoderPlugin { name = 'label-44-pos'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['44'], preambles: ['00POS01', '00POS02', '00POS03', 'POS01', 'POS02', 'POS03'], }; } - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { const decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; decodeResult.formatted.description = 'Position Report'; @@ -23,30 +23,57 @@ export class Label_44_POS extends DecoderPlugin { // Style: POS02,N38338W121179,GRD,KMHR,KPDX,0807,0003,0112,005.1 // Match: POS02,coords,flight_level_or_ground,departure_icao,arrival_icao,current_date,current_time,eta_time,unknown - const regex = /^.*,(?.*),(?.*),(?.*),(?.*),(?.*),(?.*),(?.*),(?.*)$/; + const regex = + /^.*,(?.*),(?.*),(?.*),(?.*),(?.*),(?.*),(?.*),(?.*)$/; const results = message.text.match(regex); if (results?.groups) { if (options.debug) { - console.log(`Label 44 Position Report: groups`); + console.log('Label 44 Position Report: groups'); console.log(results.groups); } - ResultFormatter.position(decodeResult, CoordinateUtils.decodeStringCoordinatesDecimalMinutes(results.groups.unsplit_coords)); - const flight_level = results.groups.flight_level_or_ground == 'GRD' || results.groups.flight_level_or_ground == '***' ? 0 : Number(results.groups.flight_level_or_ground); + ResultFormatter.position( + decodeResult, + CoordinateUtils.decodeStringCoordinatesDecimalMinutes( + results.groups.unsplit_coords, + ), + ); + const flight_level = + results.groups.flight_level_or_ground == 'GRD' || + results.groups.flight_level_or_ground == '***' + ? 0 + : Number(results.groups.flight_level_or_ground); - - ResultFormatter.month(decodeResult, Number(results.groups.current_date.substring(0, 2))); - ResultFormatter.day(decodeResult, Number(results.groups.current_date.substring(2, 4))); - ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(results.groups.current_time + '00')); + ResultFormatter.month( + decodeResult, + Number(results.groups.current_date.substring(0, 2)), + ); + ResultFormatter.day( + decodeResult, + Number(results.groups.current_date.substring(2, 4)), + ); + ResultFormatter.time_of_day( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(results.groups.current_time + '00'), + ); // TODO: ETA month and Day - ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(results.groups.eta_time + '00')); + ResultFormatter.eta( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(results.groups.eta_time + '00'), + ); - if (results.groups.fuel_in_tons != '***' && results.groups.fuel_in_tons != '****') { + if ( + results.groups.fuel_in_tons != '***' && + results.groups.fuel_in_tons != '****' + ) { decodeResult.raw.fuel_in_tons = Number(results.groups.fuel_in_tons); } - ResultFormatter.departureAirport(decodeResult, results.groups.departure_icao); + ResultFormatter.departureAirport( + decodeResult, + results.groups.departure_icao, + ); ResultFormatter.arrivalAirport(decodeResult, results.groups.arrival_icao); ResultFormatter.altitude(decodeResult, flight_level * 100); } diff --git a/lib/plugins/Label_44_Slash.test.ts b/lib/plugins/Label_44_Slash.test.ts index 09cbbfe..6a407be 100644 --- a/lib/plugins/Label_44_Slash.test.ts +++ b/lib/plugins/Label_44_Slash.test.ts @@ -1,64 +1,67 @@ -import { MessageDecoder } from "../MessageDecoder"; -import { Label_44_Slash } from "./Label_44_Slash"; +import { MessageDecoder } from '../MessageDecoder'; +import { Label_44_Slash } from './Label_44_Slash'; -describe("Label 44 Slash", () => { +describe('Label 44 Slash', () => { let plugin: Label_44_Slash; - const message = { label: "44", text: "" }; + const message = { label: '44', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); plugin = new Label_44_Slash(decoder); }); - test("matches qualifiers", () => { + test('matches qualifiers', () => { expect(plugin.decode).toBeDefined(); - expect(plugin.name).toBe("label-44-slash"); + expect(plugin.name).toBe('label-44-slash'); expect(plugin.qualifiers).toBeDefined(); expect(plugin.qualifiers()).toEqual({ - labels: ["44"], - preambles: [" /FB"], + labels: ['44'], + preambles: [' /FB'], }); }); - test("decodes variant 1", () => { + test('decodes variant 1', () => { // https://app.airframes.io/messages/3563679058 - message.text = " /FB 0160/AD KORH/N 38.655,W 75.325,JBU2834,INA03,KORH,2043"; + message.text = + ' /FB 0160/AD KORH/N 38.655,W 75.325,JBU2834,INA03,KORH,2043'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe("partial"); - expect(decodeResult.raw.flight_number).toBe("JBU2834"); - expect(decodeResult.raw.arrival_icao).toBe("KORH"); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.raw.flight_number).toBe('JBU2834'); + expect(decodeResult.raw.arrival_icao).toBe('KORH'); expect(decodeResult.raw.eta_time).toBe(74580); expect(decodeResult.raw.position.latitude).toBeCloseTo(38.655, 3); expect(decodeResult.raw.position.longitude).toBeCloseTo(-75.325, 3); expect(decodeResult.formatted.items.length).toBe(4); - expect(decodeResult.remaining.text).toBe(" /FB 0160,INA03"); + expect(decodeResult.remaining.text).toBe(' /FB 0160,INA03'); }); - test("decodes variant 2", () => { + test('decodes variant 2', () => { message.text = - " /FB ----/AD KTPA/N 27.971,W 82.558,JBU91,FLS03,KTPA,53988,53988,----,1943,1943,1,1L,VIS1L,0,2,0,,"; + ' /FB ----/AD KTPA/N 27.971,W 82.558,JBU91,FLS03,KTPA,53988,53988,----,1943,1943,1,1L,VIS1L,0,2,0,,'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe("partial"); - expect(decodeResult.raw.flight_number).toBe("JBU91"); - expect(decodeResult.raw.arrival_icao).toBe("KTPA"); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.raw.flight_number).toBe('JBU91'); + expect(decodeResult.raw.arrival_icao).toBe('KTPA'); expect(decodeResult.raw.eta_time).toBe(196688); - expect(decodeResult.raw.arrival_runway).toBe("1L"); - expect(decodeResult.raw.procedures[0].route.name).toBe("VIS1L"); + expect(decodeResult.raw.arrival_runway).toBe('1L'); + expect(decodeResult.raw.procedures[0].route.name).toBe('VIS1L'); expect(decodeResult.raw.fuel_remaining).toBe(1943); expect(decodeResult.raw.position.latitude).toBeCloseTo(27.971, 3); expect(decodeResult.raw.position.longitude).toBeCloseTo(-82.558, 3); expect(decodeResult.formatted.items.length).toBe(7); - expect(decodeResult.remaining.text).toBe(" /FB ----,FLS03,53988,----,1943,1,0,2,0,,"); + expect(decodeResult.remaining.text).toBe( + ' /FB ----,FLS03,53988,----,1943,1,0,2,0,,', + ); }); - test("does not decode invalid", () => { - message.text = "00OFF01 Bogus message"; + test('does not decode invalid', () => { + message.text = '00OFF01 Bogus message'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(false); - expect(decodeResult.decoder.decodeLevel).toBe("none"); + expect(decodeResult.decoder.decodeLevel).toBe('none'); expect(decodeResult.message).toBe(message); }); }); diff --git a/lib/plugins/Label_44_Slash.ts b/lib/plugins/Label_44_Slash.ts index 5ae813c..9b9bf9b 100644 --- a/lib/plugins/Label_44_Slash.ts +++ b/lib/plugins/Label_44_Slash.ts @@ -1,53 +1,52 @@ -import { DateTimeUtils } from "../DateTimeUtils"; -import { DecoderPlugin } from "../DecoderPlugin"; -import { DecodeResult, Message, Options } from "../DecoderPluginInterface"; -import { FlightPlanUtils } from "../utils/flight_plan_utils"; -import { ResultFormatter } from "../utils/result_formatter"; +import { DateTimeUtils } from '../DateTimeUtils'; +import { DecoderPlugin } from '../DecoderPlugin'; +import { DecodeResult, Message, Options } from '../DecoderPluginInterface'; +import { FlightPlanUtils } from '../utils/flight_plan_utils'; +import { ResultFormatter } from '../utils/result_formatter'; // Flight Briefing Report export class Label_44_Slash extends DecoderPlugin { - name = "label-44-slash"; + name = 'label-44-slash'; qualifiers() { - // eslint-disable-line class-methods-use-this return { - labels: ["44"], - preambles: [" /FB"], + labels: ['44'], + preambles: [' /FB'], }; } decode(message: Message, options: Options = {}): DecodeResult { const decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; - decodeResult.formatted.description = "Flight Briefing"; + decodeResult.formatted.description = 'Flight Briefing'; decodeResult.message = message; - const fields = message.text.split("/"); + const fields = message.text.split('/'); if (fields.length !== 4) { if (options.debug) { console.log(`Decoder: Unknown 44 message: ${message.text}`); } ResultFormatter.unknown(decodeResult, message.text); decodeResult.decoded = false; - decodeResult.decoder.decodeLevel = "none"; + decodeResult.decoder.decodeLevel = 'none'; return decodeResult; } - ResultFormatter.unknownArr(decodeResult, fields.slice(0, 2), "/"); // 0 is a space + ResultFormatter.unknownArr(decodeResult, fields.slice(0, 2), '/'); // 0 is a space // 1 is the briefing id // 2 is arrival airport, but repeated later - const data = fields[3].split(","); + const data = fields[3].split(','); if (data.length >= 6) { if (options.debug) { - console.log(`Label 44 Slash: groups`); + console.log('Label 44 Slash: groups'); console.log(data); } ResultFormatter.position(decodeResult, { latitude: - (data[0].charAt(0) === "S" ? -1 : 1) * + (data[0].charAt(0) === 'S' ? -1 : 1) * parseFloat(data[0].slice(1).trim()), longitude: - (data[1].charAt(0) === "W" ? -1 : 1) * + (data[1].charAt(0) === 'W' ? -1 : 1) * parseFloat(data[1].slice(1).trim()), }); ResultFormatter.flightNumber(decodeResult, data[2]); @@ -58,12 +57,12 @@ export class Label_44_Slash extends DecoderPlugin { DateTimeUtils.convertHHMMSSToTod(data[5]), ); if (data.length === 18) { - ResultFormatter.unknownArr(decodeResult, data.slice(6, 8), ","); // 6 is repeated arrival airport + ResultFormatter.unknownArr(decodeResult, data.slice(6, 8), ','); // 6 is repeated arrival airport ResultFormatter.remainingFuel(decodeResult, parseFloat(data[8])); - ResultFormatter.unknownArr(decodeResult, data.slice(9, 11), ","); + ResultFormatter.unknownArr(decodeResult, data.slice(9, 11), ','); ResultFormatter.arrivalRunway(decodeResult, data[11]); - FlightPlanUtils.addProcedure(decodeResult, data[12], "arrival"); - ResultFormatter.unknownArr(decodeResult, data.slice(13, 18), ","); + FlightPlanUtils.addProcedure(decodeResult, data[12], 'arrival'); + ResultFormatter.unknownArr(decodeResult, data.slice(13, 18), ','); } } else { if (options.debug) { @@ -71,12 +70,12 @@ export class Label_44_Slash extends DecoderPlugin { } ResultFormatter.unknown(decodeResult, message.text); decodeResult.decoded = false; - decodeResult.decoder.decodeLevel = "none"; + decodeResult.decoder.decodeLevel = 'none'; return decodeResult; } decodeResult.decoded = true; - decodeResult.decoder.decodeLevel = "partial"; + decodeResult.decoder.decodeLevel = 'partial'; return decodeResult; } diff --git a/lib/plugins/Label_4A.test.ts b/lib/plugins/Label_4A.test.ts index 7a81544..02bb52d 100644 --- a/lib/plugins/Label_4A.test.ts +++ b/lib/plugins/Label_4A.test.ts @@ -3,145 +3,151 @@ import { Label_4A } from './Label_4A'; describe('Label 4A', () => { let plugin: Label_4A; - const message = {label: '4A', text: ''}; + const message = { label: '4A', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); plugin = new Label_4A(decoder); }); -test('matches qualifiers', () => { - expect(plugin.decode).toBeDefined(); - expect(plugin.name).toBe('label-4a'); - expect(plugin.qualifiers).toBeDefined(); - expect(plugin.qualifiers()).toEqual({ - labels: ['4A'], + test('matches qualifiers', () => { + expect(plugin.decode).toBeDefined(); + expect(plugin.name).toBe('label-4a'); + expect(plugin.qualifiers).toBeDefined(); + expect(plugin.qualifiers()).toEqual({ + labels: ['4A'], + }); }); -}); -test('decodes variant 1', () => { - // https://app.airframes.io/messages/3451492279 - message.text = '063200,1910,.N343FR,FFT2028,KSLC,KORD,1,0632,RT0,LT0,'; - const decodeResult = plugin.decode(message); + test('decodes variant 1', () => { + // https://app.airframes.io/messages/3451492279 + message.text = '063200,1910,.N343FR,FFT2028,KSLC,KORD,1,0632,RT0,LT0,'; + const decodeResult = plugin.decode(message); - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-4a'); - expect(decodeResult.formatted.description).toBe('Latest New Format'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.remaining.text).toBe('RT0,LT0,'); - expect(decodeResult.formatted.items.length).toBe(5); - expect(decodeResult.formatted.items[0].code).toBe('MSG_TOD'); - expect(decodeResult.formatted.items[0].value).toBe('06:32:00'); - expect(decodeResult.formatted.items[1].code).toBe('TAIL'); - expect(decodeResult.formatted.items[1].value).toBe('N343FR'); - expect(decodeResult.formatted.items[2].code).toBe('CALLSIGN'); - expect(decodeResult.formatted.items[2].value).toBe('FFT2028'); - expect(decodeResult.formatted.items[3].code).toBe('ORG'); - expect(decodeResult.formatted.items[3].value).toBe('KSLC'); - expect(decodeResult.formatted.items[4].code).toBe('DST'); - expect(decodeResult.formatted.items[4].value).toBe('KORD'); -}); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-4a'); + expect(decodeResult.formatted.description).toBe('Latest New Format'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.remaining.text).toBe('RT0,LT0,'); + expect(decodeResult.formatted.items.length).toBe(5); + expect(decodeResult.formatted.items[0].code).toBe('MSG_TOD'); + expect(decodeResult.formatted.items[0].value).toBe('06:32:00'); + expect(decodeResult.formatted.items[1].code).toBe('TAIL'); + expect(decodeResult.formatted.items[1].value).toBe('N343FR'); + expect(decodeResult.formatted.items[2].code).toBe('CALLSIGN'); + expect(decodeResult.formatted.items[2].value).toBe('FFT2028'); + expect(decodeResult.formatted.items[3].code).toBe('ORG'); + expect(decodeResult.formatted.items[3].value).toBe('KSLC'); + expect(decodeResult.formatted.items[4].code).toBe('DST'); + expect(decodeResult.formatted.items[4].value).toBe('KORD'); + }); -test('decodes variant 1, no callsign', () => { - // https://app.airframes.io/messages/3452310240 - message.text = '101606,1910,.N317FR,,KMDW,----,1,1016,RT0,LT1,'; - const decodeResult = plugin.decode(message); + test('decodes variant 1, no callsign', () => { + // https://app.airframes.io/messages/3452310240 + message.text = '101606,1910,.N317FR,,KMDW,----,1,1016,RT0,LT1,'; + const decodeResult = plugin.decode(message); - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-4a'); - expect(decodeResult.formatted.description).toBe('Latest New Format'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.remaining.text).toBe('RT0,LT1,'); - expect(decodeResult.formatted.items.length).toBe(4); - expect(decodeResult.formatted.items[0].code).toBe('MSG_TOD'); - expect(decodeResult.formatted.items[0].value).toBe('10:16:06'); - expect(decodeResult.formatted.items[1].code).toBe('TAIL'); - expect(decodeResult.formatted.items[1].value).toBe('N317FR'); - expect(decodeResult.formatted.items[2].code).toBe('ORG'); - expect(decodeResult.formatted.items[2].value).toBe('KMDW'); - expect(decodeResult.formatted.items[3].code).toBe('DST'); - expect(decodeResult.formatted.items[3].value).toBe('----'); -}); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-4a'); + expect(decodeResult.formatted.description).toBe('Latest New Format'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.remaining.text).toBe('RT0,LT1,'); + expect(decodeResult.formatted.items.length).toBe(4); + expect(decodeResult.formatted.items[0].code).toBe('MSG_TOD'); + expect(decodeResult.formatted.items[0].value).toBe('10:16:06'); + expect(decodeResult.formatted.items[1].code).toBe('TAIL'); + expect(decodeResult.formatted.items[1].value).toBe('N317FR'); + expect(decodeResult.formatted.items[2].code).toBe('ORG'); + expect(decodeResult.formatted.items[2].value).toBe('KMDW'); + expect(decodeResult.formatted.items[3].code).toBe('DST'); + expect(decodeResult.formatted.items[3].value).toBe('----'); + }); -test('decodes variant 2', () => { - // https://app.airframes.io/messages/3461807403 - message.text = 'N45129W093113MSP/07 ,204436123VECTORS,,P04,268044858,46904221'; - const decodeResult = plugin.decode(message); + test('decodes variant 2', () => { + // https://app.airframes.io/messages/3461807403 + message.text = + 'N45129W093113MSP/07 ,204436123VECTORS,,P04,268044858,46904221'; + const decodeResult = plugin.decode(message); - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-4a'); - expect(decodeResult.formatted.description).toBe('Latest New Format'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.remaining.text).toBe('268044858,46904221'); - expect(decodeResult.formatted.items.length).toBe(4); - expect(decodeResult.formatted.items[0].code).toBe('POS'); - expect(decodeResult.formatted.items[0].value).toBe('45.129 N, 93.113 W'); - expect(decodeResult.formatted.items[1].code).toBe('ALT'); - expect(decodeResult.formatted.items[1].value).toBe('12300 feet'); - expect(decodeResult.formatted.items[2].code).toBe('ROUTE'); - expect(decodeResult.formatted.items[2].value).toBe('MSP/07@20:44:36 > VECTORS'); - expect(decodeResult.formatted.items[3].code).toBe('OATEMP'); - expect(decodeResult.formatted.items[3].value).toBe('4 degrees'); -}); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-4a'); + expect(decodeResult.formatted.description).toBe('Latest New Format'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.remaining.text).toBe('268044858,46904221'); + expect(decodeResult.formatted.items.length).toBe(4); + expect(decodeResult.formatted.items[0].code).toBe('POS'); + expect(decodeResult.formatted.items[0].value).toBe('45.129 N, 93.113 W'); + expect(decodeResult.formatted.items[1].code).toBe('ALT'); + expect(decodeResult.formatted.items[1].value).toBe('12300 feet'); + expect(decodeResult.formatted.items[2].code).toBe('ROUTE'); + expect(decodeResult.formatted.items[2].value).toBe( + 'MSP/07@20:44:36 > VECTORS', + ); + expect(decodeResult.formatted.items[3].code).toBe('OATEMP'); + expect(decodeResult.formatted.items[3].value).toBe('4 degrees'); + }); -test('decodes variant 2, C-Band', () => { - // https://app.airframes.io/messages/3461407615 - message.text = 'M60ALH0752N22456E077014OSE35 ,192027370VEX36 ,192316,M46,275043309,85220111'; - const decodeResult = plugin.decode(message); + test('decodes variant 2, C-Band', () => { + // https://app.airframes.io/messages/3461407615 + message.text = + 'M60ALH0752N22456E077014OSE35 ,192027370VEX36 ,192316,M46,275043309,85220111'; + const decodeResult = plugin.decode(message); - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-4a'); - expect(decodeResult.formatted.description).toBe('Latest New Format'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.remaining.text).toBe('275043309,85220111'); - expect(decodeResult.formatted.items.length).toBe(5); - expect(decodeResult.formatted.items[0].code).toBe('FLIGHT'); - expect(decodeResult.formatted.items[0].value).toBe('LH752'); - expect(decodeResult.formatted.items[1].code).toBe('POS'); - expect(decodeResult.formatted.items[1].value).toBe('22.456 N, 77.014 E'); - expect(decodeResult.formatted.items[2].code).toBe('ALT'); - expect(decodeResult.formatted.items[2].value).toBe('37000 feet'); - expect(decodeResult.formatted.items[3].code).toBe('ROUTE'); - expect(decodeResult.formatted.items[3].value).toBe('OSE35@19:20:27 > VEX36@19:23:16'); - expect(decodeResult.formatted.items[4].code).toBe('OATEMP'); - expect(decodeResult.formatted.items[4].value).toBe('-46 degrees'); -}); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-4a'); + expect(decodeResult.formatted.description).toBe('Latest New Format'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.remaining.text).toBe('275043309,85220111'); + expect(decodeResult.formatted.items.length).toBe(5); + expect(decodeResult.formatted.items[0].code).toBe('FLIGHT'); + expect(decodeResult.formatted.items[0].value).toBe('LH752'); + expect(decodeResult.formatted.items[1].code).toBe('POS'); + expect(decodeResult.formatted.items[1].value).toBe('22.456 N, 77.014 E'); + expect(decodeResult.formatted.items[2].code).toBe('ALT'); + expect(decodeResult.formatted.items[2].value).toBe('37000 feet'); + expect(decodeResult.formatted.items[3].code).toBe('ROUTE'); + expect(decodeResult.formatted.items[3].value).toBe( + 'OSE35@19:20:27 > VEX36@19:23:16', + ); + expect(decodeResult.formatted.items[4].code).toBe('OATEMP'); + expect(decodeResult.formatted.items[4].value).toBe('-46 degrees'); + }); -test('decodes variant 3', () => { - // https://globe.adsbexchange.com/?icao=A39AC6&showTrace=2024-09-22×tamp=1727009085 - message.text = '124442,1320, 138,33467,N 41.093,W 72.677'; - const decodeResult = plugin.decode(message); + test('decodes variant 3', () => { + // https://globe.adsbexchange.com/?icao=A39AC6&showTrace=2024-09-22×tamp=1727009085 + message.text = '124442,1320, 138,33467,N 41.093,W 72.677'; + const decodeResult = plugin.decode(message); - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-4a'); - expect(decodeResult.formatted.description).toBe('Latest New Format'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.remaining.text).toBe(' 138'); - expect(decodeResult.formatted.items.length).toBe(4); - expect(decodeResult.formatted.items[0].code).toBe('MSG_TOD'); - expect(decodeResult.formatted.items[0].value).toBe('12:44:42'); - expect(decodeResult.formatted.items[1].code).toBe('ETA'); - expect(decodeResult.formatted.items[1].value).toBe('13:20:00'); - expect(decodeResult.formatted.items[2].code).toBe('ALT'); - expect(decodeResult.formatted.items[2].value).toBe('33467 feet'); - expect(decodeResult.formatted.items[3].code).toBe('POS'); - expect(decodeResult.formatted.items[3].value).toBe('41.093 N, 72.677 W'); -}); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-4a'); + expect(decodeResult.formatted.description).toBe('Latest New Format'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.remaining.text).toBe(' 138'); + expect(decodeResult.formatted.items.length).toBe(4); + expect(decodeResult.formatted.items[0].code).toBe('MSG_TOD'); + expect(decodeResult.formatted.items[0].value).toBe('12:44:42'); + expect(decodeResult.formatted.items[1].code).toBe('ETA'); + expect(decodeResult.formatted.items[1].value).toBe('13:20:00'); + expect(decodeResult.formatted.items[2].code).toBe('ALT'); + expect(decodeResult.formatted.items[2].value).toBe('33467 feet'); + expect(decodeResult.formatted.items[3].code).toBe('POS'); + expect(decodeResult.formatted.items[3].value).toBe('41.093 N, 72.677 W'); + }); + + test('decodes ', () => { + // https://app.airframes.io/messages/3449413366 + message.text = 'DIS01,182103,WEN3100,WRONG CREW HAHAHA'; + const decodeResult = plugin.decode(message); -test('decodes ', () => { - // https://app.airframes.io/messages/3449413366 - message.text = 'DIS01,182103,WEN3100,WRONG CREW HAHAHA'; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(false); - expect(decodeResult.decoder.decodeLevel).toBe('none'); - expect(decodeResult.decoder.name).toBe('label-4a'); - expect(decodeResult.formatted.description).toBe('Latest New Format'); - expect(decodeResult.formatted.items.length).toBe(0); + expect(decodeResult.decoded).toBe(false); + expect(decodeResult.decoder.decodeLevel).toBe('none'); + expect(decodeResult.decoder.name).toBe('label-4a'); + expect(decodeResult.formatted.description).toBe('Latest New Format'); + expect(decodeResult.formatted.items.length).toBe(0); + }); }); -}); \ No newline at end of file diff --git a/lib/plugins/Label_4A.ts b/lib/plugins/Label_4A.ts index 987a439..8add037 100644 --- a/lib/plugins/Label_4A.ts +++ b/lib/plugins/Label_4A.ts @@ -8,79 +8,99 @@ import { Waypoint } from '../types/waypoint'; export class Label_4A extends DecoderPlugin { name = 'label-4a'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['4A'], }; } - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { const decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; decodeResult.message = message; decodeResult.formatted.description = 'Latest New Format'; - // Inmarsat C-band seems to prefix normal messages with a message number and flight number let text = message.text; if (text.match(/^M\d{2}A\w{6}/)) { - ResultFormatter.flightNumber(decodeResult, message.text.substring(4, 10).replace(/^([A-Z]+)0*/g, "$1")); - text = text.substring(10); + ResultFormatter.flightNumber( + decodeResult, + message.text.substring(4, 10).replace(/^([A-Z]+)0*/g, '$1'), + ); + text = text.substring(10); } decodeResult.decoded = true; - const fields = text.split(","); + const fields = text.split(','); if (fields.length === 11) { - // variant 1 - ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[0])); - ResultFormatter.tail(decodeResult, fields[2].replace(".", "")); - if (fields[3]) - ResultFormatter.callsign(decodeResult, fields[3]); - ResultFormatter.departureAirport(decodeResult, fields[4]); - ResultFormatter.arrivalAirport(decodeResult, fields[5]); - const alt = text.substring(48, 51); - if(alt !== '') { - ResultFormatter.altitude(decodeResult, Number(alt) * 100); - } - ResultFormatter.unknownArr(decodeResult, fields.slice(8)); + // variant 1 + ResultFormatter.time_of_day( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(fields[0]), + ); + ResultFormatter.tail(decodeResult, fields[2].replace('.', '')); + if (fields[3]) ResultFormatter.callsign(decodeResult, fields[3]); + ResultFormatter.departureAirport(decodeResult, fields[4]); + ResultFormatter.arrivalAirport(decodeResult, fields[5]); + const alt = text.substring(48, 51); + if (alt !== '') { + ResultFormatter.altitude(decodeResult, Number(alt) * 100); + } + ResultFormatter.unknownArr(decodeResult, fields.slice(8)); } else if (fields.length === 6) { - if (fields[0].match(/^[NS]/)) { - // variant 2 - ResultFormatter.position(decodeResult, CoordinateUtils.decodeStringCoordinates(fields[0].substring(0, 13))); - let wp1: Waypoint = { - name: fields[0].substring(13).trim(), - time: DateTimeUtils.convertHHMMSSToTod(fields[1].substring(0, 6)), - timeFormat: 'tod', - }; - ResultFormatter.altitude(decodeResult, Number(fields[1].substring(6, 9)) * 100); - let wp2: Waypoint = { - name: fields[1].substring(9).trim(), - time: DateTimeUtils.convertHHMMSSToTod(fields[2]), - timeFormat: 'tod', - }; - ResultFormatter.route(decodeResult, {waypoints: [wp1, wp2]}); - ResultFormatter.temperature(decodeResult, fields[3]); - ResultFormatter.unknownArr(decodeResult, fields.slice(4)); - } else { - // variant 3 - ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[0])); - ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[1])); - ResultFormatter.unknown(decodeResult, fields[2]); - ResultFormatter.altitude(decodeResult, Number(fields[3])); - ResultFormatter.position(decodeResult, CoordinateUtils.decodeStringCoordinates((fields[4]+fields[5]).replace(/[ \.]/g, ""))); - } + if (fields[0].match(/^[NS]/)) { + // variant 2 + ResultFormatter.position( + decodeResult, + CoordinateUtils.decodeStringCoordinates(fields[0].substring(0, 13)), + ); + let wp1: Waypoint = { + name: fields[0].substring(13).trim(), + time: DateTimeUtils.convertHHMMSSToTod(fields[1].substring(0, 6)), + timeFormat: 'tod', + }; + ResultFormatter.altitude( + decodeResult, + Number(fields[1].substring(6, 9)) * 100, + ); + let wp2: Waypoint = { + name: fields[1].substring(9).trim(), + time: DateTimeUtils.convertHHMMSSToTod(fields[2]), + timeFormat: 'tod', + }; + ResultFormatter.route(decodeResult, { waypoints: [wp1, wp2] }); + ResultFormatter.temperature(decodeResult, fields[3]); + ResultFormatter.unknownArr(decodeResult, fields.slice(4)); + } else { + // variant 3 + ResultFormatter.time_of_day( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(fields[0]), + ); + ResultFormatter.eta( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(fields[1]), + ); + ResultFormatter.unknown(decodeResult, fields[2]); + ResultFormatter.altitude(decodeResult, Number(fields[3])); + ResultFormatter.position( + decodeResult, + CoordinateUtils.decodeStringCoordinates( + (fields[4] + fields[5]).replace(/[ \.]/g, ''), + ), + ); + } } else { - decodeResult.decoded = false; - ResultFormatter.unknown(decodeResult, text); + decodeResult.decoded = false; + ResultFormatter.unknown(decodeResult, text); } if (decodeResult.decoded) { - if(!decodeResult.remaining.text) - decodeResult.decoder.decodeLevel = 'full'; - else - decodeResult.decoder.decodeLevel = 'partial'; + if (!decodeResult.remaining.text) + decodeResult.decoder.decodeLevel = 'full'; + else decodeResult.decoder.decodeLevel = 'partial'; } else { - decodeResult.decoder.decodeLevel = 'none'; + decodeResult.decoder.decodeLevel = 'none'; } return decodeResult; diff --git a/lib/plugins/Label_4A_01.test.ts b/lib/plugins/Label_4A_01.test.ts index 6a92263..b42f13b 100644 --- a/lib/plugins/Label_4A_01.test.ts +++ b/lib/plugins/Label_4A_01.test.ts @@ -2,7 +2,7 @@ import { MessageDecoder } from '../MessageDecoder'; import { Label_4A_01 } from './Label_4A_01'; describe('Label 4A preamble 01', () => { - let plugin : Label_4A_01; + let plugin: Label_4A_01; const message = { label: '4A', text: '' }; beforeEach(() => { @@ -10,56 +10,56 @@ describe('Label 4A preamble 01', () => { plugin = new Label_4A_01(decoder); }); -test('matches qualifiers', () => { - expect(plugin.decode).toBeDefined(); - expect(plugin.name).toBe('label-4a-01'); - expect(plugin.qualifiers).toBeDefined(); - expect(plugin.qualifiers()).toEqual({ - labels: ['4A'], - preambles: ['01'], + test('matches qualifiers', () => { + expect(plugin.decode).toBeDefined(); + expect(plugin.name).toBe('label-4a-01'); + expect(plugin.qualifiers).toBeDefined(); + expect(plugin.qualifiers()).toEqual({ + labels: ['4A'], + preambles: ['01'], + }); }); -}); -test('decodes variant 1', () => { - // https://app.airframes.io/messages/3450562911 - message.text = '01DCAP VIR41R/190203EGLLKSFO\r\n+ 1418158.0+ 24.8'; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-4a-01'); - expect(decodeResult.formatted.description).toBe('Latest New Format'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.remaining.text).toBe('158.0'); - expect(decodeResult.formatted.items.length).toBe(7); - expect(decodeResult.formatted.items[0].code).toBe('STATE_CHANGE'); - expect(decodeResult.formatted.items[0].value).toBe('Descent -> Approach'); - expect(decodeResult.formatted.items[1].code).toBe('CALLSIGN'); - expect(decodeResult.formatted.items[1].value).toBe('VIR41R'); - expect(decodeResult.formatted.items[2].code).toBe('MSG_TOD'); - expect(decodeResult.formatted.items[2].value).toBe('19:02:03'); - expect(decodeResult.formatted.items[3].code).toBe('ORG'); - expect(decodeResult.formatted.items[3].value).toBe('EGLL'); - expect(decodeResult.formatted.items[4].code).toBe('DST'); - expect(decodeResult.formatted.items[4].value).toBe('KSFO'); - expect(decodeResult.formatted.items[5].code).toBe('ALT'); - expect(decodeResult.formatted.items[5].value).toBe("1418 feet"); - expect(decodeResult.formatted.items[6].code).toBe('OATEMP'); - expect(decodeResult.formatted.items[6].value).toBe('24.8 degrees'); -}); + test('decodes variant 1', () => { + // https://app.airframes.io/messages/3450562911 + message.text = '01DCAP VIR41R/190203EGLLKSFO\r\n+ 1418158.0+ 24.8'; + const decodeResult = plugin.decode(message); -// disabled because all messages should decode -test.skip('decodes Label 4A_01 ', () => { - const decoder = new MessageDecoder(); - const decoderPlugin = new Label_4A_01(decoder); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-4a-01'); + expect(decodeResult.formatted.description).toBe('Latest New Format'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.remaining.text).toBe('158.0'); + expect(decodeResult.formatted.items.length).toBe(7); + expect(decodeResult.formatted.items[0].code).toBe('STATE_CHANGE'); + expect(decodeResult.formatted.items[0].value).toBe('Descent -> Approach'); + expect(decodeResult.formatted.items[1].code).toBe('CALLSIGN'); + expect(decodeResult.formatted.items[1].value).toBe('VIR41R'); + expect(decodeResult.formatted.items[2].code).toBe('MSG_TOD'); + expect(decodeResult.formatted.items[2].value).toBe('19:02:03'); + expect(decodeResult.formatted.items[3].code).toBe('ORG'); + expect(decodeResult.formatted.items[3].value).toBe('EGLL'); + expect(decodeResult.formatted.items[4].code).toBe('DST'); + expect(decodeResult.formatted.items[4].value).toBe('KSFO'); + expect(decodeResult.formatted.items[5].code).toBe('ALT'); + expect(decodeResult.formatted.items[5].value).toBe('1418 feet'); + expect(decodeResult.formatted.items[6].code).toBe('OATEMP'); + expect(decodeResult.formatted.items[6].value).toBe('24.8 degrees'); + }); - message.text = '4A_01 Bogus message'; - const decodeResult = decoderPlugin.decode(message); - - expect(decodeResult.decoded).toBe(false); - expect(decodeResult.decoder.decodeLevel).toBe('none'); - expect(decodeResult.decoder.name).toBe('label-4a-01'); - expect(decodeResult.formatted.description).toBe('Latest New Format'); - expect(decodeResult.formatted.items.length).toBe(0); + // disabled because all messages should decode + test.skip('decodes Label 4A_01 ', () => { + const decoder = new MessageDecoder(); + const decoderPlugin = new Label_4A_01(decoder); + + message.text = '4A_01 Bogus message'; + const decodeResult = decoderPlugin.decode(message); + + expect(decodeResult.decoded).toBe(false); + expect(decodeResult.decoder.decodeLevel).toBe('none'); + expect(decodeResult.decoder.name).toBe('label-4a-01'); + expect(decodeResult.formatted.description).toBe('Latest New Format'); + expect(decodeResult.formatted.items.length).toBe(0); + }); }); -}); \ No newline at end of file diff --git a/lib/plugins/Label_4A_01.ts b/lib/plugins/Label_4A_01.ts index 57bc43f..d22cbcb 100644 --- a/lib/plugins/Label_4A_01.ts +++ b/lib/plugins/Label_4A_01.ts @@ -6,42 +6,46 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_4A_01 extends DecoderPlugin { name = 'label-4a-01'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['4A'], preambles: ['01'], }; } - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { const decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; decodeResult.message = message; decodeResult.formatted.description = 'Latest New Format'; decodeResult.decoded = true; - let rgx = message.text.match(/^01([A-Z]{2})([A-Z]{2})\s*(\w+)\/(\d{6})([A-Z]{4})([A-Z]{4})\r\n([+-]\s*\d+)(\d{3}\.\d)([+-]\s*\d+\.\d)/); + let rgx = message.text.match( + /^01([A-Z]{2})([A-Z]{2})\s*(\w+)\/(\d{6})([A-Z]{4})([A-Z]{4})\r\n([+-]\s*\d+)(\d{3}\.\d)([+-]\s*\d+\.\d)/, + ); if (rgx) { - ResultFormatter.state_change(decodeResult, rgx[1], rgx[2]); - ResultFormatter.callsign(decodeResult, rgx[3]); - ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(rgx[4] + "00")); - ResultFormatter.departureAirport(decodeResult, rgx[5]); - ResultFormatter.arrivalAirport(decodeResult, rgx[6]); - ResultFormatter.altitude(decodeResult, Number(rgx[7].replace(/ /g, ""))); - ResultFormatter.unknown(decodeResult, rgx[8]); - ResultFormatter.temperature(decodeResult, rgx[9].replace(/ /g, "")); + ResultFormatter.state_change(decodeResult, rgx[1], rgx[2]); + ResultFormatter.callsign(decodeResult, rgx[3]); + ResultFormatter.time_of_day( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(rgx[4] + '00'), + ); + ResultFormatter.departureAirport(decodeResult, rgx[5]); + ResultFormatter.arrivalAirport(decodeResult, rgx[6]); + ResultFormatter.altitude(decodeResult, Number(rgx[7].replace(/ /g, ''))); + ResultFormatter.unknown(decodeResult, rgx[8]); + ResultFormatter.temperature(decodeResult, rgx[9].replace(/ /g, '')); } else { - decodeResult.decoded = false; - ResultFormatter.unknown(decodeResult, message.text); + decodeResult.decoded = false; + ResultFormatter.unknown(decodeResult, message.text); } if (decodeResult.decoded) { - if(!decodeResult.remaining.text) - decodeResult.decoder.decodeLevel = 'full'; - else - decodeResult.decoder.decodeLevel = 'partial'; + if (!decodeResult.remaining.text) + decodeResult.decoder.decodeLevel = 'full'; + else decodeResult.decoder.decodeLevel = 'partial'; } else { - decodeResult.decoder.decodeLevel = 'none'; + decodeResult.decoder.decodeLevel = 'none'; } return decodeResult; diff --git a/lib/plugins/Label_4A_DIS.test.ts b/lib/plugins/Label_4A_DIS.test.ts index 80051f0..fcd3611 100644 --- a/lib/plugins/Label_4A_DIS.test.ts +++ b/lib/plugins/Label_4A_DIS.test.ts @@ -3,51 +3,51 @@ import { Label_4A_DIS } from './Label_4A_DIS'; describe('Label 4A preamble DIS', () => { let plugin: Label_4A_DIS; - const message = {label: '4A', text: ''}; + const message = { label: '4A', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); plugin = new Label_4A_DIS(decoder); }); -test('matches Label 4A_DIS qualifiers', () => { - expect(plugin.decode).toBeDefined(); - expect(plugin.name).toBe('label-4a-dis'); - expect(plugin.qualifiers).toBeDefined(); - expect(plugin.qualifiers()).toEqual({ - labels: ['4A'], - preambles: ['DIS'], + test('matches Label 4A_DIS qualifiers', () => { + expect(plugin.decode).toBeDefined(); + expect(plugin.name).toBe('label-4a-dis'); + expect(plugin.qualifiers).toBeDefined(); + expect(plugin.qualifiers()).toEqual({ + labels: ['4A'], + preambles: ['DIS'], + }); }); -}); -test('decodes Label 4A_DIS', () => { - // https://app.airframes.io/messages/3450166197 - message.text = 'DIS01,190009,WEN3140,@HOLD CNX'; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('full'); - expect(decodeResult.decoder.name).toBe('label-4a-dis'); - expect(decodeResult.formatted.description).toBe('Latest New Format'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.remaining.text).toBe(undefined); - expect(decodeResult.formatted.items.length).toBe(3); - expect(decodeResult.formatted.items[0].code).toBe('MSG_TOD'); - expect(decodeResult.formatted.items[0].value).toBe('00:09:00'); - expect(decodeResult.formatted.items[1].code).toBe('CALLSIGN'); - expect(decodeResult.formatted.items[1].value).toBe('WEN3140'); - expect(decodeResult.formatted.items[2].code).toBe('FREE_TEXT'); - expect(decodeResult.formatted.items[2].value).toBe('@HOLD CNX'); -}); + test('decodes Label 4A_DIS', () => { + // https://app.airframes.io/messages/3450166197 + message.text = 'DIS01,190009,WEN3140,@HOLD CNX'; + const decodeResult = plugin.decode(message); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('full'); + expect(decodeResult.decoder.name).toBe('label-4a-dis'); + expect(decodeResult.formatted.description).toBe('Latest New Format'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.remaining.text).toBe(undefined); + expect(decodeResult.formatted.items.length).toBe(3); + expect(decodeResult.formatted.items[0].code).toBe('MSG_TOD'); + expect(decodeResult.formatted.items[0].value).toBe('00:09:00'); + expect(decodeResult.formatted.items[1].code).toBe('CALLSIGN'); + expect(decodeResult.formatted.items[1].value).toBe('WEN3140'); + expect(decodeResult.formatted.items[2].code).toBe('FREE_TEXT'); + expect(decodeResult.formatted.items[2].value).toBe('@HOLD CNX'); + }); + + // disabled because all messages should decode + test.skip('decodes Label 4A_DIS ', () => { + message.text = '4A_DIS Bogus message'; + const decodeResult = plugin.decode(message); -// disabled because all messages should decode -test.skip('decodes Label 4A_DIS ', () => { - message.text = '4A_DIS Bogus message'; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(false); - expect(decodeResult.decoder.decodeLevel).toBe('none'); - expect(decodeResult.decoder.name).toBe('label-4a-dis'); - expect(decodeResult.formatted.description).toBe('Latest New Format'); - expect(decodeResult.formatted.items.length).toBe(0); + expect(decodeResult.decoded).toBe(false); + expect(decodeResult.decoder.decodeLevel).toBe('none'); + expect(decodeResult.decoder.name).toBe('label-4a-dis'); + expect(decodeResult.formatted.description).toBe('Latest New Format'); + expect(decodeResult.formatted.items.length).toBe(0); + }); }); -}); \ No newline at end of file diff --git a/lib/plugins/Label_4A_DIS.ts b/lib/plugins/Label_4A_DIS.ts index e530291..4043b40 100644 --- a/lib/plugins/Label_4A_DIS.ts +++ b/lib/plugins/Label_4A_DIS.ts @@ -6,32 +6,34 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_4A_DIS extends DecoderPlugin { name = 'label-4a-dis'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['4A'], preambles: ['DIS'], }; } - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { const decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; decodeResult.message = message; decodeResult.formatted.description = 'Latest New Format'; decodeResult.decoded = true; - const fields = message.text.split(","); - ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[1].substring(2) + "00")); + const fields = message.text.split(','); + ResultFormatter.time_of_day( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(fields[1].substring(2) + '00'), + ); ResultFormatter.callsign(decodeResult, fields[2]); - ResultFormatter.freetext(decodeResult, fields.slice(3).join("")); + ResultFormatter.freetext(decodeResult, fields.slice(3).join('')); if (decodeResult.decoded) { - if(!decodeResult.remaining.text) - decodeResult.decoder.decodeLevel = 'full'; - else - decodeResult.decoder.decodeLevel = 'partial'; + if (!decodeResult.remaining.text) + decodeResult.decoder.decodeLevel = 'full'; + else decodeResult.decoder.decodeLevel = 'partial'; } else { - decodeResult.decoder.decodeLevel = 'none'; + decodeResult.decoder.decodeLevel = 'none'; } return decodeResult; diff --git a/lib/plugins/Label_4A_DOOR.test.ts b/lib/plugins/Label_4A_DOOR.test.ts index c0ad375..0dff596 100644 --- a/lib/plugins/Label_4A_DOOR.test.ts +++ b/lib/plugins/Label_4A_DOOR.test.ts @@ -3,49 +3,49 @@ import { Label_4A_DOOR } from './Label_4A_DOOR'; describe('Label 4A preamble DOOR', () => { let plugin: Label_4A_DOOR; - const message = {label: '4A', text: ''}; + const message = { label: '4A', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); plugin = new Label_4A_DOOR(decoder); }); -test('matches qualifiers', () => { - expect(plugin.decode).toBeDefined(); - expect(plugin.name).toBe('label-4a-door'); - expect(plugin.qualifiers).toBeDefined(); - expect(plugin.qualifiers()).toEqual({ - labels: ['4A'], - preambles: ['DOOR'], + test('matches qualifiers', () => { + expect(plugin.decode).toBeDefined(); + expect(plugin.name).toBe('label-4a-door'); + expect(plugin.qualifiers).toBeDefined(); + expect(plugin.qualifiers()).toEqual({ + labels: ['4A'], + preambles: ['DOOR'], + }); }); -}); -test('decodes variant 1', () => { - // https://app.airframes.io/messages/3453841057 - message.text = 'DOOR/FWDENTRY CLSD 1440'; - const decodeResult = plugin.decode(message); + test('decodes variant 1', () => { + // https://app.airframes.io/messages/3453841057 + message.text = 'DOOR/FWDENTRY CLSD 1440'; + const decodeResult = plugin.decode(message); - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('full'); - expect(decodeResult.decoder.name).toBe('label-4a-door'); - expect(decodeResult.formatted.description).toBe('Latest New Format'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.remaining.text).toBe(undefined); - expect(decodeResult.formatted.items.length).toBe(2); - expect(decodeResult.formatted.items[0].code).toBe('DOOR'); - expect(decodeResult.formatted.items[0].value).toBe('FWDENTRY CLSD'); - expect(decodeResult.formatted.items[1].code).toBe('MSG_TOD'); - expect(decodeResult.formatted.items[1].value).toBe('14:40:00'); -}); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('full'); + expect(decodeResult.decoder.name).toBe('label-4a-door'); + expect(decodeResult.formatted.description).toBe('Latest New Format'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.remaining.text).toBe(undefined); + expect(decodeResult.formatted.items.length).toBe(2); + expect(decodeResult.formatted.items[0].code).toBe('DOOR'); + expect(decodeResult.formatted.items[0].value).toBe('FWDENTRY CLSD'); + expect(decodeResult.formatted.items[1].code).toBe('MSG_TOD'); + expect(decodeResult.formatted.items[1].value).toBe('14:40:00'); + }); -// disabled because all messages should decode -test.skip('decodes Label 4A_DOOR ', () => { - message.text = '4A_DOOR Bogus message'; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(false); - expect(decodeResult.decoder.decodeLevel).toBe('none'); - expect(decodeResult.decoder.name).toBe('label-4a-door'); - expect(decodeResult.formatted.description).toBe('Latest New Format'); - expect(decodeResult.formatted.items.length).toBe(0); + // disabled because all messages should decode + test.skip('decodes Label 4A_DOOR ', () => { + message.text = '4A_DOOR Bogus message'; + const decodeResult = plugin.decode(message); + + expect(decodeResult.decoded).toBe(false); + expect(decodeResult.decoder.decodeLevel).toBe('none'); + expect(decodeResult.decoder.name).toBe('label-4a-door'); + expect(decodeResult.formatted.description).toBe('Latest New Format'); + expect(decodeResult.formatted.items.length).toBe(0); + }); }); -}); \ No newline at end of file diff --git a/lib/plugins/Label_4A_DOOR.ts b/lib/plugins/Label_4A_DOOR.ts index a9014ed..936c498 100644 --- a/lib/plugins/Label_4A_DOOR.ts +++ b/lib/plugins/Label_4A_DOOR.ts @@ -6,35 +6,41 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_4A_DOOR extends DecoderPlugin { name = 'label-4a-door'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['4A'], preambles: ['DOOR'], }; } - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { const decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; decodeResult.message = message; decodeResult.formatted.description = 'Latest New Format'; decodeResult.decoded = true; - const fields = message.text.split(" "); + const fields = message.text.split(' '); if (fields.length === 3) { - ResultFormatter.door_event(decodeResult, fields[0].split("/")[1], fields[1]); - ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[2] + "00")); + ResultFormatter.door_event( + decodeResult, + fields[0].split('/')[1], + fields[1], + ); + ResultFormatter.time_of_day( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(fields[2] + '00'), + ); } else { - decodeResult.decoded = false; - ResultFormatter.unknown(decodeResult, message.text); + decodeResult.decoded = false; + ResultFormatter.unknown(decodeResult, message.text); } if (decodeResult.decoded) { - if(!decodeResult.remaining.text) - decodeResult.decoder.decodeLevel = 'full'; - else - decodeResult.decoder.decodeLevel = 'partial'; + if (!decodeResult.remaining.text) + decodeResult.decoder.decodeLevel = 'full'; + else decodeResult.decoder.decodeLevel = 'partial'; } else { - decodeResult.decoder.decodeLevel = 'none'; + decodeResult.decoder.decodeLevel = 'none'; } return decodeResult; diff --git a/lib/plugins/Label_4A_Slash_01.test.ts b/lib/plugins/Label_4A_Slash_01.test.ts index ecc75f4..3cccf1e 100644 --- a/lib/plugins/Label_4A_Slash_01.test.ts +++ b/lib/plugins/Label_4A_Slash_01.test.ts @@ -3,49 +3,49 @@ import { Label_4A_Slash_01 } from './Label_4A_Slash_01'; describe('Label 4A preamble /01', () => { let plugin: Label_4A_Slash_01; - const message = {label: '4A', text: ''}; + const message = { label: '4A', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); plugin = new Label_4A_Slash_01(decoder); }); -test('matches qualifiers', () => { - expect(plugin.decode).toBeDefined(); - expect(plugin.name).toBe('label-4a-slash-01'); - expect(plugin.qualifiers).toBeDefined(); - expect(plugin.qualifiers()).toEqual({ - labels: ['4A'], - preambles: ['/01'], + test('matches qualifiers', () => { + expect(plugin.decode).toBeDefined(); + expect(plugin.name).toBe('label-4a-slash-01'); + expect(plugin.qualifiers).toBeDefined(); + expect(plugin.qualifiers()).toEqual({ + labels: ['4A'], + preambles: ['/01'], + }); }); -}); -test('decodes Label 4A_Slash_01', () => { - const decoder = new MessageDecoder(); - const decoderPlugin = new Label_4A_Slash_01(decoder); - - // https://app.airframes.io/messages/3460403039 - message.text = '/01-C'; - const decodeResult = decoderPlugin.decode(message); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-4a-slash-01'); - expect(decodeResult.formatted.description).toBe('Latest New Format'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.remaining.text).toBe('C'); - expect(decodeResult.formatted.items.length).toBe(0); -}); + test('decodes Label 4A_Slash_01', () => { + const decoder = new MessageDecoder(); + const decoderPlugin = new Label_4A_Slash_01(decoder); + + // https://app.airframes.io/messages/3460403039 + message.text = '/01-C'; + const decodeResult = decoderPlugin.decode(message); -// disabled because all messages should decode -test.skip('decodes Label 4A_Slash_01 ', () => { - message.text = '4A_Slash_01 Bogus message'; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(false); - expect(decodeResult.decoder.decodeLevel).toBe('none'); - expect(decodeResult.decoder.name).toBe('label-4a-slash-01'); - expect(decodeResult.formatted.description).toBe('Latest New Format'); - expect(decodeResult.formatted.items.length).toBe(0); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-4a-slash-01'); + expect(decodeResult.formatted.description).toBe('Latest New Format'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.remaining.text).toBe('C'); + expect(decodeResult.formatted.items.length).toBe(0); + }); + + // disabled because all messages should decode + test.skip('decodes Label 4A_Slash_01 ', () => { + message.text = '4A_Slash_01 Bogus message'; + const decodeResult = plugin.decode(message); + + expect(decodeResult.decoded).toBe(false); + expect(decodeResult.decoder.decodeLevel).toBe('none'); + expect(decodeResult.decoder.name).toBe('label-4a-slash-01'); + expect(decodeResult.formatted.description).toBe('Latest New Format'); + expect(decodeResult.formatted.items.length).toBe(0); + }); }); -}); \ No newline at end of file diff --git a/lib/plugins/Label_4A_Slash_01.ts b/lib/plugins/Label_4A_Slash_01.ts index 32885de..d1004ad 100644 --- a/lib/plugins/Label_4A_Slash_01.ts +++ b/lib/plugins/Label_4A_Slash_01.ts @@ -1,39 +1,37 @@ import { DecoderPlugin } from '../DecoderPlugin'; import { DecodeResult, Message, Options } from '../DecoderPluginInterface'; -import { CoordinateUtils } from '../utils/coordinate_utils'; import { ResultFormatter } from '../utils/result_formatter'; export class Label_4A_Slash_01 extends DecoderPlugin { name = 'label-4a-slash-01'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['4A'], preambles: ['/01'], }; } - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { const decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; decodeResult.message = message; decodeResult.formatted.description = 'Latest New Format'; decodeResult.decoded = true; - if (message.text.length === 5 && message.text.substring(0,4) === "/01-") { - ResultFormatter.unknown(decodeResult, message.text.substring(4)); + if (message.text.length === 5 && message.text.substring(0, 4) === '/01-') { + ResultFormatter.unknown(decodeResult, message.text.substring(4)); } else { - decodeResult.decoded = false; - ResultFormatter.unknown(decodeResult, message.text); + decodeResult.decoded = false; + ResultFormatter.unknown(decodeResult, message.text); } if (decodeResult.decoded) { - if(!decodeResult.remaining.text) - decodeResult.decoder.decodeLevel = 'full'; - else - decodeResult.decoder.decodeLevel = 'partial'; + if (!decodeResult.remaining.text) + decodeResult.decoder.decodeLevel = 'full'; + else decodeResult.decoder.decodeLevel = 'partial'; } else { - decodeResult.decoder.decodeLevel = 'none'; + decodeResult.decoder.decodeLevel = 'none'; } return decodeResult; diff --git a/lib/plugins/Label_4J_POS.test.ts b/lib/plugins/Label_4J_POS.test.ts index 68180fe..839eb2c 100644 --- a/lib/plugins/Label_4J_POS.test.ts +++ b/lib/plugins/Label_4J_POS.test.ts @@ -2,16 +2,14 @@ import { MessageDecoder } from '../MessageDecoder'; import { Label_4J_POS } from './Label_4J_POS'; describe('Label 4J POS', () => { - let plugin: Label_4J_POS; - const message = {label: '4J', text: ''}; + const message = { label: '4J', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); plugin = new Label_4J_POS(decoder); }); - test('matches qualifiers', () => { expect(plugin.decode).toBeDefined(); expect(plugin.name).toBe('label-4j-pos'); @@ -25,7 +23,8 @@ describe('Label 4J POS', () => { // Disabled due to checksum mismatch. Possibly non-ascii characters in message? test.skip('decodes msg 1', () => { // https://app.airframes.io/messages/2434848463 - message.text = 'POS/ID91459S,BANKR31,/DC03032024,142813/MR64,0/ET31539/PSN39277W077359,142800,240,N39300W077110,031430,N38560W077150,M28,27619,MT370/CG311,160,350/FB732/VR329071'; + message.text = + 'POS/ID91459S,BANKR31,/DC03032024,142813/MR64,0/ET31539/PSN39277W077359,142800,240,N39300W077110,031430,N38560W077150,M28,27619,MT370/CG311,160,350/FB732/VR329071'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -40,23 +39,30 @@ describe('Label 4J POS', () => { expect(decodeResult.formatted.items[1].value).toBe('BANKR31'); expect(decodeResult.formatted.items[2].label).toBe('Day of Month'); expect(decodeResult.formatted.items[2].value).toBe('3'); - expect(decodeResult.formatted.items[3].label).toBe('Estimated Time of Arrival'); + expect(decodeResult.formatted.items[3].label).toBe( + 'Estimated Time of Arrival', + ); expect(decodeResult.formatted.items[3].value).toBe('15:39:00'); expect(decodeResult.formatted.items[4].label).toBe('Aircraft Position'); expect(decodeResult.formatted.items[4].value).toBe('39.462 N, 77.598 W'); expect(decodeResult.formatted.items[5].label).toBe('Aircraft Route'); - expect(decodeResult.formatted.items[5].value).toBe('(39.500 N, 77.183 W)@14:28:00 > (38.933 N, 77.250 W)@03:14:30 > ?'); + expect(decodeResult.formatted.items[5].value).toBe( + '(39.500 N, 77.183 W)@14:28:00 > (38.933 N, 77.250 W)@03:14:30 > ?', + ); expect(decodeResult.formatted.items[6].label).toBe('Altitude'); expect(decodeResult.formatted.items[6].value).toBe('24000 feet'); - expect(decodeResult.formatted.items[7].label).toBe('Outside Air Temperature (C)'); + expect(decodeResult.formatted.items[7].label).toBe( + 'Outside Air Temperature (C)', + ); expect(decodeResult.formatted.items[7].value).toBe('-28 degrees'); expect(decodeResult.formatted.items[8].label).toBe('Message Checksum'); expect(decodeResult.formatted.items[8].value).toBe('0x9071'); - expect(decodeResult.remaining.text).toBe('MR64,0,27619,MT370/CG311,160,350/FB732/VR32'); + expect(decodeResult.remaining.text).toBe( + 'MR64,0,27619,MT370/CG311,160,350/FB732/VR32', + ); }); test('decodes ', () => { - message.text = 'POS/ Bogus message'; const decodeResult = plugin.decode(message); diff --git a/lib/plugins/Label_4J_POS.ts b/lib/plugins/Label_4J_POS.ts index 0dd6be9..d170cec 100644 --- a/lib/plugins/Label_4J_POS.ts +++ b/lib/plugins/Label_4J_POS.ts @@ -5,7 +5,7 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_4J_POS extends DecoderPlugin { name = 'label-4j-pos'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['4J'], preambles: ['POS/'], @@ -15,16 +15,18 @@ export class Label_4J_POS extends DecoderPlugin { // copied from Label_H1.ts since i don't really want to have to have // something named like that decode more than 1 type // if we figure out a good name, i'll combine them - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { let decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; decodeResult.message = message; - const msg = message.text.replace(/\n|\r/g, ""); + const msg = message.text.replace(/\n|\r/g, ''); const decoded = H1Helper.decodeH1Message(decodeResult, msg); decodeResult.decoded = decoded; - decodeResult.decoder.decodeLevel = !decodeResult.remaining.text ? 'full' : 'partial'; + decodeResult.decoder.decodeLevel = !decodeResult.remaining.text + ? 'full' + : 'partial'; if (decodeResult.formatted.items.length === 0) { if (options.debug) { console.log(`Decoder: Unknown H1 message: ${message.text}`); diff --git a/lib/plugins/Label_4N.test.ts b/lib/plugins/Label_4N.test.ts index 286dcb7..33fccd0 100644 --- a/lib/plugins/Label_4N.test.ts +++ b/lib/plugins/Label_4N.test.ts @@ -3,137 +3,147 @@ import { Label_4N } from './Label_4N'; describe('Label 4N', () => { let plugin: Label_4N; - const message = {label: '4N', text: ''}; + const message = { label: '4N', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); plugin = new Label_4N(decoder); }); -test('matches Label 4N qualifiers', () => { - const decoder = new MessageDecoder(); - const decoderPlugin = new Label_4N(decoder); + test('matches Label 4N qualifiers', () => { + const decoder = new MessageDecoder(); + const decoderPlugin = new Label_4N(decoder); - expect(decoderPlugin.decode).toBeDefined(); - expect(decoderPlugin.name).toBe('label-4n'); -}); + expect(decoderPlugin.decode).toBeDefined(); + expect(decoderPlugin.name).toBe('label-4n'); + }); -test('decodes Label 4N variant 1', () => { - // https://globe.adsbexchange.com/?icao=A15027&showTrace=2024-09-23×tamp=1727057017 - message.text = '22024N MCI JFK1\r\n0013 0072 N040586 W074421 230'; - const decodeResult = plugin.decode(message); + test('decodes Label 4N variant 1', () => { + // https://globe.adsbexchange.com/?icao=A15027&showTrace=2024-09-23×tamp=1727057017 + message.text = '22024N MCI JFK1\r\n0013 0072 N040586 W074421 230'; + const decodeResult = plugin.decode(message); - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-4n'); - expect(decodeResult.formatted.description).toBe('Airline Defined'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.raw.day).toBe('22'); - expect(decodeResult.remaining.text).toBe('02 0013 0072'); - expect(decodeResult.formatted.items.length).toBe(4); - expect(decodeResult.formatted.items[0].code).toBe('ORG'); - expect(decodeResult.formatted.items[0].value).toBe('MCI'); - expect(decodeResult.formatted.items[1].code).toBe('DST'); - expect(decodeResult.formatted.items[1].value).toBe('JFK'); - expect(decodeResult.formatted.items[2].code).toBe('POS'); - expect(decodeResult.formatted.items[2].value).toBe('40.977 N, 74.702 W'); - expect(decodeResult.formatted.items[3].code).toBe('ALT'); - expect(decodeResult.formatted.items[3].value).toBe('23000 feet'); -}); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-4n'); + expect(decodeResult.formatted.description).toBe('Airline Defined'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.raw.day).toBe('22'); + expect(decodeResult.remaining.text).toBe('02 0013 0072'); + expect(decodeResult.formatted.items.length).toBe(4); + expect(decodeResult.formatted.items[0].code).toBe('ORG'); + expect(decodeResult.formatted.items[0].value).toBe('MCI'); + expect(decodeResult.formatted.items[1].code).toBe('DST'); + expect(decodeResult.formatted.items[1].value).toBe('JFK'); + expect(decodeResult.formatted.items[2].code).toBe('POS'); + expect(decodeResult.formatted.items[2].value).toBe('40.977 N, 74.702 W'); + expect(decodeResult.formatted.items[3].code).toBe('ALT'); + expect(decodeResult.formatted.items[3].value).toBe('23000 feet'); + }); -test('decodes Label 4N variant 2B', () => {// https://app.airframes.io/messages/3421601874 - message.text = '285,B,69005074-507,10/12,+36.081,-094.810,35014,002.3,ELP,SDF,SDF,17R/,17L/,0,0,,,,,,0,0,0,0,1,,,,,247.0,014.2,261.2,421A'; - const decodeResult = plugin.decode(message); + test('decodes Label 4N variant 2B', () => { + // https://app.airframes.io/messages/3421601874 + message.text = + '285,B,69005074-507,10/12,+36.081,-094.810,35014,002.3,ELP,SDF,SDF,17R/,17L/,0,0,,,,,,0,0,0,0,1,,,,,247.0,014.2,261.2,421A'; + const decodeResult = plugin.decode(message); - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-4n'); - expect(decodeResult.formatted.description).toBe('Airline Defined'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.raw.date).toBe('10/12'); - expect(decodeResult.remaining.text).toBe('B,69005074-507,002.3,0,0,0,0,0,0,1,247.0,014.2,261.2'); - expect(decodeResult.formatted.items.length).toBe(8); - expect(decodeResult.formatted.items[0].code).toBe('POS'); - expect(decodeResult.formatted.items[0].value).toBe('36.081 N, 94.810 W'); - expect(decodeResult.formatted.items[1].code).toBe('ALT'); - expect(decodeResult.formatted.items[1].value).toBe('35014 feet'); - expect(decodeResult.formatted.items[2].code).toBe('ORG'); - expect(decodeResult.formatted.items[2].value).toBe('ELP'); - expect(decodeResult.formatted.items[3].code).toBe('DST'); - expect(decodeResult.formatted.items[3].value).toBe('SDF'); - expect(decodeResult.formatted.items[4].code).toBe('ALT_DST'); - expect(decodeResult.formatted.items[4].value).toBe('SDF'); - expect(decodeResult.formatted.items[5].code).toBe('ARWY'); - expect(decodeResult.formatted.items[5].value).toBe('17R'); - expect(decodeResult.formatted.items[6].code).toBe('ALT_ARWY'); - expect(decodeResult.formatted.items[6].value).toBe('17L'); - expect(decodeResult.formatted.items[7].code).toBe('CHECKSUM'); - expect(decodeResult.formatted.items[7].value).toBe('0x421a'); -}); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-4n'); + expect(decodeResult.formatted.description).toBe('Airline Defined'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.raw.date).toBe('10/12'); + expect(decodeResult.remaining.text).toBe( + 'B,69005074-507,002.3,0,0,0,0,0,0,1,247.0,014.2,261.2', + ); + expect(decodeResult.formatted.items.length).toBe(8); + expect(decodeResult.formatted.items[0].code).toBe('POS'); + expect(decodeResult.formatted.items[0].value).toBe('36.081 N, 94.810 W'); + expect(decodeResult.formatted.items[1].code).toBe('ALT'); + expect(decodeResult.formatted.items[1].value).toBe('35014 feet'); + expect(decodeResult.formatted.items[2].code).toBe('ORG'); + expect(decodeResult.formatted.items[2].value).toBe('ELP'); + expect(decodeResult.formatted.items[3].code).toBe('DST'); + expect(decodeResult.formatted.items[3].value).toBe('SDF'); + expect(decodeResult.formatted.items[4].code).toBe('ALT_DST'); + expect(decodeResult.formatted.items[4].value).toBe('SDF'); + expect(decodeResult.formatted.items[5].code).toBe('ARWY'); + expect(decodeResult.formatted.items[5].value).toBe('17R'); + expect(decodeResult.formatted.items[6].code).toBe('ALT_ARWY'); + expect(decodeResult.formatted.items[6].value).toBe('17L'); + expect(decodeResult.formatted.items[7].code).toBe('CHECKSUM'); + expect(decodeResult.formatted.items[7].value).toBe('0x421a'); + }); -test('decodes Label 4N variant 2C', () => { - // https://globe.adsbexchange.com/?icao=A3E08D&showTrace=2024-09-24×tamp=1727181643 - message.text = '285,C,,09/24,,,,,EWR,PHL,PHL,09R/,/,0,0,,,,,,1,0,0,0,1,0,,0,0,198.5,014.5,213.0,9BCD'; - const decodeResult = plugin.decode(message); + test('decodes Label 4N variant 2C', () => { + // https://globe.adsbexchange.com/?icao=A3E08D&showTrace=2024-09-24×tamp=1727181643 + message.text = + '285,C,,09/24,,,,,EWR,PHL,PHL,09R/,/,0,0,,,,,,1,0,0,0,1,0,,0,0,198.5,014.5,213.0,9BCD'; + const decodeResult = plugin.decode(message); - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-4n'); - expect(decodeResult.formatted.description).toBe('Airline Defined'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.raw.date).toBe('09/24'); - expect(decodeResult.remaining.text).toBe('C,0,0,1,0,0,0,1,0,0,0,198.5,014.5,213.0'); - expect(decodeResult.formatted.items.length).toBe(5); - expect(decodeResult.formatted.items[0].code).toBe('ORG'); - expect(decodeResult.formatted.items[0].value).toBe('EWR'); - expect(decodeResult.formatted.items[1].code).toBe('DST'); - expect(decodeResult.formatted.items[1].value).toBe('PHL'); - expect(decodeResult.formatted.items[2].code).toBe('ALT_DST'); - expect(decodeResult.formatted.items[2].value).toBe('PHL'); - expect(decodeResult.formatted.items[3].code).toBe('ARWY'); - expect(decodeResult.formatted.items[3].value).toBe('09R'); - expect(decodeResult.formatted.items[4].code).toBe('CHECKSUM'); - expect(decodeResult.formatted.items[4].value).toBe('0x9bcd'); -}); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-4n'); + expect(decodeResult.formatted.description).toBe('Airline Defined'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.raw.date).toBe('09/24'); + expect(decodeResult.remaining.text).toBe( + 'C,0,0,1,0,0,0,1,0,0,0,198.5,014.5,213.0', + ); + expect(decodeResult.formatted.items.length).toBe(5); + expect(decodeResult.formatted.items[0].code).toBe('ORG'); + expect(decodeResult.formatted.items[0].value).toBe('EWR'); + expect(decodeResult.formatted.items[1].code).toBe('DST'); + expect(decodeResult.formatted.items[1].value).toBe('PHL'); + expect(decodeResult.formatted.items[2].code).toBe('ALT_DST'); + expect(decodeResult.formatted.items[2].value).toBe('PHL'); + expect(decodeResult.formatted.items[3].code).toBe('ARWY'); + expect(decodeResult.formatted.items[3].value).toBe('09R'); + expect(decodeResult.formatted.items[4].code).toBe('CHECKSUM'); + expect(decodeResult.formatted.items[4].value).toBe('0x9bcd'); + }); -test('decodes Label 4N variant 2C (C-band)', () => { - // https://app.airframes.io/messages/3422221702 - message.text = 'M85AUP0109285,C,,10/12,,,,,NRT,ANC,ANC,07R/,33/,0,0,,,,,,0,0,0,0,1,0,,0,0,709.8,048.7,758.5,75F3'; - const decodeResult = plugin.decode(message); + test('decodes Label 4N variant 2C (C-band)', () => { + // https://app.airframes.io/messages/3422221702 + message.text = + 'M85AUP0109285,C,,10/12,,,,,NRT,ANC,ANC,07R/,33/,0,0,,,,,,0,0,0,0,1,0,,0,0,709.8,048.7,758.5,75F3'; + const decodeResult = plugin.decode(message); - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-4n'); - expect(decodeResult.formatted.description).toBe('Airline Defined'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.raw.flight_number).toBe('UP109'); - expect(decodeResult.raw.date).toBe('10/12'); - expect(decodeResult.remaining.text).toBe('C,0,0,0,0,0,0,1,0,0,0,709.8,048.7,758.5'); - expect(decodeResult.formatted.items.length).toBe(7); - expect(decodeResult.formatted.items[0].code).toBe('FLIGHT'); - expect(decodeResult.formatted.items[0].value).toBe('UP109'); - expect(decodeResult.formatted.items[1].code).toBe('ORG'); - expect(decodeResult.formatted.items[1].value).toBe('NRT'); - expect(decodeResult.formatted.items[2].code).toBe('DST'); - expect(decodeResult.formatted.items[2].value).toBe('ANC'); - expect(decodeResult.formatted.items[3].code).toBe('ALT_DST'); - expect(decodeResult.formatted.items[3].value).toBe('ANC'); - expect(decodeResult.formatted.items[4].code).toBe('ARWY'); - expect(decodeResult.formatted.items[4].value).toBe('07R'); - expect(decodeResult.formatted.items[5].code).toBe('ALT_ARWY'); - expect(decodeResult.formatted.items[5].value).toBe('33'); - expect(decodeResult.formatted.items[6].code).toBe('CHECKSUM'); - expect(decodeResult.formatted.items[6].value).toBe('0x75f3'); -}); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-4n'); + expect(decodeResult.formatted.description).toBe('Airline Defined'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.raw.flight_number).toBe('UP109'); + expect(decodeResult.raw.date).toBe('10/12'); + expect(decodeResult.remaining.text).toBe( + 'C,0,0,0,0,0,0,1,0,0,0,709.8,048.7,758.5', + ); + expect(decodeResult.formatted.items.length).toBe(7); + expect(decodeResult.formatted.items[0].code).toBe('FLIGHT'); + expect(decodeResult.formatted.items[0].value).toBe('UP109'); + expect(decodeResult.formatted.items[1].code).toBe('ORG'); + expect(decodeResult.formatted.items[1].value).toBe('NRT'); + expect(decodeResult.formatted.items[2].code).toBe('DST'); + expect(decodeResult.formatted.items[2].value).toBe('ANC'); + expect(decodeResult.formatted.items[3].code).toBe('ALT_DST'); + expect(decodeResult.formatted.items[3].value).toBe('ANC'); + expect(decodeResult.formatted.items[4].code).toBe('ARWY'); + expect(decodeResult.formatted.items[4].value).toBe('07R'); + expect(decodeResult.formatted.items[5].code).toBe('ALT_ARWY'); + expect(decodeResult.formatted.items[5].value).toBe('33'); + expect(decodeResult.formatted.items[6].code).toBe('CHECKSUM'); + expect(decodeResult.formatted.items[6].value).toBe('0x75f3'); + }); -test('decodes Label 4N ', () => { - message.text = '4N Bogus message'; - const decodeResult = plugin.decode(message); + test('decodes Label 4N ', () => { + message.text = '4N Bogus message'; + const decodeResult = plugin.decode(message); - expect(decodeResult.decoded).toBe(false); - expect(decodeResult.decoder.decodeLevel).toBe('none'); - expect(decodeResult.decoder.name).toBe('label-4n'); - expect(decodeResult.formatted.description).toBe('Airline Defined'); - expect(decodeResult.formatted.items.length).toBe(0); + expect(decodeResult.decoded).toBe(false); + expect(decodeResult.decoder.decodeLevel).toBe('none'); + expect(decodeResult.decoder.name).toBe('label-4n'); + expect(decodeResult.formatted.description).toBe('Airline Defined'); + expect(decodeResult.formatted.items.length).toBe(0); + }); }); -}); \ No newline at end of file diff --git a/lib/plugins/Label_4N.ts b/lib/plugins/Label_4N.ts index 7f9c343..6cb9f5d 100644 --- a/lib/plugins/Label_4N.ts +++ b/lib/plugins/Label_4N.ts @@ -6,13 +6,13 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_4N extends DecoderPlugin { name = 'label-4n'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['4N'], }; } - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { const decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; decodeResult.message = message; @@ -21,48 +21,70 @@ export class Label_4N extends DecoderPlugin { // Inmarsat C-band seems to prefix normal messages with a message number and flight number let text = message.text; if (text.match(/^M\d{2}A\w{6}/)) { - ResultFormatter.flightNumber(decodeResult, message.text.substring(4, 10).replace(/^([A-Z]+)0*/g, "$1")); - text = text.substring(10); + ResultFormatter.flightNumber( + decodeResult, + message.text.substring(4, 10).replace(/^([A-Z]+)0*/g, '$1'), + ); + text = text.substring(10); } decodeResult.decoded = true; - const fields = text.split(","); + const fields = text.split(','); if (text.length === 51) { - // variant 1 - decodeResult.raw.day = text.substring(0, 2); - ResultFormatter.departureAirport(decodeResult, text.substring(8, 11)); - ResultFormatter.arrivalAirport(decodeResult, text.substring(13, 16)); - ResultFormatter.position(decodeResult, CoordinateUtils.decodeStringCoordinatesDecimalMinutes(text.substring(30, 45).replace(/^(.)0/, "$1"))); - ResultFormatter.altitude(decodeResult, Number(text.substring(48, 51)) * 100); - ResultFormatter.unknownArr(decodeResult, [text.substring(2, 4), text.substring(19, 29)], " "); + // variant 1 + decodeResult.raw.day = text.substring(0, 2); + ResultFormatter.departureAirport(decodeResult, text.substring(8, 11)); + ResultFormatter.arrivalAirport(decodeResult, text.substring(13, 16)); + ResultFormatter.position( + decodeResult, + CoordinateUtils.decodeStringCoordinatesDecimalMinutes( + text.substring(30, 45).replace(/^(.)0/, '$1'), + ), + ); + ResultFormatter.altitude( + decodeResult, + Number(text.substring(48, 51)) * 100, + ); + ResultFormatter.unknownArr( + decodeResult, + [text.substring(2, 4), text.substring(19, 29)], + ' ', + ); } else if (fields.length === 33) { - // variant 2 - decodeResult.raw.date = fields[3]; - if (fields[1] === "B") { - ResultFormatter.position(decodeResult, {latitude: Number(fields[4]), longitude: Number(fields[5])}); - ResultFormatter.altitude(decodeResult, Number(fields[6])); - } - ResultFormatter.departureAirport(decodeResult, fields[8]); - ResultFormatter.arrivalAirport(decodeResult, fields[9]); - ResultFormatter.alternateAirport(decodeResult, fields[10]); - ResultFormatter.arrivalRunway(decodeResult, fields[11].split("/")[0]); - if (fields[12].length > 1) { - ResultFormatter.alternateRunway(decodeResult, fields[12].split("/")[0]); - } - ResultFormatter.checksum(decodeResult, fields[32]); - ResultFormatter.unknownArr(decodeResult, [...fields.slice(1,3), fields[7], ...fields.slice(13, 32)].filter((f) => f != "")); + // variant 2 + decodeResult.raw.date = fields[3]; + if (fields[1] === 'B') { + ResultFormatter.position(decodeResult, { + latitude: Number(fields[4]), + longitude: Number(fields[5]), + }); + ResultFormatter.altitude(decodeResult, Number(fields[6])); + } + ResultFormatter.departureAirport(decodeResult, fields[8]); + ResultFormatter.arrivalAirport(decodeResult, fields[9]); + ResultFormatter.alternateAirport(decodeResult, fields[10]); + ResultFormatter.arrivalRunway(decodeResult, fields[11].split('/')[0]); + if (fields[12].length > 1) { + ResultFormatter.alternateRunway(decodeResult, fields[12].split('/')[0]); + } + ResultFormatter.checksum(decodeResult, fields[32]); + ResultFormatter.unknownArr( + decodeResult, + [...fields.slice(1, 3), fields[7], ...fields.slice(13, 32)].filter( + (f) => f != '', + ), + ); } else { - decodeResult.decoded = false; - ResultFormatter.unknown(decodeResult, text); + decodeResult.decoded = false; + ResultFormatter.unknown(decodeResult, text); } if (decodeResult.decoded) { - if(!decodeResult.remaining.text) - decodeResult.decoder.decodeLevel = 'full'; - else - decodeResult.decoder.decodeLevel = 'partial'; + if (!decodeResult.remaining.text) + decodeResult.decoder.decodeLevel = 'full'; + else decodeResult.decoder.decodeLevel = 'partial'; } else { - decodeResult.decoder.decodeLevel = 'none'; + decodeResult.decoder.decodeLevel = 'none'; } return decodeResult; diff --git a/lib/plugins/Label_4T_AGFSR.test.ts b/lib/plugins/Label_4T_AGFSR.test.ts index 4439b60..022f079 100644 --- a/lib/plugins/Label_4T_AGFSR.test.ts +++ b/lib/plugins/Label_4T_AGFSR.test.ts @@ -2,16 +2,14 @@ import { MessageDecoder } from '../MessageDecoder'; import { Label_4T_AGFSR } from './Label_4T_AGFSR'; describe('Label 4T ETA', () => { - let plugin: Label_4T_AGFSR; - const message = {label: '4T', text: ''}; + const message = { label: '4T', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); plugin = new Label_4T_AGFSR(decoder); }); - test('matches qualifiers', () => { expect(plugin.decode).toBeDefined(); expect(plugin.name).toBe('label-4t-agfsr'); @@ -22,12 +20,12 @@ describe('Label 4T ETA', () => { }); }); - test('decodes msg 1', () => { // https://app.airframes.io/messages/3576265474 // https://www.flightaware.com/live/flight/ACA620/history/20241108/0245Z/CYYZ/CYHZ // https://globe.airplanes.live/?icao=c05bd2 - message.text = 'AGFSR AC0620/07/08/YYZYHZ/0340Z/453/4435.1N07143.4W/350/ /0063/0035/ /281065/----/ /512/0240/0253/----/----'; + message.text = + 'AGFSR AC0620/07/08/YYZYHZ/0340Z/453/4435.1N07143.4W/350/ /0063/0035/ /281065/----/ /512/0240/0253/----/----'; const decodeResult = plugin.decode(message); @@ -51,18 +49,21 @@ describe('Label 4T ETA', () => { expect(decodeResult.formatted.items[6].value).toBe('44.585 N, 70.277 W'); expect(decodeResult.formatted.items[7].label).toBe('Altitude'); expect(decodeResult.formatted.items[7].value).toBe('35000 feet'); - expect(decodeResult.remaining.text).toBe('453/ /0063/0035/ /281065/----/ /512/0240/0253/----/----'); + expect(decodeResult.remaining.text).toBe( + '453/ /0063/0035/ /281065/----/ /512/0240/0253/----/----', + ); }); test('decodes msg 2', () => { // https://app.airframes.io/messages/3576317322 // https://www.flightaware.com/live/flight/CFNOH/history/20241108/0335Z/CYUL/SBGR // https://globe.airplanes.live/?icao=c023c8 - message.text = 'AGFSR AC0096/07/08/YULGRU/0354Z/833/4417.8N07232.6W/254/CLIMB /0565/0033/-33/275049/0289/144/453/0319/0339/****/****'; + message.text = + 'AGFSR AC0096/07/08/YULGRU/0354Z/833/4417.8N07232.6W/254/CLIMB /0565/0033/-33/275049/0289/144/453/0319/0339/****/****'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial') + expect(decodeResult.decoder.decodeLevel).toBe('partial'); expect(decodeResult.formatted.description).toBe('Position Report'); expect(decodeResult.formatted.items.length).toBe(8); expect(decodeResult.formatted.items[0].label).toBe('Flight Number'); @@ -81,11 +82,12 @@ describe('Label 4T ETA', () => { expect(decodeResult.formatted.items[6].value).toBe('44.297 N, 71.457 W'); expect(decodeResult.formatted.items[7].label).toBe('Altitude'); expect(decodeResult.formatted.items[7].value).toBe('25400 feet'); - expect(decodeResult.remaining.text).toBe('833/CLIMB /0565/0033/-33/275049/0289/144/453/0319/0339/****/****'); + expect(decodeResult.remaining.text).toBe( + '833/CLIMB /0565/0033/-33/275049/0289/144/453/0319/0339/****/****', + ); }); test('decodes ', () => { - message.text = 'POS/ Bogus message'; const decodeResult = plugin.decode(message); diff --git a/lib/plugins/Label_4T_AGFSR.ts b/lib/plugins/Label_4T_AGFSR.ts index d7937d1..c7ac9bb 100644 --- a/lib/plugins/Label_4T_AGFSR.ts +++ b/lib/plugins/Label_4T_AGFSR.ts @@ -8,14 +8,14 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_4T_AGFSR extends DecoderPlugin { name = 'label-4t-agfsr'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['4T'], preambles: ['AGFSR'], }; } - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { const decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; decodeResult.formatted.description = 'Position Report'; @@ -23,7 +23,7 @@ export class Label_4T_AGFSR extends DecoderPlugin { const data = message.text.substring(5).split('/'); - if(!message.text.startsWith('AGFSR') || data.length !== 20) { + if (!message.text.startsWith('AGFSR') || data.length !== 20) { if (options.debug) { console.log(`Decoder: Unknown 4T message: ${message.text}`); } @@ -36,17 +36,28 @@ export class Label_4T_AGFSR extends DecoderPlugin { ResultFormatter.flightNumber(decodeResult, data[0].trim()); ResultFormatter.departureDay(decodeResult, Number(data[1])); ResultFormatter.arrivalDay(decodeResult, Number(data[2])); - ResultFormatter.departureAirport(decodeResult, data[3].substring(0,3), 'IATA'); + ResultFormatter.departureAirport( + decodeResult, + data[3].substring(0, 3), + 'IATA', + ); ResultFormatter.arrivalAirport(decodeResult, data[3].substring(3), 'IATA'); - ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(data[4].substring(0,4))); // HHMMZ + ResultFormatter.time_of_day( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(data[4].substring(0, 4)), + ); // HHMMZ ResultFormatter.unknown(decodeResult, data[5], '/'); - const lat = data[6].substring(0,7); - const lon = data[6].substring(7,15); + const lat = data[6].substring(0, 7); + const lon = data[6].substring(7, 15); ResultFormatter.position(decodeResult, { - latitude: CoordinateUtils.getDirection(lat[6]) * Number(lat.substring(0,2)) + Number(lat.substring(2,6))/60, - longitude: CoordinateUtils.getDirection(lon[7]) * Number(lon.substring(0,3)) + Number(lon.substring(3, 7))/60 + latitude: + CoordinateUtils.getDirection(lat[6]) * Number(lat.substring(0, 2)) + + Number(lat.substring(2, 6)) / 60, + longitude: + CoordinateUtils.getDirection(lon[7]) * Number(lon.substring(0, 3)) + + Number(lon.substring(3, 7)) / 60, }); - ResultFormatter.altitude(decodeResult, 100* Number(data[7])); + ResultFormatter.altitude(decodeResult, 100 * Number(data[7])); // 8 - phase of flight? // 11 - temperature? ResultFormatter.unknownArr(decodeResult, data.slice(8), '/'); diff --git a/lib/plugins/Label_4T_ETA.test.ts b/lib/plugins/Label_4T_ETA.test.ts index 2dc493c..3522022 100644 --- a/lib/plugins/Label_4T_ETA.test.ts +++ b/lib/plugins/Label_4T_ETA.test.ts @@ -2,16 +2,14 @@ import { MessageDecoder } from '../MessageDecoder'; import { Label_4T_ETA } from './Label_4T_ETA'; describe('Label 4T ETA', () => { - let plugin: Label_4T_ETA; - const message = {label: '4T', text: ''}; + const message = { label: '4T', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); plugin = new Label_4T_ETA(decoder); }); - test('matches qualifiers', () => { expect(plugin.decode).toBeDefined(); expect(plugin.name).toBe('label-4t-eta'); @@ -22,7 +20,6 @@ describe('Label 4T ETA', () => { }); }); - test('decodes msg 1', () => { message.text = 'ETA AC7221/13/14 YYZ 0902Z'; @@ -40,13 +37,13 @@ describe('Label 4T ETA', () => { expect(decodeResult.formatted.items[2].value).toBe('14'); expect(decodeResult.formatted.items[3].label).toBe('Destination'); expect(decodeResult.formatted.items[3].value).toBe('YYZ'); - expect(decodeResult.formatted.items[4].label).toBe('Estimated Time of Arrival'); + expect(decodeResult.formatted.items[4].label).toBe( + 'Estimated Time of Arrival', + ); expect(decodeResult.formatted.items[4].value).toBe('09:02:00'); }); - test('decodes ', () => { - message.text = 'ETA Bogus message'; const decodeResult = plugin.decode(message); diff --git a/lib/plugins/Label_4T_ETA.ts b/lib/plugins/Label_4T_ETA.ts index 2c3e074..22d3f85 100644 --- a/lib/plugins/Label_4T_ETA.ts +++ b/lib/plugins/Label_4T_ETA.ts @@ -1,21 +1,20 @@ import { DateTimeUtils } from '../DateTimeUtils'; import { DecoderPlugin } from '../DecoderPlugin'; import { DecodeResult, Message, Options } from '../DecoderPluginInterface'; -import { CoordinateUtils } from '../utils/coordinate_utils'; import { ResultFormatter } from '../utils/result_formatter'; // General Aviation Position Report export class Label_4T_ETA extends DecoderPlugin { name = 'label-4t-eta'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['4T'], preambles: ['ETA'], }; } - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { const decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; decodeResult.formatted.description = 'ETA Report'; @@ -23,7 +22,7 @@ export class Label_4T_ETA extends DecoderPlugin { const data = message.text.substring(3).split('/'); - if(!message.text.startsWith('ETA') || data.length !== 3) { + if (!message.text.startsWith('ETA') || data.length !== 3) { if (options.debug) { console.log(`Decoder: Unknown 4T message: ${message.text}`); } @@ -38,7 +37,10 @@ export class Label_4T_ETA extends DecoderPlugin { const etaData = data[2].split(' '); ResultFormatter.arrivalDay(decodeResult, Number(etaData[0])); ResultFormatter.arrivalAirport(decodeResult, etaData[1], 'IATA'); - ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(etaData[2].substring(0,4))); + ResultFormatter.eta( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(etaData[2].substring(0, 4)), + ); decodeResult.decoded = true; decodeResult.decoder.decodeLevel = 'full'; diff --git a/lib/plugins/Label_58.test.ts b/lib/plugins/Label_58.test.ts index 29e4004..7f13657 100644 --- a/lib/plugins/Label_58.test.ts +++ b/lib/plugins/Label_58.test.ts @@ -2,58 +2,57 @@ import { MessageDecoder } from '../MessageDecoder'; import { Label_58 } from './Label_58'; describe('Label_58', () => { - let plugin: Label_58; - const message = {label: '58', text: ''}; + let plugin: Label_58; + const message = { label: '58', text: '' }; - beforeEach(() => { - const decoder = new MessageDecoder(); - plugin = new Label_58(decoder); - }); + beforeEach(() => { + const decoder = new MessageDecoder(); + plugin = new Label_58(decoder); + }); - test('matches qualifiers', () => { - expect(plugin.decode).toBeDefined(); - expect(plugin.name).toBe('label-58'); - expect(plugin.qualifiers).toBeDefined(); - expect(plugin.qualifiers()).toEqual({ - labels: ['58'], - }); + test('matches qualifiers', () => { + expect(plugin.decode).toBeDefined(); + expect(plugin.name).toBe('label-58'); + expect(plugin.qualifiers).toBeDefined(); + expect(plugin.qualifiers()).toEqual({ + labels: ['58'], }); + }); - test('decodes variant 1', () => { - message.text = 'OG0704/06/230942/N39.214/W76.106/22683/N/' - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.raw.flight_number).toBe('OG0704'); - expect(decodeResult.raw.day).toBe(6); - expect(decodeResult.raw.time_of_day).toBe(83382); - expect(decodeResult.raw.position.latitude).toBe(39.214); - expect(decodeResult.raw.position.longitude).toBe(-76.106); - expect(decodeResult.raw.altitude).toBe(22683); - expect(decodeResult.formatted.items.length).toBe(5); - expect(decodeResult.formatted.items[0].label).toBe('Flight Number'); - expect(decodeResult.formatted.items[0].value).toBe('OG0704'); - expect(decodeResult.formatted.items[1].label).toBe('Day of Month'); - expect(decodeResult.formatted.items[1].value).toBe('6'); - expect(decodeResult.formatted.items[2].label).toBe('Message Timestamp'); - expect(decodeResult.formatted.items[2].value).toBe('23:09:42'); - expect(decodeResult.formatted.items[3].label).toBe('Aircraft Position'); - expect(decodeResult.formatted.items[3].value).toBe('39.214 N, 76.106 W'); - expect(decodeResult.formatted.items[4].label).toBe('Altitude'); - expect(decodeResult.formatted.items[4].value).toBe('22683 feet'); - expect(decodeResult.remaining.text).toBe('N/'); - }); + test('decodes variant 1', () => { + message.text = 'OG0704/06/230942/N39.214/W76.106/22683/N/'; + const decodeResult = plugin.decode(message); - test('does not decode ', () => { + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.raw.flight_number).toBe('OG0704'); + expect(decodeResult.raw.day).toBe(6); + expect(decodeResult.raw.time_of_day).toBe(83382); + expect(decodeResult.raw.position.latitude).toBe(39.214); + expect(decodeResult.raw.position.longitude).toBe(-76.106); + expect(decodeResult.raw.altitude).toBe(22683); + expect(decodeResult.formatted.items.length).toBe(5); + expect(decodeResult.formatted.items[0].label).toBe('Flight Number'); + expect(decodeResult.formatted.items[0].value).toBe('OG0704'); + expect(decodeResult.formatted.items[1].label).toBe('Day of Month'); + expect(decodeResult.formatted.items[1].value).toBe('6'); + expect(decodeResult.formatted.items[2].label).toBe('Message Timestamp'); + expect(decodeResult.formatted.items[2].value).toBe('23:09:42'); + expect(decodeResult.formatted.items[3].label).toBe('Aircraft Position'); + expect(decodeResult.formatted.items[3].value).toBe('39.214 N, 76.106 W'); + expect(decodeResult.formatted.items[4].label).toBe('Altitude'); + expect(decodeResult.formatted.items[4].value).toBe('22683 feet'); + expect(decodeResult.remaining.text).toBe('N/'); + }); - message.text = 'Bogus/message'; - const decodeResult = plugin.decode(message); + test('does not decode ', () => { + message.text = 'Bogus/message'; + const decodeResult = plugin.decode(message); - expect(decodeResult.decoded).toBe(false); - expect(decodeResult.decoder.decodeLevel).toBe('none'); - expect(decodeResult.decoder.name).toBe('label-58'); - expect(decodeResult.formatted.description).toBe('Position Report'); - expect(decodeResult.message).toBe(message); - }); -}); \ No newline at end of file + expect(decodeResult.decoded).toBe(false); + expect(decodeResult.decoder.decodeLevel).toBe('none'); + expect(decodeResult.decoder.name).toBe('label-58'); + expect(decodeResult.formatted.description).toBe('Position Report'); + expect(decodeResult.message).toBe(message); + }); +}); diff --git a/lib/plugins/Label_58.ts b/lib/plugins/Label_58.ts index 430b1e4..266f962 100644 --- a/lib/plugins/Label_58.ts +++ b/lib/plugins/Label_58.ts @@ -8,7 +8,7 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_58 extends DecoderPlugin { name = 'label-58'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['58'], }; @@ -24,12 +24,17 @@ export class Label_58 extends DecoderPlugin { if (data.length === 8) { ResultFormatter.flightNumber(decodeResult, data[0]); ResultFormatter.day(decodeResult, Number(data[1])); - ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(data[2])); + ResultFormatter.time_of_day( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(data[2]), + ); const lat = data[3]; const lon = data[4]; ResultFormatter.position(decodeResult, { - latitude: CoordinateUtils.getDirection(lat[0]) * Number(lat.substring(1)), - longitude:CoordinateUtils.getDirection(lon[0]) * Number(lon.substring(1)) + latitude: + CoordinateUtils.getDirection(lat[0]) * Number(lat.substring(1)), + longitude: + CoordinateUtils.getDirection(lon[0]) * Number(lon.substring(1)), }); ResultFormatter.altitude(decodeResult, Number(data[5])); ResultFormatter.unknown(decodeResult, data[6], '/'); diff --git a/lib/plugins/Label_5Z_Slash.test.ts b/lib/plugins/Label_5Z_Slash.test.ts index 0fc4af2..84a1105 100644 --- a/lib/plugins/Label_5Z_Slash.test.ts +++ b/lib/plugins/Label_5Z_Slash.test.ts @@ -3,7 +3,7 @@ import { Label_5Z_Slash } from './Label_5Z_Slash'; describe('Label 5Z', () => { let plugin: Label_5Z_Slash; - const message = {label: '5Z', text: ''}; + const message = { label: '5Z', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); @@ -20,13 +20,12 @@ describe('Label 5Z', () => { }); }); - test('/TXT', () => { message.text = '/TXT\r\nDID U GET THE TIMES'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); expect(decodeResult.decoder.decodeLevel).toBe('full'); - expect(decodeResult.raw.airline).toBeUndefined() + expect(decodeResult.raw.airline).toBeUndefined(); expect(decodeResult.raw.text).toBe('DID U GET THE TIMES'); expect(decodeResult.formatted.items.length).toBe(1); expect(decodeResult.formatted.items[0].label).toBe('Text Message'); @@ -48,7 +47,9 @@ describe('Label 5Z', () => { expect(decodeResult.formatted.items[0].label).toBe('Airline'); expect(decodeResult.formatted.items[0].value).toBe('United Airlines'); expect(decodeResult.formatted.items[1].label).toBe('Message Type'); - expect(decodeResult.formatted.items[1].value).toBe('Request Departure Clearance (B3)'); + expect(decodeResult.formatted.items[1].value).toBe( + 'Request Departure Clearance (B3)', + ); expect(decodeResult.formatted.items[2].label).toBe('Origin'); expect(decodeResult.formatted.items[2].value).toBe('ATL'); expect(decodeResult.formatted.items[3].label).toBe('Destination'); @@ -74,7 +75,9 @@ describe('Label 5Z', () => { expect(decodeResult.formatted.items[0].label).toBe('Airline'); expect(decodeResult.formatted.items[0].value).toBe('United Airlines'); expect(decodeResult.formatted.items[1].label).toBe('Message Type'); - expect(decodeResult.formatted.items[1].value).toBe('Request Departure Clearance (B3)'); + expect(decodeResult.formatted.items[1].value).toBe( + 'Request Departure Clearance (B3)', + ); expect(decodeResult.formatted.items[2].label).toBe('Origin'); expect(decodeResult.formatted.items[2].value).toBe('DCA'); expect(decodeResult.formatted.items[3].label).toBe('Destination'); @@ -84,7 +87,8 @@ describe('Label 5Z', () => { }); test('/B3 request', () => { - message.text = '/B3 TO DATA REQ / KIAH KBOS 14 152532 R4R /---- BOPT/OFF C0.000/1 LNO G1600'; + message.text = + '/B3 TO DATA REQ / KIAH KBOS 14 152532 R4R /---- BOPT/OFF C0.000/1 LNO G1600'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -100,7 +104,9 @@ describe('Label 5Z', () => { expect(decodeResult.formatted.items[0].label).toBe('Airline'); expect(decodeResult.formatted.items[0].value).toBe('United Airlines'); expect(decodeResult.formatted.items[1].label).toBe('Message Type'); - expect(decodeResult.formatted.items[1].value).toBe('Request Departure Clearance (B3)'); + expect(decodeResult.formatted.items[1].value).toBe( + 'Request Departure Clearance (B3)', + ); expect(decodeResult.formatted.items[2].label).toBe('Origin'); expect(decodeResult.formatted.items[2].value).toBe('KIAH'); expect(decodeResult.formatted.items[3].label).toBe('Destination'); @@ -109,7 +115,9 @@ describe('Label 5Z', () => { expect(decodeResult.formatted.items[4].value).toBe('15:25:32'); expect(decodeResult.formatted.items[5].label).toBe('Arrival Runway'); expect(decodeResult.formatted.items[5].value).toBe('4R'); - expect(decodeResult.remaining.text).toBe('---- BOPT/OFF C0.000/1 LNO G1600'); + expect(decodeResult.remaining.text).toBe( + '---- BOPT/OFF C0.000/1 LNO G1600', + ); }); test('/ET variant 1', () => { @@ -132,7 +140,9 @@ describe('Label 5Z', () => { expect(decodeResult.formatted.items[3].value).toBe('KBNA'); expect(decodeResult.formatted.items[4].label).toBe('Message Timestamp'); expect(decodeResult.formatted.items[4].value).toBe('12:25:59'); - expect(decodeResult.formatted.items[5].label).toBe('Estimated Time of Arrival'); + expect(decodeResult.formatted.items[5].label).toBe( + 'Estimated Time of Arrival', + ); expect(decodeResult.formatted.items[5].value).toBe('13:36:00'); expect(decodeResult.remaining.text).toBe('AUTO'); @@ -159,7 +169,8 @@ describe('Label 5Z', () => { }); test('/C3 Request', () => { - message.text = '/C3 GATE REQ / KBNA KEWR 22 115400 0554 ---- ---- ---- ----'; + message.text = + '/C3 GATE REQ / KBNA KEWR 22 115400 0554 ---- ---- ---- ----'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -185,7 +196,6 @@ describe('Label 5Z', () => { }); test('/ does not decode invalid', () => { - message.text = '/ Bogus message'; const decodeResult = plugin.decode(message); diff --git a/lib/plugins/Label_5Z_Slash.ts b/lib/plugins/Label_5Z_Slash.ts index 78e3ba1..5eaf64d 100644 --- a/lib/plugins/Label_5Z_Slash.ts +++ b/lib/plugins/Label_5Z_Slash.ts @@ -33,10 +33,10 @@ export class Label_5Z_Slash extends DecoderPlugin { W1: 'Request Weather for City', }; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['5Z'], - preambles: ['/'] + preambles: ['/'], }; } @@ -47,7 +47,8 @@ export class Label_5Z_Slash extends DecoderPlugin { decodeResult.formatted.description = 'Airline Designated Downlink'; const lines = message.text.split('\r\n'); - if (lines[0] === '/TXT') { // not UA, but starts with `/` + if (lines[0] === '/TXT') { + // not UA, but starts with `/` ResultFormatter.text(decodeResult, lines.slice(1).join('\r\n')); decodeResult.decoded = true; decodeResult.decoder.decodeLevel = 'full'; @@ -81,12 +82,23 @@ export class Label_5Z_Slash extends DecoderPlugin { ResultFormatter.departureAirport(decodeResult, info[1]); ResultFormatter.arrivalAirport(decodeResult, info[2]); decodeResult.raw.day = Number(info[3]); - ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(info[4])); + ResultFormatter.time_of_day( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(info[4]), + ); ResultFormatter.arrivalRunway(decodeResult, info[5].slice(1)); ResultFormatter.unknownArr(decodeResult, data.slice(3), '/'); } else if (type === 'B3') { - ResultFormatter.departureAirport(decodeResult, header[1].substring(0, 3), 'IATA'); - ResultFormatter.arrivalAirport(decodeResult, header[1].substring(3), 'IATA'); + ResultFormatter.departureAirport( + decodeResult, + header[1].substring(0, 3), + 'IATA', + ); + ResultFormatter.arrivalAirport( + decodeResult, + header[1].substring(3), + 'IATA', + ); decodeResult.raw.day = Number(header[2]); ResultFormatter.arrivalRunway(decodeResult, header[3].slice(1)); if (header.length > 4) { @@ -98,21 +110,38 @@ export class Label_5Z_Slash extends DecoderPlugin { ResultFormatter.departureAirport(decodeResult, info[1]); ResultFormatter.arrivalAirport(decodeResult, info[2]); decodeResult.raw.day = Number(info[3]); - ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(info[4])); + ResultFormatter.time_of_day( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(info[4]), + ); ResultFormatter.unknownArr(decodeResult, info.slice(5), ' '); } else if (type === 'C3') { - ResultFormatter.departureAirport(decodeResult, header[1].substring(0, 3), 'IATA'); - ResultFormatter.arrivalAirport(decodeResult, header[1].substring(3), 'IATA'); + ResultFormatter.departureAirport( + decodeResult, + header[1].substring(0, 3), + 'IATA', + ); + ResultFormatter.arrivalAirport( + decodeResult, + header[1].substring(3), + 'IATA', + ); } else if (type === 'ET') { const airports = data[2].split(' '); // aiports[0] is blank ResultFormatter.departureAirport(decodeResult, airports[1]); ResultFormatter.arrivalAirport(decodeResult, airports[2]); decodeResult.raw.day = Number(airports[3]); - ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(airports[4])); + ResultFormatter.time_of_day( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(airports[4]), + ); const estimates = data[3].split(' '); - ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(estimates[1])); + ResultFormatter.eta( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(estimates[1]), + ); ResultFormatter.unknown(decodeResult, estimates[2]); } else { if (options.debug) { @@ -120,13 +149,15 @@ export class Label_5Z_Slash extends DecoderPlugin { } } decodeResult.decoded = true; - decodeResult.decoder.decodeLevel = decodeResult.remaining.text ? 'partial' : 'full'; + decodeResult.decoder.decodeLevel = decodeResult.remaining.text + ? 'partial' + : 'full'; } else { // Unknown if (options.debug) { console.log(`Decoder: Unknown 5Z message: ${message.text}`); } - ResultFormatter.unknown(decodeResult, message.text); + ResultFormatter.unknown(decodeResult, message.text); decodeResult.decoded = false; decodeResult.decoder.decodeLevel = 'none'; } diff --git a/lib/plugins/Label_80.test.ts b/lib/plugins/Label_80.test.ts index 2177b02..df34054 100644 --- a/lib/plugins/Label_80.test.ts +++ b/lib/plugins/Label_80.test.ts @@ -2,153 +2,161 @@ import { MessageDecoder } from '../MessageDecoder'; import { Label_80 } from './Label_80'; describe('Label 80', () => { - - let plugin : Label_80; - const message = {label: '80', text: ''}; - - beforeEach(() => { - const decoder = new MessageDecoder(); - plugin = new Label_80(decoder); + let plugin: Label_80; + const message = { label: '80', text: '' }; + + beforeEach(() => { + const decoder = new MessageDecoder(); + plugin = new Label_80(decoder); + }); + test('matches qualifiers', () => { + expect(plugin.decode).toBeDefined(); + expect(plugin.name).toBe('label-80'); + expect(plugin.qualifiers).toBeDefined(); + expect(plugin.qualifiers()).toEqual({ + labels: ['80'], + preambles: [], }); -test('matches qualifiers', () => { - expect(plugin.decode).toBeDefined(); - expect(plugin.name).toBe('label-80'); - expect(plugin.qualifiers).toBeDefined(); - expect(plugin.qualifiers()).toEqual({ - labels: ['80'], - preambles: [], -}); -}); - -test('decodes POSRPT variant 1', () => { - // https://app.airframes.io/messages/377573108 - message.text = '3N01 POSRPT 5891/04 KIAH/MMGL .XA-VOI\r\n/POS N29395W095133/ALT +15608/MCH 558/FOB 0100/ETA 0410'; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.raw.flight_number).toBe('5891'); - expect(decodeResult.raw.day).toBe(4); - expect(decodeResult.raw.position.latitude).toBe(29.395); - expect(decodeResult.raw.position.longitude).toBe(-95.133); - expect(decodeResult.raw.altitude).toBe(15608); - expect(decodeResult.raw.tail).toBe('XA-VOI'); - expect(decodeResult.raw.departure_icao).toBe('KIAH'); - expect(decodeResult.raw.arrival_icao).toBe('MMGL'); - expect(decodeResult.raw.mach).toBe(0.558); - expect(decodeResult.raw.fuel_on_board).toBe(100); - //expect(decodeResult.raw.eta_time).toBe('15000'); - expect(decodeResult.formatted.items.length).toBe(10); - expect(decodeResult.remaining.text).toBe('3N01 POSRPT/'); -}); -test('decodes POSRPT variant 2', () => { - // https://app.airframes.io/messages/2416917371 - message.text = '3N01 POSRPT 0581/27 KIAD/MSLP .N962AV/04H 11:02\r\n/NWYP CIGAR /HDG 233/MCH 782\r\n/POS N3539.2W07937.2/FL 360/TAS 445/SAT -060\r\n/SWND 110/DWND 306/FOB N009414/ETA 14:26.0 '; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-80'); - expect(decodeResult.formatted.description).toBe('Airline Defined Position Report'); - expect(decodeResult.raw.position.latitude).toBe(35.391999999999996); // FIXME?: 35.392 - expect(decodeResult.raw.position.longitude).toBe(-79.372); - expect(decodeResult.raw.departure_icao).toBe('KIAD'); - expect(decodeResult.raw.arrival_icao).toBe('MSLP'); - expect(decodeResult.raw.tail).toBe('N962AV'); - expect(decodeResult.raw.next_waypoint).toBe('CIGAR'); - expect(decodeResult.raw.heading).toBe(233); - expect(decodeResult.raw.mach).toBe(0.782); - expect(decodeResult.raw.altitude).toBe(36000); - expect(decodeResult.raw.airspeed).toBe(445); - expect(decodeResult.raw.outside_air_temperature).toBe(-60); - //expect(decodeResult.raw.wind_data[0].windSpeed).toBe(110); - //expect(decodeResult.raw.wind_data[0].windDirection).toBe(306); - expect(decodeResult.raw.fuel_on_board).toBe(9414); - expect(decodeResult.raw.eta_time).toBe(51960); - expect(decodeResult.formatted.items.length).toBe(13); - expect(decodeResult.remaining.text).toBe('3N01 POSRPT/04H 11:02////SWND 110/DWND 306'); - -}); - -test('decodes POSRPT variant 3', () => { - message.text = '/FB 0105/AD KCHS/N3950.1,W07548.3,3P01 POSRPT 0267/20 KBOS/KCHS .N3275J\n/UTC 143605/POS N3950.1 W07548.3/ALT 38007\n/SPD 334/FOB 0105/ETA 1622'; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.raw.flight_number).toBe('0267'); - expect(decodeResult.raw.day).toBe(20); - expect(decodeResult.raw.fuel_on_board).toBe(105); - expect(decodeResult.raw.arrival_icao).toBe('KCHS'); - expect(decodeResult.raw.departure_icao).toBe('KBOS'); - expect(decodeResult.raw.tail).toBe('N3275J'); - expect(decodeResult.raw.time_of_day).toBe(52565); - expect(decodeResult.raw.position.latitude).toBe(39.501); - expect(decodeResult.raw.position.longitude).toBe(-75.483); - expect(decodeResult.raw.altitude).toBe(38007); - expect(decodeResult.raw.groundspeed).toBe(334); - expect(decodeResult.raw.fuel_on_board).toBe(105); - expect(decodeResult.raw.eta_time).toBe(58920); - expect(decodeResult.formatted.items.length).toBe(11); - expect(decodeResult.remaining.text).toBe('N3950.1/W07548.3 3P01 POSRPT//'); -}); - -test('decodes POS variant 1', () => { - message.text = '3C01 POS N39328W077307 ,,143700, , , ,P47,124,0069'; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.raw.position.latitude).toBe(39.328); - expect(decodeResult.raw.position.longitude).toBe(-77.307); - expect(decodeResult.raw.outside_air_temperature).toBe(47); - expect(decodeResult.raw.time_of_day).toBe(52620); - expect(decodeResult.raw.airspeed).toBe(124); - expect(decodeResult.raw.fuel_on_board).toBe(69); - expect(decodeResult.formatted.items.length).toBe(5); - expect(decodeResult.remaining.text).toBe('3C01 POS,, , '); -}); - -test('decodes OPNORM variant 1', () => { - message.text = '3M01 OPNORM 0411/20 KEWR/MMMX .XA-MAT '; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.raw.flight_number).toBe('0411'); - expect(decodeResult.raw.day).toBe(20); - expect(decodeResult.raw.departure_icao).toBe('KEWR'); - expect(decodeResult.raw.arrival_icao).toBe('MMMX'); - expect(decodeResult.raw.tail).toBe('XA-MAT'); - expect(decodeResult.formatted.items.length).toBe(5); - expect(decodeResult.remaining.text).toBe('3M01 OPNORM'); -}); - - -test('decodes INRANG variant 1', () => { - message.text = '3701 INRANG 3451/20 KSBD/KBWI .N613AZ\n/ETA 1254/ERT '; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.raw.flight_number).toBe('3451'); - expect(decodeResult.raw.day).toBe(20); - expect(decodeResult.raw.departure_icao).toBe('KSBD'); - expect(decodeResult.raw.arrival_icao).toBe('KBWI'); - expect(decodeResult.raw.tail).toBe('N613AZ'); - expect(decodeResult.raw.eta_time).toBe(46440); - expect(decodeResult.formatted.items.length).toBe(6); - expect(decodeResult.remaining.text).toBe('3701 INRANG//ERT'); -}); - - -test('does not decode invalid messages', () => { - message.text = '3N01 POSRPT Bogus message'; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(false); - expect(decodeResult.decoder.decodeLevel).toBe('none'); - expect(decodeResult.decoder.name).toBe('label-80'); - expect(decodeResult.formatted.description).toBe('Airline Defined Position Report'); - expect(decodeResult.formatted.items.length).toBe(0); -}); + }); + + test('decodes POSRPT variant 1', () => { + // https://app.airframes.io/messages/377573108 + message.text = + '3N01 POSRPT 5891/04 KIAH/MMGL .XA-VOI\r\n/POS N29395W095133/ALT +15608/MCH 558/FOB 0100/ETA 0410'; + const decodeResult = plugin.decode(message); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.raw.flight_number).toBe('5891'); + expect(decodeResult.raw.day).toBe(4); + expect(decodeResult.raw.position.latitude).toBe(29.395); + expect(decodeResult.raw.position.longitude).toBe(-95.133); + expect(decodeResult.raw.altitude).toBe(15608); + expect(decodeResult.raw.tail).toBe('XA-VOI'); + expect(decodeResult.raw.departure_icao).toBe('KIAH'); + expect(decodeResult.raw.arrival_icao).toBe('MMGL'); + expect(decodeResult.raw.mach).toBe(0.558); + expect(decodeResult.raw.fuel_on_board).toBe(100); + //expect(decodeResult.raw.eta_time).toBe('15000'); + expect(decodeResult.formatted.items.length).toBe(10); + expect(decodeResult.remaining.text).toBe('3N01 POSRPT/'); + }); + test('decodes POSRPT variant 2', () => { + // https://app.airframes.io/messages/2416917371 + message.text = + '3N01 POSRPT 0581/27 KIAD/MSLP .N962AV/04H 11:02\r\n/NWYP CIGAR /HDG 233/MCH 782\r\n/POS N3539.2W07937.2/FL 360/TAS 445/SAT -060\r\n/SWND 110/DWND 306/FOB N009414/ETA 14:26.0 '; + const decodeResult = plugin.decode(message); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-80'); + expect(decodeResult.formatted.description).toBe( + 'Airline Defined Position Report', + ); + expect(decodeResult.raw.position.latitude).toBe(35.391999999999996); // FIXME?: 35.392 + expect(decodeResult.raw.position.longitude).toBe(-79.372); + expect(decodeResult.raw.departure_icao).toBe('KIAD'); + expect(decodeResult.raw.arrival_icao).toBe('MSLP'); + expect(decodeResult.raw.tail).toBe('N962AV'); + expect(decodeResult.raw.next_waypoint).toBe('CIGAR'); + expect(decodeResult.raw.heading).toBe(233); + expect(decodeResult.raw.mach).toBe(0.782); + expect(decodeResult.raw.altitude).toBe(36000); + expect(decodeResult.raw.airspeed).toBe(445); + expect(decodeResult.raw.outside_air_temperature).toBe(-60); + //expect(decodeResult.raw.wind_data[0].windSpeed).toBe(110); + //expect(decodeResult.raw.wind_data[0].windDirection).toBe(306); + expect(decodeResult.raw.fuel_on_board).toBe(9414); + expect(decodeResult.raw.eta_time).toBe(51960); + expect(decodeResult.formatted.items.length).toBe(13); + expect(decodeResult.remaining.text).toBe( + '3N01 POSRPT/04H 11:02////SWND 110/DWND 306', + ); + }); + + test('decodes POSRPT variant 3', () => { + message.text = + '/FB 0105/AD KCHS/N3950.1,W07548.3,3P01 POSRPT 0267/20 KBOS/KCHS .N3275J\n/UTC 143605/POS N3950.1 W07548.3/ALT 38007\n/SPD 334/FOB 0105/ETA 1622'; + const decodeResult = plugin.decode(message); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.raw.flight_number).toBe('0267'); + expect(decodeResult.raw.day).toBe(20); + expect(decodeResult.raw.fuel_on_board).toBe(105); + expect(decodeResult.raw.arrival_icao).toBe('KCHS'); + expect(decodeResult.raw.departure_icao).toBe('KBOS'); + expect(decodeResult.raw.tail).toBe('N3275J'); + expect(decodeResult.raw.time_of_day).toBe(52565); + expect(decodeResult.raw.position.latitude).toBe(39.501); + expect(decodeResult.raw.position.longitude).toBe(-75.483); + expect(decodeResult.raw.altitude).toBe(38007); + expect(decodeResult.raw.groundspeed).toBe(334); + expect(decodeResult.raw.fuel_on_board).toBe(105); + expect(decodeResult.raw.eta_time).toBe(58920); + expect(decodeResult.formatted.items.length).toBe(11); + expect(decodeResult.remaining.text).toBe('N3950.1/W07548.3 3P01 POSRPT//'); + }); + + test('decodes POS variant 1', () => { + message.text = + '3C01 POS N39328W077307 ,,143700, , , ,P47,124,0069'; + const decodeResult = plugin.decode(message); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.raw.position.latitude).toBe(39.328); + expect(decodeResult.raw.position.longitude).toBe(-77.307); + expect(decodeResult.raw.outside_air_temperature).toBe(47); + expect(decodeResult.raw.time_of_day).toBe(52620); + expect(decodeResult.raw.airspeed).toBe(124); + expect(decodeResult.raw.fuel_on_board).toBe(69); + expect(decodeResult.formatted.items.length).toBe(5); + expect(decodeResult.remaining.text).toBe( + '3C01 POS,, , ', + ); + }); + + test('decodes OPNORM variant 1', () => { + message.text = '3M01 OPNORM 0411/20 KEWR/MMMX .XA-MAT '; + const decodeResult = plugin.decode(message); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.raw.flight_number).toBe('0411'); + expect(decodeResult.raw.day).toBe(20); + expect(decodeResult.raw.departure_icao).toBe('KEWR'); + expect(decodeResult.raw.arrival_icao).toBe('MMMX'); + expect(decodeResult.raw.tail).toBe('XA-MAT'); + expect(decodeResult.formatted.items.length).toBe(5); + expect(decodeResult.remaining.text).toBe('3M01 OPNORM'); + }); + + test('decodes INRANG variant 1', () => { + message.text = '3701 INRANG 3451/20 KSBD/KBWI .N613AZ\n/ETA 1254/ERT '; + const decodeResult = plugin.decode(message); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.raw.flight_number).toBe('3451'); + expect(decodeResult.raw.day).toBe(20); + expect(decodeResult.raw.departure_icao).toBe('KSBD'); + expect(decodeResult.raw.arrival_icao).toBe('KBWI'); + expect(decodeResult.raw.tail).toBe('N613AZ'); + expect(decodeResult.raw.eta_time).toBe(46440); + expect(decodeResult.formatted.items.length).toBe(6); + expect(decodeResult.remaining.text).toBe('3701 INRANG//ERT'); + }); + + test('does not decode invalid messages', () => { + message.text = '3N01 POSRPT Bogus message'; + const decodeResult = plugin.decode(message); + + expect(decodeResult.decoded).toBe(false); + expect(decodeResult.decoder.decodeLevel).toBe('none'); + expect(decodeResult.decoder.name).toBe('label-80'); + expect(decodeResult.formatted.description).toBe( + 'Airline Defined Position Report', + ); + expect(decodeResult.formatted.items.length).toBe(0); + }); }); diff --git a/lib/plugins/Label_80.ts b/lib/plugins/Label_80.ts index 557890b..8499fc1 100644 --- a/lib/plugins/Label_80.ts +++ b/lib/plugins/Label_80.ts @@ -1,7 +1,6 @@ import { DateTimeUtils } from '../DateTimeUtils'; import { DecoderPlugin } from '../DecoderPlugin'; import { DecodeResult, Message, Options } from '../DecoderPluginInterface'; -import { Wind } from '../types/wind'; import { CoordinateUtils } from '../utils/coordinate_utils'; import { ResultFormatter } from '../utils/result_formatter'; @@ -10,7 +9,7 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_80 extends DecoderPlugin { name = 'label-80'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['80'], preambles: [], @@ -24,32 +23,36 @@ export class Label_80 extends DecoderPlugin { decodeResult.formatted.description = 'Airline Defined Position Report'; const lines = message.text.split(/\r?\n/); - if(lines.length === 1 && lines[0].includes(',')) { + if (lines.length === 1 && lines[0].includes(',')) { this.parseCsvFormat(lines[0], decodeResult); } else { - const header = lines[0].trim(); - const headerParts = header.split(','); - for(let i=0; i 0) { decodeResult.decoded = true; - decodeResult.decoder.decodeLevel = decodeResult.remaining.text === undefined ? 'full' : 'partial'; + decodeResult.decoder.decodeLevel = + decodeResult.remaining.text === undefined ? 'full' : 'partial'; } return decodeResult; @@ -58,13 +61,13 @@ export class Label_80 extends DecoderPlugin { private parseHeader(header: string, results: DecodeResult) { //3N01 POSRPT 0581/27 KIAD/MSLP .N962AV/04H 11:02 const fields = header.split('/'); - if(fields.length < 3) { + if (fields.length < 3) { ResultFormatter.unknown(results, header, '/'); return; } const msgInfo = fields[0].split(/\s+/); - if(msgInfo.length === 3) { - ResultFormatter.unknownArr(results, msgInfo.slice(0,2), ' '); + if (msgInfo.length === 3) { + ResultFormatter.unknownArr(results, msgInfo.slice(0, 2), ' '); ResultFormatter.flightNumber(results, msgInfo[2]); } else { ResultFormatter.unknown(results, header, '/'); @@ -72,7 +75,7 @@ export class Label_80 extends DecoderPlugin { } const otherInfo1 = fields[1].split(/\s+/); - if(otherInfo1.length === 2) { + if (otherInfo1.length === 2) { ResultFormatter.day(results, parseInt(otherInfo1[0], 10)); ResultFormatter.departureAirport(results, otherInfo1[1]); } else { @@ -80,21 +83,21 @@ export class Label_80 extends DecoderPlugin { } const otherInfo2 = fields[2].split(/\s+/); - if(otherInfo2.length === 2) { + if (otherInfo2.length === 2) { ResultFormatter.arrivalAirport(results, otherInfo2[0]); ResultFormatter.tail(results, otherInfo2[1].replace('.', '')); } else { ResultFormatter.unknownArr(results, otherInfo2, ' '); } - if(fields.length > 3) { + if (fields.length > 3) { ResultFormatter.unknownArr(results, fields.slice(3), '/'); } } private parseTags(part: string, results: DecodeResult) { - const kvPair = part.split(/\s+/) - if(kvPair.length < 2) { + const kvPair = part.split(/\s+/); + if (kvPair.length < 2) { ResultFormatter.unknown(results, part, '/'); return; } @@ -103,11 +106,15 @@ export class Label_80 extends DecoderPlugin { switch (tag) { case 'POS': - // don't use decodeStringCoordinates because of different position format + // don't use decodeStringCoordinates because of different position format const posRegex = /^(?[NS])(?.+)(?[EW])(?.+)/; const posResult = val.match(posRegex); - const lat = Number(posResult?.groups?.lat) * (posResult?.groups?.latd === 'S' ? -1 : 1); - const lon = Number(posResult?.groups?.lng) * (posResult?.groups?.lngd === 'W' ? -1 : 1); + const lat = + Number(posResult?.groups?.lat) * + (posResult?.groups?.latd === 'S' ? -1 : 1); + const lon = + Number(posResult?.groups?.lng) * + (posResult?.groups?.lngd === 'W' ? -1 : 1); const position = { latitude: Number.isInteger(lat) ? lat / 1000 : lat / 100, longitude: Number.isInteger(lon) ? lon / 1000 : lon / 100, @@ -137,10 +144,16 @@ export class Label_80 extends DecoderPlugin { break; case 'FOB': // Strip non-numeric like 'N' in 'N009414' - ResultFormatter.currentFuel(results, parseInt(val.replace(/\D/g, ''), 10)); + ResultFormatter.currentFuel( + results, + parseInt(val.replace(/\D/g, ''), 10), + ); break; case 'UTC': - ResultFormatter.time_of_day(results, DateTimeUtils.convertHHMMSSToTod(val)); + ResultFormatter.time_of_day( + results, + DateTimeUtils.convertHHMMSSToTod(val), + ); break; case 'ETA': const hhmm = val.split('.')[0].replace(':', ''); @@ -170,14 +183,26 @@ export class Label_80 extends DecoderPlugin { private parseCsvFormat(text: string, results: DecodeResult) { const csvParts = text.split(','); const header = csvParts[0].trim().split(/\s+/); - ResultFormatter.unknown(results,header[0], ' '); - ResultFormatter.unknown(results,header[1], ' '); - ResultFormatter.position(results, CoordinateUtils.decodeStringCoordinates(header[2])); + ResultFormatter.unknown(results, header[0], ' '); + ResultFormatter.unknown(results, header[1], ' '); + ResultFormatter.position( + results, + CoordinateUtils.decodeStringCoordinates(header[2]), + ); ResultFormatter.unknown(results, csvParts[1]); - ResultFormatter.time_of_day(results, DateTimeUtils.convertHHMMSSToTod(csvParts[2])); - ResultFormatter.unknownArr(results, csvParts.slice(4,6), ','); - ResultFormatter.temperature(results, ((csvParts[6].charAt(0) === 'M' ? -1 : 1) * parseInt(csvParts[6].slice(1), 10)).toString()); + ResultFormatter.time_of_day( + results, + DateTimeUtils.convertHHMMSSToTod(csvParts[2]), + ); + ResultFormatter.unknownArr(results, csvParts.slice(4, 6), ','); + ResultFormatter.temperature( + results, + ( + (csvParts[6].charAt(0) === 'M' ? -1 : 1) * + parseInt(csvParts[6].slice(1), 10) + ).toString(), + ); ResultFormatter.airspeed(results, parseInt(csvParts[7], 10)); - ResultFormatter.currentFuel( results, parseInt( csvParts[8], 10)); + ResultFormatter.currentFuel(results, parseInt(csvParts[8], 10)); } -} \ No newline at end of file +} diff --git a/lib/plugins/Label_83.test.ts b/lib/plugins/Label_83.test.ts index 2b9625a..3dee744 100644 --- a/lib/plugins/Label_83.test.ts +++ b/lib/plugins/Label_83.test.ts @@ -3,177 +3,178 @@ import { Label_83 } from './Label_83'; describe('Label 83', () => { let plugin: Label_83; - const message = {label: '83', text: ''}; + const message = { label: '83', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); plugin = new Label_83(decoder); }); -test('matches Label 83 qualifiers', () => { - expect(plugin.decode).toBeDefined(); - expect(plugin.name).toBe('label-83'); -}); + test('matches Label 83 qualifiers', () => { + expect(plugin.decode).toBeDefined(); + expect(plugin.name).toBe('label-83'); + }); -test('decodes Label 83 variant 1', () => { - // https://globe.adsbexchange.com/?icao=A2A3B5&showTrace=2024-09-22×tamp=1726967032 - message.text = 'KLAX,KEWR,220103, 40.53,- 74.47, 3836,212, 140.0, 19700'; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-83'); - expect(decodeResult.formatted.description).toBe('Airline Defined'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.raw.departure_icao).toBe('KLAX'); - expect(decodeResult.raw.arrival_icao).toBe('KEWR'); - expect(decodeResult.raw.day).toBe('22'); - expect(decodeResult.raw.position.latitude).toBe(40.53); - expect(decodeResult.raw.position.longitude).toBe(-74.47); - expect(decodeResult.raw.altitude).toBe(3836); - expect(decodeResult.raw.groundspeed).toBe(212); - expect(decodeResult.raw.heading).toBe(140); - expect(decodeResult.remaining.text).toBe('19700'); - expect(decodeResult.formatted.items.length).toBe(6); - expect(decodeResult.formatted.items[0].type).toBe('icao'); - expect(decodeResult.formatted.items[0].value).toBe('KLAX'); - expect(decodeResult.formatted.items[1].type).toBe('icao'); - expect(decodeResult.formatted.items[1].value).toBe('KEWR'); - expect(decodeResult.formatted.items[2].type).toBe('aircraft_position'); - expect(decodeResult.formatted.items[2].value).toBe('40.530 N, 74.470 W'); - expect(decodeResult.formatted.items[3].type).toBe('altitude'); - expect(decodeResult.formatted.items[3].value).toBe('3836 feet'); - expect(decodeResult.formatted.items[4].type).toBe('aircraft_groundspeed'); - expect(decodeResult.formatted.items[4].value).toBe('212 knots'); - expect(decodeResult.formatted.items[5].type).toBe('heading'); - expect(decodeResult.formatted.items[5].value).toBe('140'); -}); + test('decodes Label 83 variant 1', () => { + // https://globe.adsbexchange.com/?icao=A2A3B5&showTrace=2024-09-22×tamp=1726967032 + message.text = 'KLAX,KEWR,220103, 40.53,- 74.47, 3836,212, 140.0, 19700'; + const decodeResult = plugin.decode(message); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-83'); + expect(decodeResult.formatted.description).toBe('Airline Defined'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.raw.departure_icao).toBe('KLAX'); + expect(decodeResult.raw.arrival_icao).toBe('KEWR'); + expect(decodeResult.raw.day).toBe('22'); + expect(decodeResult.raw.position.latitude).toBe(40.53); + expect(decodeResult.raw.position.longitude).toBe(-74.47); + expect(decodeResult.raw.altitude).toBe(3836); + expect(decodeResult.raw.groundspeed).toBe(212); + expect(decodeResult.raw.heading).toBe(140); + expect(decodeResult.remaining.text).toBe('19700'); + expect(decodeResult.formatted.items.length).toBe(6); + expect(decodeResult.formatted.items[0].type).toBe('icao'); + expect(decodeResult.formatted.items[0].value).toBe('KLAX'); + expect(decodeResult.formatted.items[1].type).toBe('icao'); + expect(decodeResult.formatted.items[1].value).toBe('KEWR'); + expect(decodeResult.formatted.items[2].type).toBe('aircraft_position'); + expect(decodeResult.formatted.items[2].value).toBe('40.530 N, 74.470 W'); + expect(decodeResult.formatted.items[3].type).toBe('altitude'); + expect(decodeResult.formatted.items[3].value).toBe('3836 feet'); + expect(decodeResult.formatted.items[4].type).toBe('aircraft_groundspeed'); + expect(decodeResult.formatted.items[4].value).toBe('212 knots'); + expect(decodeResult.formatted.items[5].type).toBe('heading'); + expect(decodeResult.formatted.items[5].value).toBe('140'); + }); -test('decodes Label 83 variant 1 (C-band)', () => { - // https://app.airframes.io/messages/3413113024 - message.text = 'M05AUA0007KIAH,RJAA,110012, 39.12,-175.10,39001,265,-107.6, 64900'; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-83'); - expect(decodeResult.formatted.description).toBe('Airline Defined'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.raw.flight_number).toBe('UA7'); - expect(decodeResult.raw.departure_icao).toBe('KIAH'); - expect(decodeResult.raw.arrival_icao).toBe('RJAA'); - expect(decodeResult.raw.day).toBe('11'); - expect(decodeResult.raw.position.latitude).toBe(39.12); - expect(decodeResult.raw.position.longitude).toBe(-175.1); - expect(decodeResult.raw.altitude).toBe(39001); - expect(decodeResult.raw.groundspeed).toBe(265); - expect(decodeResult.raw.heading).toBe(-107.6); - expect(decodeResult.remaining.text).toBe('64900'); - expect(decodeResult.formatted.items.length).toBe(7); - expect(decodeResult.formatted.items[0].type).toBe('flight_number'); - expect(decodeResult.formatted.items[0].value).toBe('UA7'); - expect(decodeResult.formatted.items[1].type).toBe('icao'); - expect(decodeResult.formatted.items[1].value).toBe('KIAH'); - expect(decodeResult.formatted.items[2].type).toBe('icao'); - expect(decodeResult.formatted.items[2].value).toBe('RJAA'); - expect(decodeResult.formatted.items[3].type).toBe('aircraft_position'); - expect(decodeResult.formatted.items[3].value).toBe('39.120 N, 175.100 W'); - expect(decodeResult.formatted.items[4].type).toBe('altitude'); - expect(decodeResult.formatted.items[4].value).toBe('39001 feet'); - expect(decodeResult.formatted.items[5].type).toBe('aircraft_groundspeed'); - expect(decodeResult.formatted.items[5].value).toBe('265 knots'); - expect(decodeResult.formatted.items[6].type).toBe('heading'); - expect(decodeResult.formatted.items[6].value).toBe('-107.6'); -}); + test('decodes Label 83 variant 1 (C-band)', () => { + // https://app.airframes.io/messages/3413113024 + message.text = + 'M05AUA0007KIAH,RJAA,110012, 39.12,-175.10,39001,265,-107.6, 64900'; + const decodeResult = plugin.decode(message); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-83'); + expect(decodeResult.formatted.description).toBe('Airline Defined'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.raw.flight_number).toBe('UA7'); + expect(decodeResult.raw.departure_icao).toBe('KIAH'); + expect(decodeResult.raw.arrival_icao).toBe('RJAA'); + expect(decodeResult.raw.day).toBe('11'); + expect(decodeResult.raw.position.latitude).toBe(39.12); + expect(decodeResult.raw.position.longitude).toBe(-175.1); + expect(decodeResult.raw.altitude).toBe(39001); + expect(decodeResult.raw.groundspeed).toBe(265); + expect(decodeResult.raw.heading).toBe(-107.6); + expect(decodeResult.remaining.text).toBe('64900'); + expect(decodeResult.formatted.items.length).toBe(7); + expect(decodeResult.formatted.items[0].type).toBe('flight_number'); + expect(decodeResult.formatted.items[0].value).toBe('UA7'); + expect(decodeResult.formatted.items[1].type).toBe('icao'); + expect(decodeResult.formatted.items[1].value).toBe('KIAH'); + expect(decodeResult.formatted.items[2].type).toBe('icao'); + expect(decodeResult.formatted.items[2].value).toBe('RJAA'); + expect(decodeResult.formatted.items[3].type).toBe('aircraft_position'); + expect(decodeResult.formatted.items[3].value).toBe('39.120 N, 175.100 W'); + expect(decodeResult.formatted.items[4].type).toBe('altitude'); + expect(decodeResult.formatted.items[4].value).toBe('39001 feet'); + expect(decodeResult.formatted.items[5].type).toBe('aircraft_groundspeed'); + expect(decodeResult.formatted.items[5].value).toBe('265 knots'); + expect(decodeResult.formatted.items[6].type).toBe('heading'); + expect(decodeResult.formatted.items[6].value).toBe('-107.6'); + }); -test('decodes Label 83 variant 2', () => { - // https://globe.adsbexchange.com/?icao=478F43&showTrace=2024-09-22×tamp=1727022863 - message.text = '4DH3 ETAT2 0907/22 ENGM/KEWR .LN-RKO\r\n/ETA 1641'; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-83'); - expect(decodeResult.formatted.description).toBe('Airline Defined'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.raw.departure_icao).toBe('ENGM'); - expect(decodeResult.raw.arrival_icao).toBe('KEWR'); - expect(decodeResult.raw.day).toBe('22'); - expect(decodeResult.raw.tail).toBe('LN-RKO'); - expect(decodeResult.raw.eta_time).toBe(60060); - expect(decodeResult.remaining.text).toBe('0907'); - expect(decodeResult.formatted.items.length).toBe(4); - expect(decodeResult.formatted.items[0].code).toBe('ORG'); - expect(decodeResult.formatted.items[0].value).toBe('ENGM'); - expect(decodeResult.formatted.items[1].code).toBe('DST'); - expect(decodeResult.formatted.items[1].value).toBe('KEWR'); - expect(decodeResult.formatted.items[2].code).toBe('TAIL'); - expect(decodeResult.formatted.items[2].value).toBe('LN-RKO'); - expect(decodeResult.formatted.items[3].code).toBe('ETA'); - expect(decodeResult.formatted.items[3].value).toBe('16:41:00'); -}); + test('decodes Label 83 variant 2', () => { + // https://globe.adsbexchange.com/?icao=478F43&showTrace=2024-09-22×tamp=1727022863 + message.text = '4DH3 ETAT2 0907/22 ENGM/KEWR .LN-RKO\r\n/ETA 1641'; + const decodeResult = plugin.decode(message); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-83'); + expect(decodeResult.formatted.description).toBe('Airline Defined'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.raw.departure_icao).toBe('ENGM'); + expect(decodeResult.raw.arrival_icao).toBe('KEWR'); + expect(decodeResult.raw.day).toBe('22'); + expect(decodeResult.raw.tail).toBe('LN-RKO'); + expect(decodeResult.raw.eta_time).toBe(60060); + expect(decodeResult.remaining.text).toBe('0907'); + expect(decodeResult.formatted.items.length).toBe(4); + expect(decodeResult.formatted.items[0].code).toBe('ORG'); + expect(decodeResult.formatted.items[0].value).toBe('ENGM'); + expect(decodeResult.formatted.items[1].code).toBe('DST'); + expect(decodeResult.formatted.items[1].value).toBe('KEWR'); + expect(decodeResult.formatted.items[2].code).toBe('TAIL'); + expect(decodeResult.formatted.items[2].value).toBe('LN-RKO'); + expect(decodeResult.formatted.items[3].code).toBe('ETA'); + expect(decodeResult.formatted.items[3].value).toBe('16:41:00'); + }); -test('decodes Label 83 variant 3', () => { - const decoder = new MessageDecoder(); - const decoderPlugin = new Label_83(decoder); - - // https://globe.adsbexchange.com/?icao=AC15A1&showTrace=2024-09-22×tamp=1726977342 - message.text = '001PR22035539N4038.6W07427.80292500008'; - const decodeResult = decoderPlugin.decode(message); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-83'); - expect(decodeResult.formatted.description).toBe('Airline Defined'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.raw.day).toBe('22'); - expect(decodeResult.raw.position.latitude).toBe(40.64333333333333); - expect(decodeResult.raw.position.longitude).toBe(-74.46333333333334); - expect(decodeResult.raw.altitude).toBe(2925); - expect(decodeResult.remaining.text).toBe('00008'); - expect(decodeResult.formatted.items.length).toBe(2); - expect(decodeResult.formatted.items[0].type).toBe('aircraft_position'); - expect(decodeResult.formatted.items[0].value).toBe('40.643 N, 74.463 W'); - expect(decodeResult.formatted.items[1].type).toBe('altitude'); - expect(decodeResult.formatted.items[1].value).toBe('2925 feet'); -}); + test('decodes Label 83 variant 3', () => { + const decoder = new MessageDecoder(); + const decoderPlugin = new Label_83(decoder); + + // https://globe.adsbexchange.com/?icao=AC15A1&showTrace=2024-09-22×tamp=1726977342 + message.text = '001PR22035539N4038.6W07427.80292500008'; + const decodeResult = decoderPlugin.decode(message); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-83'); + expect(decodeResult.formatted.description).toBe('Airline Defined'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.raw.day).toBe('22'); + expect(decodeResult.raw.position.latitude).toBe(40.64333333333333); + expect(decodeResult.raw.position.longitude).toBe(-74.46333333333334); + expect(decodeResult.raw.altitude).toBe(2925); + expect(decodeResult.remaining.text).toBe('00008'); + expect(decodeResult.formatted.items.length).toBe(2); + expect(decodeResult.formatted.items[0].type).toBe('aircraft_position'); + expect(decodeResult.formatted.items[0].value).toBe('40.643 N, 74.463 W'); + expect(decodeResult.formatted.items[1].type).toBe('altitude'); + expect(decodeResult.formatted.items[1].value).toBe('2925 feet'); + }); -test('decodes Label 83 variant 3 (C-band)', () => { - const decoder = new MessageDecoder(); - const decoderPlugin = new Label_83(decoder); - - // https://app.airframes.io/messages/3413346742 - message.text = 'M09AXA0001001PR11013423N0556.6E11603.0000000----'; - const decodeResult = decoderPlugin.decode(message); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-83'); - expect(decodeResult.formatted.description).toBe('Airline Defined'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.raw.flight_number).toBe('XA1'); - expect(decodeResult.raw.day).toBe('11'); - expect(decodeResult.raw.position.latitude).toBe(5.943333333333333); - expect(decodeResult.raw.position.longitude).toBe(116.05); - expect(decodeResult.raw.altitude).toBe(0); - expect(decodeResult.remaining.text).toBe('0----'); - expect(decodeResult.formatted.items.length).toBe(3); - expect(decodeResult.formatted.items[0].type).toBe('flight_number'); - expect(decodeResult.formatted.items[0].value).toBe('XA1'); - expect(decodeResult.formatted.items[1].type).toBe('aircraft_position'); - expect(decodeResult.formatted.items[1].value).toBe('5.943 N, 116.050 E'); - expect(decodeResult.formatted.items[2].type).toBe('altitude'); - expect(decodeResult.formatted.items[2].value).toBe('0 feet'); -}); + test('decodes Label 83 variant 3 (C-band)', () => { + const decoder = new MessageDecoder(); + const decoderPlugin = new Label_83(decoder); + + // https://app.airframes.io/messages/3413346742 + message.text = 'M09AXA0001001PR11013423N0556.6E11603.0000000----'; + const decodeResult = decoderPlugin.decode(message); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-83'); + expect(decodeResult.formatted.description).toBe('Airline Defined'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.raw.flight_number).toBe('XA1'); + expect(decodeResult.raw.day).toBe('11'); + expect(decodeResult.raw.position.latitude).toBe(5.943333333333333); + expect(decodeResult.raw.position.longitude).toBe(116.05); + expect(decodeResult.raw.altitude).toBe(0); + expect(decodeResult.remaining.text).toBe('0----'); + expect(decodeResult.formatted.items.length).toBe(3); + expect(decodeResult.formatted.items[0].type).toBe('flight_number'); + expect(decodeResult.formatted.items[0].value).toBe('XA1'); + expect(decodeResult.formatted.items[1].type).toBe('aircraft_position'); + expect(decodeResult.formatted.items[1].value).toBe('5.943 N, 116.050 E'); + expect(decodeResult.formatted.items[2].type).toBe('altitude'); + expect(decodeResult.formatted.items[2].value).toBe('0 feet'); + }); -test('decodes Label 83 ', () => { - message.text = '83 Bogus message'; - const decodeResult = plugin.decode(message); + test('decodes Label 83 ', () => { + message.text = '83 Bogus message'; + const decodeResult = plugin.decode(message); - expect(decodeResult.decoded).toBe(false); - expect(decodeResult.decoder.decodeLevel).toBe('none'); - expect(decodeResult.decoder.name).toBe('label-83'); - expect(decodeResult.formatted.description).toBe('Airline Defined'); - expect(decodeResult.formatted.items.length).toBe(0); + expect(decodeResult.decoded).toBe(false); + expect(decodeResult.decoder.decodeLevel).toBe('none'); + expect(decodeResult.decoder.name).toBe('label-83'); + expect(decodeResult.formatted.description).toBe('Airline Defined'); + expect(decodeResult.formatted.items.length).toBe(0); + }); }); -}); \ No newline at end of file diff --git a/lib/plugins/Label_83.ts b/lib/plugins/Label_83.ts index acdd900..eb65c33 100644 --- a/lib/plugins/Label_83.ts +++ b/lib/plugins/Label_83.ts @@ -7,13 +7,13 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_83 extends DecoderPlugin { name = 'label-83'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['83'], }; } - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { const decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; decodeResult.message = message; @@ -22,62 +22,68 @@ export class Label_83 extends DecoderPlugin { // Inmarsat C-band seems to prefix normal messages with a message number and flight number let text = message.text; if (text.match(/^M\d{2}A\w{6}/)) { - ResultFormatter.flightNumber(decodeResult, message.text.substring(4, 10).replace(/^([A-Z]+)0*/g, "$1")); - text = text.substring(10); + ResultFormatter.flightNumber( + decodeResult, + message.text.substring(4, 10).replace(/^([A-Z]+)0*/g, '$1'), + ); + text = text.substring(10); } decodeResult.decoded = true; - if (text.substring(0, 10) === "4DH3 ETAT2") { - // variant 2 - const fields = text.split(/\s+/); - if (fields[2].length > 5) { - decodeResult.raw.day = fields[2].substring(5); - } - ResultFormatter.unknown(decodeResult, fields[2].substring(0, 4)); - const subfields = fields[3].split("/"); - ResultFormatter.departureAirport(decodeResult, subfields[0]); - ResultFormatter.arrivalAirport(decodeResult, subfields[1]); - ResultFormatter.tail(decodeResult, fields[4].replace(/\./g, "")); - ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(fields[6] + "00")); - } else if (text.substring(0, 5) === "001PR") { - // variant 3 - decodeResult.raw.day = text.substring(5, 7); - const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(text.substring(13, 28).replace(/\./g, "")) - if (position) { - ResultFormatter.position(decodeResult, position); - } - ResultFormatter.altitude(decodeResult, Number(text.substring(28, 33))); - ResultFormatter.unknown(decodeResult, text.substring(33)); + if (text.substring(0, 10) === '4DH3 ETAT2') { + // variant 2 + const fields = text.split(/\s+/); + if (fields[2].length > 5) { + decodeResult.raw.day = fields[2].substring(5); + } + ResultFormatter.unknown(decodeResult, fields[2].substring(0, 4)); + const subfields = fields[3].split('/'); + ResultFormatter.departureAirport(decodeResult, subfields[0]); + ResultFormatter.arrivalAirport(decodeResult, subfields[1]); + ResultFormatter.tail(decodeResult, fields[4].replace(/\./g, '')); + ResultFormatter.eta( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(fields[6] + '00'), + ); + } else if (text.substring(0, 5) === '001PR') { + // variant 3 + decodeResult.raw.day = text.substring(5, 7); + const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes( + text.substring(13, 28).replace(/\./g, ''), + ); + if (position) { + ResultFormatter.position(decodeResult, position); + } + ResultFormatter.altitude(decodeResult, Number(text.substring(28, 33))); + ResultFormatter.unknown(decodeResult, text.substring(33)); } else { - const fields = text.replace(/\s/g, "").split(','); - if (fields.length === 9) { - // variant 1 - ResultFormatter.departureAirport(decodeResult, fields[0]); - ResultFormatter.arrivalAirport(decodeResult, fields[1]); - decodeResult.raw.day = fields[2].substring(0,2); - decodeResult.raw.time = fields[2].substring(2); - ResultFormatter.position(decodeResult, - { - latitude: Number(fields[3].replace(/\s/g, "")), - longitude: Number(fields[4].replace(/\s/g, "")), - }); - ResultFormatter.altitude(decodeResult, Number(fields[5])); - ResultFormatter.groundspeed(decodeResult, Number(fields[6])); - ResultFormatter.heading(decodeResult, Number(fields[7])); - ResultFormatter.unknown(decodeResult, fields[8]); - } else { - decodeResult.decoded = false; - ResultFormatter.unknown(decodeResult, message.text); - } + const fields = text.replace(/\s/g, '').split(','); + if (fields.length === 9) { + // variant 1 + ResultFormatter.departureAirport(decodeResult, fields[0]); + ResultFormatter.arrivalAirport(decodeResult, fields[1]); + decodeResult.raw.day = fields[2].substring(0, 2); + decodeResult.raw.time = fields[2].substring(2); + ResultFormatter.position(decodeResult, { + latitude: Number(fields[3].replace(/\s/g, '')), + longitude: Number(fields[4].replace(/\s/g, '')), + }); + ResultFormatter.altitude(decodeResult, Number(fields[5])); + ResultFormatter.groundspeed(decodeResult, Number(fields[6])); + ResultFormatter.heading(decodeResult, Number(fields[7])); + ResultFormatter.unknown(decodeResult, fields[8]); + } else { + decodeResult.decoded = false; + ResultFormatter.unknown(decodeResult, message.text); + } } if (decodeResult.decoded) { - if(!decodeResult.remaining.text) - decodeResult.decoder.decodeLevel = 'full'; - else - decodeResult.decoder.decodeLevel = 'partial'; + if (!decodeResult.remaining.text) + decodeResult.decoder.decodeLevel = 'full'; + else decodeResult.decoder.decodeLevel = 'partial'; } else { - decodeResult.decoder.decodeLevel = 'none'; + decodeResult.decoder.decodeLevel = 'none'; } return decodeResult; diff --git a/lib/plugins/Label_8E.test.ts b/lib/plugins/Label_8E.test.ts index daf51c4..737fb3d 100644 --- a/lib/plugins/Label_8E.test.ts +++ b/lib/plugins/Label_8E.test.ts @@ -3,41 +3,43 @@ import { Label_8E } from './Label_8E'; describe('Label 8E', () => { let plugin: Label_8E; - const message = {label: '8E', text: ''}; + const message = { label: '8E', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); plugin = new Label_8E(decoder); }); -test('matches qualifiers', () => { - const decoder = new MessageDecoder(); - const decoderPlugin = new Label_8E(decoder); + test('matches qualifiers', () => { + const decoder = new MessageDecoder(); + const decoderPlugin = new Label_8E(decoder); - expect(decoderPlugin.decode).toBeDefined(); - expect(decoderPlugin.name).toBe('label-8e'); - expect(decoderPlugin.qualifiers).toBeDefined(); - expect(decoderPlugin.qualifiers()).toEqual({ - labels: ['8E'], + expect(decoderPlugin.decode).toBeDefined(); + expect(decoderPlugin.name).toBe('label-8e'); + expect(decoderPlugin.qualifiers).toBeDefined(); + expect(decoderPlugin.qualifiers()).toEqual({ + labels: ['8E'], + }); + }); + test('decodes variant 1', () => { + message.text = 'EGSS,1618'; + const decodeResult = plugin.decode(message); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.name).toBe('label-8e'); + expect(decodeResult.formatted.description).toBe('ETA Report'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.raw.arrival_icao).toBe('EGSS'); + expect(decodeResult.formatted.items.length).toBe(2); + expect(decodeResult.formatted.items[0].type).toBe('time_of_day'); + expect(decodeResult.formatted.items[0].code).toBe('ETA'); + expect(decodeResult.formatted.items[0].label).toBe( + 'Estimated Time of Arrival', + ); + expect(decodeResult.formatted.items[0].value).toBe('16:18:00'); + expect(decodeResult.formatted.items[1].type).toBe('icao'); + expect(decodeResult.formatted.items[1].code).toBe('DST'); + expect(decodeResult.formatted.items[1].label).toBe('Destination'); + expect(decodeResult.formatted.items[1].value).toBe('EGSS'); }); }); -test('decodes variant 1', () => { - message.text = 'EGSS,1618'; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.name).toBe('label-8e'); - expect(decodeResult.formatted.description).toBe('ETA Report'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.raw.arrival_icao).toBe('EGSS'); - expect(decodeResult.formatted.items.length).toBe(2); - expect(decodeResult.formatted.items[0].type).toBe('time_of_day'); - expect(decodeResult.formatted.items[0].code).toBe('ETA'); - expect(decodeResult.formatted.items[0].label).toBe('Estimated Time of Arrival'); - expect(decodeResult.formatted.items[0].value).toBe('16:18:00'); - expect(decodeResult.formatted.items[1].type).toBe('icao'); - expect(decodeResult.formatted.items[1].code).toBe('DST'); - expect(decodeResult.formatted.items[1].label).toBe('Destination'); - expect(decodeResult.formatted.items[1].value).toBe('EGSS'); -}); -}); \ No newline at end of file diff --git a/lib/plugins/Label_8E.ts b/lib/plugins/Label_8E.ts index ba5e141..c84ca13 100644 --- a/lib/plugins/Label_8E.ts +++ b/lib/plugins/Label_8E.ts @@ -7,13 +7,13 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_8E extends DecoderPlugin { name = 'label-8e'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { - labels: ["8E"], + labels: ['8E'], }; } - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { const decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; decodeResult.formatted.description = 'ETA Report'; @@ -25,11 +25,14 @@ export class Label_8E extends DecoderPlugin { const results = message.text.match(regex); if (results?.groups) { if (options.debug) { - console.log(`Label 8E ETA: groups`); + console.log('Label 8E ETA: groups'); console.log(results.groups); } - ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(results.groups.arrival_eta)); + ResultFormatter.eta( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(results.groups.arrival_eta), + ); ResultFormatter.arrivalAirport(decodeResult, results.groups.arrival_icao); } diff --git a/lib/plugins/Label_B6.ts b/lib/plugins/Label_B6.ts index 5ff62c6..9e3dde3 100644 --- a/lib/plugins/Label_B6.ts +++ b/lib/plugins/Label_B6.ts @@ -5,21 +5,21 @@ import { DecodeResult, Message, Options } from '../DecoderPluginInterface'; export class Label_B6_Forwardslash extends DecoderPlugin { name = 'label-b6-forwardslash'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['B6'], preambles: ['/'], }; } - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { const decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; decodeResult.formatted.description = 'CPDLC Message'; decodeResult.message = message; if (options.debug) { - console.log("CPDLC: " + message); + console.log('CPDLC: ' + message); } return decodeResult; diff --git a/lib/plugins/Label_ColonComma.ts b/lib/plugins/Label_ColonComma.ts index 8690a39..ba9e5a0 100644 --- a/lib/plugins/Label_ColonComma.ts +++ b/lib/plugins/Label_ColonComma.ts @@ -4,24 +4,25 @@ import { DecodeResult, Message, Options } from '../DecoderPluginInterface'; export class Label_ColonComma extends DecoderPlugin { name = 'label-colon-comma'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: [':;'], }; } - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { const decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; decodeResult.raw.frequency = Number(message.text) / 1000; - decodeResult.formatted.description = 'Aircraft Transceiver Frequency Change'; + decodeResult.formatted.description = + 'Aircraft Transceiver Frequency Change'; decodeResult.formatted.items.push({ type: 'frequency', label: 'Frequency', value: `${decodeResult.raw.frequency} MHz`, - code: 'FREQ' + code: 'FREQ', }); decodeResult.decoded = true; diff --git a/lib/plugins/Label_H1.test.ts b/lib/plugins/Label_H1.test.ts index 7579afb..0526d1b 100644 --- a/lib/plugins/Label_H1.test.ts +++ b/lib/plugins/Label_H1.test.ts @@ -2,7 +2,6 @@ import { MessageDecoder } from '../MessageDecoder'; import { Label_H1 } from './Label_H1'; describe('Label_H1 INI', () => { - let plugin: Label_H1; beforeEach(() => { @@ -10,7 +9,6 @@ describe('Label_H1 INI', () => { plugin = new Label_H1(decoder); }); - test('matches Label H1 Preamble FLR qualifiers', () => { expect(plugin.decode).toBeDefined(); expect(plugin.name).toBe('label-h1'); @@ -19,4 +17,4 @@ describe('Label_H1 INI', () => { labels: ['H1'], }); }); -}); \ No newline at end of file +}); diff --git a/lib/plugins/Label_H1.ts b/lib/plugins/Label_H1.ts index 8a3a4dc..67a3214 100644 --- a/lib/plugins/Label_H1.ts +++ b/lib/plugins/Label_H1.ts @@ -5,36 +5,46 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_H1 extends DecoderPlugin { name = 'label-h1'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['H1'], }; } - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { let decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; decodeResult.message = message; - const msg = message.text.replace(/\n|\r/g, ""); + const msg = message.text.replace(/\n|\r/g, ''); const parts = msg.split('#'); let decoded = false; - if(parts.length === 1) { - decoded = H1Helper.decodeH1Message(decodeResult, msg); + if (parts.length === 1) { + decoded = H1Helper.decodeH1Message(decodeResult, msg); } else if (parts.length == 2) { - const offset = isNaN(parseInt(parts[1][1])) ? 3: 4; - decoded = H1Helper.decodeH1Message(decodeResult, msg.slice(parts[0].length + offset)); // skip up to # and then a little more + const offset = isNaN(parseInt(parts[1][1])) ? 3 : 4; + decoded = H1Helper.decodeH1Message( + decodeResult, + msg.slice(parts[0].length + offset), + ); // skip up to # and then a little more if (decoded && parts[0].length > 0) { - // ResultFormatter.unknown(decodeResult, parts[0].substring(0, 4)); - ResultFormatter.flightNumber(decodeResult, parts[0].substring(4)); - //ResultFormatter.unknown(decodeResult, parts[1].length == 5 ? parts[1].substring(0, 2) : parts[1].substring(0, 3), '#'); - // hack of the highest degree as we've parsed the rest of the message already but we want the remaining text to be in order - decodeResult.remaining.text = parts[0].substring(0, 4) + '#' + parts[1].substring(0, offset -1) + '/' + decodeResult.remaining.text; + // ResultFormatter.unknown(decodeResult, parts[0].substring(0, 4)); + ResultFormatter.flightNumber(decodeResult, parts[0].substring(4)); + //ResultFormatter.unknown(decodeResult, parts[1].length == 5 ? parts[1].substring(0, 2) : parts[1].substring(0, 3), '#'); + // hack of the highest degree as we've parsed the rest of the message already but we want the remaining text to be in order + decodeResult.remaining.text = + parts[0].substring(0, 4) + + '#' + + parts[1].substring(0, offset - 1) + + '/' + + decodeResult.remaining.text; } } decodeResult.decoded = decoded; - decodeResult.decoder.decodeLevel = !decodeResult.remaining.text ? 'full' : 'partial'; + decodeResult.decoder.decodeLevel = !decodeResult.remaining.text + ? 'full' + : 'partial'; if (decodeResult.formatted.items.length === 0) { if (options.debug) { console.log(`Decoder: Unknown H1 message: ${message.text}`); diff --git a/lib/plugins/Label_H1_FLR.test.ts b/lib/plugins/Label_H1_FLR.test.ts index 2e2b563..82394c2 100644 --- a/lib/plugins/Label_H1_FLR.test.ts +++ b/lib/plugins/Label_H1_FLR.test.ts @@ -3,126 +3,137 @@ import { Label_H1_FLR } from './Label_H1_FLR'; describe('Label H1 Preamble FLR', () => { let plugin: Label_H1_FLR; - const message = {label: 'H1', text: ''}; + const message = { label: 'H1', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); plugin = new Label_H1_FLR(decoder); }); -test('matches qualifiers', () => { - expect(plugin.decode).toBeDefined(); - expect(plugin.name).toBe('label-h1-flr'); - expect(plugin.qualifiers).toBeDefined(); - expect(plugin.qualifiers()).toEqual({ - labels: ['H1'], - preambles: ['FLR', '#CFBFLR'], + test('matches qualifiers', () => { + expect(plugin.decode).toBeDefined(); + expect(plugin.name).toBe('label-h1-flr'); + expect(plugin.qualifiers).toBeDefined(); + expect(plugin.qualifiers()).toEqual({ + labels: ['H1'], + preambles: ['FLR', '#CFBFLR'], + }); }); -}); -test('decodes FWC2', () => { - // https://app.airframes.io/messages/2436863787 - message.text = 'FLR/FR24030411230034583106FWC2 :NO DATA FROM GPS1 /IDECAM 2 ,ECAM 1 '; - const decodeResult = plugin.decode(message); + test('decodes FWC2', () => { + // https://app.airframes.io/messages/2436863787 + message.text = + 'FLR/FR24030411230034583106FWC2 :NO DATA FROM GPS1 /IDECAM 2 ,ECAM 1 '; + const decodeResult = plugin.decode(message); - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-h1-flr'); - expect(decodeResult.formatted.description).toBe('Fault Log Report'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.raw.message_timestamp).toBe(1712143380); - expect(decodeResult.formatted.items.length).toBe(1); - expect(decodeResult.formatted.items[0].type).toBe('fault'); - expect(decodeResult.formatted.items[0].code).toBe('FR'); - expect(decodeResult.formatted.items[0].label).toBe('Fault Report'); - expect(decodeResult.formatted.items[0].value).toBe('FWC2 :NO DATA FROM GPS1 /IDECAM 2 ,ECAM 1 '); - expect(decodeResult.remaining.text).toBe('34583106'); -}); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-h1-flr'); + expect(decodeResult.formatted.description).toBe('Fault Log Report'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.raw.message_timestamp).toBe(1712143380); + expect(decodeResult.formatted.items.length).toBe(1); + expect(decodeResult.formatted.items[0].type).toBe('fault'); + expect(decodeResult.formatted.items[0].code).toBe('FR'); + expect(decodeResult.formatted.items[0].label).toBe('Fault Report'); + expect(decodeResult.formatted.items[0].value).toBe( + 'FWC2 :NO DATA FROM GPS1 /IDECAM 2 ,ECAM 1 ', + ); + expect(decodeResult.remaining.text).toBe('34583106'); + }); -test('decodes Label H1 Preamble FLR CDSU', () => { - // https://app.airframes.io/messages/2436949441 - message.text = 'FLR/PNRC12860AA07/FR24030411040023723406CDSU(9RA)/DU SD(4WT2) /IDEIS 1 '; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-h1-flr'); - expect(decodeResult.formatted.description).toBe('Fault Log Report'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.raw.message_timestamp).toBe(1712142240); - expect(decodeResult.formatted.items.length).toBe(1); - expect(decodeResult.formatted.items[0].type).toBe('fault'); - expect(decodeResult.formatted.items[0].code).toBe('FR'); - expect(decodeResult.formatted.items[0].label).toBe('Fault Report'); - expect(decodeResult.formatted.items[0].value).toBe('CDSU(9RA)/DU SD(4WT2) /IDEIS 1 '); - expect(decodeResult.remaining.text).toBe('PNRC12860AA07/23723406'); -}); + test('decodes Label H1 Preamble FLR CDSU', () => { + // https://app.airframes.io/messages/2436949441 + message.text = + 'FLR/PNRC12860AA07/FR24030411040023723406CDSU(9RA)/DU SD(4WT2) /IDEIS 1 '; + const decodeResult = plugin.decode(message); -test('decodes Label H1 Preamble FLR RA1', () => { - // https://app.airframes.io/messages/2436701901 - message.text = 'FLR/FR24030409560034423306RA1/IDEFCS 1 ,EFCS 2 '; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-h1-flr'); - expect(decodeResult.formatted.description).toBe('Fault Log Report'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.raw.message_timestamp).toBe(1712138160); - expect(decodeResult.formatted.items.length).toBe(1); - expect(decodeResult.formatted.items[0].type).toBe('fault'); - expect(decodeResult.formatted.items[0].code).toBe('FR'); - expect(decodeResult.formatted.items[0].label).toBe('Fault Report'); - expect(decodeResult.formatted.items[0].value).toBe('RA1/IDEFCS 1 ,EFCS 2 '); - expect(decodeResult.remaining.text).toBe('34423306'); -}); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-h1-flr'); + expect(decodeResult.formatted.description).toBe('Fault Log Report'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.raw.message_timestamp).toBe(1712142240); + expect(decodeResult.formatted.items.length).toBe(1); + expect(decodeResult.formatted.items[0].type).toBe('fault'); + expect(decodeResult.formatted.items[0].code).toBe('FR'); + expect(decodeResult.formatted.items[0].label).toBe('Fault Report'); + expect(decodeResult.formatted.items[0].value).toBe( + 'CDSU(9RA)/DU SD(4WT2) /IDEIS 1 ', + ); + expect(decodeResult.remaining.text).toBe('PNRC12860AA07/23723406'); + }); -test('decodes Label H1 Preamble FLR LQD', () => { - // https://app.airframes.io/messages/2436740709 - message.text = 'FLR/FR24030410030038316206LIQD LVL XMTR (40MG)/IDTOILET '; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-h1-flr'); - expect(decodeResult.formatted.description).toBe('Fault Log Report'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.raw.message_timestamp).toBe(1712138580); - expect(decodeResult.formatted.items.length).toBe(1); - expect(decodeResult.formatted.items[0].type).toBe('fault'); - expect(decodeResult.formatted.items[0].code).toBe('FR'); - expect(decodeResult.formatted.items[0].label).toBe('Fault Report'); - expect(decodeResult.formatted.items[0].value).toBe('LIQD LVL XMTR (40MG)/IDTOILET '); - expect(decodeResult.remaining.text).toBe('38316206'); -}); + test('decodes Label H1 Preamble FLR RA1', () => { + // https://app.airframes.io/messages/2436701901 + message.text = 'FLR/FR24030409560034423306RA1/IDEFCS 1 ,EFCS 2 '; + const decodeResult = plugin.decode(message); -test('decodes Label H1 Preamble FLR LQD', () => { - // https://app.airframes.io/messages/2437260976 - message.text = '#CFBFLR/FR24030412400034723406ATC1(1SH1)/TCAS(1000SG) /IDTCAS '; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-h1-flr'); - expect(decodeResult.formatted.description).toBe('Fault Log Report'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.raw.message_timestamp).toBe(1712148000); - expect(decodeResult.formatted.items.length).toBe(1); - expect(decodeResult.formatted.items[0].type).toBe('fault'); - expect(decodeResult.formatted.items[0].code).toBe('FR'); - expect(decodeResult.formatted.items[0].label).toBe('Fault Report'); - expect(decodeResult.formatted.items[0].value).toBe('ATC1(1SH1)/TCAS(1000SG) /IDTCAS '); - expect(decodeResult.remaining.text).toBe('34723406'); -}); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-h1-flr'); + expect(decodeResult.formatted.description).toBe('Fault Log Report'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.raw.message_timestamp).toBe(1712138160); + expect(decodeResult.formatted.items.length).toBe(1); + expect(decodeResult.formatted.items[0].type).toBe('fault'); + expect(decodeResult.formatted.items[0].code).toBe('FR'); + expect(decodeResult.formatted.items[0].label).toBe('Fault Report'); + expect(decodeResult.formatted.items[0].value).toBe('RA1/IDEFCS 1 ,EFCS 2 '); + expect(decodeResult.remaining.text).toBe('34423306'); + }); -test('decodes Label H1 Preamble FLR invalid', () => { - message.text = 'FLR '; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(false); - expect(decodeResult.decoder.decodeLevel).toBe('none'); - expect(decodeResult.decoder.name).toBe('label-h1-flr'); - expect(decodeResult.formatted.description).toBe('Fault Log Report'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.remaining.text).toBe(message.text); + test('decodes Label H1 Preamble FLR LQD', () => { + // https://app.airframes.io/messages/2436740709 + message.text = 'FLR/FR24030410030038316206LIQD LVL XMTR (40MG)/IDTOILET '; + const decodeResult = plugin.decode(message); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-h1-flr'); + expect(decodeResult.formatted.description).toBe('Fault Log Report'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.raw.message_timestamp).toBe(1712138580); + expect(decodeResult.formatted.items.length).toBe(1); + expect(decodeResult.formatted.items[0].type).toBe('fault'); + expect(decodeResult.formatted.items[0].code).toBe('FR'); + expect(decodeResult.formatted.items[0].label).toBe('Fault Report'); + expect(decodeResult.formatted.items[0].value).toBe( + 'LIQD LVL XMTR (40MG)/IDTOILET ', + ); + expect(decodeResult.remaining.text).toBe('38316206'); + }); + + test('decodes Label H1 Preamble FLR LQD', () => { + // https://app.airframes.io/messages/2437260976 + message.text = + '#CFBFLR/FR24030412400034723406ATC1(1SH1)/TCAS(1000SG) /IDTCAS '; + const decodeResult = plugin.decode(message); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-h1-flr'); + expect(decodeResult.formatted.description).toBe('Fault Log Report'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.raw.message_timestamp).toBe(1712148000); + expect(decodeResult.formatted.items.length).toBe(1); + expect(decodeResult.formatted.items[0].type).toBe('fault'); + expect(decodeResult.formatted.items[0].code).toBe('FR'); + expect(decodeResult.formatted.items[0].label).toBe('Fault Report'); + expect(decodeResult.formatted.items[0].value).toBe( + 'ATC1(1SH1)/TCAS(1000SG) /IDTCAS ', + ); + expect(decodeResult.remaining.text).toBe('34723406'); + }); + + test('decodes Label H1 Preamble FLR invalid', () => { + message.text = 'FLR '; + const decodeResult = plugin.decode(message); + + expect(decodeResult.decoded).toBe(false); + expect(decodeResult.decoder.decodeLevel).toBe('none'); + expect(decodeResult.decoder.name).toBe('label-h1-flr'); + expect(decodeResult.formatted.description).toBe('Fault Log Report'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.remaining.text).toBe(message.text); + }); }); -}); \ No newline at end of file diff --git a/lib/plugins/Label_H1_FLR.ts b/lib/plugins/Label_H1_FLR.ts index 193106f..061aa0f 100644 --- a/lib/plugins/Label_H1_FLR.ts +++ b/lib/plugins/Label_H1_FLR.ts @@ -6,14 +6,14 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_H1_FLR extends DecoderPlugin { name = 'label-h1-flr'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { - labels: ["H1"], + labels: ['H1'], preambles: ['FLR', '#CFBFLR'], }; } - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { let decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; decodeResult.formatted.description = 'Fault Log Report'; @@ -21,23 +21,29 @@ export class Label_H1_FLR extends DecoderPlugin { const parts = message.text.split('/FR'); - if(parts.length > 1){ + if (parts.length > 1) { //decode header // can go away, here because i want to decode it const fields = parts[0].split('/'); // 0 is the msg type - for(let i=1; i { - let plugin: Label_H1; - const message = {label: 'H1', text: ''}; + const message = { label: 'H1', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); @@ -12,7 +11,8 @@ describe('Label_H1 FPN', () => { }); test('decodes Label H1 Preamble FPN landing', () => { - message.text = 'FPN/RI:DA:KEWR:AA:KDFW:CR:EWRDFW01(17L)..SAAME.J6.HVQ.Q68.LITTR..MEEOW..FEWWW:A:SEEVR4.FEWWW:F:VECTOR..DISCO..RIVET:AP:ILS 17L.RIVET:F:TACKEC8B5'; + message.text = + 'FPN/RI:DA:KEWR:AA:KDFW:CR:EWRDFW01(17L)..SAAME.J6.HVQ.Q68.LITTR..MEEOW..FEWWW:A:SEEVR4.FEWWW:F:VECTOR..DISCO..RIVET:AP:ILS 17L.RIVET:F:TACKEC8B5'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -26,13 +26,21 @@ describe('Label_H1 FPN', () => { expect(decodeResult.formatted.items[2].label).toBe('Destination'); expect(decodeResult.formatted.items[2].value).toBe('KDFW'); expect(decodeResult.formatted.items[3].label).toBe('Company Route'); - expect(decodeResult.formatted.items[3].value).toBe('EWRDFW01(17L): >> SAAME > J6 > HVQ > Q68 > LITTR >> MEEOW >> FEWWW'); + expect(decodeResult.formatted.items[3].value).toBe( + 'EWRDFW01(17L): >> SAAME > J6 > HVQ > Q68 > LITTR >> MEEOW >> FEWWW', + ); expect(decodeResult.formatted.items[4].label).toBe('Arrival Procedure'); - expect(decodeResult.formatted.items[4].value).toBe('SEEVR4 starting at FEWWW'); + expect(decodeResult.formatted.items[4].value).toBe( + 'SEEVR4 starting at FEWWW', + ); expect(decodeResult.formatted.items[5].label).toBe('Aircraft Route'); - expect(decodeResult.formatted.items[5].value).toBe('VECTOR >> DISCO >> RIVET'); + expect(decodeResult.formatted.items[5].value).toBe( + 'VECTOR >> DISCO >> RIVET', + ); expect(decodeResult.formatted.items[6].label).toBe('Approach Procedure'); - expect(decodeResult.formatted.items[6].value).toBe('ILS 17L starting at RIVET'); + expect(decodeResult.formatted.items[6].value).toBe( + 'ILS 17L starting at RIVET', + ); expect(decodeResult.formatted.items[7].label).toBe('Aircraft Route'); expect(decodeResult.formatted.items[7].value).toBe('TACKE'); expect(decodeResult.formatted.items[8].label).toBe('Message Checksum'); @@ -41,7 +49,8 @@ describe('Label_H1 FPN', () => { test('decodes Label H1 Preamble FPN full flight', () => { // https://app.airframes.io/messages/2161768398 - message.text = 'FPN/FNAAL1956/RP:DA:KPHL:AA:KPHX:CR:PHLPHX61:R:27L(26O):D:PHL3:A:EAGUL6.ZUN:AP:ILS26..AIR,N40010W080490.J110.BOWRR..VLA,N39056W089097..STL,N38516W090289..GIBSN,N38430W092244..TYGER,N38410W094050..GCK,N37551W100435..DIXAN,N36169W105573..ZUN,N34579W109093293B'; + message.text = + 'FPN/FNAAL1956/RP:DA:KPHL:AA:KPHX:CR:PHLPHX61:R:27L(26O):D:PHL3:A:EAGUL6.ZUN:AP:ILS26..AIR,N40010W080490.J110.BOWRR..VLA,N39056W089097..STL,N38516W090289..GIBSN,N38430W092244..TYGER,N38410W094050..GCK,N37551W100435..DIXAN,N36169W105573..ZUN,N34579W109093293B'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -65,21 +74,26 @@ describe('Label_H1 FPN', () => { expect(decodeResult.formatted.items[6].label).toBe('Departure Procedure'); expect(decodeResult.formatted.items[6].value).toBe('PHL3'); expect(decodeResult.formatted.items[7].label).toBe('Arrival Procedure'); - expect(decodeResult.formatted.items[7].value).toBe('EAGUL6 starting at ZUN'); + expect(decodeResult.formatted.items[7].value).toBe( + 'EAGUL6 starting at ZUN', + ); expect(decodeResult.formatted.items[8].label).toBe('Approach Procedure'); - expect(decodeResult.formatted.items[8].value).toBe('ILS26: >> AIR(40.017 N, 80.817 W) > J110 > BOWRR >> VLA(39.093 N, 89.162 W) >> STL(38.860 N, 90.482 W) >> GIBSN(38.717 N, 92.407 W) >> TYGER(38.683 N, 94.083 W) >> GCK(37.918 N, 100.725 W) >> DIXAN(36.282 N, 105.955 W) >> ZUN(34.965 N, 109.155 W)'); + expect(decodeResult.formatted.items[8].value).toBe( + 'ILS26: >> AIR(40.017 N, 80.817 W) > J110 > BOWRR >> VLA(39.093 N, 89.162 W) >> STL(38.860 N, 90.482 W) >> GIBSN(38.717 N, 92.407 W) >> TYGER(38.683 N, 94.083 W) >> GCK(37.918 N, 100.725 W) >> DIXAN(36.282 N, 105.955 W) >> ZUN(34.965 N, 109.155 W)', + ); expect(decodeResult.formatted.items[9].label).toBe('Message Checksum'); expect(decodeResult.formatted.items[9].value).toBe('0x293b'); }); test('decodes Label H1 Preamble FPN in-flight', () => { // https://app.airframes.io/messages/2161761202 - message.text = 'FPN/FNUAL1187/RP:DA:KSFO:AA:KPHX:F:KAYEX,N36292W120569..LOSHN,N35509W120000..BOILE,N34253W118016..BLH,N33358W114457DDFB'; + message.text = + 'FPN/FNUAL1187/RP:DA:KSFO:AA:KPHX:F:KAYEX,N36292W120569..LOSHN,N35509W120000..BOILE,N34253W118016..BLH,N33358W114457DDFB'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); expect(decodeResult.decoder.decodeLevel).toBe('full'); - expect(decodeResult.raw.flight_number).toBe('UAL1187') + expect(decodeResult.raw.flight_number).toBe('UAL1187'); expect(decodeResult.formatted.description).toBe('Flight Plan'); expect(decodeResult.formatted.items.length).toBe(5); expect(decodeResult.formatted.items[0].label).toBe('Route Status'); @@ -89,14 +103,17 @@ describe('Label_H1 FPN', () => { expect(decodeResult.formatted.items[2].label).toBe('Destination'); expect(decodeResult.formatted.items[2].value).toBe('KPHX'); expect(decodeResult.formatted.items[3].label).toBe('Aircraft Route'); - expect(decodeResult.formatted.items[3].value).toBe('KAYEX(36.487 N, 120.948 W) >> LOSHN(35.848 N, 120.000 W) >> BOILE(34.422 N, 118.027 W) >> BLH(33.597 N, 114.762 W)'); + expect(decodeResult.formatted.items[3].value).toBe( + 'KAYEX(36.487 N, 120.948 W) >> LOSHN(35.848 N, 120.000 W) >> BOILE(34.422 N, 118.027 W) >> BLH(33.597 N, 114.762 W)', + ); expect(decodeResult.formatted.items[4].label).toBe('Message Checksum'); expect(decodeResult.formatted.items[4].value).toBe('0xddfb'); }); test('decodes Label H1 Preamble FPN with WS', () => { // https://app.airframes.io/messages/2372685289 - message.text = 'FPN/TS140017,021724/RP:DA:EHAM:AA:KMSP..N55064W000477..N55163W001141..ERAKA..N60000W020000..N61000W030000:WS:N61000W030000,370..N61000W040000..N60000W050000..URTAK:WS:URTAK,380..LAKES:WS:LAKES,400..N57000W070000..N54300W080000..N49000W090000..DLH..COLDD:A:BAINY3:AP:ILS30L(30L)/PR4356,344,360,1060,,,13,,,30,,,P50,M40,36090,,3296,292/DTKMSP,30L,172,215117156D'; + message.text = + 'FPN/TS140017,021724/RP:DA:EHAM:AA:KMSP..N55064W000477..N55163W001141..ERAKA..N60000W020000..N61000W030000:WS:N61000W030000,370..N61000W040000..N60000W050000..URTAK:WS:URTAK,380..LAKES:WS:LAKES,400..N57000W070000..N54300W080000..N49000W090000..DLH..COLDD:A:BAINY3:AP:ILS30L(30L)/PR4356,344,360,1060,,,13,,,30,,,P50,M40,36090,,3296,292/DTKMSP,30L,172,215117156D'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -109,7 +126,9 @@ describe('Label_H1 FPN', () => { expect(decodeResult.formatted.items[1].label).toBe('Origin'); expect(decodeResult.formatted.items[1].value).toBe('EHAM'); expect(decodeResult.formatted.items[2].label).toBe('Destination'); - expect(decodeResult.formatted.items[2].value).toBe('KMSP..N55064W000477..N55163W001141..ERAKA..N60000W020000..N61000W030000'); // FIXME - just 'KMSP' + expect(decodeResult.formatted.items[2].value).toBe( + 'KMSP..N55064W000477..N55163W001141..ERAKA..N60000W020000..N61000W030000', + ); // FIXME - just 'KMSP' expect(decodeResult.formatted.items[3].label).toBe('Arrival Procedure'); expect(decodeResult.formatted.items[3].value).toBe('BAINY3'); expect(decodeResult.formatted.items[4].label).toBe('Approach Procedure'); @@ -118,16 +137,21 @@ describe('Label_H1 FPN', () => { expect(decodeResult.formatted.items[5].value).toBe('30L'); expect(decodeResult.formatted.items[6].label).toBe('Fuel On Board'); expect(decodeResult.formatted.items[6].value).toBe('172'); - expect(decodeResult.formatted.items[7].label).toBe('Estimated Time of Arrival'); + expect(decodeResult.formatted.items[7].label).toBe( + 'Estimated Time of Arrival', + ); expect(decodeResult.formatted.items[7].value).toBe('21:51:17'); expect(decodeResult.formatted.items[8].label).toBe('Message Checksum'); expect(decodeResult.formatted.items[8].value).toBe('0x156d'); - expect(decodeResult.remaining.text).toBe(':WS:N61000W030000,370..N61000W040000..N60000W050000..URTAK:WS:URTAK,380..LAKES:WS:LAKES,400..N57000W070000..N54300W080000..N49000W090000..DLH..COLDD/PR4356,344,360,1060,,,13,,,30,,,P50,M40,36090,,3296,292,KMSP,30L,172,215117'); + expect(decodeResult.remaining.text).toBe( + ':WS:N61000W030000,370..N61000W040000..N60000W050000..URTAK:WS:URTAK,380..LAKES:WS:LAKES,400..N57000W070000..N54300W080000..N49000W090000..DLH..COLDD/PR4356,344,360,1060,,,13,,,30,,,P50,M40,36090,,3296,292,KMSP,30L,172,215117', + ); }); test('decodes Label H1 Preamble FPN with newlines', () => { // https://app.airframes.io/messages/2381936957 - message.text = 'FPN/SN2125/FNQFA780/RI:DA:YPPH:CR:PERMEL001:AA:YMML..MEMUP,S33451E\r\n120525.Y53.WENDY0560' + message.text = + 'FPN/SN2125/FNQFA780/RI:DA:YPPH:CR:PERMEL001:AA:YMML..MEMUP,S33451E\r\n120525.Y53.WENDY0560'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -143,14 +167,17 @@ describe('Label_H1 FPN', () => { expect(decodeResult.formatted.items[2].label).toBe('Company Route'); expect(decodeResult.formatted.items[2].value).toBe('PERMEL001'); expect(decodeResult.formatted.items[3].label).toBe('Destination'); - expect(decodeResult.formatted.items[3].value).toBe('YMML..MEMUP,S33451E120525.Y53.WENDY'); //TODO - pull out route + expect(decodeResult.formatted.items[3].value).toBe( + 'YMML..MEMUP,S33451E120525.Y53.WENDY', + ); //TODO - pull out route expect(decodeResult.formatted.items[4].label).toBe('Message Checksum'); expect(decodeResult.formatted.items[4].value).toBe('0x0560'); }); test('decodes Label H1 Preamble FPN with SN and TS', () => { // https://app.airframes.io/messages/2372685289 - message.text = 'FPN/TS155631,170224/SN155631/RP:DA:PHNL:AA:KASE:D:MKK5.KOLEA:F:KOLEA,N22354W155133..CLUTS,N23002W154393.R465.CINNY,N36109W124456..OAL,N38002W117462.J58.ILC,N38150W114237..EYELO,N38455W110469..SAKES,N38500W110163.J80.DBL,N39264W106537F5E1' + message.text = + 'FPN/TS155631,170224/SN155631/RP:DA:PHNL:AA:KASE:D:MKK5.KOLEA:F:KOLEA,N22354W155133..CLUTS,N23002W154393.R465.CINNY,N36109W124456..OAL,N38002W117462.J58.ILC,N38150W114237..EYELO,N38455W110469..SAKES,N38500W110163.J80.DBL,N39264W106537F5E1'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -166,16 +193,21 @@ describe('Label_H1 FPN', () => { expect(decodeResult.formatted.items[2].label).toBe('Destination'); expect(decodeResult.formatted.items[2].value).toBe('KASE'); expect(decodeResult.formatted.items[3].label).toBe('Departure Procedure'); - expect(decodeResult.formatted.items[3].value).toBe('MKK5 starting at KOLEA'); + expect(decodeResult.formatted.items[3].value).toBe( + 'MKK5 starting at KOLEA', + ); expect(decodeResult.formatted.items[4].label).toBe('Aircraft Route'); - expect(decodeResult.formatted.items[4].value).toBe('KOLEA(22.590 N, 155.222 W) >> CLUTS(23.003 N, 154.655 W) > R465 > CINNY(36.182 N, 124.760 W) >> OAL(38.003 N, 117.770 W) > J58 > ILC(38.250 N, 114.395 W) >> EYELO(38.758 N, 110.782 W) >> SAKES(38.833 N, 110.272 W) > J80 > DBL(39.440 N, 106.895 W)'); + expect(decodeResult.formatted.items[4].value).toBe( + 'KOLEA(22.590 N, 155.222 W) >> CLUTS(23.003 N, 154.655 W) > R465 > CINNY(36.182 N, 124.760 W) >> OAL(38.003 N, 117.770 W) > J58 > ILC(38.250 N, 114.395 W) >> EYELO(38.758 N, 110.782 W) >> SAKES(38.833 N, 110.272 W) > J80 > DBL(39.440 N, 106.895 W)', + ); expect(decodeResult.formatted.items[5].label).toBe('Message Checksum'); expect(decodeResult.formatted.items[5].value).toBe('0xf5e1'); }); test('decodes Label H1 Preamble FPN with FN', () => { // https://app.airframes.io/messages/3281723743 - message.text = 'FPN/RP:DA:KSEA:AA:KPHX:D:SUMMA2.SUMMA:F:SUMMA,N46371W121593..LTJ,N45428W121061..IMB,N44389W119427.Q35..CORKR,N36050W112240..TENTS,N35295W112271:A:BRUSR1.TENTS/FNFFT17245D16'; + message.text = + 'FPN/RP:DA:KSEA:AA:KPHX:D:SUMMA2.SUMMA:F:SUMMA,N46371W121593..LTJ,N45428W121061..IMB,N44389W119427.Q35..CORKR,N36050W112240..TENTS,N35295W112271:A:BRUSR1.TENTS/FNFFT17245D16'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -190,18 +222,25 @@ describe('Label_H1 FPN', () => { expect(decodeResult.formatted.items[2].label).toBe('Destination'); expect(decodeResult.formatted.items[2].value).toBe('KPHX'); expect(decodeResult.formatted.items[3].label).toBe('Departure Procedure'); - expect(decodeResult.formatted.items[3].value).toBe('SUMMA2 starting at SUMMA'); + expect(decodeResult.formatted.items[3].value).toBe( + 'SUMMA2 starting at SUMMA', + ); expect(decodeResult.formatted.items[4].label).toBe('Aircraft Route'); - expect(decodeResult.formatted.items[4].value).toBe('SUMMA(46.618 N, 121.988 W) >> LTJ(45.713 N, 121.102 W) >> IMB(44.648 N, 119.712 W) > Q35 >> CORKR(36.083 N, 112.400 W) >> TENTS(35.492 N, 112.452 W)'); + expect(decodeResult.formatted.items[4].value).toBe( + 'SUMMA(46.618 N, 121.988 W) >> LTJ(45.713 N, 121.102 W) >> IMB(44.648 N, 119.712 W) > Q35 >> CORKR(36.083 N, 112.400 W) >> TENTS(35.492 N, 112.452 W)', + ); expect(decodeResult.formatted.items[5].label).toBe('Arrival Procedure'); - expect(decodeResult.formatted.items[5].value).toBe('BRUSR1 starting at TENTS'); + expect(decodeResult.formatted.items[5].value).toBe( + 'BRUSR1 starting at TENTS', + ); expect(decodeResult.formatted.items[6].label).toBe('Message Checksum'); expect(decodeResult.formatted.items[6].value).toBe('0x5d16'); }); test('decodes Label H1 Preamble #M1BFPN', () => { // https://app.airframes.io/messages/2400593101 - message.text = '#M1BFPN/RI:DA:KJFK:AA:KPHX:CR:JFKPHX20..KG701..KG702..KP702..KP703..KD601..KD602..PUB..ALS.J102.GUP:A:EAGUL6.GUP:F:HOMRR90E1'; + message.text = + '#M1BFPN/RI:DA:KJFK:AA:KPHX:CR:JFKPHX20..KG701..KG702..KP702..KP703..KD601..KD602..PUB..ALS.J102.GUP:A:EAGUL6.GUP:F:HOMRR90E1'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -215,9 +254,13 @@ describe('Label_H1 FPN', () => { expect(decodeResult.formatted.items[2].label).toBe('Destination'); expect(decodeResult.formatted.items[2].value).toBe('KPHX'); expect(decodeResult.formatted.items[3].label).toBe('Company Route'); - expect(decodeResult.formatted.items[3].value).toBe('JFKPHX20: >> KG701 >> KG702 >> KP702 >> KP703 >> KD601 >> KD602 >> PUB >> ALS > J102 > GUP'); + expect(decodeResult.formatted.items[3].value).toBe( + 'JFKPHX20: >> KG701 >> KG702 >> KP702 >> KP703 >> KD601 >> KD602 >> PUB >> ALS > J102 > GUP', + ); expect(decodeResult.formatted.items[4].label).toBe('Arrival Procedure'); - expect(decodeResult.formatted.items[4].value).toBe('EAGUL6 starting at GUP'); + expect(decodeResult.formatted.items[4].value).toBe( + 'EAGUL6 starting at GUP', + ); expect(decodeResult.formatted.items[5].label).toBe('Aircraft Route'); expect(decodeResult.formatted.items[5].value).toBe('HOMRR'); expect(decodeResult.formatted.items[6].label).toBe('Message Checksum'); @@ -228,7 +271,8 @@ describe('Label_H1 FPN', () => { // This might move to a different parser, but while we're here... test('decodes Label H1 #M1BFPN No Preamble', () => { // https://app.airframes.io/messages/2400409588 - message.text = 'F37AKL0767#M1BFPN/TS232008,022324/RP:DA:TNCA:AA:TNCB:R:11O:D:ADRI1F..IRLEP.A574..PJG:AP:RNV10(10O)/PR,,110,,183,7,13,,M7,25,,,P30,M40,36090,13,3455,300/DTTNCB,10O,119,23440847C0'; + message.text = + 'F37AKL0767#M1BFPN/TS232008,022324/RP:DA:TNCA:AA:TNCB:R:11O:D:ADRI1F..IRLEP.A574..PJG:AP:RNV10(10O)/PR,,110,,183,7,13,,M7,25,,,P30,M40,36090,13,3455,300/DTTNCB,10O,119,23440847C0'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -249,7 +293,9 @@ describe('Label_H1 FPN', () => { expect(decodeResult.raw.checksum).toBe(0x47c0); expect(decodeResult.formatted.description).toBe('Flight Plan'); expect(decodeResult.formatted.items.length).toBe(11); - expect(decodeResult.remaining.text).toBe('F37A#M1B/PR,,110,,183,7,13,,M7,25,,,P30,M40,36090,13,3455,300'); + expect(decodeResult.remaining.text).toBe( + 'F37A#M1B/PR,,110,,183,7,13,,M7,25,,,P30,M40,36090,13,3455,300', + ); }); test('decodes Label H1 Preamble FPN ', () => { @@ -262,8 +308,10 @@ describe('Label_H1 FPN', () => { expect(decodeResult.message).toBe(message); }); - test('decode RM', () => { // TODO: enable - message.text = 'FPN/ID88194A,RCH857,PMZM107QP021/MR2,/RM:AA:FJDG:F:DOH.N300..NOLSU.P307..SETSI.P307..PARAR..N20000E063000..RIGLO.L516..ELKEL.L516..BUMMR/WP,,,,E3E9'; + test('decode RM', () => { + // TODO: enable + message.text = + 'FPN/ID88194A,RCH857,PMZM107QP021/MR2,/RM:AA:FJDG:F:DOH.N300..NOLSU.P307..SETSI.P307..PARAR..N20000E063000..RIGLO.L516..ELKEL.L516..BUMMR/WP,,,,E3E9'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -282,7 +330,7 @@ describe('Label_H1 FPN', () => { expect(decodeResult.message).toBe(message); }); - test('Decodes Empty Message', () => { + test('Decodes Empty Message', () => { message.text = 'FPNEEE6'; const decodeResult = plugin.decode(message); @@ -291,4 +339,4 @@ describe('Label_H1 FPN', () => { expect(decodeResult.raw.checksum).toBe(0xeee6); expect(decodeResult.formatted.items.length).toBe(1); }); -}); \ No newline at end of file +}); diff --git a/lib/plugins/Label_H1_FTX.test.ts b/lib/plugins/Label_H1_FTX.test.ts index e24953e..be3dcf7 100644 --- a/lib/plugins/Label_H1_FTX.test.ts +++ b/lib/plugins/Label_H1_FTX.test.ts @@ -2,9 +2,8 @@ import { MessageDecoder } from '../MessageDecoder'; import { Label_H1 } from './Label_H1'; describe('Label_H1 FTX', () => { - let plugin: Label_H1; - const message = {label: 'H1', text: ''}; + const message = { label: 'H1', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); @@ -14,7 +13,8 @@ describe('Label_H1 FTX', () => { // disabled due to checksum failure. could be hidden characters in the source message test.skip('decodes Label H1 Preamble FTX valid', () => { // https://app.airframes.io/messages/3402014738 - message.text = 'FTX/ID23544S,HIFI21,7VZ007B1S276/MR2,/FXFYI .. TAF KSUX 021720Z 0218 0318 20017G28KT P6SM SKC FM022200 22012G18KT P6SM SKC .. PUTS YOUR CXWIND AT 26KT ON RWY 13 .. REDUCES TO 18KT AT 22Z4FEF'; + message.text = + 'FTX/ID23544S,HIFI21,7VZ007B1S276/MR2,/FXFYI .. TAF KSUX 021720Z 0218 0318 20017G28KT P6SM SKC FM022200 22012G18KT P6SM SKC .. PUTS YOUR CXWIND AT 26KT ON RWY 13 .. REDUCES TO 18KT AT 22Z4FEF'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -26,7 +26,9 @@ describe('Label_H1 FTX', () => { expect(decodeResult.formatted.items[1].label).toBe('Flight Number'); expect(decodeResult.formatted.items[1].value).toBe('HIFI21'); expect(decodeResult.formatted.items[2].label).toBe('Free Text'); - expect(decodeResult.formatted.items[2].value).toBe('FYI .. TAF KSUX 021720Z 0218 0318 20017G28KT P6SM SKC FM022200 22012G18KT P6SM SKC .. PUTS YOUR CXWIND AT 26KT ON RWY 13 .. REDUCES TO 18KT AT 22Z'); + expect(decodeResult.formatted.items[2].value).toBe( + 'FYI .. TAF KSUX 021720Z 0218 0318 20017G28KT P6SM SKC FM022200 22012G18KT P6SM SKC .. PUTS YOUR CXWIND AT 26KT ON RWY 13 .. REDUCES TO 18KT AT 22Z', + ); expect(decodeResult.formatted.items[3].label).toBe('Message Checksum'); expect(decodeResult.formatted.items[3].value).toBe('0x4fef'); expect(decodeResult.remaining.text).toBe('MR2,'); @@ -35,7 +37,8 @@ describe('Label_H1 FTX', () => { // disabled due to checksum failure. could be hidden characters in the source message test.skip('decodes Label H1 Preamble - #MDFTX valid', () => { // https://app.airframes.io/messages/3400555283 - message.text = '- #MDFTX/ID77170A,RCH836,ABZ01G6XH273/MR2,/FXIRAN IS LAUNCHING MISSILES TOWARDS ISRAEL. YOUR FLIGHT PATH IS CURRENTLY NORTH OF PROJECTED MISSILE TRACKS. EXERCIZE EXTREME CAUTION.4A99' + message.text = + '- #MDFTX/ID77170A,RCH836,ABZ01G6XH273/MR2,/FXIRAN IS LAUNCHING MISSILES TOWARDS ISRAEL. YOUR FLIGHT PATH IS CURRENTLY NORTH OF PROJECTED MISSILE TRACKS. EXERCIZE EXTREME CAUTION.4A99'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -47,7 +50,9 @@ describe('Label_H1 FTX', () => { expect(decodeResult.formatted.items[1].label).toBe('Flight Number'); expect(decodeResult.formatted.items[1].value).toBe('RCH836'); expect(decodeResult.formatted.items[2].label).toBe('Free Text'); - expect(decodeResult.formatted.items[2].value).toBe('IRAN IS LAUNCHING MISSILES TOWARDS ISRAEL. YOUR FLIGHT PATH IS CURRENTLY NORTH OF PROJECTED MISSILE TRACKS. EXERCIZE EXTREME CAUTION.'); + expect(decodeResult.formatted.items[2].value).toBe( + 'IRAN IS LAUNCHING MISSILES TOWARDS ISRAEL. YOUR FLIGHT PATH IS CURRENTLY NORTH OF PROJECTED MISSILE TRACKS. EXERCIZE EXTREME CAUTION.', + ); expect(decodeResult.formatted.items[3].label).toBe('Message Checksum'); expect(decodeResult.formatted.items[3].value).toBe('0x4a99'); expect(decodeResult.remaining.text).toBe('- #MD/MR2,'); @@ -61,7 +66,6 @@ describe('Label_H1 FTX', () => { }); test('does not decode invalid message', () => { - message.text = 'FTX Bogus message'; const decodeResult = plugin.decode(message); diff --git a/lib/plugins/Label_H1_INI.test.ts b/lib/plugins/Label_H1_INI.test.ts index de9f2ce..d786ec4 100644 --- a/lib/plugins/Label_H1_INI.test.ts +++ b/lib/plugins/Label_H1_INI.test.ts @@ -2,9 +2,8 @@ import { MessageDecoder } from '../MessageDecoder'; import { Label_H1 } from './Label_H1'; describe('Label_H1 INI', () => { - let plugin: Label_H1; - const message = {label: 'H1', text: ''}; + const message = { label: 'H1', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); @@ -13,7 +12,8 @@ describe('Label_H1 INI', () => { test('decodes valid', () => { // https://app.airframes.io/messages/3401344857 - message.text = 'INI/ID70045B,RCH2050,AJM363201271/MR2,000/AFKDOV,KBHM/TD271115,131545EE' + message.text = + 'INI/ID70045B,RCH2050,AJM363201271/MR2,000/AFKDOV,KBHM/TD271115,131545EE'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -28,9 +28,13 @@ describe('Label_H1 INI', () => { expect(decodeResult.formatted.items[2].value).toBe('KDOV'); expect(decodeResult.formatted.items[3].label).toBe('Destination'); expect(decodeResult.formatted.items[3].value).toBe('KBHM'); - expect(decodeResult.formatted.items[4].label).toBe('Planned Departure Time'); + expect(decodeResult.formatted.items[4].label).toBe( + 'Planned Departure Time', + ); expect(decodeResult.formatted.items[4].value).toBe('YYYY-MM-27T11:15:00Z'); - expect(decodeResult.formatted.items[5].label).toBe('Estimated Departure Time'); + expect(decodeResult.formatted.items[5].label).toBe( + 'Estimated Departure Time', + ); expect(decodeResult.formatted.items[5].value).toBe('13:15'); expect(decodeResult.formatted.items[6].label).toBe('Message Checksum'); expect(decodeResult.formatted.items[6].value).toBe('0x45ee'); @@ -39,7 +43,8 @@ describe('Label_H1 INI', () => { test('#MD valid', () => { // https://app.airframes.io/messages/3400583424 - message.text = '- #MDINI/ID99206A,RCH206,AAM7029H1275/MR0,0/AFKSUU,KBUR/TD011535,1535EE66' + message.text = + '- #MDINI/ID99206A,RCH206,AAM7029H1275/MR0,0/AFKSUU,KBUR/TD011535,1535EE66'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -54,9 +59,13 @@ describe('Label_H1 INI', () => { expect(decodeResult.formatted.items[2].value).toBe('KSUU'); expect(decodeResult.formatted.items[3].label).toBe('Destination'); expect(decodeResult.formatted.items[3].value).toBe('KBUR'); - expect(decodeResult.formatted.items[4].label).toBe('Planned Departure Time'); + expect(decodeResult.formatted.items[4].label).toBe( + 'Planned Departure Time', + ); expect(decodeResult.formatted.items[4].value).toBe('YYYY-MM-01T15:35:00Z'); - expect(decodeResult.formatted.items[5].label).toBe('Estimated Departure Time'); + expect(decodeResult.formatted.items[5].label).toBe( + 'Estimated Departure Time', + ); expect(decodeResult.formatted.items[5].value).toBe('15:35'); expect(decodeResult.formatted.items[6].label).toBe('Message Checksum'); expect(decodeResult.formatted.items[6].value).toBe('0xee66'); @@ -64,7 +73,6 @@ describe('Label_H1 INI', () => { }); test('INI ', () => { - message.text = 'INI Bogus message'; const decodeResult = plugin.decode(message); diff --git a/lib/plugins/Label_H1_OHMA.test.ts b/lib/plugins/Label_H1_OHMA.test.ts index 26d161a..ded06bb 100644 --- a/lib/plugins/Label_H1_OHMA.test.ts +++ b/lib/plugins/Label_H1_OHMA.test.ts @@ -3,106 +3,122 @@ import { Label_H1_OHMA } from './Label_H1_OHMA'; describe('Label H1 Preamble OHMA', () => { let plugin: Label_H1_OHMA; - const message = {label: 'H1', text: ''}; + const message = { label: 'H1', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); plugin = new Label_H1_OHMA(decoder); }); -test('matches Label H1 Preamble OHMA qualifiers', () => { - const decoder = new MessageDecoder(); - const decoderPlugin = new Label_H1_OHMA(decoder); + test('matches Label H1 Preamble OHMA qualifiers', () => { + const decoder = new MessageDecoder(); + const decoderPlugin = new Label_H1_OHMA(decoder); - expect(decoderPlugin.decode).toBeDefined(); - expect(decoderPlugin.name).toBe('label-h1-ohma'); - expect(decoderPlugin.qualifiers).toBeDefined(); - expect(decoderPlugin.qualifiers()).toEqual({ - labels: ['H1'], - preambles: ['OHMA', '/RTNBOCR.OHMA', '#T1B/RTNBOCR.OHMA'], + expect(decoderPlugin.decode).toBeDefined(); + expect(decoderPlugin.name).toBe('label-h1-ohma'); + expect(decoderPlugin.qualifiers).toBeDefined(); + expect(decoderPlugin.qualifiers()).toEqual({ + labels: ['H1'], + preambles: ['OHMA', '/RTNBOCR.OHMA', '#T1B/RTNBOCR.OHMA'], + }); }); -}); -test('decodes Label H1 Preamble OHMA valid', () => { - // https://app.airframes.io/messages/3126721669 - message.text = 'OHMAeJy1Ul2PojAU/SubPiuhlA/ljQHMkAElgDuTXTakqx2nCaAp1WQz8b/vFXTGEcw+LX26veece88p7+jARMO3NbKRpqhohCrWNHTDoH7P0arkrJbBOkd2jhaPkZOjUX6BeFSytqGpmj5WJ2NVyzC2ydTWiWJN9B8teE0lBRSIUS52Ja1ZA+VPqCXl5Xxf/WaiVZkTyzD0bsB2zcr2si1fS755kxfamu2okHvBHNDbCukCuMU+PSzSFk+F4Ada9vqLxLvSuxq9dELDtK56IdukEqZkvOpbVG0VXIJFYnYW2QFCuqzXFm5Jm+YcG2xLKyYFX32CB3V70XXQPzv2EX8R+Vn3BrxuJK1XH2HW9KxYyRgWv5gDJCSx72CnOH8dR1/RLhXl9+4f6MF1xRhgJPuSNUzeI1nE+hY5L7ZtO4/RmFjRQwj+4MNqCJf4pNhp/reosggXbhjEke8Fzrxwk2WQ+kUSZ8U/wgNi5kcxhpy9G2PENBVtqt+kcSZoQ4QJViyTDBO8YDYb4GBDwQTfUOIIx4mfpsvEH1xMhzHmfc7gbkTR1R7HcZ9m4eL53m5YmeAhTjA/jbrDMhULT08P3p0jOv4FG0dfvQ=='; - const decodeResult = plugin.decode(message); + test('decodes Label H1 Preamble OHMA valid', () => { + // https://app.airframes.io/messages/3126721669 + message.text = + 'OHMAeJy1Ul2PojAU/SubPiuhlA/ljQHMkAElgDuTXTakqx2nCaAp1WQz8b/vFXTGEcw+LX26veece88p7+jARMO3NbKRpqhohCrWNHTDoH7P0arkrJbBOkd2jhaPkZOjUX6BeFSytqGpmj5WJ2NVyzC2ydTWiWJN9B8teE0lBRSIUS52Ja1ZA+VPqCXl5Xxf/WaiVZkTyzD0bsB2zcr2si1fS755kxfamu2okHvBHNDbCukCuMU+PSzSFk+F4Ada9vqLxLvSuxq9dELDtK56IdukEqZkvOpbVG0VXIJFYnYW2QFCuqzXFm5Jm+YcG2xLKyYFX32CB3V70XXQPzv2EX8R+Vn3BrxuJK1XH2HW9KxYyRgWv5gDJCSx72CnOH8dR1/RLhXl9+4f6MF1xRhgJPuSNUzeI1nE+hY5L7ZtO4/RmFjRQwj+4MNqCJf4pNhp/reosggXbhjEke8Fzrxwk2WQ+kUSZ8U/wgNi5kcxhpy9G2PENBVtqt+kcSZoQ4QJViyTDBO8YDYb4GBDwQTfUOIIx4mfpsvEH1xMhzHmfc7gbkTR1R7HcZ9m4eL53m5YmeAhTjA/jbrDMhULT08P3p0jOv4FG0dfvQ=='; + const decodeResult = plugin.decode(message); - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('full'); - expect(decodeResult.decoder.name).toBe('label-h1-ohma'); - expect(decodeResult.formatted.description).toBe('OHMA Message'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.raw.ohma).toBe('{\"version\":\"2.0\",\"message\":\"{\\\"clientId\\\":\\\"OHMA\\\",\\\"messageDate\\\":\\\"2024-08-02T11:39:43.784Z\\\",\\\"data\\\":{\\\"airplanes\\\":[{\\\"tailNumber\\\":\\\"N37554\\\",\\\"model\\\":\\\"\\\",\\\"flights\\\":[{\\\"departureAirportCode\\\":\\\"KBOS\\\",\\\"arrivalAirportCode\\\":\\\"KORD\\\",\\\"flightNumber\\\":\\\"UAL567\\\",\\\"flightLegStartTime\\\":\\\"2024-08-02T10:01:33.736Z\\\",\\\"events\\\":[{\\\"eventClassId\\\":\\\"parametric\\\",\\\"eventTime\\\":\\\"2024-08-02T11:39:43.784Z\\\",\\\"eventType\\\":\\\"OHMA_META\\\",\\\"instances\\\":[{\\\"name\\\":\\\"mtPartNumber\\\",\\\"values\\\":[\\\"\\\"]},{\\\"name\\\":\\\"mtCarlVersion\\\",\\\"values\\\":[\\\"4.5\\\"]},{\\\"name\\\":\\\"mtRulesetVersion\\\",\\\"values\\\":[\\\"737 MAX:::AHM-37MBL8-000010L:::1\\\"]}]},{\\\"eventClassId\\\":\\\"parametric\\\",\\\"eventTime\\\":\\\"2024-08-02T11:39:43.784Z\\\",\\\"eventType\\\":\\\"TM1_CLIPMEDIAN_CRUISE_RPT_A\\\",\\\"instances\\\":[{\\\"name\\\":\\\"TM1TEMP1_MED\\\",\\\"values\\\":[366.294]},{\\\"name\\\":\\\"TM1TEMP2_MED\\\",\\\"values\\\":[381.763]},{\\\"name\\\":\\\"TM1TEMPDIFF_MED\\\",\\\"values\\\":[15.131]},{\\\"name\\\":\\\"PM1PRESSURE1_MED\\\",\\\"values\\\":[34.766]},{\\\"name\\\":\\\"PM1PRESSURE2_MED\\\",\\\"values\\\":[33.406]},{\\\"name\\\":\\\"PACKFLOWDIFF_MED\\\",\\\"values\\\":[11.816]},{\\\"name\\\":\\\"PACKINPRESDIFF_MED\\\",\\\"values\\\":[6.719]}]}]}]}]}}\"}'); - expect(decodeResult.formatted.items.length).toBe(1); - expect(decodeResult.formatted.items[0].type).toBe('ohma'); - expect(decodeResult.formatted.items[0].code).toBe('OHMA'); - expect(decodeResult.formatted.items[0].label).toBe('OHMA Downlink'); - expect(decodeResult.formatted.items[0].value.length).toBe(2658); // instead of comparing to a giant json obj -}); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('full'); + expect(decodeResult.decoder.name).toBe('label-h1-ohma'); + expect(decodeResult.formatted.description).toBe('OHMA Message'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.raw.ohma).toBe( + '{"version":"2.0","message":"{\\"clientId\\":\\"OHMA\\",\\"messageDate\\":\\"2024-08-02T11:39:43.784Z\\",\\"data\\":{\\"airplanes\\":[{\\"tailNumber\\":\\"N37554\\",\\"model\\":\\"\\",\\"flights\\":[{\\"departureAirportCode\\":\\"KBOS\\",\\"arrivalAirportCode\\":\\"KORD\\",\\"flightNumber\\":\\"UAL567\\",\\"flightLegStartTime\\":\\"2024-08-02T10:01:33.736Z\\",\\"events\\":[{\\"eventClassId\\":\\"parametric\\",\\"eventTime\\":\\"2024-08-02T11:39:43.784Z\\",\\"eventType\\":\\"OHMA_META\\",\\"instances\\":[{\\"name\\":\\"mtPartNumber\\",\\"values\\":[\\"\\"]},{\\"name\\":\\"mtCarlVersion\\",\\"values\\":[\\"4.5\\"]},{\\"name\\":\\"mtRulesetVersion\\",\\"values\\":[\\"737 MAX:::AHM-37MBL8-000010L:::1\\"]}]},{\\"eventClassId\\":\\"parametric\\",\\"eventTime\\":\\"2024-08-02T11:39:43.784Z\\",\\"eventType\\":\\"TM1_CLIPMEDIAN_CRUISE_RPT_A\\",\\"instances\\":[{\\"name\\":\\"TM1TEMP1_MED\\",\\"values\\":[366.294]},{\\"name\\":\\"TM1TEMP2_MED\\",\\"values\\":[381.763]},{\\"name\\":\\"TM1TEMPDIFF_MED\\",\\"values\\":[15.131]},{\\"name\\":\\"PM1PRESSURE1_MED\\",\\"values\\":[34.766]},{\\"name\\":\\"PM1PRESSURE2_MED\\",\\"values\\":[33.406]},{\\"name\\":\\"PACKFLOWDIFF_MED\\",\\"values\\":[11.816]},{\\"name\\":\\"PACKINPRESDIFF_MED\\",\\"values\\":[6.719]}]}]}]}]}}"}', + ); + expect(decodeResult.formatted.items.length).toBe(1); + expect(decodeResult.formatted.items[0].type).toBe('ohma'); + expect(decodeResult.formatted.items[0].code).toBe('OHMA'); + expect(decodeResult.formatted.items[0].label).toBe('OHMA Downlink'); + expect(decodeResult.formatted.items[0].value.length).toBe(2658); // instead of comparing to a giant json obj + }); -test('decodes Label H1 Preamble OHMA RTN', () => { - // https://app.airframes.io/messages/3125950763 - message.text = '/RTNBOCR.OHMAeJyrVipLLSrOzM9TslIy0jNQ0lHKTS0uTkxPBfJL81JS0zLzUlOUagH7TQzW' - const decodeResult = plugin.decode(message, { debug: true }); + test('decodes Label H1 Preamble OHMA RTN', () => { + // https://app.airframes.io/messages/3125950763 + message.text = + '/RTNBOCR.OHMAeJyrVipLLSrOzM9TslIy0jNQ0lHKTS0uTkxPBfJL81JS0zLzUlOUagH7TQzW'; + const decodeResult = plugin.decode(message, { debug: true }); - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('full'); - expect(decodeResult.decoder.name).toBe('label-h1-ohma'); - expect(decodeResult.formatted.description).toBe('OHMA Message'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.raw.ohma).toBe('{\"version\":\"2.0\",\"message\":\"undefined\"}'); - expect(decodeResult.formatted.items.length).toBe(1); - expect(decodeResult.formatted.items[0].type).toBe('ohma'); - expect(decodeResult.formatted.items[0].code).toBe('OHMA'); - expect(decodeResult.formatted.items[0].label).toBe('OHMA Downlink'); - expect(decodeResult.formatted.items[0].value).toBe('undefined'); -}); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('full'); + expect(decodeResult.decoder.name).toBe('label-h1-ohma'); + expect(decodeResult.formatted.description).toBe('OHMA Message'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.raw.ohma).toBe( + '{"version":"2.0","message":"undefined"}', + ); + expect(decodeResult.formatted.items.length).toBe(1); + expect(decodeResult.formatted.items[0].type).toBe('ohma'); + expect(decodeResult.formatted.items[0].code).toBe('OHMA'); + expect(decodeResult.formatted.items[0].label).toBe('OHMA Downlink'); + expect(decodeResult.formatted.items[0].value).toBe('undefined'); + }); -test.skip('decodes Label H1 Preamble OHMA partial', () => { - // https://app.airframes.io/messages/3126673935 - message.text = '#T1B/RTNBOCR.OHMAeJy1km+vmjAUxr/K0tfSlPLXvuMi5pJRL0Hc3MZCOu0cSUFTqsly43dfBb3zKmavBq9O+3uec84Dr+DAZVttG0AAhgiMQM3blm24rl8LsBIVb1S8LgApwMszDQowKi7IhCneXWCEbQP5BsI58gmyiWVD3/S+dvCaKaYpbcYquROs4a0uv+lasUrM9vUPLjuXZ' - const decodeResult = plugin.decode(message, { debug: true }); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('full'); - expect(decodeResult.decoder.name).toBe('label-h1-ohma'); - expect(decodeResult.formatted.description).toBe('OHMA Message'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.raw.ohma).toBe('{"version":"2.0","message":"{\"clientId\":\"OHMA\",\"messageDate\":\"2024-08-02T08:04:34.817Z\",\"data\":{\"airplanes\":[{\"tailNumber\":\"'); - expect(decodeResult.formatted.items.length).toBe(1); - expect(decodeResult.formatted.items[0].type).toBe('ohma'); - expect(decodeResult.formatted.items[0].code).toBe('OHMA'); - expect(decodeResult.formatted.items[0].label).toBe('OHMA Downlink'); - expect(decodeResult.formatted.items[0].value).toBe('DO WE EVEN WANT IT FORMATTED?'); -}); + test.skip('decodes Label H1 Preamble OHMA partial', () => { + // https://app.airframes.io/messages/3126673935 + message.text = + '#T1B/RTNBOCR.OHMAeJy1km+vmjAUxr/K0tfSlPLXvuMi5pJRL0Hc3MZCOu0cSUFTqsly43dfBb3zKmavBq9O+3uec84Dr+DAZVttG0AAhgiMQM3blm24rl8LsBIVb1S8LgApwMszDQowKi7IhCneXWCEbQP5BsI58gmyiWVD3/S+dvCaKaYpbcYquROs4a0uv+lasUrM9vUPLjuXZ'; + const decodeResult = plugin.decode(message, { debug: true }); -test('decodes Label H1 Preamble OHMA partial', () => { - // https://app.airframes.io/messages/3284234176 - message.text = 'OHMAeJy1lF+PmkAUxb9Kw/MyGRj+v1HEYhaQ4OxqrI2Z6tSSIBpAk2bjd+8Aau3szCZ9qD4NnN899x7v+Kacad0Uh0rxFB1A5UnZ06YhO8rObytlUxa0aifbleKtlGmU+CvlaXWTjEhL+xc61A0Vuiq0MHQ90/UMBJBjL3vxlrSEqVgxUtTHklS0Ycev7NySokxP+++07qvMMj' - const decodeResult = plugin.decode(message, { debug: true }); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('full'); + expect(decodeResult.decoder.name).toBe('label-h1-ohma'); + expect(decodeResult.formatted.description).toBe('OHMA Message'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.raw.ohma).toBe( + '{"version":"2.0","message":"{"clientId":"OHMA","messageDate":"2024-08-02T08:04:34.817Z","data":{"airplanes":[{"tailNumber":"', + ); + expect(decodeResult.formatted.items.length).toBe(1); + expect(decodeResult.formatted.items[0].type).toBe('ohma'); + expect(decodeResult.formatted.items[0].code).toBe('OHMA'); + expect(decodeResult.formatted.items[0].label).toBe('OHMA Downlink'); + expect(decodeResult.formatted.items[0].value).toBe( + 'DO WE EVEN WANT IT FORMATTED?', + ); + }); - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('full'); - expect(decodeResult.decoder.name).toBe('label-h1-ohma'); - expect(decodeResult.formatted.description).toBe('OHMA Message'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.raw.ohma).toBe('{\"version\":\"2.0\",\"message\":\"{\\\"clientId\\\":\\\"OHMA\\\",\\\"messageDate\\\":\\\"2024-09-06T09:59:43.387Z\\\",\\\"data\\\":{\\\"airplanes\\\":[{\\\"tailNumber\\\":\\\"SP'); - expect(decodeResult.formatted.items.length).toBe(1); - expect(decodeResult.formatted.items[0].type).toBe('ohma'); - expect(decodeResult.formatted.items[0].code).toBe('OHMA'); - expect(decodeResult.formatted.items[0].label).toBe('OHMA Downlink'); - expect(decodeResult.formatted.items[0].value).toBe('{\"version\":\"2.0\",\"message\":\"{\\\"clientId\\\":\\\"OHMA\\\",\\\"messageDate\\\":\\\"2024-09-06T09:59:43.387Z\\\",\\\"data\\\":{\\\"airplanes\\\":[{\\\"tailNumber\\\":\\\"SP'); -}); + test('decodes Label H1 Preamble OHMA partial', () => { + // https://app.airframes.io/messages/3284234176 + message.text = + 'OHMAeJy1lF+PmkAUxb9Kw/MyGRj+v1HEYhaQ4OxqrI2Z6tSSIBpAk2bjd+8Aau3szCZ9qD4NnN899x7v+Kacad0Uh0rxFB1A5UnZ06YhO8rObytlUxa0aifbleKtlGmU+CvlaXWTjEhL+xc61A0Vuiq0MHQ90/UMBJBjL3vxlrSEqVgxUtTHklS0Ycev7NySokxP+++07qvMMj'; + const decodeResult = plugin.decode(message, { debug: true }); -test('decodes Label H1 Preamble OHMA invalid', () => { - message.text = 'OHMA '; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(false); - expect(decodeResult.decoder.decodeLevel).toBe('none'); - expect(decodeResult.decoder.name).toBe('label-h1-ohma'); - expect(decodeResult.formatted.description).toBe('OHMA Message'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.remaining.text).toBe(message.text); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('full'); + expect(decodeResult.decoder.name).toBe('label-h1-ohma'); + expect(decodeResult.formatted.description).toBe('OHMA Message'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.raw.ohma).toBe( + '{"version":"2.0","message":"{\\"clientId\\":\\"OHMA\\",\\"messageDate\\":\\"2024-09-06T09:59:43.387Z\\",\\"data\\":{\\"airplanes\\":[{\\"tailNumber\\":\\"SP', + ); + expect(decodeResult.formatted.items.length).toBe(1); + expect(decodeResult.formatted.items[0].type).toBe('ohma'); + expect(decodeResult.formatted.items[0].code).toBe('OHMA'); + expect(decodeResult.formatted.items[0].label).toBe('OHMA Downlink'); + expect(decodeResult.formatted.items[0].value).toBe( + '{"version":"2.0","message":"{\\"clientId\\":\\"OHMA\\",\\"messageDate\\":\\"2024-09-06T09:59:43.387Z\\",\\"data\\":{\\"airplanes\\":[{\\"tailNumber\\":\\"SP', + ); + }); + + test('decodes Label H1 Preamble OHMA invalid', () => { + message.text = 'OHMA '; + const decodeResult = plugin.decode(message); + + expect(decodeResult.decoded).toBe(false); + expect(decodeResult.decoder.decodeLevel).toBe('none'); + expect(decodeResult.decoder.name).toBe('label-h1-ohma'); + expect(decodeResult.formatted.description).toBe('OHMA Message'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.remaining.text).toBe(message.text); + }); }); -}); \ No newline at end of file diff --git a/lib/plugins/Label_H1_OHMA.ts b/lib/plugins/Label_H1_OHMA.ts index 974c2b0..1c6f8a2 100644 --- a/lib/plugins/Label_H1_OHMA.ts +++ b/lib/plugins/Label_H1_OHMA.ts @@ -2,21 +2,20 @@ import { DecoderPlugin } from '../DecoderPlugin'; import { DecodeResult, Message, Options } from '../DecoderPluginInterface'; import { ResultFormatter } from '../utils/result_formatter'; -import * as zlib from "minizlib"; +import * as zlib from 'minizlib'; import { Buffer } from 'node:buffer'; - export class Label_H1_OHMA extends DecoderPlugin { name = 'label-h1-ohma'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { - labels: ["H1"], + labels: ['H1'], preambles: ['OHMA', '/RTNBOCR.OHMA', '#T1B/RTNBOCR.OHMA'], }; } - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { let decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; decodeResult.formatted.description = 'OHMA Message'; @@ -30,7 +29,7 @@ export class Label_H1_OHMA extends DecoderPlugin { decompress.flush(zlib.constants.Z_SYNC_FLUSH); const result = decompress.read(); const jsonText = result?.toString() || ''; - + let formattedMsg; let jsonMessage; try { @@ -50,7 +49,7 @@ export class Label_H1_OHMA extends DecoderPlugin { decodeResult.raw.ohma = jsonText; decodeResult.formatted.items.push({ type: 'ohma', - code: 'OHMA' , + code: 'OHMA', label: 'OHMA Downlink', value: formattedMsg, }); @@ -64,7 +63,6 @@ export class Label_H1_OHMA extends DecoderPlugin { decodeResult.decoder.decodeLevel = 'none'; } - return decodeResult; + return decodeResult; } } - diff --git a/lib/plugins/Label_H1_PER.test.ts b/lib/plugins/Label_H1_PER.test.ts index f1c062e..2c2b2fd 100644 --- a/lib/plugins/Label_H1_PER.test.ts +++ b/lib/plugins/Label_H1_PER.test.ts @@ -2,40 +2,42 @@ import { MessageDecoder } from '../MessageDecoder'; import { Label_H1 } from './Label_H1'; describe('Label_H1 PER', () => { - let plugin: Label_H1; - const message = {label: 'H1', text: ''}; + const message = { label: 'H1', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); plugin = new Label_H1(decoder); }); - test('decodes short variant', () => { - message.text = 'PER/PR1337,262,320,222,,60,24,275103,M53,180,P52,P02917' + message.text = 'PER/PR1337,262,320,222,,60,24,275103,M53,180,P52,P02917'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); expect(decodeResult.decoder.decodeLevel).toBe('partial'); expect(decodeResult.raw.checksum).toBe(0x2917); expect(decodeResult.formatted.items.length).toBe(1); - expect(decodeResult.remaining.text).toBe('PR1337,262,320,222,,60,24,275103,M53,180,P52,P0'); + expect(decodeResult.remaining.text).toBe( + 'PR1337,262,320,222,,60,24,275103,M53,180,P52,P0', + ); }); test('long variant', () => { - message.text = 'PER/PR1218,276,340,134,,0,68,,M56,180,,,P30,P0,33936,,1084,284388D' + message.text = + 'PER/PR1218,276,340,134,,0,68,,M56,180,,,P30,P0,33936,,1084,284388D'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); expect(decodeResult.decoder.decodeLevel).toBe('partial'); expect(decodeResult.raw.checksum).toBe(0x388d); expect(decodeResult.formatted.items.length).toBe(1); - expect(decodeResult.remaining.text).toBe('PR1218,276,340,134,,0,68,,M56,180,,,P30,P0,33936,,1084,284'); + expect(decodeResult.remaining.text).toBe( + 'PR1218,276,340,134,,0,68,,M56,180,,,P30,P0,33936,,1084,284', + ); }); test('does not decode invalid message', () => { - message.text = 'PER Bogus message'; const decodeResult = plugin.decode(message); diff --git a/lib/plugins/Label_H1_POS.test.ts b/lib/plugins/Label_H1_POS.test.ts index 0f1a291..56eb685 100644 --- a/lib/plugins/Label_H1_POS.test.ts +++ b/lib/plugins/Label_H1_POS.test.ts @@ -2,9 +2,8 @@ import { MessageDecoder } from '../MessageDecoder'; import { Label_H1 } from './Label_H1'; describe('Label_H1 POS', () => { - let plugin: Label_H1; - const message = {label: 'H1', text: ''}; + const message = { label: 'H1', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); @@ -12,8 +11,8 @@ describe('Label_H1 POS', () => { }); test('variant 1', () => { - - message.text = 'POSN43312W123174,EASON,215754,370,EBINY,220601,ELENN,M48,02216,185/TS215754,0921227A40'; + message.text = + 'POSN43312W123174,EASON,215754,370,EBINY,220601,ELENN,M48,02216,185/TS215754,0921227A40'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -36,18 +35,24 @@ describe('Label_H1 POS', () => { expect(decodeResult.formatted.items[2].type).toBe('aircraft_route'); expect(decodeResult.formatted.items[2].code).toBe('ROUTE'); expect(decodeResult.formatted.items[2].label).toBe('Aircraft Route'); - expect(decodeResult.formatted.items[2].value).toBe('EASON@21:57:54 > EBINY@22:06:01 > ELENN'); - expect(decodeResult.formatted.items[3].type).toBe('outside_air_temperature'); + expect(decodeResult.formatted.items[2].value).toBe( + 'EASON@21:57:54 > EBINY@22:06:01 > ELENN', + ); + expect(decodeResult.formatted.items[3].type).toBe( + 'outside_air_temperature', + ); expect(decodeResult.formatted.items[3].code).toBe('OATEMP'); - expect(decodeResult.formatted.items[3].label).toBe('Outside Air Temperature (C)'); + expect(decodeResult.formatted.items[3].label).toBe( + 'Outside Air Temperature (C)', + ); expect(decodeResult.formatted.items[3].value).toBe('-48 degrees'); expect(decodeResult.formatted.items[4].label).toBe('Message Checksum'); expect(decodeResult.formatted.items[4].value).toBe('0x7a40'); }); test('variant 2', () => { - - message.text = 'POSN45209W122550,PEGTY,220309,134,MINNE,220424,HISKU,M6,060013,269,366,355K,292K,730A5B'; + message.text = + 'POSN45209W122550,PEGTY,220309,134,MINNE,220424,HISKU,M6,060013,269,366,355K,292K,730A5B'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -70,18 +75,24 @@ describe('Label_H1 POS', () => { expect(decodeResult.formatted.items[2].type).toBe('aircraft_route'); expect(decodeResult.formatted.items[2].code).toBe('ROUTE'); expect(decodeResult.formatted.items[2].label).toBe('Aircraft Route'); - expect(decodeResult.formatted.items[2].value).toBe('PEGTY@22:03:09 > MINNE@22:04:24 > HISKU'); - expect(decodeResult.formatted.items[3].type).toBe('outside_air_temperature'); + expect(decodeResult.formatted.items[2].value).toBe( + 'PEGTY@22:03:09 > MINNE@22:04:24 > HISKU', + ); + expect(decodeResult.formatted.items[3].type).toBe( + 'outside_air_temperature', + ); expect(decodeResult.formatted.items[3].code).toBe('OATEMP'); - expect(decodeResult.formatted.items[3].label).toBe('Outside Air Temperature (C)'); + expect(decodeResult.formatted.items[3].label).toBe( + 'Outside Air Temperature (C)', + ); expect(decodeResult.formatted.items[3].value).toBe('-6 degrees'); expect(decodeResult.formatted.items[4].label).toBe('Message Checksum'); expect(decodeResult.formatted.items[4].value).toBe('0x0a5b'); }); test('variant 3', () => { - - message.text = 'POSN43030W122406,IBALL,220516,380,AARON,220816,MOXEE,M47,0047,86/TS220516,092122BF64'; + message.text = + 'POSN43030W122406,IBALL,220516,380,AARON,220816,MOXEE,M47,0047,86/TS220516,092122BF64'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -104,18 +115,24 @@ describe('Label_H1 POS', () => { expect(decodeResult.formatted.items[2].type).toBe('aircraft_route'); expect(decodeResult.formatted.items[2].code).toBe('ROUTE'); expect(decodeResult.formatted.items[2].label).toBe('Aircraft Route'); - expect(decodeResult.formatted.items[2].value).toBe('IBALL@22:05:16 > AARON@22:08:16 > MOXEE'); - expect(decodeResult.formatted.items[3].type).toBe('outside_air_temperature'); + expect(decodeResult.formatted.items[2].value).toBe( + 'IBALL@22:05:16 > AARON@22:08:16 > MOXEE', + ); + expect(decodeResult.formatted.items[3].type).toBe( + 'outside_air_temperature', + ); expect(decodeResult.formatted.items[3].code).toBe('OATEMP'); - expect(decodeResult.formatted.items[3].label).toBe('Outside Air Temperature (C)'); + expect(decodeResult.formatted.items[3].label).toBe( + 'Outside Air Temperature (C)', + ); expect(decodeResult.formatted.items[3].value).toBe('-47 degrees'); expect(decodeResult.formatted.items[4].label).toBe('Message Checksum'); expect(decodeResult.formatted.items[4].value).toBe('0xbf64'); }); test('variant 4', () => { - - message.text = 'POSN33225W079428,SCOOB,232933,340,ENEME,235712,FETAL,M42,003051,15857F6'; + message.text = + 'POSN33225W079428,SCOOB,232933,340,ENEME,235712,FETAL,M42,003051,15857F6'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -138,20 +155,25 @@ describe('Label_H1 POS', () => { expect(decodeResult.formatted.items[2].type).toBe('aircraft_route'); expect(decodeResult.formatted.items[2].code).toBe('ROUTE'); expect(decodeResult.formatted.items[2].label).toBe('Aircraft Route'); - expect(decodeResult.formatted.items[2].value).toBe('SCOOB@23:29:33 > ENEME@23:57:12 > FETAL'); - expect(decodeResult.formatted.items[3].type).toBe('outside_air_temperature'); + expect(decodeResult.formatted.items[2].value).toBe( + 'SCOOB@23:29:33 > ENEME@23:57:12 > FETAL', + ); + expect(decodeResult.formatted.items[3].type).toBe( + 'outside_air_temperature', + ); expect(decodeResult.formatted.items[3].code).toBe('OATEMP'); - expect(decodeResult.formatted.items[3].label).toBe('Outside Air Temperature (C)'); + expect(decodeResult.formatted.items[3].label).toBe( + 'Outside Air Temperature (C)', + ); expect(decodeResult.formatted.items[3].value).toBe('-42 degrees'); expect(decodeResult.formatted.items[4].label).toBe('Message Checksum'); expect(decodeResult.formatted.items[4].value).toBe('0x57f6'); }); - test('variant 5', () => { - // https://app.airframes.io/messages/2184441420 - message.text = 'POSN38531W078000,CSN-01,112309,310,CYN-02,114151,ACK,M40,26067,22479226'; + message.text = + 'POSN38531W078000,CSN-01,112309,310,CYN-02,114151,ACK,M40,26067,22479226'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -159,7 +181,7 @@ describe('Label_H1 POS', () => { expect(decodeResult.formatted.description).toBe('Position Report'); expect(decodeResult.message).toBe(message); expect(decodeResult.raw.position.latitude).toBe(38.885); - expect(decodeResult.raw.position.longitude).toBe(-78.000); + expect(decodeResult.raw.position.longitude).toBe(-78.0); expect(decodeResult.raw.altitude).toBe(31000); expect(decodeResult.raw.outside_air_temperature).toBe(-40); expect(decodeResult.formatted.items.length).toBe(5); @@ -174,19 +196,25 @@ describe('Label_H1 POS', () => { expect(decodeResult.formatted.items[2].type).toBe('aircraft_route'); expect(decodeResult.formatted.items[2].code).toBe('ROUTE'); expect(decodeResult.formatted.items[2].label).toBe('Aircraft Route'); - expect(decodeResult.formatted.items[2].value).toBe('CSN-01@11:23:09 > CYN-02@11:41:51 > ACK'); - expect(decodeResult.formatted.items[3].type).toBe('outside_air_temperature'); + expect(decodeResult.formatted.items[2].value).toBe( + 'CSN-01@11:23:09 > CYN-02@11:41:51 > ACK', + ); + expect(decodeResult.formatted.items[3].type).toBe( + 'outside_air_temperature', + ); expect(decodeResult.formatted.items[3].code).toBe('OATEMP'); - expect(decodeResult.formatted.items[3].label).toBe('Outside Air Temperature (C)'); + expect(decodeResult.formatted.items[3].label).toBe( + 'Outside Air Temperature (C)', + ); expect(decodeResult.formatted.items[3].value).toBe('-40 degrees'); expect(decodeResult.formatted.items[4].label).toBe('Message Checksum'); expect(decodeResult.formatted.items[4].value).toBe('0x9226'); }); test('variant 6', () => { - // https://app.airframes.io/messages/2295027018 - message.text = 'POS/RFSCOOB.KEMPR.ECG.OHPEA.TOMMZ.OXANA.ZZTOP.OMALA.WILYY.KANUX.GALVN.KASAR.LNHOM.SLUKA.FIPEK.PUYYA.PLING.KOLAO.JETSSF2FC'; + message.text = + 'POS/RFSCOOB.KEMPR.ECG.OHPEA.TOMMZ.OXANA.ZZTOP.OMALA.WILYY.KANUX.GALVN.KASAR.LNHOM.SLUKA.FIPEK.PUYYA.PLING.KOLAO.JETSSF2FC'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -197,14 +225,14 @@ describe('Label_H1 POS', () => { expect(decodeResult.formatted.items[0].label).toBe('Route Status'); expect(decodeResult.formatted.items[0].value).toBe('Route Filed'); expect(decodeResult.formatted.items[1].label).toBe('Aircraft Route'); - expect(decodeResult.formatted.items[1].value).toBe('SCOOB > KEMPR > ECG > OHPEA > TOMMZ > OXANA > ZZTOP > OMALA > WILYY > KANUX > GALVN > KASAR > LNHOM > SLUKA > FIPEK > PUYYA > PLING > KOLAO > JETSS'); + expect(decodeResult.formatted.items[1].value).toBe( + 'SCOOB > KEMPR > ECG > OHPEA > TOMMZ > OXANA > ZZTOP > OMALA > WILYY > KANUX > GALVN > KASAR > LNHOM > SLUKA > FIPEK > PUYYA > PLING > KOLAO > JETSS', + ); expect(decodeResult.formatted.items[2].label).toBe('Message Checksum'); expect(decodeResult.formatted.items[2].value).toBe('0xf2fc'); - }); test('', () => { - message.text = 'POS Bogus message'; const decodeResult = plugin.decode(message); @@ -215,9 +243,9 @@ describe('Label_H1 POS', () => { }); test('#short variant', () => { - // https://app.airframes.io/messages/2368445399 - message.text = '#M1BPOSN37533W096476,ROKNE,185212,330,DOSOA,190059,BUM,M50,272100,1571541' + message.text = + '#M1BPOSN37533W096476,ROKNE,185212,330,DOSOA,190059,BUM,M50,272100,1571541'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -229,8 +257,12 @@ describe('Label_H1 POS', () => { expect(decodeResult.formatted.items[1].label).toBe('Altitude'); expect(decodeResult.formatted.items[1].value).toBe('33000 feet'); expect(decodeResult.formatted.items[2].label).toBe('Aircraft Route'); - expect(decodeResult.formatted.items[2].value).toBe('ROKNE@18:52:12 > DOSOA@19:00:59 > BUM'); - expect(decodeResult.formatted.items[3].label).toBe('Outside Air Temperature (C)'); + expect(decodeResult.formatted.items[2].value).toBe( + 'ROKNE@18:52:12 > DOSOA@19:00:59 > BUM', + ); + expect(decodeResult.formatted.items[3].label).toBe( + 'Outside Air Temperature (C)', + ); expect(decodeResult.formatted.items[3].value).toBe('-50 degrees'); expect(decodeResult.formatted.items[4].label).toBe('Message Checksum'); expect(decodeResult.formatted.items[4].value).toBe('0x1541'); @@ -240,9 +272,9 @@ describe('Label_H1 POS', () => { // broken as there is no checksum test.skip('# long variant', () => { - // https://app.airframes.io/messages/2366921571 - message.text = '#M1BPOSN29510W098448,RW04,140407,188,TATAR,4,140445,ALISS,M12,246048,374K,282K,1223,133,KSAT,KELP,,70,151437,415,73/PR1223,222,240,133,,44,40,252074,M22,180,P0,P0/RI:DA:KSAT:AA:KELP..TATAR:D:ALISS6:F:ALISS..FST'; + message.text = + '#M1BPOSN29510W098448,RW04,140407,188,TATAR,4,140445,ALISS,M12,246048,374K,282K,1223,133,KSAT,KELP,,70,151437,415,73/PR1223,222,240,133,,44,40,252074,M22,180,P0,P0/RI:DA:KSAT:AA:KELP..TATAR:D:ALISS6:F:ALISS..FST'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -268,14 +300,18 @@ describe('Label_H1 POS', () => { expect(decodeResult.formatted.items[8].label).toBe('Aircraft Route'); expect(decodeResult.formatted.items[8].value).toBe('ALISS >> FST'); expect(decodeResult.formatted.items[9].label).toBe('Aircraft Route'); - expect(decodeResult.formatted.items[9].value).toBe('TATAR@14:04:07 > ALISS@14:04:45 > ?'); // FIXME - ? should be FST - expect(decodeResult.remaining.text).toBe('188,4,M12,246048,374K,282K,1223,133,,70,151437,73/PR1223,222,133,,44,40,252074,M22,180,P0'); + expect(decodeResult.formatted.items[9].value).toBe( + 'TATAR@14:04:07 > ALISS@14:04:45 > ?', + ); // FIXME - ? should be FST + expect(decodeResult.remaining.text).toBe( + '188,4,M12,246048,374K,282K,1223,133,,70,151437,73/PR1223,222,133,,44,40,252074,M22,180,P0', + ); }); test('# variant 7', () => { - // https://app.airframes.io/messages/2434835903 - message.text = 'F37AMCLL93#M1BPOS/ID746026,,/DC03032024,173207/MR1,/ET031846/PSN42579W108090,173207,320,WAIDE,031759,WEDAK,M49,267070,T468/CG264,110,360/FB742/VR324E17'; + message.text = + 'F37AMCLL93#M1BPOS/ID746026,,/DC03032024,173207/MR1,/ET031846/PSN42579W108090,173207,320,WAIDE,031759,WEDAK,M49,267070,T468/CG264,110,360/FB742/VR324E17'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -307,9 +343,9 @@ describe('Label_H1 POS', () => { }); test('variant 8', () => { - // https://app.airframes.io/messages/2500335076 - message.text = 'POS/TS080616,210324/DTMMGL,29O,64,103316/PR1754,231,350,189,,0,0,,M45,185,,,P16,P0,36000,,1565,250/RP:DA:MMTJ:AA:MMGL:R:27O:D:TUMA2B..SANFE.UT4..LMM:A:LONV1D:AP:ILSZ29.PLADE(29O)9D1C'; + message.text = + 'POS/TS080616,210324/DTMMGL,29O,64,103316/PR1754,231,350,189,,0,0,,M45,185,,,P16,P0,36000,,1565,250/RP:DA:MMTJ:AA:MMGL:R:27O:D:TUMA2B..SANFE.UT4..LMM:A:LONV1D:AP:ILSZ29.PLADE(29O)9D1C'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -322,7 +358,9 @@ describe('Label_H1 POS', () => { expect(decodeResult.formatted.items[1].value).toBe('29O'); expect(decodeResult.formatted.items[2].label).toBe('Fuel On Board'); expect(decodeResult.formatted.items[2].value).toBe('64'); - expect(decodeResult.formatted.items[3].label).toBe('Estimated Time of Arrival'); + expect(decodeResult.formatted.items[3].label).toBe( + 'Estimated Time of Arrival', + ); expect(decodeResult.formatted.items[3].value).toBe('10:33:16'); expect(decodeResult.formatted.items[4].label).toBe('Route Status'); expect(decodeResult.formatted.items[4].value).toBe('Route Planned'); @@ -333,20 +371,26 @@ describe('Label_H1 POS', () => { expect(decodeResult.formatted.items[7].label).toBe('Departure Runway'); expect(decodeResult.formatted.items[7].value).toBe('27O'); expect(decodeResult.formatted.items[8].label).toBe('Departure Procedure'); - expect(decodeResult.formatted.items[8].value).toBe('TUMA2B: >> SANFE > UT4 >> LMM'); + expect(decodeResult.formatted.items[8].value).toBe( + 'TUMA2B: >> SANFE > UT4 >> LMM', + ); expect(decodeResult.formatted.items[9].label).toBe('Arrival Procedure'); expect(decodeResult.formatted.items[9].value).toBe('LONV1D'); expect(decodeResult.formatted.items[10].label).toBe('Approach Procedure'); - expect(decodeResult.formatted.items[10].value).toBe('ILSZ29 starting at PLADE(29O)'); + expect(decodeResult.formatted.items[10].value).toBe( + 'ILSZ29 starting at PLADE(29O)', + ); expect(decodeResult.formatted.items[11].label).toBe('Message Checksum'); expect(decodeResult.formatted.items[11].value).toBe('0x9d1c'); - expect(decodeResult.remaining.text).toBe('PR1754,231,350,189,,0,0,,M45,185,,,P16,P0,36000,,1565,250'); + expect(decodeResult.remaining.text).toBe( + 'PR1754,231,350,189,,0,0,,M45,185,,,P16,P0,36000,,1565,250', + ); }); test('variant 1 with offset', () => { - // https://app.airframes.io/messages/2500335076 - message.text = 'POSN33204W114082,BLH176-0093,062056,330,SALOM180-0127,062211,KOFFA180-0148,M49,30628,3201251'; + message.text = + 'POSN33204W114082,BLH176-0093,062056,330,SALOM180-0127,062211,KOFFA180-0148,M49,30628,3201251'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -358,8 +402,12 @@ describe('Label_H1 POS', () => { expect(decodeResult.formatted.items[1].label).toBe('Altitude'); expect(decodeResult.formatted.items[1].value).toBe('33000 feet'); expect(decodeResult.formatted.items[2].label).toBe('Aircraft Route'); - expect(decodeResult.formatted.items[2].value).toBe('BLH[176° 9.3nm]@06:20:56 > SALOM[180° 12.7nm]@06:22:11 > KOFFA[180° 14.8nm]'); - expect(decodeResult.formatted.items[3].label).toBe('Outside Air Temperature (C)'); + expect(decodeResult.formatted.items[2].value).toBe( + 'BLH[176° 9.3nm]@06:20:56 > SALOM[180° 12.7nm]@06:22:11 > KOFFA[180° 14.8nm]', + ); + expect(decodeResult.formatted.items[3].label).toBe( + 'Outside Air Temperature (C)', + ); expect(decodeResult.formatted.items[3].value).toBe('-49 degrees'); expect(decodeResult.formatted.items[4].label).toBe('Message Checksum'); expect(decodeResult.formatted.items[4].value).toBe('0x1251'); @@ -367,9 +415,9 @@ describe('Label_H1 POS', () => { }); test('variant 9', () => { - // https://app.airframes.io/messages/3110992692 - message.text = 'POSN39164W077259,FORKL,231828,51,THRET,231917,TOOPR,P16,1726,167,/TS231828,2807244841'; + message.text = + 'POSN39164W077259,FORKL,231828,51,THRET,231917,TOOPR,P16,1726,167,/TS231828,2807244841'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -381,8 +429,12 @@ describe('Label_H1 POS', () => { expect(decodeResult.formatted.items[1].label).toBe('Altitude'); expect(decodeResult.formatted.items[1].value).toBe('5100 feet'); expect(decodeResult.formatted.items[2].label).toBe('Aircraft Route'); - expect(decodeResult.formatted.items[2].value).toBe('FORKL@23:18:28 > THRET@23:19:17 > TOOPR'); - expect(decodeResult.formatted.items[3].label).toBe('Outside Air Temperature (C)'); + expect(decodeResult.formatted.items[2].value).toBe( + 'FORKL@23:18:28 > THRET@23:19:17 > TOOPR', + ); + expect(decodeResult.formatted.items[3].label).toBe( + 'Outside Air Temperature (C)', + ); expect(decodeResult.formatted.items[3].value).toBe('16 degrees'); expect(decodeResult.formatted.items[4].label).toBe('Message Checksum'); expect(decodeResult.formatted.items[4].value).toBe('0x4841'); @@ -390,9 +442,9 @@ describe('Label_H1 POS', () => { }); test('variant 10', () => { - // https://app.airframes.io/messages/3393214562 - message.text = 'POSN51194E004527,EH556,104241,174,CIV,105208,MEDIL,M20,290016,191/RP:DA:EHEH:AA:LEIB..CIV.N872.MEDIL.UN872.KOVIN.UM728.RESMI.UN857.DISAK..DIRMO..ETAMO..ADEKA..MOKDI..MEN..BADAM..KANIG..KENAS.N855.POS/PR1496,150,370,191,,55,10,248028,M47,30,P19,P0/FHCIV,105208,273K,3226,175,M41,252027,450,N,221,62.MEDIL,105411,267K,3439,172,M44,250028,459,N,203,15.PITHI,105533,259K,3584,170,M47,249028,456,N,203,10.LESDO,105859,252K,3700,167,M47,248028,456,N,203,25.KOVIN,110153,252K,3700,164,M47,248028,456,N,203,21.DUCRA,110705,252K,3700,160,M47,248028,456,N,213,37.RESMI,111101,251K,3700,156,M47,248028,455,N,213,28.DEKOD,111325,251K,3700,154,M47,248028,455,N,192,17.DISAK,111438,251K,3700,153,M47,248028,454,N,172,9.DIRMO,112306,251K,3700,145,M47,248028,454,N,178,63.ETAMO,112514,250K,3700,143,M47,248028,453,N,158,16.ADEKA,113339,250K,3700,136,M47,248028,454,N,147,64.MOKDI,114139,251K,3700,129,M47,248028,454,N,181,59.MEN,114429,251K,3700,127,M47,248028,454,N,181,21.BADAM,114843,251K,3700,123,M47,248028,454,N,179,31.KANIG,120154,250K,3700,111,M47,248028,453,N,185,97.KENAS,121800,250K,3700,98,M47,248028,453,N,177,119.POS,122257,250K,3018,96,M45,248023,395,N,182,34.LEIB,124503,150K,2,89,P15,000000,161,N,231,103/DTLEIB,,89,124503,7353B2'; + message.text = + 'POSN51194E004527,EH556,104241,174,CIV,105208,MEDIL,M20,290016,191/RP:DA:EHEH:AA:LEIB..CIV.N872.MEDIL.UN872.KOVIN.UM728.RESMI.UN857.DISAK..DIRMO..ETAMO..ADEKA..MOKDI..MEN..BADAM..KANIG..KENAS.N855.POS/PR1496,150,370,191,,55,10,248028,M47,30,P19,P0/FHCIV,105208,273K,3226,175,M41,252027,450,N,221,62.MEDIL,105411,267K,3439,172,M44,250028,459,N,203,15.PITHI,105533,259K,3584,170,M47,249028,456,N,203,10.LESDO,105859,252K,3700,167,M47,248028,456,N,203,25.KOVIN,110153,252K,3700,164,M47,248028,456,N,203,21.DUCRA,110705,252K,3700,160,M47,248028,456,N,213,37.RESMI,111101,251K,3700,156,M47,248028,455,N,213,28.DEKOD,111325,251K,3700,154,M47,248028,455,N,192,17.DISAK,111438,251K,3700,153,M47,248028,454,N,172,9.DIRMO,112306,251K,3700,145,M47,248028,454,N,178,63.ETAMO,112514,250K,3700,143,M47,248028,453,N,158,16.ADEKA,113339,250K,3700,136,M47,248028,454,N,147,64.MOKDI,114139,251K,3700,129,M47,248028,454,N,181,59.MEN,114429,251K,3700,127,M47,248028,454,N,181,21.BADAM,114843,251K,3700,123,M47,248028,454,N,179,31.KANIG,120154,250K,3700,111,M47,248028,453,N,185,97.KENAS,121800,250K,3700,98,M47,248028,453,N,177,119.POS,122257,250K,3018,96,M45,248023,395,N,182,34.LEIB,124503,150K,2,89,P15,000000,161,N,231,103/DTLEIB,,89,124503,7353B2'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -404,31 +456,42 @@ describe('Label_H1 POS', () => { expect(decodeResult.formatted.items[1].label).toBe('Altitude'); expect(decodeResult.formatted.items[1].value).toBe('17400 feet'); expect(decodeResult.formatted.items[2].label).toBe('Aircraft Route'); - expect(decodeResult.formatted.items[2].value).toBe('EH556@10:42:41 > CIV@10:52:08 > MEDIL'); - expect(decodeResult.formatted.items[3].label).toBe('Outside Air Temperature (C)'); + expect(decodeResult.formatted.items[2].value).toBe( + 'EH556@10:42:41 > CIV@10:52:08 > MEDIL', + ); + expect(decodeResult.formatted.items[3].label).toBe( + 'Outside Air Temperature (C)', + ); expect(decodeResult.formatted.items[3].value).toBe('-20 degrees'); expect(decodeResult.formatted.items[4].label).toBe('Route Status'); expect(decodeResult.formatted.items[4].value).toBe('Route Planned'); expect(decodeResult.formatted.items[5].label).toBe('Origin'); expect(decodeResult.formatted.items[5].value).toBe('EHEH'); expect(decodeResult.formatted.items[6].label).toBe('Destination'); - expect(decodeResult.formatted.items[6].value).toBe('LEIB..CIV.N872.MEDIL.UN872.KOVIN.UM728.RESMI.UN857.DISAK..DIRMO..ETAMO..ADEKA..MOKDI..MEN..BADAM..KANIG..KENAS.N855.POS'); + expect(decodeResult.formatted.items[6].value).toBe( + 'LEIB..CIV.N872.MEDIL.UN872.KOVIN.UM728.RESMI.UN857.DISAK..DIRMO..ETAMO..ADEKA..MOKDI..MEN..BADAM..KANIG..KENAS.N855.POS', + ); expect(decodeResult.formatted.items[7].label).toBe('Arrival Runway'); // FIXME should remove expect(decodeResult.formatted.items[7].value).toBe(''); // FIXME should remove expect(decodeResult.formatted.items[8].label).toBe('Fuel On Board'); expect(decodeResult.formatted.items[8].value).toBe('89'); - expect(decodeResult.formatted.items[9].label).toBe('Estimated Time of Arrival'); + expect(decodeResult.formatted.items[9].label).toBe( + 'Estimated Time of Arrival', + ); expect(decodeResult.formatted.items[9].value).toBe('12:45:03'); expect(decodeResult.formatted.items[10].label).toBe('Fuel Remaining'); expect(decodeResult.formatted.items[10].value).toBe('73'); expect(decodeResult.formatted.items[11].label).toBe('Message Checksum'); expect(decodeResult.formatted.items[11].value).toBe('0x53b2'); - expect(decodeResult.remaining.text).toBe('290016,191/PR1496,150,370,191,,55,10,248028,M47,30,P19,P0/FHCIV,105208,273K,3226,175,M41,252027,450,N,221,62.MEDIL,105411,267K,3439,172,M44,250028,459,N,203,15.PITHI,105533,259K,3584,170,M47,249028,456,N,203,10.LESDO,105859,252K,3700,167,M47,248028,456,N,203,25.KOVIN,110153,252K,3700,164,M47,248028,456,N,203,21.DUCRA,110705,252K,3700,160,M47,248028,456,N,213,37.RESMI,111101,251K,3700,156,M47,248028,455,N,213,28.DEKOD,111325,251K,3700,154,M47,248028,455,N,192,17.DISAK,111438,251K,3700,153,M47,248028,454,N,172,9.DIRMO,112306,251K,3700,145,M47,248028,454,N,178,63.ETAMO,112514,250K,3700,143,M47,248028,453,N,158,16.ADEKA,113339,250K,3700,136,M47,248028,454,N,147,64.MOKDI,114139,251K,3700,129,M47,248028,454,N,181,59.MEN,114429,251K,3700,127,M47,248028,454,N,181,21.BADAM,114843,251K,3700,123,M47,248028,454,N,179,31.KANIG,120154,250K,3700,111,M47,248028,453,N,185,97.KENAS,121800,250K,3700,98,M47,248028,453,N,177,119.POS,122257,250K,3018,96,M45,248023,395,N,182,34.LEIB,124503,150K,2,89,P15,000000,161,N,231,103,LEIB,,89,124503,73'); + expect(decodeResult.remaining.text).toBe( + '290016,191/PR1496,150,370,191,,55,10,248028,M47,30,P19,P0/FHCIV,105208,273K,3226,175,M41,252027,450,N,221,62.MEDIL,105411,267K,3439,172,M44,250028,459,N,203,15.PITHI,105533,259K,3584,170,M47,249028,456,N,203,10.LESDO,105859,252K,3700,167,M47,248028,456,N,203,25.KOVIN,110153,252K,3700,164,M47,248028,456,N,203,21.DUCRA,110705,252K,3700,160,M47,248028,456,N,213,37.RESMI,111101,251K,3700,156,M47,248028,455,N,213,28.DEKOD,111325,251K,3700,154,M47,248028,455,N,192,17.DISAK,111438,251K,3700,153,M47,248028,454,N,172,9.DIRMO,112306,251K,3700,145,M47,248028,454,N,178,63.ETAMO,112514,250K,3700,143,M47,248028,453,N,158,16.ADEKA,113339,250K,3700,136,M47,248028,454,N,147,64.MOKDI,114139,251K,3700,129,M47,248028,454,N,181,59.MEN,114429,251K,3700,127,M47,248028,454,N,181,21.BADAM,114843,251K,3700,123,M47,248028,454,N,179,31.KANIG,120154,250K,3700,111,M47,248028,453,N,185,97.KENAS,121800,250K,3700,98,M47,248028,453,N,177,119.POS,122257,250K,3018,96,M45,248023,395,N,182,34.LEIB,124503,150K,2,89,P15,000000,161,N,231,103,LEIB,,89,124503,73', + ); }); test('does not decode /.POS', () => { // https://app.airframes.io/messages/2500488708 - message.text = '/.POS/TS100316,210324/PSS35333W058220,,100316,250,S37131W059150,101916,S39387W060377,M23,27282,241,780,MANUAL,0,813E711'; + message.text = + '/.POS/TS100316,210324/PSS35333W058220,,100316,250,S37131W059150,101916,S39387W060377,M23,27282,241,780,MANUAL,0,813E711'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(false); @@ -437,7 +500,8 @@ describe('Label_H1 POS', () => { }); test('decodes duplicate data', () => { - message.text = 'POSN39220W078258,MRB18,044034,340,EYT19,044427,COL20,M49,27369,436,813/PSN39220W078258,MRB18,044034,340,EYT19,044427,COL20,M49,27369,436,813,ECON CRZ,0,25140035' + message.text = + 'POSN39220W078258,MRB18,044034,340,EYT19,044427,COL20,M49,27369,436,813/PSN39220W078258,MRB18,044034,340,EYT19,044427,COL20,M49,27369,436,813,ECON CRZ,0,25140035'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -456,10 +520,16 @@ describe('Label_H1 POS', () => { expect(decodeResult.formatted.items[2].type).toBe('aircraft_route'); expect(decodeResult.formatted.items[2].code).toBe('ROUTE'); expect(decodeResult.formatted.items[2].label).toBe('Aircraft Route'); - expect(decodeResult.formatted.items[2].value).toBe('MRB18@04:40:34 > EYT19@04:44:27 > COL20'); - expect(decodeResult.formatted.items[3].type).toBe('outside_air_temperature'); + expect(decodeResult.formatted.items[2].value).toBe( + 'MRB18@04:40:34 > EYT19@04:44:27 > COL20', + ); + expect(decodeResult.formatted.items[3].type).toBe( + 'outside_air_temperature', + ); expect(decodeResult.formatted.items[3].code).toBe('OATEMP'); - expect(decodeResult.formatted.items[3].label).toBe('Outside Air Temperature (C)'); + expect(decodeResult.formatted.items[3].label).toBe( + 'Outside Air Temperature (C)', + ); expect(decodeResult.formatted.items[3].value).toBe('-49 degrees'); // YAY DUPLICATES! expect(decodeResult.formatted.items[4].type).toBe('aircraft_position'); @@ -473,21 +543,26 @@ describe('Label_H1 POS', () => { expect(decodeResult.formatted.items[6].type).toBe('aircraft_route'); expect(decodeResult.formatted.items[6].code).toBe('ROUTE'); expect(decodeResult.formatted.items[6].label).toBe('Aircraft Route'); - expect(decodeResult.formatted.items[6].value).toBe('MRB18@04:40:34 > EYT19@04:44:27 > COL20'); - expect(decodeResult.formatted.items[7].type).toBe('outside_air_temperature'); + expect(decodeResult.formatted.items[6].value).toBe( + 'MRB18@04:40:34 > EYT19@04:44:27 > COL20', + ); + expect(decodeResult.formatted.items[7].type).toBe( + 'outside_air_temperature', + ); expect(decodeResult.formatted.items[7].code).toBe('OATEMP'); - expect(decodeResult.formatted.items[7].label).toBe('Outside Air Temperature (C)'); + expect(decodeResult.formatted.items[7].label).toBe( + 'Outside Air Temperature (C)', + ); expect(decodeResult.formatted.items[7].value).toBe('-49 degrees'); // And then the end expect(decodeResult.formatted.items[8].label).toBe('Message Checksum'); expect(decodeResult.formatted.items[8].value).toBe('0x0035'); - expect(decodeResult.remaining.text).toBe('27369,436,27369,436,813,ECON CRZ,0,2514'); - + expect(decodeResult.remaining.text).toBe( + '27369,436,27369,436,813,ECON CRZ,0,2514', + ); }); - test('decodes Label H1 Preamble #M1BPOS ', () => { - message.text = '#M1BPOS Bogus message'; const decodeResult = plugin.decode(message); diff --git a/lib/plugins/Label_H1_PRG.test.ts b/lib/plugins/Label_H1_PRG.test.ts index 97f46d2..09d1d10 100644 --- a/lib/plugins/Label_H1_PRG.test.ts +++ b/lib/plugins/Label_H1_PRG.test.ts @@ -2,16 +2,14 @@ import { MessageDecoder } from '../MessageDecoder'; import { Label_H1 } from './Label_H1'; describe('Label H1 Preamble PRG', () => { - let plugin: Label_H1; - const message = {label: 'H1', text: ''}; + const message = { label: 'H1', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); plugin = new Label_H1(decoder); }); - test('decodes Label H1 Preamble PRG message', () => { // https://app.airframes.io/messages/2400672008 message.text = 'PRG/FNEXS67TP/DTEGPF,23O,67,214405,298262'; @@ -28,7 +26,9 @@ describe('Label H1 Preamble PRG', () => { expect(decodeResult.formatted.items[1].value).toBe('23O'); expect(decodeResult.formatted.items[2].label).toBe('Fuel On Board'); expect(decodeResult.formatted.items[2].value).toBe('67'); - expect(decodeResult.formatted.items[3].label).toBe('Estimated Time of Arrival'); + expect(decodeResult.formatted.items[3].label).toBe( + 'Estimated Time of Arrival', + ); expect(decodeResult.formatted.items[3].value).toBe('21:44:05'); expect(decodeResult.formatted.items[4].label).toBe('Fuel Remaining'); expect(decodeResult.formatted.items[4].value).toBe('29'); @@ -38,7 +38,8 @@ describe('Label H1 Preamble PRG', () => { test('decodes Label H1 Preamble PRG/LR', () => { // https://app.airframes.io/messages/2400672684 - message.text = 'PRG/LR,000855,SWA786,KBWI,KBOS,04R,25,1285,1192,93,1284,P4,001004,8,141K,D713,000950,2,291483'; + message.text = + 'PRG/LR,000855,SWA786,KBWI,KBOS,04R,25,1285,1192,93,1284,P4,001004,8,141K,D713,000950,2,291483'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -56,12 +57,15 @@ describe('Label H1 Preamble PRG', () => { expect(decodeResult.formatted.items[3].value).toBe('04R'); expect(decodeResult.formatted.items[4].label).toBe('Message Checksum'); expect(decodeResult.formatted.items[4].value).toBe('0x1483'); - expect(decodeResult.remaining.text).toBe('000855,25,1285,1192,93,1284,P4,001004,8,141K,D713,000950,2,29'); + expect(decodeResult.remaining.text).toBe( + '000855,25,1285,1192,93,1284,P4,001004,8,141K,D713,000950,2,29', + ); }); test('decodes Label H1 Preamble #M1BPRG', () => { // https://app.airframes.io/messages/2403492320 - message.text = '#M1BPRG/DTEKCH,22L,69,141204,042/FNSAS87W/TS132645,2402246784'; + message.text = + '#M1BPRG/DTEKCH,22L,69,141204,042/FNSAS87W/TS132645,2402246784'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -76,7 +80,9 @@ describe('Label H1 Preamble PRG', () => { expect(decodeResult.formatted.items[1].value).toBe('22L'); expect(decodeResult.formatted.items[2].label).toBe('Fuel On Board'); expect(decodeResult.formatted.items[2].value).toBe('69'); - expect(decodeResult.formatted.items[3].label).toBe('Estimated Time of Arrival'); + expect(decodeResult.formatted.items[3].label).toBe( + 'Estimated Time of Arrival', + ); expect(decodeResult.formatted.items[3].value).toBe('14:12:04'); expect(decodeResult.formatted.items[4].label).toBe('Fuel Remaining'); expect(decodeResult.formatted.items[4].value).toBe('42'); @@ -86,7 +92,8 @@ describe('Label H1 Preamble PRG', () => { test('decodes Label H1 PRG with flightplan', () => { // https://app.airframes.io/messages/2403398879 - message.text = 'PRG/DTGCRR,03O,62,163055/PR1544,286,350,239,,0,66,,,50,,,P19,P0,27440,,1305,303/RP:DA:EDDB:AA:GCRR..N50059E004552..N49588E004338..N49241E002528..N49122E002199..PON.UN872..ERIGA..FUJTI:WS:FUJTI,360..BAKUP..BATAX..TAKAV..VEDOD:A:TERT1P:AP:ILSZ03.BAPAL(03O)226D'; + message.text = + 'PRG/DTGCRR,03O,62,163055/PR1544,286,350,239,,0,66,,,50,,,P19,P0,27440,,1305,303/RP:DA:EDDB:AA:GCRR..N50059E004552..N49588E004338..N49241E002528..N49122E002199..PON.UN872..ERIGA..FUJTI:WS:FUJTI,360..BAKUP..BATAX..TAKAV..VEDOD:A:TERT1P:AP:ILSZ03.BAPAL(03O)226D'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -99,24 +106,31 @@ describe('Label H1 Preamble PRG', () => { expect(decodeResult.formatted.items[1].value).toBe('03O'); expect(decodeResult.formatted.items[2].label).toBe('Fuel On Board'); expect(decodeResult.formatted.items[2].value).toBe('62'); - expect(decodeResult.formatted.items[3].label).toBe('Estimated Time of Arrival'); + expect(decodeResult.formatted.items[3].label).toBe( + 'Estimated Time of Arrival', + ); expect(decodeResult.formatted.items[3].value).toBe('16:30:55'); expect(decodeResult.formatted.items[4].label).toBe('Route Status'); expect(decodeResult.formatted.items[4].value).toBe('Route Planned'); expect(decodeResult.formatted.items[5].label).toBe('Origin'); expect(decodeResult.formatted.items[5].value).toBe('EDDB'); expect(decodeResult.formatted.items[6].label).toBe('Destination'); - expect(decodeResult.formatted.items[6].value).toBe('GCRR..N50059E004552..N49588E004338..N49241E002528..N49122E002199..PON.UN872..ERIGA..FUJTI'); // TODO - pull out route + expect(decodeResult.formatted.items[6].value).toBe( + 'GCRR..N50059E004552..N49588E004338..N49241E002528..N49122E002199..PON.UN872..ERIGA..FUJTI', + ); // TODO - pull out route expect(decodeResult.formatted.items[7].label).toBe('Arrival Procedure'); expect(decodeResult.formatted.items[7].value).toBe('TERT1P'); expect(decodeResult.formatted.items[8].label).toBe('Approach Procedure'); - expect(decodeResult.formatted.items[8].value).toBe('ILSZ03 starting at BAPAL(03O)'); + expect(decodeResult.formatted.items[8].value).toBe( + 'ILSZ03 starting at BAPAL(03O)', + ); expect(decodeResult.formatted.items[9].label).toBe('Message Checksum'); expect(decodeResult.formatted.items[9].value).toBe('0x226d'); - expect(decodeResult.remaining.text).toBe(':WS:FUJTI,360..BAKUP..BATAX..TAKAV..VEDOD'); + expect(decodeResult.remaining.text).toBe( + ':WS:FUJTI,360..BAKUP..BATAX..TAKAV..VEDOD', + ); }); - // TODO Fix this test test.skip('decodes named runway', () => { message.text = 'PRG/DT,KMDW,31R,62,031854,524,N38584W077333,171,732B3C'; diff --git a/lib/plugins/Label_H1_PWI.test.ts b/lib/plugins/Label_H1_PWI.test.ts index b8ac4d1..6a3ec69 100644 --- a/lib/plugins/Label_H1_PWI.test.ts +++ b/lib/plugins/Label_H1_PWI.test.ts @@ -2,53 +2,58 @@ import { MessageDecoder } from '../MessageDecoder'; import { Label_H1 } from './Label_H1'; describe('Label H1 PWI', () => { - let plugin: Label_H1; - const message = {label: 'H1', text: ''}; + let plugin: Label_H1; + const message = { label: 'H1', text: '' }; - beforeEach(() => { - const decoder = new MessageDecoder(); - plugin = new Label_H1(decoder); + beforeEach(() => { + const decoder = new MessageDecoder(); + plugin = new Label_H1(decoder); + }); + + test('matches Label H1 Preamble PWI qualifiers', () => { + expect(plugin.decode).toBeDefined(); + expect(plugin.name).toBe('label-h1'); + expect(plugin.qualifiers).toBeDefined(); + expect(plugin.qualifiers()).toEqual({ + labels: ['H1'], }); + }); - test('matches Label H1 Preamble PWI qualifiers', () => { - expect(plugin.decode).toBeDefined(); - expect(plugin.name).toBe('label-h1'); - expect(plugin.qualifiers).toBeDefined(); - expect(plugin.qualifiers()).toEqual({ - labels: ['H1'], - }); - }); - + test('decodes Label H1 Preamble PWI valid', () => { + message.text = + 'PWI/WD390,COLZI,258070.AWYAT,252071.IPTAY,250065.CHOPZ,244069.MGMRY,234065.CATLN,230060/WD340,COLZI,256073,340M41.AWYAT,252070,340M41.IPTAY,244059,340M41.CHOPZ,240059,340M41.MGMRY,232056,340M41.CATLN,218053,340M40/WD300,COLZI,256065.AWYAT,254062.IPTAY,250051.CHOPZ,248050.MGMRY,232044.CATLN,222047/WD240,COLZI,260045.AWYAT,258048.IPTAY,254043.CHOPZ,256041.MGMRY,238035.CATLN,226034/DD300214059.240214040.180236024.100250018:,,,,/CB300246040.240246017.180226015.1002100080338'; + const decodeResult = plugin.decode(message); - test('decodes Label H1 Preamble PWI valid', () => { - message.text = 'PWI/WD390,COLZI,258070.AWYAT,252071.IPTAY,250065.CHOPZ,244069.MGMRY,234065.CATLN,230060/WD340,COLZI,256073,340M41.AWYAT,252070,340M41.IPTAY,244059,340M41.CHOPZ,240059,340M41.MGMRY,232056,340M41.CATLN,218053,340M40/WD300,COLZI,256065.AWYAT,254062.IPTAY,250051.CHOPZ,248050.MGMRY,232044.CATLN,222047/WD240,COLZI,260045.AWYAT,258048.IPTAY,254043.CHOPZ,256041.MGMRY,238035.CATLN,226034/DD300214059.240214040.180236024.100250018:,,,,/CB300246040.240246017.180226015.1002100080338' - const decodeResult = plugin.decode(message); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.formatted.items.length).toBe(25); + expect(decodeResult.formatted.items[0].type).toBe('wind_data'); + expect(decodeResult.formatted.items[0].code).toBe('WIND'); + expect(decodeResult.formatted.items[0].label).toBe('Wind Data'); + expect(decodeResult.formatted.items[0].value).toBe( + 'COLZI at FL390: 258° at 70kt', + ); + expect(decodeResult.formatted.items[1].type).toBe('wind_data'); + expect(decodeResult.formatted.items[1].code).toBe('WIND'); + expect(decodeResult.formatted.items[1].label).toBe('Wind Data'); + expect(decodeResult.formatted.items[1].value).toBe( + 'AWYAT at FL390: 252° at 71kt', + ); + expect(decodeResult.formatted.items[24].label).toBe('Message Checksum'); + expect(decodeResult.formatted.items[24].value).toBe('0x0338'); + expect(decodeResult.remaining.text).toBe( + 'DD300214059.240214040.180236024.100250018:,,,,/CB300246040.240246017.180226015.100210008', + ); + }); - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.formatted.items.length).toBe(25); - expect(decodeResult.formatted.items[0].type).toBe('wind_data'); - expect(decodeResult.formatted.items[0].code).toBe('WIND'); - expect(decodeResult.formatted.items[0].label).toBe('Wind Data'); - expect(decodeResult.formatted.items[0].value).toBe('COLZI at FL390: 258° at 70kt'); - expect(decodeResult.formatted.items[1].type).toBe('wind_data'); - expect(decodeResult.formatted.items[1].code).toBe('WIND'); - expect(decodeResult.formatted.items[1].label).toBe('Wind Data'); - expect(decodeResult.formatted.items[1].value).toBe('AWYAT at FL390: 252° at 71kt'); - expect(decodeResult.formatted.items[24].label).toBe('Message Checksum'); - expect(decodeResult.formatted.items[24].value).toBe('0x0338'); - expect(decodeResult.remaining.text).toBe('DD300214059.240214040.180236024.100250018:,,,,/CB300246040.240246017.180226015.100210008'); - }); + test('decodes Label H1 Preamble POS ', () => { + message.text = 'PWI Bogus message'; + const decodeResult = plugin.decode(message); - test('decodes Label H1 Preamble POS ', () => { - - message.text = 'PWI Bogus message'; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(false); - expect(decodeResult.decoder.decodeLevel).toBe('none'); - expect(decodeResult.decoder.name).toBe('label-h1'); - expect(decodeResult.formatted.description).toBe('Unknown'); - expect(decodeResult.message).toBe(message); - }); -}); \ No newline at end of file + expect(decodeResult.decoded).toBe(false); + expect(decodeResult.decoder.decodeLevel).toBe('none'); + expect(decodeResult.decoder.name).toBe('label-h1'); + expect(decodeResult.formatted.description).toBe('Unknown'); + expect(decodeResult.message).toBe(message); + }); +}); diff --git a/lib/plugins/Label_H1_Slash.test.ts b/lib/plugins/Label_H1_Slash.test.ts index ee70a04..8276160 100644 --- a/lib/plugins/Label_H1_Slash.test.ts +++ b/lib/plugins/Label_H1_Slash.test.ts @@ -3,7 +3,7 @@ import { Label_H1_Slash } from './Label_H1_Slash'; describe('Label H1 /', () => { let plugin: Label_H1_Slash; - const message = {label: 'H1', text: ''}; + const message = { label: 'H1', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); @@ -22,7 +22,8 @@ describe('Label H1 /', () => { test('decodes variant 1', () => { // https://app.airframes.io/messages/2500488708 - message.text = '/.POS/TS100316,210324/PSS35333W058220,,100316,250,S37131W059150,101916,S39387W060377,M23,27282,241,780,MANUAL,0,813E711'; + message.text = + '/.POS/TS100316,210324/PSS35333W058220,,100316,250,S37131W059150,101916,S39387W060377,M23,27282,241,780,MANUAL,0,813E711'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -35,8 +36,12 @@ describe('Label H1 /', () => { expect(decodeResult.formatted.items[1].label).toBe('Altitude'); expect(decodeResult.formatted.items[1].value).toBe('25000 feet'); expect(decodeResult.formatted.items[2].label).toBe('Aircraft Route'); - expect(decodeResult.formatted.items[2].value).toBe('@10:03:16 > (37.218 S, 59.250 W)@10:19:16 > (39.645 S, 60.628 W)'); - expect(decodeResult.formatted.items[3].label).toBe('Outside Air Temperature (C)'); + expect(decodeResult.formatted.items[2].value).toBe( + '@10:03:16 > (37.218 S, 59.250 W)@10:19:16 > (39.645 S, 60.628 W)', + ); + expect(decodeResult.formatted.items[3].label).toBe( + 'Outside Air Temperature (C)', + ); expect(decodeResult.formatted.items[3].value).toBe('-23 degrees'); expect(decodeResult.formatted.items[4].label).toBe('Message Checksum'); expect(decodeResult.formatted.items[4].value).toBe('0xe711'); @@ -44,7 +49,8 @@ describe('Label H1 /', () => { }); test('decodes variant 2', () => { - message.text = '/HDQDLUA.POSN38332W080082,RONZZ,135753,320,LEVII,140454,WISTA,M45,20967,194/GAHDQDLUA/CA/TS135753,1411240721'; + message.text = + '/HDQDLUA.POSN38332W080082,RONZZ,135753,320,LEVII,140454,WISTA,M45,20967,194/GAHDQDLUA/CA/TS135753,1411240721'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -57,8 +63,12 @@ describe('Label H1 /', () => { expect(decodeResult.formatted.items[1].label).toBe('Altitude'); expect(decodeResult.formatted.items[1].value).toBe('32000 feet'); expect(decodeResult.formatted.items[2].label).toBe('Aircraft Route'); - expect(decodeResult.formatted.items[2].value).toBe('RONZZ@13:57:53 > LEVII@14:04:54 > WISTA'); - expect(decodeResult.formatted.items[3].label).toBe('Outside Air Temperature (C)'); + expect(decodeResult.formatted.items[2].value).toBe( + 'RONZZ@13:57:53 > LEVII@14:04:54 > WISTA', + ); + expect(decodeResult.formatted.items[3].label).toBe( + 'Outside Air Temperature (C)', + ); expect(decodeResult.formatted.items[3].value).toBe('-45 degrees'); expect(decodeResult.formatted.items[4].label).toBe('Message Checksum'); expect(decodeResult.formatted.items[4].value).toBe('0x0721'); @@ -66,7 +76,8 @@ describe('Label H1 /', () => { }); test('decodes variant 3', () => { - message.text = '/.POS/TS140122,141124N38321W078003,,140122,450,,140122,,M56,24739,127,8306763'; + message.text = + '/.POS/TS140122,141124N38321W078003,,140122,450,,140122,,M56,24739,127,8306763'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -79,8 +90,12 @@ describe('Label H1 /', () => { expect(decodeResult.formatted.items[1].label).toBe('Altitude'); expect(decodeResult.formatted.items[1].value).toBe('45000 feet'); expect(decodeResult.formatted.items[2].label).toBe('Aircraft Route'); - expect(decodeResult.formatted.items[2].value).toBe('@14:01:22 > @14:01:22 > ?'); - expect(decodeResult.formatted.items[3].label).toBe('Outside Air Temperature (C)'); + expect(decodeResult.formatted.items[2].value).toBe( + '@14:01:22 > @14:01:22 > ?', + ); + expect(decodeResult.formatted.items[3].label).toBe( + 'Outside Air Temperature (C)', + ); expect(decodeResult.formatted.items[3].value).toBe('-56 degrees'); expect(decodeResult.formatted.items[4].label).toBe('Message Checksum'); expect(decodeResult.formatted.items[4].value).toBe('0x6763'); @@ -88,7 +103,6 @@ describe('Label H1 /', () => { }); test('does not decode invalid', () => { - message.text = '/.POS Bogus message'; const decodeResult = plugin.decode(message); diff --git a/lib/plugins/Label_H1_Slash.ts b/lib/plugins/Label_H1_Slash.ts index 475cdd5..03cd260 100644 --- a/lib/plugins/Label_H1_Slash.ts +++ b/lib/plugins/Label_H1_Slash.ts @@ -1,33 +1,29 @@ -import { DateTimeUtils } from '../DateTimeUtils'; import { DecoderPlugin } from '../DecoderPlugin'; import { DecodeResult, Message, Options } from '../DecoderPluginInterface'; -import { Waypoint } from '../types/waypoint'; -import { CoordinateUtils } from '../utils/coordinate_utils'; import { H1Helper } from '../utils/h1_helper'; import { ResultFormatter } from '../utils/result_formatter'; -import { RouteUtils } from '../utils/route_utils'; export class Label_H1_Slash extends DecoderPlugin { name = 'label-h1-slash'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['H1'], - preambles: ['/'] + preambles: ['/'], }; } - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { let decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; decodeResult.formatted.description = 'Position Report'; decodeResult.message = message; - + const checksum = message.text.slice(-4); const data = message.text.slice(0, message.text.length - 4); const fields = data.split('/'); - if(fields[0] !== '') { + if (fields[0] !== '') { ResultFormatter.unknown(decodeResult, message.text); decodeResult.decoded = false; decodeResult.decoder.decodeLevel = 'none'; @@ -36,23 +32,36 @@ export class Label_H1_Slash extends DecoderPlugin { const headerData = fields[1].split('.'); ResultFormatter.unknown(decodeResult, headerData[0]); - if(headerData[1] === 'POS' && fields[2].startsWith('TS') && fields[2].length > 15) { + if ( + headerData[1] === 'POS' && + fields[2].startsWith('TS') && + fields[2].length > 15 + ) { // variant 3 hack // rip out the timestamp and process the rest - H1Helper.processPosition(decodeResult, fields[2].substring(15).split(',')); - } else if(headerData[1] === 'POS') { + H1Helper.processPosition( + decodeResult, + fields[2].substring(15).split(','), + ); + } else if (headerData[1] === 'POS') { // do nothing - } else if(headerData[1].startsWith('POS')) { - H1Helper.processPosition(decodeResult, headerData[1].substring(3).split(',')); + } else if (headerData[1].startsWith('POS')) { + H1Helper.processPosition( + decodeResult, + headerData[1].substring(3).split(','), + ); } else { ResultFormatter.unknown(decodeResult, headerData[1], '.'); } - for(let i=2; i { - let plugin: Label_H1_StarPOS; - const message = {label: 'H1', text: ''}; + const message = { label: 'H1', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); @@ -12,7 +11,6 @@ describe('Label H1 *POS', () => { }); test('decodes variant 1', () => { - message.text = '*POS10300950N3954W07759363312045802M5230175'; const decodeResult = plugin.decode(message); @@ -22,7 +20,7 @@ describe('Label H1 *POS', () => { expect(decodeResult.raw.day).toBe(30); expect(decodeResult.raw.month).toBe(10); expect(decodeResult.raw.time_of_day).toBe(35400); - expect(decodeResult.raw.position.latitude).toBe(39.900000); + expect(decodeResult.raw.position.latitude).toBe(39.9); expect(decodeResult.raw.position.longitude).toBe(-77.98333333333333); expect(decodeResult.raw.altitude).toBe(36331); expect(decodeResult.formatted.items.length).toBe(5); @@ -36,13 +34,11 @@ describe('Label H1 *POS', () => { expect(decodeResult.formatted.items[3].value).toBe('39.900 N, 77.983 W'); expect(decodeResult.formatted.items[4].label).toBe('Altitude'); expect(decodeResult.formatted.items[4].value).toBe('36331 feet'); - + expect(decodeResult.remaining.text).toBe('2045802M5230175'); }); - test('does not decode ', () => { - message.text = '*POS Bogus message'; const decodeResult = plugin.decode(message); diff --git a/lib/plugins/Label_H1_StarPOS.ts b/lib/plugins/Label_H1_StarPOS.ts index 22283be..211e8b9 100644 --- a/lib/plugins/Label_H1_StarPOS.ts +++ b/lib/plugins/Label_H1_StarPOS.ts @@ -6,14 +6,14 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_H1_StarPOS extends DecoderPlugin { name = 'label-h1-star-pos'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['H1'], preambles: ['*POS'], }; } - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { let decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; decodeResult.formatted.description = 'Position Report'; @@ -33,10 +33,18 @@ export class Label_H1_StarPOS extends DecoderPlugin { ResultFormatter.month(decodeResult, Number(msg.substring(4, 6))); ResultFormatter.day(decodeResult, Number(msg.substring(6, 8))); - ResultFormatter.time_of_day(decodeResult, DateTimeUtils.convertHHMMSSToTod(msg.substring(8, 12))); - ResultFormatter.position(decodeResult, { // Deg Min, no sec - latitude: CoordinateUtils.getDirection(msg.substring(12,13)) * (Number(msg.substring(13, 15)) + Number(msg.substring(15, 17))/60), - longitude: CoordinateUtils.getDirection(msg.substring(17,18)) * (Number(msg.substring(18, 21)) + Number(msg.substring(21, 23))/60) + ResultFormatter.time_of_day( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(msg.substring(8, 12)), + ); + ResultFormatter.position(decodeResult, { + // Deg Min, no sec + latitude: + CoordinateUtils.getDirection(msg.substring(12, 13)) * + (Number(msg.substring(13, 15)) + Number(msg.substring(15, 17)) / 60), + longitude: + CoordinateUtils.getDirection(msg.substring(17, 18)) * + (Number(msg.substring(18, 21)) + Number(msg.substring(21, 23)) / 60), }); ResultFormatter.altitude(decodeResult, Number(msg.substring(23, 28))); ResultFormatter.unknown(decodeResult, msg.substring(28)); diff --git a/lib/plugins/Label_H1_WRN.test.ts b/lib/plugins/Label_H1_WRN.test.ts index 3bdb058..f6599f0 100644 --- a/lib/plugins/Label_H1_WRN.test.ts +++ b/lib/plugins/Label_H1_WRN.test.ts @@ -2,129 +2,134 @@ import { MessageDecoder } from '../MessageDecoder'; import { Label_H1_WRN } from './Label_H1_WRN'; describe('Label H1 WRN', () => { - let plugin: Label_H1_WRN; - const message = {label: 'H1', text: ''}; + const message = { label: 'H1', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); plugin = new Label_H1_WRN(decoder); }); -test('matches Label H1 Preamble WRN qualifiers', () => { - expect(plugin.decode).toBeDefined(); - expect(plugin.name).toBe('label-h1-wrn'); - expect(plugin.qualifiers).toBeDefined(); - expect(plugin.qualifiers()).toEqual({ - labels: ['H1'], - preambles: ['WRN', '#CFBWRN'], + test('matches Label H1 Preamble WRN qualifiers', () => { + expect(plugin.decode).toBeDefined(); + expect(plugin.name).toBe('label-h1-wrn'); + expect(plugin.qualifiers).toBeDefined(); + expect(plugin.qualifiers()).toEqual({ + labels: ['H1'], + preambles: ['WRN', '#CFBWRN'], + }); }); -}); -test('decodes Label H1 Preamble WRN FUEL', () => { - // https://app.airframes.io/messages/2435520576 - message.text = 'WRN/WN24030400580028000006FUEL L TK PUMP 1 2 LO PR '; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-h1-wrn'); - expect(decodeResult.formatted.description).toBe('Warning Message'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.raw.message_timestamp).toBe(1712105880); - expect(decodeResult.formatted.items.length).toBe(1); - expect(decodeResult.formatted.items[0].type).toBe('warning'); - expect(decodeResult.formatted.items[0].code).toBe('WRN'); - expect(decodeResult.formatted.items[0].label).toBe('Warning Message'); - expect(decodeResult.formatted.items[0].value).toBe('FUEL L TK PUMP 1 2 LO PR '); - expect(decodeResult.remaining.text).toBe('28000006'); -}); + test('decodes Label H1 Preamble WRN FUEL', () => { + // https://app.airframes.io/messages/2435520576 + message.text = 'WRN/WN24030400580028000006FUEL L TK PUMP 1 2 LO PR '; + const decodeResult = plugin.decode(message); -test('decodes Label H1 Preamble WRN NAV', () => { - // https://app.airframes.io/messages/2435409810 - message.text = 'WRN/PNRC12860AA07/WN24030316250034000006NAV ADS-B RPTG 1 FAULT '; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-h1-wrn'); - expect(decodeResult.formatted.description).toBe('Warning Message'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.raw.message_timestamp).toBe(1709483100); - expect(decodeResult.formatted.items.length).toBe(1); - expect(decodeResult.formatted.items[0].type).toBe('warning'); - expect(decodeResult.formatted.items[0].code).toBe('WRN'); - expect(decodeResult.formatted.items[0].label).toBe('Warning Message'); - expect(decodeResult.formatted.items[0].value).toBe('NAV ADS-B RPTG 1 FAULT '); - expect(decodeResult.remaining.text).toBe('PNRC12860AA07/34000006'); -}); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-h1-wrn'); + expect(decodeResult.formatted.description).toBe('Warning Message'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.raw.message_timestamp).toBe(1712105880); + expect(decodeResult.formatted.items.length).toBe(1); + expect(decodeResult.formatted.items[0].type).toBe('warning'); + expect(decodeResult.formatted.items[0].code).toBe('WRN'); + expect(decodeResult.formatted.items[0].label).toBe('Warning Message'); + expect(decodeResult.formatted.items[0].value).toBe( + 'FUEL L TK PUMP 1 2 LO PR ', + ); + expect(decodeResult.remaining.text).toBe('28000006'); + }); -test('decodes Label H1 Preamble WRN F/CTL', () => { - // https://app.airframes.io/messages/2435339713 - message.text = 'WRN/PNRC12860AA07/WN24030322050027000002F/CTL ELAC 1 FAULT '; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-h1-wrn'); - expect(decodeResult.formatted.description).toBe('Warning Message'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.raw.message_timestamp).toBe(1709503500); - expect(decodeResult.formatted.items.length).toBe(1); - expect(decodeResult.formatted.items[0].type).toBe('warning'); - expect(decodeResult.formatted.items[0].code).toBe('WRN'); - expect(decodeResult.formatted.items[0].label).toBe('Warning Message'); - expect(decodeResult.formatted.items[0].value).toBe('F/CTL ELAC 1 FAULT '); - expect(decodeResult.remaining.text).toBe('PNRC12860AA07/27000002'); -}); + test('decodes Label H1 Preamble WRN NAV', () => { + // https://app.airframes.io/messages/2435409810 + message.text = + 'WRN/PNRC12860AA07/WN24030316250034000006NAV ADS-B RPTG 1 FAULT '; + const decodeResult = plugin.decode(message); -test('decodes Label H1 Preamble WRN ENG', () => { - // https://app.airframes.io/messages/2434945276 - message.text = 'WRN/PNRC12860AA07/WN24030316580030210006ENG 1 A.ICE '; - const decodeResult = plugin.decode(message); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-h1-wrn'); + expect(decodeResult.formatted.description).toBe('Warning Message'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.raw.message_timestamp).toBe(1709483100); + expect(decodeResult.formatted.items.length).toBe(1); + expect(decodeResult.formatted.items[0].type).toBe('warning'); + expect(decodeResult.formatted.items[0].code).toBe('WRN'); + expect(decodeResult.formatted.items[0].label).toBe('Warning Message'); + expect(decodeResult.formatted.items[0].value).toBe( + 'NAV ADS-B RPTG 1 FAULT ', + ); + expect(decodeResult.remaining.text).toBe('PNRC12860AA07/34000006'); + }); - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-h1-wrn'); - expect(decodeResult.formatted.description).toBe('Warning Message'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.raw.message_timestamp).toBe(1709485080); - expect(decodeResult.formatted.items.length).toBe(1); - expect(decodeResult.formatted.items[0].type).toBe('warning'); - expect(decodeResult.formatted.items[0].code).toBe('WRN'); - expect(decodeResult.formatted.items[0].label).toBe('Warning Message'); - expect(decodeResult.formatted.items[0].value).toBe('ENG 1 A.ICE '); - expect(decodeResult.remaining.text).toBe('PNRC12860AA07/30210006'); -}); + test('decodes Label H1 Preamble WRN F/CTL', () => { + // https://app.airframes.io/messages/2435339713 + message.text = + 'WRN/PNRC12860AA07/WN24030322050027000002F/CTL ELAC 1 FAULT '; + const decodeResult = plugin.decode(message); -test('decodes Label H1 Preamble #CFBWRN', () => { - // https://app.airframes.io/messages/2434735527 - message.text = '#CFBWRN/WN24030312040034580006NAV GPS1 FAULT '; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-h1-wrn'); - expect(decodeResult.formatted.description).toBe('Warning Message'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.raw.message_timestamp).toBe(1709467440); - expect(decodeResult.formatted.items.length).toBe(1); - expect(decodeResult.formatted.items[0].type).toBe('warning'); - expect(decodeResult.formatted.items[0].code).toBe('WRN'); - expect(decodeResult.formatted.items[0].label).toBe('Warning Message'); - expect(decodeResult.formatted.items[0].value).toBe('NAV GPS1 FAULT '); - expect(decodeResult.remaining.text).toBe('34580006'); -}); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-h1-wrn'); + expect(decodeResult.formatted.description).toBe('Warning Message'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.raw.message_timestamp).toBe(1709503500); + expect(decodeResult.formatted.items.length).toBe(1); + expect(decodeResult.formatted.items[0].type).toBe('warning'); + expect(decodeResult.formatted.items[0].code).toBe('WRN'); + expect(decodeResult.formatted.items[0].label).toBe('Warning Message'); + expect(decodeResult.formatted.items[0].value).toBe('F/CTL ELAC 1 FAULT '); + expect(decodeResult.remaining.text).toBe('PNRC12860AA07/27000002'); + }); + + test('decodes Label H1 Preamble WRN ENG', () => { + // https://app.airframes.io/messages/2434945276 + message.text = 'WRN/PNRC12860AA07/WN24030316580030210006ENG 1 A.ICE '; + const decodeResult = plugin.decode(message); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-h1-wrn'); + expect(decodeResult.formatted.description).toBe('Warning Message'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.raw.message_timestamp).toBe(1709485080); + expect(decodeResult.formatted.items.length).toBe(1); + expect(decodeResult.formatted.items[0].type).toBe('warning'); + expect(decodeResult.formatted.items[0].code).toBe('WRN'); + expect(decodeResult.formatted.items[0].label).toBe('Warning Message'); + expect(decodeResult.formatted.items[0].value).toBe('ENG 1 A.ICE '); + expect(decodeResult.remaining.text).toBe('PNRC12860AA07/30210006'); + }); + + test('decodes Label H1 Preamble #CFBWRN', () => { + // https://app.airframes.io/messages/2434735527 + message.text = '#CFBWRN/WN24030312040034580006NAV GPS1 FAULT '; + const decodeResult = plugin.decode(message); -test('decodes Label H1 Preamble WRN invalid', () => { - message.text = 'WRN '; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(false); - expect(decodeResult.decoder.decodeLevel).toBe('none'); - expect(decodeResult.decoder.name).toBe('label-h1-wrn'); - expect(decodeResult.formatted.description).toBe('Warning Message'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.remaining.text).toBe(message.text); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-h1-wrn'); + expect(decodeResult.formatted.description).toBe('Warning Message'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.raw.message_timestamp).toBe(1709467440); + expect(decodeResult.formatted.items.length).toBe(1); + expect(decodeResult.formatted.items[0].type).toBe('warning'); + expect(decodeResult.formatted.items[0].code).toBe('WRN'); + expect(decodeResult.formatted.items[0].label).toBe('Warning Message'); + expect(decodeResult.formatted.items[0].value).toBe('NAV GPS1 FAULT '); + expect(decodeResult.remaining.text).toBe('34580006'); + }); + + test('decodes Label H1 Preamble WRN invalid', () => { + message.text = 'WRN '; + const decodeResult = plugin.decode(message); + + expect(decodeResult.decoded).toBe(false); + expect(decodeResult.decoder.decodeLevel).toBe('none'); + expect(decodeResult.decoder.name).toBe('label-h1-wrn'); + expect(decodeResult.formatted.description).toBe('Warning Message'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.remaining.text).toBe(message.text); + }); }); -}); \ No newline at end of file diff --git a/lib/plugins/Label_H1_WRN.ts b/lib/plugins/Label_H1_WRN.ts index 34f0215..98d713d 100644 --- a/lib/plugins/Label_H1_WRN.ts +++ b/lib/plugins/Label_H1_WRN.ts @@ -6,14 +6,14 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_H1_WRN extends DecoderPlugin { name = 'label-h1-wrn'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { - labels: ["H1"], + labels: ['H1'], preambles: ['WRN', '#CFBWRN'], }; } - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { let decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; decodeResult.formatted.description = 'Warning Message'; @@ -21,20 +21,26 @@ export class Label_H1_WRN extends DecoderPlugin { const parts = message.text.split('/WN'); - if(parts.length > 1){ + if (parts.length > 1) { //decode header // can go away, here because i want to decode it const fields = parts[0].split('/'); // 0 is the msg type ResultFormatter.unknownArr(decodeResult, fields.slice(1), '/'); - const data = parts[1].substring(0,20); + const data = parts[1].substring(0, 20); const msg = parts[1].substring(20); - const datetime = data.substring(0,12); // YYMMDDHHMMSS (SS might be next 2 chars) - const date = datetime.substring(4,6) + datetime.substring(2,4)+ datetime.substring(0,2); + const datetime = data.substring(0, 12); // YYMMDDHHMMSS (SS might be next 2 chars) + const date = + datetime.substring(4, 6) + + datetime.substring(2, 4) + + datetime.substring(0, 2); ResultFormatter.unknown(decodeResult, data.substring(12), '/'); - decodeResult.raw.message_timestamp = DateTimeUtils.convertDateTimeToEpoch(datetime.substring(6), date); + decodeResult.raw.message_timestamp = DateTimeUtils.convertDateTimeToEpoch( + datetime.substring(6), + date, + ); // TODO: decode further decodeResult.raw.warning_message = msg; decodeResult.formatted.items.push({ @@ -45,7 +51,7 @@ export class Label_H1_WRN extends DecoderPlugin { }); decodeResult.decoded = true; decodeResult.decoder.decodeLevel = 'partial'; - } else { + } else { // Unknown if (options.debug) { console.log(`Decoder: Unknown H1 message: ${message.text}`); diff --git a/lib/plugins/Label_H2_02E.test.ts b/lib/plugins/Label_H2_02E.test.ts index 317e410..675a426 100644 --- a/lib/plugins/Label_H2_02E.test.ts +++ b/lib/plugins/Label_H2_02E.test.ts @@ -1,27 +1,27 @@ -import { MessageDecoder } from "../MessageDecoder"; -import { Label_H2_02E } from "./Label_H2_02E"; +import { MessageDecoder } from '../MessageDecoder'; +import { Label_H2_02E } from './Label_H2_02E'; -describe("Label_H2 02E", () => { +describe('Label_H2 02E', () => { let plugin: Label_H2_02E; - const message = { label: "H2", text: "" }; + const message = { label: 'H2', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); plugin = new Label_H2_02E(decoder); }); - test("matches qualifiers", () => { + test('matches qualifiers', () => { expect(plugin.decode).toBeDefined(); - expect(plugin.name).toBe("label-h2-02e"); + expect(plugin.name).toBe('label-h2-02e'); expect(plugin.qualifiers).toBeDefined(); expect(plugin.qualifiers()).toEqual({ - labels: ["H2"], - preambles: ["02E"], + labels: ['H2'], + preambles: ['02E'], }); }); - test("decodes discord example 1", () => { + test('decodes discord example 1', () => { message.text = - "02E20HEGNLKPRN40359E02208116253601M627259020G QN41179E02134316323599M617247037G QN41591E02100516393603M610266040G QN42393E02026716463602M600276033G QN43197E01954316533598M592299037G QN44023E01929517003596M587313033G Q"; + '02E20HEGNLKPRN40359E02208116253601M627259020G QN41179E02134316323599M617247037G QN41591E02100516393603M610266040G QN42393E02026716463602M600276033G QN43197E01954316533598M592299037G QN44023E01929517003596M587313033G Q'; const decodeResult = plugin.decode(message); /* Route: HEGN-LKPR @@ -33,46 +33,46 @@ describe("Label_H2 02E", () => { 6 44°02.3'N, 019°29.5'E 17:00 FL359 35,900 ft -58.7°C 313°/33kts */ expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe("full"); - expect(decodeResult.formatted.description).toBe("Weather Report"); + expect(decodeResult.decoder.decodeLevel).toBe('full'); + expect(decodeResult.formatted.description).toBe('Weather Report'); expect(decodeResult.message).toBe(message); const weather = decodeResult.raw.wind_data; expect(weather.length).toBe(6); - expect(decodeResult.formatted.items[0].label).toBe("Day of Month"); - expect(typeof decodeResult.formatted.items[0].value).toBe("string"); - expect(decodeResult.formatted.items[1].label).toBe("Origin"); - expect(decodeResult.formatted.items[1].value).toBe("HEGN"); - expect(decodeResult.formatted.items[2].label).toBe("Destination"); - expect(decodeResult.formatted.items[2].value).toBe("LKPR"); - expect(decodeResult.formatted.items[3].label).toBe("Wind Data"); + expect(decodeResult.formatted.items[0].label).toBe('Day of Month'); + expect(typeof decodeResult.formatted.items[0].value).toBe('string'); + expect(decodeResult.formatted.items[1].label).toBe('Origin'); + expect(decodeResult.formatted.items[1].value).toBe('HEGN'); + expect(decodeResult.formatted.items[2].label).toBe('Destination'); + expect(decodeResult.formatted.items[2].value).toBe('LKPR'); + expect(decodeResult.formatted.items[3].label).toBe('Wind Data'); expect(decodeResult.formatted.items[3].value).toBe( - "N40359E022081(40.598 N, 22.135 E)@16:25:00 at FL360: 259° at 20kt, -62.7°C at FL360" + 'N40359E022081(40.598 N, 22.135 E)@16:25:00 at FL360: 259° at 20kt, -62.7°C at FL360', ); - expect(decodeResult.formatted.items[4].label).toBe("Wind Data"); + expect(decodeResult.formatted.items[4].label).toBe('Wind Data'); expect(decodeResult.formatted.items[4].value).toBe( - "N41179E021343(41.298 N, 21.572 E)@16:32:00 at FL359: 247° at 37kt, -61.7°C at FL359" + 'N41179E021343(41.298 N, 21.572 E)@16:32:00 at FL359: 247° at 37kt, -61.7°C at FL359', ); - expect(decodeResult.formatted.items[5].label).toBe("Wind Data"); + expect(decodeResult.formatted.items[5].label).toBe('Wind Data'); expect(decodeResult.formatted.items[5].value).toBe( - "N41591E021005(41.985 N, 21.008 E)@16:39:00 at FL360: 266° at 40kt, -61°C at FL360" + 'N41591E021005(41.985 N, 21.008 E)@16:39:00 at FL360: 266° at 40kt, -61°C at FL360', ); - expect(decodeResult.formatted.items[6].label).toBe("Wind Data"); + expect(decodeResult.formatted.items[6].label).toBe('Wind Data'); expect(decodeResult.formatted.items[6].value).toBe( - "N42393E020267(42.655 N, 20.445 E)@16:46:00 at FL360: 276° at 33kt, -60°C at FL360" + 'N42393E020267(42.655 N, 20.445 E)@16:46:00 at FL360: 276° at 33kt, -60°C at FL360', ); - expect(decodeResult.formatted.items[7].label).toBe("Wind Data"); + expect(decodeResult.formatted.items[7].label).toBe('Wind Data'); expect(decodeResult.formatted.items[7].value).toBe( - "N43197E019543(43.328 N, 19.905 E)@16:53:00 at FL359: 299° at 37kt, -59.2°C at FL359" + 'N43197E019543(43.328 N, 19.905 E)@16:53:00 at FL359: 299° at 37kt, -59.2°C at FL359', ); - expect(decodeResult.formatted.items[8].label).toBe("Wind Data"); + expect(decodeResult.formatted.items[8].label).toBe('Wind Data'); expect(decodeResult.formatted.items[8].value).toBe( - "N44023E019295(44.038 N, 19.492 E)@17:00:00 at FL359: 313° at 33kt, -58.7°C at FL359" + 'N44023E019295(44.038 N, 19.492 E)@17:00:00 at FL359: 313° at 33kt, -58.7°C at FL359', ); }); - test("decodes discord example 2", () => { + test('decodes discord example 2', () => { message.text = - "02E20EGKKLBSFN45081E01757116493501M577327021G QN44401E01903016563499M575352028G QN44115E02008017033468M550319029G QN43420E02112317103296M525299036G QN43125E02214517172023M277271022G Q"; + '02E20EGKKLBSFN45081E01757116493501M577327021G QN44401E01903016563499M575352028G QN44115E02008017033468M550319029G QN43420E02112317103296M525299036G QN43125E02214517172023M277271022G Q'; const decodeResult = plugin.decode(message); /* @@ -84,83 +84,83 @@ describe("Label_H2 02E", () => { 5 FL202 ~20,200 ft -27.7°C 271°/22kts */ expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe("full"); - expect(decodeResult.formatted.description).toBe("Weather Report"); + expect(decodeResult.decoder.decodeLevel).toBe('full'); + expect(decodeResult.formatted.description).toBe('Weather Report'); expect(decodeResult.message).toBe(message); const weather = decodeResult.raw.wind_data; expect(weather.length).toBe(5); - expect(decodeResult.formatted.items[0].label).toBe("Day of Month"); - expect(typeof decodeResult.formatted.items[0].value).toBe("string"); - expect(decodeResult.formatted.items[1].label).toBe("Origin"); - expect(decodeResult.formatted.items[1].value).toBe("EGKK"); - expect(decodeResult.formatted.items[2].label).toBe("Destination"); - expect(decodeResult.formatted.items[2].value).toBe("LBSF"); - expect(decodeResult.formatted.items[3].label).toBe("Wind Data"); + expect(decodeResult.formatted.items[0].label).toBe('Day of Month'); + expect(typeof decodeResult.formatted.items[0].value).toBe('string'); + expect(decodeResult.formatted.items[1].label).toBe('Origin'); + expect(decodeResult.formatted.items[1].value).toBe('EGKK'); + expect(decodeResult.formatted.items[2].label).toBe('Destination'); + expect(decodeResult.formatted.items[2].value).toBe('LBSF'); + expect(decodeResult.formatted.items[3].label).toBe('Wind Data'); expect(decodeResult.formatted.items[3].value).toBe( - "N45081E017571(45.135 N, 17.952 E)@16:49:00 at FL350: 327° at 21kt, -57.7°C at FL350" + 'N45081E017571(45.135 N, 17.952 E)@16:49:00 at FL350: 327° at 21kt, -57.7°C at FL350', ); - expect(decodeResult.formatted.items[4].label).toBe("Wind Data"); + expect(decodeResult.formatted.items[4].label).toBe('Wind Data'); expect(decodeResult.formatted.items[4].value).toBe( - "N44401E019030(44.668 N, 19.050 E)@16:56:00 at FL349: 352° at 28kt, -57.5°C at FL349" + 'N44401E019030(44.668 N, 19.050 E)@16:56:00 at FL349: 352° at 28kt, -57.5°C at FL349', ); - expect(decodeResult.formatted.items[5].label).toBe("Wind Data"); + expect(decodeResult.formatted.items[5].label).toBe('Wind Data'); expect(decodeResult.formatted.items[5].value).toBe( - "N44115E020080(44.192 N, 20.133 E)@17:03:00 at FL346: 319° at 29kt, -55°C at FL346" + 'N44115E020080(44.192 N, 20.133 E)@17:03:00 at FL346: 319° at 29kt, -55°C at FL346', ); - expect(decodeResult.formatted.items[6].label).toBe("Wind Data"); + expect(decodeResult.formatted.items[6].label).toBe('Wind Data'); expect(decodeResult.formatted.items[6].value).toBe( - "N43420E021123(43.700 N, 21.205 E)@17:10:00 at FL329: 299° at 36kt, -52.5°C at FL329" + 'N43420E021123(43.700 N, 21.205 E)@17:10:00 at FL329: 299° at 36kt, -52.5°C at FL329', ); - expect(decodeResult.formatted.items[7].label).toBe("Wind Data"); + expect(decodeResult.formatted.items[7].label).toBe('Wind Data'); expect(decodeResult.formatted.items[7].value).toBe( - "N43125E022145(43.208 N, 22.242 E)@17:17:00 at FL202: 271° at 22kt, -27.7°C at FL202" + 'N43125E022145(43.208 N, 22.242 E)@17:17:00 at FL202: 271° at 22kt, -27.7°C at FL202', ); }); - test("decodes website example", () => { + test('decodes website example', () => { // https://app.airframes.io/messages/6025352132 message.text = - "02E20EIDWKORDN44087W08505523383800M470251091G QN43210W08520623452813M442251113G QN42461W08539523522189M295256121G QN42380W08623723591780M227266100G Q"; + '02E20EIDWKORDN44087W08505523383800M470251091G QN43210W08520623452813M442251113G QN42461W08539523522189M295256121G QN42380W08623723591780M227266100G Q'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe("full"); - expect(decodeResult.formatted.description).toBe("Weather Report"); + expect(decodeResult.decoder.decodeLevel).toBe('full'); + expect(decodeResult.formatted.description).toBe('Weather Report'); expect(decodeResult.message).toBe(message); const weather = decodeResult.raw.wind_data; expect(weather.length).toBe(4); - expect(decodeResult.formatted.items[0].label).toBe("Day of Month"); - expect(typeof decodeResult.formatted.items[0].value).toBe("string"); - expect(decodeResult.formatted.items[1].label).toBe("Origin"); - expect(decodeResult.formatted.items[1].value).toBe("EIDW"); - expect(decodeResult.formatted.items[2].label).toBe("Destination"); - expect(decodeResult.formatted.items[2].value).toBe("KORD"); - expect(decodeResult.formatted.items[3].label).toBe("Wind Data"); + expect(decodeResult.formatted.items[0].label).toBe('Day of Month'); + expect(typeof decodeResult.formatted.items[0].value).toBe('string'); + expect(decodeResult.formatted.items[1].label).toBe('Origin'); + expect(decodeResult.formatted.items[1].value).toBe('EIDW'); + expect(decodeResult.formatted.items[2].label).toBe('Destination'); + expect(decodeResult.formatted.items[2].value).toBe('KORD'); + expect(decodeResult.formatted.items[3].label).toBe('Wind Data'); expect(decodeResult.formatted.items[3].value).toBe( - "N44087W085055(44.145 N, 85.092 W)@23:38:00 at FL380: 251° at 91kt, -47°C at FL380" + 'N44087W085055(44.145 N, 85.092 W)@23:38:00 at FL380: 251° at 91kt, -47°C at FL380', ); - expect(decodeResult.formatted.items[4].label).toBe("Wind Data"); + expect(decodeResult.formatted.items[4].label).toBe('Wind Data'); expect(decodeResult.formatted.items[4].value).toBe( - "N43210W085206(43.350 N, 85.343 W)@23:45:00 at FL281: 251° at 113kt, -44.2°C at FL281" + 'N43210W085206(43.350 N, 85.343 W)@23:45:00 at FL281: 251° at 113kt, -44.2°C at FL281', ); - expect(decodeResult.formatted.items[5].label).toBe("Wind Data"); + expect(decodeResult.formatted.items[5].label).toBe('Wind Data'); expect(decodeResult.formatted.items[5].value).toBe( - "N42461W085395(42.768 N, 85.658 W)@23:52:00 at FL218: 256° at 121kt, -29.5°C at FL218" + 'N42461W085395(42.768 N, 85.658 W)@23:52:00 at FL218: 256° at 121kt, -29.5°C at FL218', ); - expect(decodeResult.formatted.items[6].label).toBe("Wind Data"); + expect(decodeResult.formatted.items[6].label).toBe('Wind Data'); expect(decodeResult.formatted.items[6].value).toBe( - "N42380W086237(42.633 N, 86.395 W)@23:59:00 at FL178: 266° at 100kt, -22.7°C at FL178" + 'N42380W086237(42.633 N, 86.395 W)@23:59:00 at FL178: 266° at 100kt, -22.7°C at FL178', ); }); - test("decodes invalid message", () => { - message.text = "02E20INVALID MESSAGE TEXT"; + test('decodes invalid message', () => { + message.text = '02E20INVALID MESSAGE TEXT'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(false); - expect(decodeResult.decoder.decodeLevel).toBe("none"); - expect(decodeResult.formatted.description).toBe("Weather Report"); + expect(decodeResult.decoder.decodeLevel).toBe('none'); + expect(decodeResult.formatted.description).toBe('Weather Report'); expect(decodeResult.message).toBe(message); - expect(decodeResult.remaining.text).toBe("02E20INVALID MESSAGE TEXT"); + expect(decodeResult.remaining.text).toBe('02E20INVALID MESSAGE TEXT'); }); }); diff --git a/lib/plugins/Label_H2_02E.ts b/lib/plugins/Label_H2_02E.ts index 99d84d0..8de531b 100644 --- a/lib/plugins/Label_H2_02E.ts +++ b/lib/plugins/Label_H2_02E.ts @@ -1,39 +1,38 @@ -import { DateTimeUtils } from "../DateTimeUtils"; -import { DecoderPlugin } from "../DecoderPlugin"; -import { DecodeResult, Message, Options } from "../DecoderPluginInterface"; -import { Wind } from "../types/wind"; -import { CoordinateUtils } from "../utils/coordinate_utils"; -import { ResultFormatter } from "../utils/result_formatter"; +import { DateTimeUtils } from '../DateTimeUtils'; +import { DecoderPlugin } from '../DecoderPlugin'; +import { DecodeResult, Message, Options } from '../DecoderPluginInterface'; +import { Wind } from '../types/wind'; +import { CoordinateUtils } from '../utils/coordinate_utils'; +import { ResultFormatter } from '../utils/result_formatter'; export class Label_H2_02E extends DecoderPlugin { - name = "label-h2-02e"; + name = 'label-h2-02e'; qualifiers() { - // eslint-disable-line class-methods-use-this return { - labels: ["H2"], - preambles: ["02E"], + labels: ['H2'], + preambles: ['02E'], }; } decode(message: Message, options: Options = {}): DecodeResult { let decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; - decodeResult.formatted.description = "Weather Report"; + decodeResult.formatted.description = 'Weather Report'; decodeResult.message = message; - const parts = message.text.split(" "); + const parts = message.text.split(' '); - if (parts[parts.length - 1] !== "Q") { + if (parts[parts.length - 1] !== 'Q') { // not a valid message decodeResult.remaining.text = message.text; decodeResult.decoded = false; - decodeResult.decoder.decodeLevel = "none"; + decodeResult.decoder.decodeLevel = 'none'; return decodeResult; } const windData: Wind[] = []; - decodeResult.remaining.text = ""; + decodeResult.remaining.text = ''; const header = parts[0]; if (header.length === 45) { @@ -46,15 +45,15 @@ export class Label_H2_02E extends DecoderPlugin { windData.push(firstWind); } else { decodeResult.remaining.text += - (decodeResult.remaining.text ? " " : "") + header.substring(13); + (decodeResult.remaining.text ? ' ' : '') + header.substring(13); } } for (let i = 1; i < parts.length - 1; i++) { const part = parts[i]; - if (part[0] !== "Q") { + if (part[0] !== 'Q') { decodeResult.remaining.text += - (decodeResult.remaining.text ? " " : "") + part; + (decodeResult.remaining.text ? ' ' : '') + part; continue; } const wind = this.parseWeatherReport(part.substring(1)); @@ -62,14 +61,14 @@ export class Label_H2_02E extends DecoderPlugin { windData.push(wind); } else { decodeResult.remaining.text += - (decodeResult.remaining.text ? " " : "") + part; + (decodeResult.remaining.text ? ' ' : '') + part; } } ResultFormatter.windData(decodeResult, windData); decodeResult.decoded = true; decodeResult.decoder.decodeLevel = - decodeResult.remaining.text.length === 0 ? "full" : "partial"; + decodeResult.remaining.text.length === 0 ? 'full' : 'partial'; return decodeResult; } @@ -83,13 +82,13 @@ export class Label_H2_02E extends DecoderPlugin { const tod = DateTimeUtils.convertHHMMSSToTod(text.substring(13, 17)); const flightLevel = parseInt(text.substring(17, 20), 10); // const altitude = parseInt(text.substring(17,21), 10) * 10; // use FL instead - const tempSign = text[21] === "M" ? -1 : 1; + const tempSign = text[21] === 'M' ? -1 : 1; const tempDegreesRaw = parseInt(text.substring(22, 25), 10); const tempDegrees = tempSign * (tempDegreesRaw / 10); const windDirection = parseInt(text.substring(25, 28), 10); const windSpeed = parseInt(text.substring(28, 31), 10); // G? - if (text[31] !== "G") { + if (text[31] !== 'G') { return null; } return { @@ -98,7 +97,7 @@ export class Label_H2_02E extends DecoderPlugin { latitude: pos.latitude, longitude: pos.longitude, time: tod, - timeFormat: "tod", + timeFormat: 'tod', }, flightLevel: flightLevel, windDirection: windDirection, diff --git a/lib/plugins/Label_HX.test.ts b/lib/plugins/Label_HX.test.ts index 0c625e4..716e4f6 100644 --- a/lib/plugins/Label_HX.test.ts +++ b/lib/plugins/Label_HX.test.ts @@ -2,66 +2,71 @@ import { MessageDecoder } from '../MessageDecoder'; import { Label_HX } from './Label_HX'; describe('Label_HX', () => { - let plugin: Label_HX; - const message = {label: 'HX', text: ''}; + const message = { label: 'HX', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); plugin = new Label_HX(decoder); }); -test('matches Label HX qualifiers', () => { - expect(plugin.decode).toBeDefined(); - expect(plugin.name).toBe('label-hx'); -}); + test('matches Label HX qualifiers', () => { + expect(plugin.decode).toBeDefined(); + expect(plugin.name).toBe('label-hx'); + }); -test('decodes Label HX variant 1', () => { - // https://globe.adsbexchange.com/?icao=A41722&showTrace=2024-09-24×tamp=1727202494 - message.text = 'RA FMT LOCATION N4009.6 W07540.8'; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('full'); - expect(decodeResult.decoder.name).toBe('label-hx'); - expect(decodeResult.formatted.description).toBe('Undelivered Uplink Report'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.raw.position.latitude).toBe(40.16); - expect(decodeResult.raw.position.longitude).toBe(-75.68); - expect(decodeResult.formatted.items.length).toBe(1); - expect(decodeResult.formatted.items[0].type).toBe('aircraft_position'); - expect(decodeResult.formatted.items[0].code).toBe('POS'); - expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); - expect(decodeResult.formatted.items[0].value).toBe('40.160 N, 75.680 W'); -}); + test('decodes Label HX variant 1', () => { + // https://globe.adsbexchange.com/?icao=A41722&showTrace=2024-09-24×tamp=1727202494 + message.text = 'RA FMT LOCATION N4009.6 W07540.8'; + const decodeResult = plugin.decode(message); -test('decodes Label HX variant 2', () => { - // https://globe.adsbexchange.com/?icao=A92EA0&showTrace=2024-09-22×tamp=1727038330 - message.text = 'RA FMT 43 GSP B02'; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-hx'); - expect(decodeResult.formatted.description).toBe('Undelivered Uplink Report'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.raw.departure_icao).toBe('GSP'); - expect(decodeResult.remaining.text).toBe('B02'); - expect(decodeResult.formatted.items.length).toBe(1); - expect(decodeResult.formatted.items[0].type).toBe('icao'); - expect(decodeResult.formatted.items[0].code).toBe('ORG'); - expect(decodeResult.formatted.items[0].label).toBe('Origin'); - expect(decodeResult.formatted.items[0].value).toBe('GSP'); -}); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('full'); + expect(decodeResult.decoder.name).toBe('label-hx'); + expect(decodeResult.formatted.description).toBe( + 'Undelivered Uplink Report', + ); + expect(decodeResult.message).toBe(message); + expect(decodeResult.raw.position.latitude).toBe(40.16); + expect(decodeResult.raw.position.longitude).toBe(-75.68); + expect(decodeResult.formatted.items.length).toBe(1); + expect(decodeResult.formatted.items[0].type).toBe('aircraft_position'); + expect(decodeResult.formatted.items[0].code).toBe('POS'); + expect(decodeResult.formatted.items[0].label).toBe('Aircraft Position'); + expect(decodeResult.formatted.items[0].value).toBe('40.160 N, 75.680 W'); + }); -test('decodes Label HX ', () => { - message.text = 'HX Bogus message'; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(false); - expect(decodeResult.decoder.decodeLevel).toBe('none'); - expect(decodeResult.decoder.name).toBe('label-hx'); - expect(decodeResult.formatted.description).toBe('Undelivered Uplink Report'); - expect(decodeResult.formatted.items.length).toBe(0); + test('decodes Label HX variant 2', () => { + // https://globe.adsbexchange.com/?icao=A92EA0&showTrace=2024-09-22×tamp=1727038330 + message.text = 'RA FMT 43 GSP B02'; + const decodeResult = plugin.decode(message); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-hx'); + expect(decodeResult.formatted.description).toBe( + 'Undelivered Uplink Report', + ); + expect(decodeResult.message).toBe(message); + expect(decodeResult.raw.departure_icao).toBe('GSP'); + expect(decodeResult.remaining.text).toBe('B02'); + expect(decodeResult.formatted.items.length).toBe(1); + expect(decodeResult.formatted.items[0].type).toBe('icao'); + expect(decodeResult.formatted.items[0].code).toBe('ORG'); + expect(decodeResult.formatted.items[0].label).toBe('Origin'); + expect(decodeResult.formatted.items[0].value).toBe('GSP'); + }); + + test('decodes Label HX ', () => { + message.text = 'HX Bogus message'; + const decodeResult = plugin.decode(message); + + expect(decodeResult.decoded).toBe(false); + expect(decodeResult.decoder.decodeLevel).toBe('none'); + expect(decodeResult.decoder.name).toBe('label-hx'); + expect(decodeResult.formatted.description).toBe( + 'Undelivered Uplink Report', + ); + expect(decodeResult.formatted.items.length).toBe(0); + }); }); -}); \ No newline at end of file diff --git a/lib/plugins/Label_HX.ts b/lib/plugins/Label_HX.ts index 0e695e0..040a384 100644 --- a/lib/plugins/Label_HX.ts +++ b/lib/plugins/Label_HX.ts @@ -1,19 +1,18 @@ import { DecoderPlugin } from '../DecoderPlugin'; import { DecodeResult, Message, Options } from '../DecoderPluginInterface'; -import { CoordinateUtils } from '../utils/coordinate_utils'; import { ResultFormatter } from '../utils/result_formatter'; export class Label_HX extends DecoderPlugin { name = 'label-hx'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['HX'], preambles: ['RA FMT LOCATION', 'RA FMT 43'], }; } - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { const decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; decodeResult.message = message; @@ -22,33 +21,32 @@ export class Label_HX extends DecoderPlugin { const parts = message.text.split(' '); decodeResult.decoded = true; - if (parts[2] === "LOCATION") { - let latdir = parts[3].substring(0, 1); - let latdeg = Number(parts[3].substring(1, 3)); - let latmin = Number(parts[3].substring(3, 7)); - let londir = parts[4].substring(0, 1); - let londeg = Number(parts[4].substring(1, 4)); - let lonmin = Number(parts[4].substring(4, 8)); - let pos = { - latitude: (latdeg + latmin/60) * (latdir === 'N' ? 1 : -1), - longitude: (londeg + lonmin/60) * (londir === 'E' ? 1 : -1), - }; - ResultFormatter.unknownArr(decodeResult, parts.slice(5), ' '); - ResultFormatter.position(decodeResult, pos); - } else if (parts[2] === "43") { - ResultFormatter.departureAirport(decodeResult, parts[3]); - ResultFormatter.unknownArr(decodeResult, parts.slice(4), ' '); + if (parts[2] === 'LOCATION') { + let latdir = parts[3].substring(0, 1); + let latdeg = Number(parts[3].substring(1, 3)); + let latmin = Number(parts[3].substring(3, 7)); + let londir = parts[4].substring(0, 1); + let londeg = Number(parts[4].substring(1, 4)); + let lonmin = Number(parts[4].substring(4, 8)); + let pos = { + latitude: (latdeg + latmin / 60) * (latdir === 'N' ? 1 : -1), + longitude: (londeg + lonmin / 60) * (londir === 'E' ? 1 : -1), + }; + ResultFormatter.unknownArr(decodeResult, parts.slice(5), ' '); + ResultFormatter.position(decodeResult, pos); + } else if (parts[2] === '43') { + ResultFormatter.departureAirport(decodeResult, parts[3]); + ResultFormatter.unknownArr(decodeResult, parts.slice(4), ' '); } else { - decodeResult.decoded = false; + decodeResult.decoded = false; } if (decodeResult.decoded) { - if(!decodeResult.remaining.text) - decodeResult.decoder.decodeLevel = 'full'; - else - decodeResult.decoder.decodeLevel = 'partial'; + if (!decodeResult.remaining.text) + decodeResult.decoder.decodeLevel = 'full'; + else decodeResult.decoder.decodeLevel = 'partial'; } else { - decodeResult.decoder.decodeLevel = 'none'; + decodeResult.decoder.decodeLevel = 'none'; } return decodeResult; diff --git a/lib/plugins/Label_MA.test.ts b/lib/plugins/Label_MA.test.ts index 6fd9d8e..089b639 100644 --- a/lib/plugins/Label_MA.test.ts +++ b/lib/plugins/Label_MA.test.ts @@ -2,7 +2,6 @@ import { MessageDecoder } from '../MessageDecoder'; import { Label_MA } from './Label_MA'; describe('Label_MA', () => { - let plugin: Label_MA; const message = { label: 'MA', text: '' }; @@ -21,7 +20,8 @@ describe('Label_MA', () => { }); test('decodes MIAM message', () => { - message.text = 'T02!<<,:/k.E`;FOV@!\'s.16q6R+p(RK,|D2ujNJhRah?_qrNftWiI-V,@*RQs,tn,FYN$/V1!gNIc6CO;$D,1:.4?dF952;>XP$"B"Ok-Fr\'0^k?rP]3&UGoPX;\\XP$\"B\"Ok-Fr'0^k?rP]3&UGoPX;\\ { expect(decodeResult.raw.label).toBe('H1'); expect(decodeResult.raw.sublabel).toBe('DF'); expect(decodeResult.raw.tail).toBe('F-HREV'); - expect(decodeResult.raw.text).toBe('A350,000354,1,1,TB000000/REP035,01,02;H01,035,01,02,4000,00066,.F-HREV,3,0,08,12,19,06,58,42,071/H02,KSFO LFPO,FBU711 ,S0285,S0385,S0142S0185/H03, /A10,06,58,40,+26535,+01198,045,+0493,321723,XXXXXX,--------,0423,+0573/A11,06,58,32,+26356,+01221,041,+0502,324326,XXXXXX,--------,0424,+0614/:'); + expect(decodeResult.raw.text).toBe( + 'A350,000354,1,1,TB000000/REP035,01,02;H01,035,01,02,4000,00066,.F-HREV,3,0,08,12,19,06,58,42,071/H02,KSFO LFPO,FBU711 ,S0285,S0385,S0142S0185/H03, /A10,06,58,40,+26535,+01198,045,+0493,321723,XXXXXX,--------,0423,+0573/A11,06,58,32,+26356,+01221,041,+0502,324326,XXXXXX,--------,0424,+0614/:', + ); expect(decodeResult.formatted.items.length).toBe(4); }); test('decodes partial message', () => { - message.text = 'T22!<<2>\/k&_k6:\"c1!\'s.16q0U2odKk@|D,eVtdm+WF(#Ll8Dja==EY8V4KISInKk%((%5&@p?Z\\NSZ9tj[Xr0rXqe`+A(J,f[fotFmHmS'; + message.text = + 'T22!<<2>\/k&_k6:"c1!\'s.16q0U2odKk@|D,eVtdm+WF(#Ll8Dja==EY8V4KISInKk%((%5&@p?Z\\NSZ9tj[Xr0rXqe`+A(J,f[fotFmHmS'; const decodeResult = plugin.decode(message); expect(decodeResult.decoded).toBe(true); @@ -49,7 +52,8 @@ describe('Label_MA', () => { }); test('decodes twice', () => { - message.text = "T20!<<,B/k&,Z:/=Hs!'iLt0/1h8|*KJNa__;Kes\"hV'&4]J&&&;\"X/O6%0d7TMg5=d7'gcZ:j\"2EiA7k`-`$]f/r>q-b^jGP>WX4Dk3IrQ:U4%?]H$4HCd0Z(^Yq+lXXR!=niT!#;:5UAQ`JU@Bfn/*)TSu?G=LuOlLaFSmkGi]eE[!o&j@iX^g0r)T9!Ye)k6aOmSf`8G)8,hS5Fq_trW*!!"; + message.text = + 'T20!<<,B/k&,Z:/=Hs!\'iLt0/1h8|*KJNa__;Kes"hV\'&4]J&&&;"X/O6%0d7TMg5=d7\'gcZ:j"2EiA7k`-`$]f/r>q-b^jGP>WX4Dk3IrQ:U4%?]H$4HCd0Z(^Yq+lXXR!=niT!#;:5UAQ`JU@Bfn/*)TSu?G=LuOlLaFSmkGi]eE[!o&j@iX^g0r)T9!Ye)k6aOmSf`8G)8,hS5Fq_trW*!!'; // decodes to label 80 // 3D01 RMPSRV 2501/02 LEMD/MMUN .EC-NOI\n/LAV Y/CAB Y/MEDA N/SEC N/WAS N/WAT N/FUEL N/WCHRR 01/WCHRS --/WCHRC --/UMNR --/MAAS N\nBUENAS NOCHES, ETA 0055Z Y NOS HAN PEDIDO UNA WCHR QUE AL PARECER NO TENIAMOS CONSTANCIA DE ELLA, POR SI PODEIS PEDIRLO A OPS CUN. MUCHAS GRACIAS const decodeResult = plugin.decode(message); @@ -67,4 +71,4 @@ describe('Label_MA', () => { expect(decodeResult.raw.day).toBe(2); expect(decodeResult.formatted.items.length).toBe(7); // Tail twice }); -}); \ No newline at end of file +}); diff --git a/lib/plugins/Label_MA.ts b/lib/plugins/Label_MA.ts index 06595d0..2d35efb 100644 --- a/lib/plugins/Label_MA.ts +++ b/lib/plugins/Label_MA.ts @@ -5,51 +5,67 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_MA extends DecoderPlugin { name = 'label-ma'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['MA'], }; } - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { let decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; decodeResult.message = message; const miamResult = MIAMCoreUtils.parse(message.text); - if (miamResult.decoded && miamResult.message.data && miamResult.message.data.acars) { - + if ( + miamResult.decoded && + miamResult.message.data && + miamResult.message.data.acars + ) { decodeResult.decoded = true; ResultFormatter.label(decodeResult, miamResult.message.data.acars.label); - if(miamResult.message.data.acars.sublabel) { - ResultFormatter.sublabel(decodeResult, miamResult.message.data.acars.sublabel); + if (miamResult.message.data.acars.sublabel) { + ResultFormatter.sublabel( + decodeResult, + miamResult.message.data.acars.sublabel, + ); } - if(miamResult.message.data.acars.tail) { - ResultFormatter.tail(decodeResult, miamResult.message.data.acars.tail.replace('.', '')); + if (miamResult.message.data.acars.tail) { + ResultFormatter.tail( + decodeResult, + miamResult.message.data.acars.tail.replace('.', ''), + ); } const messageText = miamResult.message.data.acars.text; // Only transplant message text if the MIAM core decoded message passed CRC and is complete - if(miamResult.message.data.crcOk && miamResult.message.data.complete && messageText) { + if ( + miamResult.message.data.crcOk && + miamResult.message.data.complete && + messageText + ) { decodeResult.decoder.decodeLevel = 'full'; - const decoded = this.decoder.decode({ - label: miamResult.message.data.acars.label, - sublabel: miamResult.message.data.acars.sublabel, - text: messageText, - }, options); + const decoded = this.decoder.decode( + { + label: miamResult.message.data.acars.label, + sublabel: miamResult.message.data.acars.sublabel, + text: messageText, + }, + options, + ); - if(decoded.decoded) { + if (decoded.decoded) { // decodeResult.decoder.decodeLevel = decoded.decoder.decodeLevel; - decodeResult.raw = {...decodeResult.raw, ...decoded.raw }; + decodeResult.raw = { ...decodeResult.raw, ...decoded.raw }; decodeResult.formatted.items.push(...decoded.formatted.items); decodeResult.remaining = decoded.remaining; } else { ResultFormatter.text(decodeResult, messageText); - decodeResult.remaining = {text: messageText}; + decodeResult.remaining = { text: messageText }; } - } else if(messageText) { + } else if (messageText) { decodeResult.decoder.decodeLevel = 'partial'; - decodeResult.remaining = {text: messageText}; + decodeResult.remaining = { text: messageText }; } else { decodeResult.decoder.decodeLevel = 'partial'; } diff --git a/lib/plugins/Label_QP.ts b/lib/plugins/Label_QP.ts index 65252aa..7480bcf 100644 --- a/lib/plugins/Label_QP.ts +++ b/lib/plugins/Label_QP.ts @@ -6,27 +6,31 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_QP extends DecoderPlugin { name = 'label-qp'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['QP'], }; } - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { const decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; decodeResult.formatted.description = 'OUT Report'; - ResultFormatter.departureAirport(decodeResult, message.text.substring(0, 4)); + ResultFormatter.departureAirport( + decodeResult, + message.text.substring(0, 4), + ); ResultFormatter.arrivalAirport(decodeResult, message.text.substring(4, 8)); - ResultFormatter.out(decodeResult, DateTimeUtils.convertHHMMSSToTod(message.text.substring(8, 12))); + ResultFormatter.out( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(message.text.substring(8, 12)), + ); ResultFormatter.unknown(decodeResult, message.text.substring(12)); decodeResult.decoded = true; - if(!decodeResult.remaining.text) - decodeResult.decoder.decodeLevel = 'full'; - else - decodeResult.decoder.decodeLevel = 'partial'; + if (!decodeResult.remaining.text) decodeResult.decoder.decodeLevel = 'full'; + else decodeResult.decoder.decodeLevel = 'partial'; return decodeResult; } diff --git a/lib/plugins/Label_QQ.test.ts b/lib/plugins/Label_QQ.test.ts index 8c3b4e0..10ce20d 100644 --- a/lib/plugins/Label_QQ.test.ts +++ b/lib/plugins/Label_QQ.test.ts @@ -3,129 +3,129 @@ import { Label_QQ } from './Label_QQ'; describe('Label QQ', () => { let plugin: Label_QQ; - const message = {label: 'QQ', text: ''}; + const message = { label: 'QQ', text: '' }; beforeEach(() => { const decoder = new MessageDecoder(); plugin = new Label_QQ(decoder); }); -test('matches Label QQ qualifiers', () => { - expect(plugin.decode).toBeDefined(); - expect(plugin.name).toBe('label-qq'); -}); + test('matches Label QQ qualifiers', () => { + expect(plugin.decode).toBeDefined(); + expect(plugin.name).toBe('label-qq'); + }); -test('decodes Label QQ variant 1', () => { - // https://app.airframes.io/messages/3409269161 - message.text = 'KSDLKLAS0025\r\n001FE09002543N3336.6W11155.90281750042'; - const decodeResult = plugin.decode(message); + test('decodes Label QQ variant 1', () => { + // https://app.airframes.io/messages/3409269161 + message.text = 'KSDLKLAS0025\r\n001FE09002543N3336.6W11155.90281750042'; + const decodeResult = plugin.decode(message); - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-qq'); - expect(decodeResult.formatted.description).toBe('OFF Report'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.raw.departure_icao).toBe('KSDL'); - expect(decodeResult.raw.arrival_icao).toBe('KLAS'); - expect(decodeResult.raw.day).toBe('09'); - expect(decodeResult.raw.position.latitude).toBe(33.61); - expect(decodeResult.raw.position.longitude).toBe(-111.93166666666667); - expect(decodeResult.raw.groundspeed).toBe(175); - expect(decodeResult.remaining.text).toBe('028,0042'); - expect(decodeResult.formatted.items.length).toBe(5); - expect(decodeResult.formatted.items[0].type).toBe('icao'); - expect(decodeResult.formatted.items[0].code).toBe('ORG'); - expect(decodeResult.formatted.items[0].label).toBe('Origin'); - expect(decodeResult.formatted.items[0].value).toBe('KSDL'); - expect(decodeResult.formatted.items[1].type).toBe('icao'); - expect(decodeResult.formatted.items[1].code).toBe('DST'); - expect(decodeResult.formatted.items[1].label).toBe('Destination'); - expect(decodeResult.formatted.items[1].value).toBe('KLAS'); - expect(decodeResult.formatted.items[2].type).toBe('time_of_day'); - expect(decodeResult.formatted.items[2].code).toBe('OFF'); - expect(decodeResult.formatted.items[2].label).toBe('Takeoff Time'); - expect(decodeResult.formatted.items[2].value).toBe('00:25:43'); - expect(decodeResult.formatted.items[3].type).toBe('aircraft_position'); - expect(decodeResult.formatted.items[3].code).toBe('POS'); - expect(decodeResult.formatted.items[3].label).toBe('Aircraft Position'); - expect(decodeResult.formatted.items[3].value).toBe('33.610 N, 111.932 W'); - expect(decodeResult.formatted.items[4].type).toBe('aircraft_groundspeed'); - expect(decodeResult.formatted.items[4].code).toBe('GSPD'); - expect(decodeResult.formatted.items[4].label).toBe('Aircraft Groundspeed'); - expect(decodeResult.formatted.items[4].value).toBe('175 knots'); -}); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-qq'); + expect(decodeResult.formatted.description).toBe('OFF Report'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.raw.departure_icao).toBe('KSDL'); + expect(decodeResult.raw.arrival_icao).toBe('KLAS'); + expect(decodeResult.raw.day).toBe('09'); + expect(decodeResult.raw.position.latitude).toBe(33.61); + expect(decodeResult.raw.position.longitude).toBe(-111.93166666666667); + expect(decodeResult.raw.groundspeed).toBe(175); + expect(decodeResult.remaining.text).toBe('028,0042'); + expect(decodeResult.formatted.items.length).toBe(5); + expect(decodeResult.formatted.items[0].type).toBe('icao'); + expect(decodeResult.formatted.items[0].code).toBe('ORG'); + expect(decodeResult.formatted.items[0].label).toBe('Origin'); + expect(decodeResult.formatted.items[0].value).toBe('KSDL'); + expect(decodeResult.formatted.items[1].type).toBe('icao'); + expect(decodeResult.formatted.items[1].code).toBe('DST'); + expect(decodeResult.formatted.items[1].label).toBe('Destination'); + expect(decodeResult.formatted.items[1].value).toBe('KLAS'); + expect(decodeResult.formatted.items[2].type).toBe('time_of_day'); + expect(decodeResult.formatted.items[2].code).toBe('OFF'); + expect(decodeResult.formatted.items[2].label).toBe('Takeoff Time'); + expect(decodeResult.formatted.items[2].value).toBe('00:25:43'); + expect(decodeResult.formatted.items[3].type).toBe('aircraft_position'); + expect(decodeResult.formatted.items[3].code).toBe('POS'); + expect(decodeResult.formatted.items[3].label).toBe('Aircraft Position'); + expect(decodeResult.formatted.items[3].value).toBe('33.610 N, 111.932 W'); + expect(decodeResult.formatted.items[4].type).toBe('aircraft_groundspeed'); + expect(decodeResult.formatted.items[4].code).toBe('GSPD'); + expect(decodeResult.formatted.items[4].label).toBe('Aircraft Groundspeed'); + expect(decodeResult.formatted.items[4].value).toBe('175 knots'); + }); -test('decodes Label QQ variant 2', () => { - // https://app.airframes.io/messages/3406443370 - message.text = 'KLGBKLAX0004\r\n001FE07000444N3349.8W11810.1---0200009'; - const decodeResult = plugin.decode(message); + test('decodes Label QQ variant 2', () => { + // https://app.airframes.io/messages/3406443370 + message.text = 'KLGBKLAX0004\r\n001FE07000444N3349.8W11810.1---0200009'; + const decodeResult = plugin.decode(message); - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-qq'); - expect(decodeResult.formatted.description).toBe('OFF Report'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.raw.departure_icao).toBe('KLGB'); - expect(decodeResult.raw.arrival_icao).toBe('KLAX'); - expect(decodeResult.raw.day).toBe('07'); - expect(decodeResult.raw.position.latitude).toBe(33.83); - expect(decodeResult.raw.position.longitude).toBe(-118.16833333333334); - expect(decodeResult.remaining.text).toBe('---,020,0009'); - expect(decodeResult.formatted.items.length).toBe(4); - expect(decodeResult.formatted.items[0].type).toBe('icao'); - expect(decodeResult.formatted.items[0].code).toBe('ORG'); - expect(decodeResult.formatted.items[0].label).toBe('Origin'); - expect(decodeResult.formatted.items[0].value).toBe('KLGB'); - expect(decodeResult.formatted.items[1].type).toBe('icao'); - expect(decodeResult.formatted.items[1].code).toBe('DST'); - expect(decodeResult.formatted.items[1].label).toBe('Destination'); - expect(decodeResult.formatted.items[1].value).toBe('KLAX'); - expect(decodeResult.formatted.items[2].type).toBe('time_of_day'); - expect(decodeResult.formatted.items[2].code).toBe('OFF'); - expect(decodeResult.formatted.items[2].label).toBe('Takeoff Time'); - expect(decodeResult.formatted.items[2].value).toBe('00:04:44'); - expect(decodeResult.formatted.items[3].type).toBe('aircraft_position'); - expect(decodeResult.formatted.items[3].code).toBe('POS'); - expect(decodeResult.formatted.items[3].label).toBe('Aircraft Position'); - expect(decodeResult.formatted.items[3].value).toBe('33.830 N, 118.168 W'); -}); + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-qq'); + expect(decodeResult.formatted.description).toBe('OFF Report'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.raw.departure_icao).toBe('KLGB'); + expect(decodeResult.raw.arrival_icao).toBe('KLAX'); + expect(decodeResult.raw.day).toBe('07'); + expect(decodeResult.raw.position.latitude).toBe(33.83); + expect(decodeResult.raw.position.longitude).toBe(-118.16833333333334); + expect(decodeResult.remaining.text).toBe('---,020,0009'); + expect(decodeResult.formatted.items.length).toBe(4); + expect(decodeResult.formatted.items[0].type).toBe('icao'); + expect(decodeResult.formatted.items[0].code).toBe('ORG'); + expect(decodeResult.formatted.items[0].label).toBe('Origin'); + expect(decodeResult.formatted.items[0].value).toBe('KLGB'); + expect(decodeResult.formatted.items[1].type).toBe('icao'); + expect(decodeResult.formatted.items[1].code).toBe('DST'); + expect(decodeResult.formatted.items[1].label).toBe('Destination'); + expect(decodeResult.formatted.items[1].value).toBe('KLAX'); + expect(decodeResult.formatted.items[2].type).toBe('time_of_day'); + expect(decodeResult.formatted.items[2].code).toBe('OFF'); + expect(decodeResult.formatted.items[2].label).toBe('Takeoff Time'); + expect(decodeResult.formatted.items[2].value).toBe('00:04:44'); + expect(decodeResult.formatted.items[3].type).toBe('aircraft_position'); + expect(decodeResult.formatted.items[3].code).toBe('POS'); + expect(decodeResult.formatted.items[3].label).toBe('Aircraft Position'); + expect(decodeResult.formatted.items[3].value).toBe('33.830 N, 118.168 W'); + }); -test('decodes Label QQ variant 3', () => { - // https://app.airframes.io/messages/3409293914 - message.text = 'CYOWKMEM0058/OFFRPT/090155'; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(true); - expect(decodeResult.decoder.decodeLevel).toBe('partial'); - expect(decodeResult.decoder.name).toBe('label-qq'); - expect(decodeResult.formatted.description).toBe('OFF Report'); - expect(decodeResult.message).toBe(message); - expect(decodeResult.raw.departure_icao).toBe('CYOW'); - expect(decodeResult.raw.arrival_icao).toBe('KMEM'); - expect(decodeResult.remaining.text).toBe('/OFFRPT/090155'); - expect(decodeResult.formatted.items.length).toBe(3); - expect(decodeResult.formatted.items[0].type).toBe('icao'); - expect(decodeResult.formatted.items[0].code).toBe('ORG'); - expect(decodeResult.formatted.items[0].label).toBe('Origin'); - expect(decodeResult.formatted.items[0].value).toBe('CYOW'); - expect(decodeResult.formatted.items[1].type).toBe('icao'); - expect(decodeResult.formatted.items[1].code).toBe('DST'); - expect(decodeResult.formatted.items[1].label).toBe('Destination'); - expect(decodeResult.formatted.items[1].value).toBe('KMEM'); - expect(decodeResult.formatted.items[2].type).toBe('time_of_day'); - expect(decodeResult.formatted.items[2].code).toBe('OFF'); - expect(decodeResult.formatted.items[2].label).toBe('Takeoff Time'); - expect(decodeResult.formatted.items[2].value).toBe('00:58:00'); -}); + test('decodes Label QQ variant 3', () => { + // https://app.airframes.io/messages/3409293914 + message.text = 'CYOWKMEM0058/OFFRPT/090155'; + const decodeResult = plugin.decode(message); + + expect(decodeResult.decoded).toBe(true); + expect(decodeResult.decoder.decodeLevel).toBe('partial'); + expect(decodeResult.decoder.name).toBe('label-qq'); + expect(decodeResult.formatted.description).toBe('OFF Report'); + expect(decodeResult.message).toBe(message); + expect(decodeResult.raw.departure_icao).toBe('CYOW'); + expect(decodeResult.raw.arrival_icao).toBe('KMEM'); + expect(decodeResult.remaining.text).toBe('/OFFRPT/090155'); + expect(decodeResult.formatted.items.length).toBe(3); + expect(decodeResult.formatted.items[0].type).toBe('icao'); + expect(decodeResult.formatted.items[0].code).toBe('ORG'); + expect(decodeResult.formatted.items[0].label).toBe('Origin'); + expect(decodeResult.formatted.items[0].value).toBe('CYOW'); + expect(decodeResult.formatted.items[1].type).toBe('icao'); + expect(decodeResult.formatted.items[1].code).toBe('DST'); + expect(decodeResult.formatted.items[1].label).toBe('Destination'); + expect(decodeResult.formatted.items[1].value).toBe('KMEM'); + expect(decodeResult.formatted.items[2].type).toBe('time_of_day'); + expect(decodeResult.formatted.items[2].code).toBe('OFF'); + expect(decodeResult.formatted.items[2].label).toBe('Takeoff Time'); + expect(decodeResult.formatted.items[2].value).toBe('00:58:00'); + }); -// disabled because all messages should decode -test.skip('decodes Label QQ ', () => { - message.text = 'QQ Bogus message'; - const decodeResult = plugin.decode(message); - - expect(decodeResult.decoded).toBe(false); - expect(decodeResult.decoder.decodeLevel).toBe('none'); - expect(decodeResult.decoder.name).toBe('label-qq'); - expect(decodeResult.formatted.description).toBe('OFF Report'); - expect(decodeResult.formatted.items.length).toBe(0); + // disabled because all messages should decode + test.skip('decodes Label QQ ', () => { + message.text = 'QQ Bogus message'; + const decodeResult = plugin.decode(message); + + expect(decodeResult.decoded).toBe(false); + expect(decodeResult.decoder.decodeLevel).toBe('none'); + expect(decodeResult.decoder.name).toBe('label-qq'); + expect(decodeResult.formatted.description).toBe('OFF Report'); + expect(decodeResult.formatted.items.length).toBe(0); + }); }); -}); \ No newline at end of file diff --git a/lib/plugins/Label_QQ.ts b/lib/plugins/Label_QQ.ts index 9cf24dd..82f5f22 100644 --- a/lib/plugins/Label_QQ.ts +++ b/lib/plugins/Label_QQ.ts @@ -1,59 +1,68 @@ import { DecoderPlugin } from '../DecoderPlugin'; import { DecodeResult, Message, Options } from '../DecoderPluginInterface'; -import { CoordinateUtils } from '../utils/coordinate_utils'; import { ResultFormatter } from '../utils/result_formatter'; import { DateTimeUtils } from '../DateTimeUtils'; export class Label_QQ extends DecoderPlugin { name = 'label-qq'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['QQ'], }; } - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { const decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; decodeResult.message = message; decodeResult.formatted.description = 'OFF Report'; - - ResultFormatter.departureAirport(decodeResult, message.text.substring(0, 4)); + + ResultFormatter.departureAirport( + decodeResult, + message.text.substring(0, 4), + ); ResultFormatter.arrivalAirport(decodeResult, message.text.substring(4, 8)); - if (message.text.substring(12, 19) === "\r\n001FE") { - decodeResult.raw.day = message.text.substring(19, 21); - ResultFormatter.off(decodeResult, DateTimeUtils.convertHHMMSSToTod(message.text.substring(21, 27))); - let latdir = message.text.substring(27, 28); - let latdeg = Number(message.text.substring(28, 30)); - let latmin = Number(message.text.substring(30, 34)); - let londir = message.text.substring(34, 35); - let londeg = Number(message.text.substring(35, 38)); - let lonmin = Number(message.text.substring(38, 42)); - let pos = { - latitude: (latdeg + latmin/60) * (latdir === 'N' ? 1 : -1), - longitude: (londeg + lonmin/60) * (londir === 'E' ? 1 : -1), - }; - ResultFormatter.unknown(decodeResult, message.text.substring(42, 45)); - ResultFormatter.position(decodeResult, pos); - - if (decodeResult.remaining.text !== "---") { - ResultFormatter.groundspeed(decodeResult, Number(message.text.substring(45, 48))); - } else { - ResultFormatter.unknown(decodeResult, message.text.substring(45, 48)); - } - ResultFormatter.unknown(decodeResult, message.text.substring(48)); + if (message.text.substring(12, 19) === '\r\n001FE') { + decodeResult.raw.day = message.text.substring(19, 21); + ResultFormatter.off( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(message.text.substring(21, 27)), + ); + let latdir = message.text.substring(27, 28); + let latdeg = Number(message.text.substring(28, 30)); + let latmin = Number(message.text.substring(30, 34)); + let londir = message.text.substring(34, 35); + let londeg = Number(message.text.substring(35, 38)); + let lonmin = Number(message.text.substring(38, 42)); + let pos = { + latitude: (latdeg + latmin / 60) * (latdir === 'N' ? 1 : -1), + longitude: (londeg + lonmin / 60) * (londir === 'E' ? 1 : -1), + }; + ResultFormatter.unknown(decodeResult, message.text.substring(42, 45)); + ResultFormatter.position(decodeResult, pos); + + if (decodeResult.remaining.text !== '---') { + ResultFormatter.groundspeed( + decodeResult, + Number(message.text.substring(45, 48)), + ); + } else { + ResultFormatter.unknown(decodeResult, message.text.substring(45, 48)); + } + ResultFormatter.unknown(decodeResult, message.text.substring(48)); } else { - ResultFormatter.off(decodeResult, DateTimeUtils.convertHHMMSSToTod(message.text.substring(8, 12) + "00")); - ResultFormatter.unknown(decodeResult, message.text.substring(12)); + ResultFormatter.off( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(message.text.substring(8, 12) + '00'), + ); + ResultFormatter.unknown(decodeResult, message.text.substring(12)); } decodeResult.decoded = true; - if(!decodeResult.remaining.text) - decodeResult.decoder.decodeLevel = 'full'; - else - decodeResult.decoder.decodeLevel = 'partial'; + if (!decodeResult.remaining.text) decodeResult.decoder.decodeLevel = 'full'; + else decodeResult.decoder.decodeLevel = 'partial'; return decodeResult; } diff --git a/lib/plugins/Label_QR.ts b/lib/plugins/Label_QR.ts index 81d7225..baab3c0 100644 --- a/lib/plugins/Label_QR.ts +++ b/lib/plugins/Label_QR.ts @@ -6,27 +6,31 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_QR extends DecoderPlugin { name = 'label-qr'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['QR'], }; } - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { const decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; decodeResult.formatted.description = 'ON Report'; - ResultFormatter.departureAirport(decodeResult, message.text.substring(0, 4)); + ResultFormatter.departureAirport( + decodeResult, + message.text.substring(0, 4), + ); ResultFormatter.arrivalAirport(decodeResult, message.text.substring(4, 8)); - ResultFormatter.on(decodeResult, DateTimeUtils.convertHHMMSSToTod(message.text.substring(8, 12))); + ResultFormatter.on( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(message.text.substring(8, 12)), + ); ResultFormatter.unknown(decodeResult, message.text.substring(12)); decodeResult.decoded = true; - if(!decodeResult.remaining.text) - decodeResult.decoder.decodeLevel = 'full'; - else - decodeResult.decoder.decodeLevel = 'partial'; + if (!decodeResult.remaining.text) decodeResult.decoder.decodeLevel = 'full'; + else decodeResult.decoder.decodeLevel = 'partial'; return decodeResult; } diff --git a/lib/plugins/Label_QS.ts b/lib/plugins/Label_QS.ts index 657fbe2..a150f19 100644 --- a/lib/plugins/Label_QS.ts +++ b/lib/plugins/Label_QS.ts @@ -6,27 +6,31 @@ import { ResultFormatter } from '../utils/result_formatter'; export class Label_QS extends DecoderPlugin { name = 'label-qs'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['QS'], }; } - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { const decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; decodeResult.formatted.description = 'IN Report'; - ResultFormatter.departureAirport(decodeResult, message.text.substring(0, 4)); + ResultFormatter.departureAirport( + decodeResult, + message.text.substring(0, 4), + ); ResultFormatter.arrivalAirport(decodeResult, message.text.substring(4, 8)); - ResultFormatter.in(decodeResult, DateTimeUtils.convertHHMMSSToTod(message.text.substring(8, 12))); + ResultFormatter.in( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(message.text.substring(8, 12)), + ); ResultFormatter.unknown(decodeResult, message.text.substring(12)); decodeResult.decoded = true; - if(!decodeResult.remaining.text) - decodeResult.decoder.decodeLevel = 'full'; - else - decodeResult.decoder.decodeLevel = 'partial'; + if (!decodeResult.remaining.text) decodeResult.decoder.decodeLevel = 'full'; + else decodeResult.decoder.decodeLevel = 'partial'; return decodeResult; } diff --git a/lib/plugins/Label_SQ.test.ts b/lib/plugins/Label_SQ.test.ts index 9510973..8e73688 100644 --- a/lib/plugins/Label_SQ.test.ts +++ b/lib/plugins/Label_SQ.test.ts @@ -20,7 +20,20 @@ describe('Label_SQ', () => { }); it('decodes version 2 ARINC ground station squitter with coordinates and frequency', () => { - message.text = 'V2AA' + '0' + '1X' + 'A' + 'JFK' + 'KJFK' + '3' + '4075' + 'N' + '7398' + 'W' + 'V136975' + '/extra'; + message.text = + 'V2AA' + + '0' + + '1X' + + 'A' + + 'JFK' + + 'KJFK' + + '3' + + '4075' + + 'N' + + '7398' + + 'W' + + 'V136975' + + '/extra'; const res = plugin.decode(message); expect(res.decoded).toBe(true); @@ -42,12 +55,12 @@ describe('Label_SQ', () => { expect(res.raw.vdlFrequency).toBeCloseTo(136.975, 6); const items = res.formatted.items; - expect(items.find(i => i.code === 'NETT')?.value).toBe('ARINC'); - expect(items.find(i => i.code === 'VER')?.value).toBe('2'); - expect(items.find(i => i.code === 'GNDSTN')?.value).toBe('KJFK3'); - expect(items.find(i => i.code === 'IATA')?.value).toBe('JFK'); - expect(items.find(i => i.code === 'ICAO')?.value).toBe('KJFK'); - expect(items.find(i => i.code === 'VDLFRQ')?.value).toContain('136.975'); + expect(items.find((i) => i.code === 'NETT')?.value).toBe('ARINC'); + expect(items.find((i) => i.code === 'VER')?.value).toBe('2'); + expect(items.find((i) => i.code === 'GNDSTN')?.value).toBe('KJFK3'); + expect(items.find((i) => i.code === 'IATA')?.value).toBe('JFK'); + expect(items.find((i) => i.code === 'ICAO')?.value).toBe('KJFK'); + expect(items.find((i) => i.code === 'VDLFRQ')?.value).toContain('136.975'); }); it('maps SITA network and handles absence of regex match gracefully', () => { @@ -59,8 +72,8 @@ describe('Label_SQ', () => { expect(res.raw.network).toBe('S'); const items = res.formatted.items; - expect(items.find(i => i.code === 'NETT')?.value).toBe('SITA'); - expect(items.find(i => i.code === 'VER')?.value).toBe('2'); + expect(items.find((i) => i.code === 'NETT')?.value).toBe('SITA'); + expect(items.find((i) => i.code === 'VER')?.value).toBe('2'); expect(res.raw.groundStation).toBeUndefined(); expect(res.raw.vdlFrequency).toBeUndefined(); @@ -75,7 +88,7 @@ describe('Label_SQ', () => { expect(res.raw.network).toBe('A'); const items = res.formatted.items; - expect(items.find(i => i.code === 'NETT')?.value).toBe('ARINC'); - expect(items.find(i => i.code === 'VER')?.value).toBe('1'); + expect(items.find((i) => i.code === 'NETT')?.value).toBe('ARINC'); + expect(items.find((i) => i.code === 'VER')?.value).toBe('1'); }); }); diff --git a/lib/plugins/Label_SQ.ts b/lib/plugins/Label_SQ.ts index 8719caa..341e7cc 100644 --- a/lib/plugins/Label_SQ.ts +++ b/lib/plugins/Label_SQ.ts @@ -4,13 +4,13 @@ import { DecodeResult, Message, Options } from '../DecoderPluginInterface'; export class Label_SQ extends DecoderPlugin { name = 'label-sq'; - qualifiers() { // eslint-disable-line class-methods-use-this + qualifiers() { return { labels: ['SQ'], }; } - decode(message: Message, options: Options = {}) : DecodeResult { + decode(message: Message, options: Options = {}): DecodeResult { const decodeResult = this.defaultResult(); decodeResult.decoder.name = this.name; @@ -19,7 +19,8 @@ export class Label_SQ extends DecoderPlugin { decodeResult.raw.network = message.text.substring(3, 4); if (decodeResult.raw.version === '2') { - const regex = /0(\d)X(?\w)(?\w\w\w)(?\w\w\w\w)(?\d)(?\d+)(?[NS])(?\d+)(?[EW])V(?\d+)\/.*/; + const regex = + /0(\d)X(?\w)(?\w\w\w)(?\w\w\w\w)(?\d)(?\d+)(?[NS])(?\d+)(?[EW])V(?\d+)\/.*/; const result = message.text.match(regex); if (result?.groups && result.length >= 8) { @@ -28,10 +29,14 @@ export class Label_SQ extends DecoderPlugin { iataCode: result.groups.iata, icaoCode: result.groups.icao, coordinates: { - latitude: (Number(result.groups.lat) / 100) * (result.groups.latd === 'S' ? -1 : 1), - longitude: (Number(result.groups.lng) / 100) * (result.groups.lngd === 'W' ? -1 : 1) - } - } + latitude: + (Number(result.groups.lat) / 100) * + (result.groups.latd === 'S' ? -1 : 1), + longitude: + (Number(result.groups.lng) / 100) * + (result.groups.lngd === 'W' ? -1 : 1), + }, + }; decodeResult.raw.vdlFrequency = Number(result.groups.vfreq) / 1000.0; } } @@ -56,11 +61,14 @@ export class Label_SQ extends DecoderPlugin { code: 'VER', label: 'Version', value: decodeResult.raw.version, - } + }, ]; if (decodeResult.raw.groundStation) { - if (decodeResult.raw.groundStation.icaoCode && decodeResult.raw.groundStation.number) { + if ( + decodeResult.raw.groundStation.icaoCode && + decodeResult.raw.groundStation.number + ) { decodeResult.formatted.items.push({ type: 'ground_station', code: 'GNDSTN', @@ -107,7 +115,7 @@ export class Label_SQ extends DecoderPlugin { type: 'vdlFrequency', code: 'VDLFRQ', label: 'VDL Frequency', - value: `${decodeResult.raw.vdlFrequency} MHz` + value: `${decodeResult.raw.vdlFrequency} MHz`, }); } decodeResult.decoded = true; diff --git a/lib/types/route.ts b/lib/types/route.ts index 962a4c0..5922fea 100644 --- a/lib/types/route.ts +++ b/lib/types/route.ts @@ -1,17 +1,17 @@ -import { Waypoint } from "./waypoint"; +import { Waypoint } from './waypoint'; /** * Representation of a route - * + * * Typically a list of waypoints, this can also be a named company route. */ export interface Route { - /** optional name. If not set, `waypoints` is required */ - name?: string, + /** optional name. If not set, `waypoints` is required */ + name?: string; - /** optional runway */ - runway?: string, + /** optional runway */ + runway?: string; - /** optional list of waypoints. If not set, `name` is required */ - waypoints?: Waypoint[], -} \ No newline at end of file + /** optional list of waypoints. If not set, `name` is required */ + waypoints?: Waypoint[]; +} diff --git a/lib/types/waypoint.ts b/lib/types/waypoint.ts index 37de532..ec2eaa4 100644 --- a/lib/types/waypoint.ts +++ b/lib/types/waypoint.ts @@ -1,8 +1,6 @@ -import { CoordinateUtils } from "../utils/coordinate_utils"; - /** * Represenation of a waypoint. - * + * * Usually used in Routes which is an array of waypoints. * Airways/Jetways can also be represented as a waypoint, by just the name. * There is no distinction between the two currently because there is no difference from the current messages @@ -10,38 +8,37 @@ import { CoordinateUtils } from "../utils/coordinate_utils"; * In the event that a waypoint is a GPS position, the name can be the raw position string (N12345W012345) */ export interface Waypoint { - /** unique identifier of the waypoint*/ - name: string; - /** - * latitude in decimal degrees - * - * if set, longitude must be provided - */ - latitude?: number; - /** longitude in decimal degrees - * - * if set, latitude must be provided - */ - longitude?: number; - /** - * time of arrival. If in future, it is an ETA. - * - * if set, timeFormat must be provided - */ - time?: number; - /** - * tod = 'Time of Day. seoconds since midnight', epoch = 'unix time. seconds since Jan 1, 1970 UTC' - * - * if set, time must be provided - */ - timeFormat?: 'tod' | 'epoch' - - /** - * offset from the actual waypoint - * - * bearing: degrees from the waypoint - * distance: distance in nautical miles - */ - offset?: {bearing: number, distance: number}; + /** unique identifier of the waypoint*/ + name: string; + /** + * latitude in decimal degrees + * + * if set, longitude must be provided + */ + latitude?: number; + /** longitude in decimal degrees + * + * if set, latitude must be provided + */ + longitude?: number; + /** + * time of arrival. If in future, it is an ETA. + * + * if set, timeFormat must be provided + */ + time?: number; + /** + * tod = 'Time of Day. seoconds since midnight', epoch = 'unix time. seconds since Jan 1, 1970 UTC' + * + * if set, time must be provided + */ + timeFormat?: 'tod' | 'epoch'; -} \ No newline at end of file + /** + * offset from the actual waypoint + * + * bearing: degrees from the waypoint + * distance: distance in nautical miles + */ + offset?: { bearing: number; distance: number }; +} diff --git a/lib/types/wind.ts b/lib/types/wind.ts index 9beebcd..1205872 100644 --- a/lib/types/wind.ts +++ b/lib/types/wind.ts @@ -1,12 +1,12 @@ -import { Waypoint } from "./waypoint"; +import { Waypoint } from './waypoint'; export interface Wind { - waypoint: Waypoint; + waypoint: Waypoint; + flightLevel: number; + windDirection: number; + windSpeed: number; + temperature?: { flightLevel: number; - windDirection: number; - windSpeed: number; - temperature?: { - flightLevel: number; - degreesC: number; - }; -} \ No newline at end of file + degreesC: number; + }; +} diff --git a/lib/utils/coordinate_utils.ts b/lib/utils/coordinate_utils.ts index 279a2a4..66e66f0 100644 --- a/lib/utils/coordinate_utils.ts +++ b/lib/utils/coordinate_utils.ts @@ -2,10 +2,12 @@ export class CoordinateUtils { /** * Decode a string of coordinates into an object with latitude and longitude in millidegrees * @param stringCoords - The string of coordinates to decode - * + * * @returns An object with latitude and longitude properties */ - public static decodeStringCoordinates(stringCoords: string) : {latitude: number, longitude: number} | undefined{ // eslint-disable-line class-methods-use-this + public static decodeStringCoordinates( + stringCoords: string, + ): { latitude: number; longitude: number } | undefined { // format: N12345W123456 or N12345 W123456 const firstChar = stringCoords.substring(0, 1); let middleChar = stringCoords.substring(6, 7); @@ -14,28 +16,37 @@ export class CoordinateUtils { middleChar = stringCoords.substring(7, 8); longitudeChars = stringCoords.substring(8, 14); } - if ((firstChar === 'N' || firstChar === 'S') && (middleChar === 'W' || middleChar === 'E')) { + if ( + (firstChar === 'N' || firstChar === 'S') && + (middleChar === 'W' || middleChar === 'E') + ) { return { - latitude: (Number(stringCoords.substring(1, 6)) / 1000) * CoordinateUtils.getDirection(firstChar), - longitude: (Number(longitudeChars) / 1000) * CoordinateUtils.getDirection(middleChar) + latitude: + (Number(stringCoords.substring(1, 6)) / 1000) * + CoordinateUtils.getDirection(firstChar), + longitude: + (Number(longitudeChars) / 1000) * + CoordinateUtils.getDirection(middleChar), }; - } + } return undefined; } - /** + /** * Decode a string of coordinates into an object with latitude and longitude in degrees and decimal minutes * @param stringCoords - The string of coordinates to decode - * + * * @returns An object with latitude and longitude properties */ - public static decodeStringCoordinatesDecimalMinutes(stringCoords: string) : {latitude: number, longitude: number} | undefined{ // eslint-disable-line class-methods-use-this + public static decodeStringCoordinatesDecimalMinutes( + stringCoords: string, + ): { latitude: number; longitude: number } | undefined { // format: N12345W123456 or N12345 W123456 const firstChar = stringCoords.substring(0, 1); let middleChar = stringCoords.substring(6, 7); let longitudeChars = stringCoords.substring(7, 13); - if (middleChar ==' ') { + if (middleChar == ' ') { middleChar = stringCoords.substring(7, 8); longitudeChars = stringCoords.substring(8, 14); } @@ -44,30 +55,42 @@ export class CoordinateUtils { const lonDeg = Math.trunc(Number(longitudeChars) / 1000); const lonMin = (Number(longitudeChars) % 1000) / 10; - if ((firstChar === 'N' || firstChar === 'S') && (middleChar === 'W' || middleChar === 'E')) { + if ( + (firstChar === 'N' || firstChar === 'S') && + (middleChar === 'W' || middleChar === 'E') + ) { return { - latitude: (latDeg + (latMin / 60)) * CoordinateUtils.getDirection(firstChar), - longitude: (lonDeg + (lonMin / 60)) * CoordinateUtils.getDirection(middleChar) + latitude: + (latDeg + latMin / 60) * CoordinateUtils.getDirection(firstChar), + longitude: + (lonDeg + lonMin / 60) * CoordinateUtils.getDirection(middleChar), }; - } + } return undefined; } - public static coordinateString(coords: {latitude: number, longitude: number}) : string { + public static coordinateString(coords: { + latitude: number; + longitude: number; + }): string { const latDir = coords.latitude > 0 ? 'N' : 'S'; const lonDir = coords.longitude > 0 ? 'E' : 'W'; return `${Math.abs(coords.latitude).toFixed(3)} ${latDir}, ${Math.abs(coords.longitude).toFixed(3)} ${lonDir}`; } - public static getDirection(coord: string):number { - if(coord.startsWith('N') || coord.startsWith('E')) { + public static getDirection(coord: string): number { + if (coord.startsWith('N') || coord.startsWith('E')) { return 1; - } else if(coord.startsWith('S') || coord.startsWith('W')) { + } else if (coord.startsWith('S') || coord.startsWith('W')) { return -1; } return NaN; } - public static dmsToDecimalDegrees(degrees: number, minutes: number, seconds: number ) : number { + public static dmsToDecimalDegrees( + degrees: number, + minutes: number, + seconds: number, + ): number { return degrees + minutes / 60 + seconds / 3600; } } diff --git a/lib/utils/flight_plan_utils.ts b/lib/utils/flight_plan_utils.ts index 4a79ab1..7a15bda 100644 --- a/lib/utils/flight_plan_utils.ts +++ b/lib/utils/flight_plan_utils.ts @@ -1,18 +1,21 @@ -import { DecodeResult } from "../DecoderPluginInterface"; -import { ResultFormatter } from "./result_formatter"; -import { RouteUtils } from "./route_utils"; +import { DecodeResult } from '../DecoderPluginInterface'; +import { ResultFormatter } from './result_formatter'; +import { RouteUtils } from './route_utils'; export class FlightPlanUtils { /** * Processes flight plan data - * + * * Expected format is [header, key1, val1, ... keyN, valN] - * + * * @param decodeResult - results * @param data - original message split by ':' * @returns whether all fields were processed or not */ - public static processFlightPlan(decodeResult: DecodeResult, data: string[]): boolean { + public static processFlightPlan( + decodeResult: DecodeResult, + data: string[], + ): boolean { let allKnownFields = FlightPlanUtils.parseHeader(decodeResult, data[0]); for (let i = 1; i < data.length; i += 2) { const key = data[i]; @@ -56,7 +59,10 @@ export class FlightPlanUtils { } return allKnownFields; } - public static parseHeader(decodeResult: DecodeResult, header: string): boolean { + public static parseHeader( + decodeResult: DecodeResult, + header: string, + ): boolean { let allKnownFields = true; if (header.startsWith('RF')) { decodeResult.formatted.items.push({ @@ -96,40 +102,43 @@ export class FlightPlanUtils { }); } else { decodeResult.remaining.text += header; - allKnownFields = false + allKnownFields = false; } return allKnownFields; - }; - - -public static addProcedure(decodeResult: DecodeResult, value: string, type: string) { - if (decodeResult.raw.procedures === undefined) { - decodeResult.raw.procedures = []; } - const data = value.split('.'); - let waypoints; - if (data.length > 1) { - waypoints = data.slice(1).map((leg) => RouteUtils.getWaypoint(leg)); + + public static addProcedure( + decodeResult: DecodeResult, + value: string, + type: string, + ) { + if (decodeResult.raw.procedures === undefined) { + decodeResult.raw.procedures = []; + } + const data = value.split('.'); + let waypoints; + if (data.length > 1) { + waypoints = data.slice(1).map((leg) => RouteUtils.getWaypoint(leg)); + } + const route = { name: data[0], waypoints: waypoints }; + decodeResult.raw.procedures.push({ type: type, route: route }); + const procedureName = type.substring(0, 1).toUpperCase() + type.slice(1); + decodeResult.formatted.items.push({ + type: 'procedure', + code: 'proc', + label: `${procedureName} Procedure`, + value: RouteUtils.routeToString(route), + }); } - const route = { name: data[0], waypoints: waypoints }; - decodeResult.raw.procedures.push({ type: type, route: route }); - const procedureName = type.substring(0, 1).toUpperCase() + type.slice(1); - decodeResult.formatted.items.push({ - type: `procedure`, - code: 'proc', - label: `${procedureName} Procedure`, - value: RouteUtils.routeToString(route), - }); -}; } function addArrivalAirport(decodeResult: DecodeResult, value: string) { ResultFormatter.arrivalAirport(decodeResult, value); -}; +} function addDepartureAirport(decodeResult: DecodeResult, value: string) { ResultFormatter.departureAirport(decodeResult, value); -}; +} function addRunway(decodeResult: DecodeResult, value: string) { // xxx(yyy) where xxx is the departure runway and yyy is the arrival runway @@ -138,12 +147,14 @@ function addRunway(decodeResult: DecodeResult, value: string) { } ResultFormatter.departureRunway(decodeResult, value.substring(0, 3)); -}; +} function addRoute(decodeResult: DecodeResult, value: string) { const route = value.split('.'); - ResultFormatter.route(decodeResult, { waypoints: route.map((leg) => RouteUtils.getWaypoint(leg)) }); -}; + ResultFormatter.route(decodeResult, { + waypoints: route.map((leg) => RouteUtils.getWaypoint(leg)), + }); +} function addCompanyRoute(decodeResult: DecodeResult, value: string) { const segments = value.split('.'); @@ -171,4 +182,4 @@ function addCompanyRoute(decodeResult: DecodeResult, value: string) { label: 'Company Route', value: RouteUtils.routeToString(decodeResult.raw.company_route), }); -}; +} diff --git a/lib/utils/h1_helper.ts b/lib/utils/h1_helper.ts index 43a1239..321a77d 100644 --- a/lib/utils/h1_helper.ts +++ b/lib/utils/h1_helper.ts @@ -1,410 +1,453 @@ -import { DateTimeUtils } from "../DateTimeUtils"; -import { DecodeResult } from "../DecoderPluginInterface"; -import { Waypoint } from "../types/waypoint"; -import { Wind } from "../types/wind"; -import { CoordinateUtils } from "./coordinate_utils"; -import { FlightPlanUtils } from "./flight_plan_utils"; -import { ResultFormatter } from "./result_formatter"; -import { RouteUtils } from "./route_utils"; +import { DateTimeUtils } from '../DateTimeUtils'; +import { DecodeResult } from '../DecoderPluginInterface'; +import { Waypoint } from '../types/waypoint'; +import { Wind } from '../types/wind'; +import { CoordinateUtils } from './coordinate_utils'; +import { FlightPlanUtils } from './flight_plan_utils'; +import { ResultFormatter } from './result_formatter'; +import { RouteUtils } from './route_utils'; export class H1Helper { - public static decodeH1Message(decodeResult: DecodeResult, message: string) { - const checksum = message.slice(-4); - const data = message.slice(0, message.length - 4); - if(calculateChecksum(data) !== checksum) { - decodeResult.decoded = false; - decodeResult.decoder.decodeLevel = 'none'; - - return false; - } - - const fields = data.split('/'); - const canDecode = parseMessageType(decodeResult, fields[0]); - if (!canDecode) { - decodeResult.decoded = false; - decodeResult.decoder.decodeLevel = 'none'; - return false; - } - - for (let i = 1; i < fields.length; ++i) { - const key = fields[i].substring(0, 2); - const data = fields[i].substring(2); - switch (key) { - case 'AF': - processAirField(decodeResult, data.split(',')); - break; - case 'CG': - processCenterOfGravity(decodeResult, data.split(',')); - break; - case 'DC': - processDateCode(decodeResult, data.split(',')); - break; - case 'DT': //processDestination? - processDT(decodeResult, data.split(',')); - break; - case 'ET': - processETA(data, decodeResult, fields, i); - break; - case 'FB': - ResultFormatter.currentFuel(decodeResult, parseInt(data, 10)); - break; - case 'FN': - decodeResult.raw.flight_number = data; - break; - case 'FX': - ResultFormatter.freetext(decodeResult, data); - break; - case 'ID': - processIdentification(decodeResult, data.split(',')); - break; - case 'LR': - processLandingReport(decodeResult, data.split(',')); - break; - case 'PR': - // TODO: decode /PR fields - ResultFormatter.unknown(decodeResult, fields[i], '/'); - break; - case 'PS': // Position - H1Helper.processPS(decodeResult, data.split(',')); - break; - case 'RF': - case 'RI': - case 'RM': - case 'RP': - // TODO - use key/data instead of whole message - FlightPlanUtils.processFlightPlan(decodeResult, fields[i].split(':')); - break; - case 'SN': - decodeResult.raw.serial_number = data; - break; - case 'TD': - processTimeOfDeparture(decodeResult, data.split(',')); - break; - case 'TS': - H1Helper.processTimeStamp(decodeResult, data.split(',')); - break; - case 'VR': - ResultFormatter.version(decodeResult, parseInt(data, 10)/10); - break; - case 'WD': - processWindData(decodeResult, data); - break; - default: - ResultFormatter.unknown(decodeResult, fields[i], '/'); - } - } - - ResultFormatter.checksum(decodeResult, checksum); - - return true; + public static decodeH1Message(decodeResult: DecodeResult, message: string) { + const checksum = message.slice(-4); + const data = message.slice(0, message.length - 4); + if (calculateChecksum(data) !== checksum) { + decodeResult.decoded = false; + decodeResult.decoder.decodeLevel = 'none'; + + return false; } - public static processPS(decodeResult: DecodeResult, data: string[]) { - const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(data[0]); - if (position) { - decodeResult.raw.position = position - decodeResult.formatted.items.push({ - type: 'aircraft_position', - code: 'POS', - label: 'Aircraft Position', - value: CoordinateUtils.coordinateString(position), - }); - } - if (data.length === 9) { // variant 7 - processRoute(decodeResult, data[3], data[1], data[5], data[4], undefined); - ResultFormatter.altitude(decodeResult, Number(data[2]) * 100); - ResultFormatter.temperature(decodeResult, data[6]); - ResultFormatter.unknown(decodeResult, data[7]); - ResultFormatter.unknown(decodeResult, data[8]); - } - if (data.length === 14) { // variant 2 - ResultFormatter.altitude(decodeResult, Number(data[3]) * 100); - processRoute(decodeResult, data[1], data[2], data[4], data[5], data[6]); - ResultFormatter.temperature(decodeResult, data[7]); - ResultFormatter.unknownArr(decodeResult, data.slice(8)); - } + const fields = data.split('/'); + const canDecode = parseMessageType(decodeResult, fields[0]); + if (!canDecode) { + decodeResult.decoded = false; + decodeResult.decoder.decodeLevel = 'none'; + return false; } - public static processPosition(decodeResult: DecodeResult, data: string[]) { - const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(data[0]); - if (position) { - ResultFormatter.position(decodeResult, position); - } - if (data.length >= 10) { // variant 1, short - ResultFormatter.altitude(decodeResult, Number(data[3]) * 100); - processRoute(decodeResult, data[1], data[2], data[4], data[5], data[6]); - ResultFormatter.temperature(decodeResult, data[7]); - ResultFormatter.unknown(decodeResult, data[8]); - ResultFormatter.unknown(decodeResult, data[9]); - } - if (data.length >= 14) { // variant 2,long - ResultFormatter.unknownArr(decodeResult, data.slice(10)); - } + for (let i = 1; i < fields.length; ++i) { + const key = fields[i].substring(0, 2); + const data = fields[i].substring(2); + switch (key) { + case 'AF': + processAirField(decodeResult, data.split(',')); + break; + case 'CG': + processCenterOfGravity(decodeResult, data.split(',')); + break; + case 'DC': + processDateCode(decodeResult, data.split(',')); + break; + case 'DT': //processDestination? + processDT(decodeResult, data.split(',')); + break; + case 'ET': + processETA(data, decodeResult, fields, i); + break; + case 'FB': + ResultFormatter.currentFuel(decodeResult, parseInt(data, 10)); + break; + case 'FN': + decodeResult.raw.flight_number = data; + break; + case 'FX': + ResultFormatter.freetext(decodeResult, data); + break; + case 'ID': + processIdentification(decodeResult, data.split(',')); + break; + case 'LR': + processLandingReport(decodeResult, data.split(',')); + break; + case 'PR': + // TODO: decode /PR fields + ResultFormatter.unknown(decodeResult, fields[i], '/'); + break; + case 'PS': // Position + H1Helper.processPS(decodeResult, data.split(',')); + break; + case 'RF': + case 'RI': + case 'RM': + case 'RP': + // TODO - use key/data instead of whole message + FlightPlanUtils.processFlightPlan(decodeResult, fields[i].split(':')); + break; + case 'SN': + decodeResult.raw.serial_number = data; + break; + case 'TD': + processTimeOfDeparture(decodeResult, data.split(',')); + break; + case 'TS': + H1Helper.processTimeStamp(decodeResult, data.split(',')); + break; + case 'VR': + ResultFormatter.version(decodeResult, parseInt(data, 10) / 10); + break; + case 'WD': + processWindData(decodeResult, data); + break; + default: + ResultFormatter.unknown(decodeResult, fields[i], '/'); + } } - public static processTimeStamp(decodeResult: DecodeResult, data: string[]) { - let time = DateTimeUtils.convertDateTimeToEpoch(data[0], data[1]); + ResultFormatter.checksum(decodeResult, checksum); - if (Number.isNaN(time)) { // convert DDMMYY to MMDDYY - TODO figure out a better way to determine - const date = data[1].substring(2, 4) + data[1].substring(0, 2) + data[1].substring(4, 6); - time = DateTimeUtils.convertDateTimeToEpoch(data[0], date); - } - decodeResult.raw.message_timestamp = time; + return true; + } + + public static processPS(decodeResult: DecodeResult, data: string[]) { + const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes( + data[0], + ); + if (position) { + decodeResult.raw.position = position; + decodeResult.formatted.items.push({ + type: 'aircraft_position', + code: 'POS', + label: 'Aircraft Position', + value: CoordinateUtils.coordinateString(position), + }); + } + if (data.length === 9) { + // variant 7 + processRoute(decodeResult, data[3], data[1], data[5], data[4], undefined); + ResultFormatter.altitude(decodeResult, Number(data[2]) * 100); + ResultFormatter.temperature(decodeResult, data[6]); + ResultFormatter.unknown(decodeResult, data[7]); + ResultFormatter.unknown(decodeResult, data[8]); + } + if (data.length === 14) { + // variant 2 + ResultFormatter.altitude(decodeResult, Number(data[3]) * 100); + processRoute(decodeResult, data[1], data[2], data[4], data[5], data[6]); + ResultFormatter.temperature(decodeResult, data[7]); + ResultFormatter.unknownArr(decodeResult, data.slice(8)); + } + } + + public static processPosition(decodeResult: DecodeResult, data: string[]) { + const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes( + data[0], + ); + if (position) { + ResultFormatter.position(decodeResult, position); + } + if (data.length >= 10) { + // variant 1, short + ResultFormatter.altitude(decodeResult, Number(data[3]) * 100); + processRoute(decodeResult, data[1], data[2], data[4], data[5], data[6]); + ResultFormatter.temperature(decodeResult, data[7]); + ResultFormatter.unknown(decodeResult, data[8]); + ResultFormatter.unknown(decodeResult, data[9]); + } + if (data.length >= 14) { + // variant 2,long + ResultFormatter.unknownArr(decodeResult, data.slice(10)); + } + } + + public static processTimeStamp(decodeResult: DecodeResult, data: string[]) { + let time = DateTimeUtils.convertDateTimeToEpoch(data[0], data[1]); + + if (Number.isNaN(time)) { + // convert DDMMYY to MMDDYY - TODO figure out a better way to determine + const date = + data[1].substring(2, 4) + + data[1].substring(0, 2) + + data[1].substring(4, 6); + time = DateTimeUtils.convertDateTimeToEpoch(data[0], date); } + decodeResult.raw.message_timestamp = time; + } } -function processETA(data: string, decodeResult: DecodeResult, fields: string[], i: number) { - if (data.length === 5) { // 1 digit day - ResultFormatter.day(decodeResult, Number(data.substring(0, 1))); - ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(data.substring(1))); - } else if (data.length === 6) { // 2 digit day - ResultFormatter.day(decodeResult, Number(data.substring(0, 2))); - ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(data.substring(2))); - } else { - ResultFormatter.unknown(decodeResult, fields[i], '/'); - } +function processETA( + data: string, + decodeResult: DecodeResult, + fields: string[], + i: number, +) { + if (data.length === 5) { + // 1 digit day + ResultFormatter.day(decodeResult, Number(data.substring(0, 1))); + ResultFormatter.eta( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(data.substring(1)), + ); + } else if (data.length === 6) { + // 2 digit day + ResultFormatter.day(decodeResult, Number(data.substring(0, 2))); + ResultFormatter.eta( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(data.substring(2)), + ); + } else { + ResultFormatter.unknown(decodeResult, fields[i], '/'); + } } function processAirField(decodeResult: DecodeResult, data: string[]) { - if (data.length === 2) { - ResultFormatter.departureAirport(decodeResult, data[0]); - ResultFormatter.arrivalAirport(decodeResult, data[1]); - } else { - ResultFormatter.unknown(decodeResult, data.join(','), 'AF/'); - } + if (data.length === 2) { + ResultFormatter.departureAirport(decodeResult, data[0]); + ResultFormatter.arrivalAirport(decodeResult, data[1]); + } else { + ResultFormatter.unknown(decodeResult, data.join(','), 'AF/'); + } } function processTimeOfDeparture(decodeResult: DecodeResult, data: string[]) { - if (data.length === 2) { - decodeResult.raw.plannedDepartureTime = data[0]; //DDHHMM - decodeResult.formatted.items.push({ - type: 'ptd', - code: "ptd", - label: 'Planned Departure Time', - value: `YYYY-MM-${data[0].substring(0, 2)}T${data[0].substring(2, 4)}:${data[0].substring(4)}:00Z`, - }); - - decodeResult.raw.plannedDepartureTime = data[1]; //HHMM - decodeResult.formatted.items.push({ - type: 'etd', - code: "etd", - label: 'Estimated Departure Time', - value: `${data[1].substring(0, 2)}:${data[1].substring(2)}`, - }); - } else { - ResultFormatter.unknown(decodeResult, data.join(','), '/TD'); - } + if (data.length === 2) { + decodeResult.raw.plannedDepartureTime = data[0]; //DDHHMM + decodeResult.formatted.items.push({ + type: 'ptd', + code: 'ptd', + label: 'Planned Departure Time', + value: `YYYY-MM-${data[0].substring(0, 2)}T${data[0].substring(2, 4)}:${data[0].substring(4)}:00Z`, + }); + + decodeResult.raw.plannedDepartureTime = data[1]; //HHMM + decodeResult.formatted.items.push({ + type: 'etd', + code: 'etd', + label: 'Estimated Departure Time', + value: `${data[1].substring(0, 2)}:${data[1].substring(2)}`, + }); + } else { + ResultFormatter.unknown(decodeResult, data.join(','), '/TD'); + } } function processIdentification(decodeResult: DecodeResult, data: string[]) { - ResultFormatter.tail(decodeResult, data[0]) - if (data.length > 1) { - ResultFormatter.flightNumber(decodeResult, data[1]); - } - if (data.length > 2) { //TODO: figure out what this is - decodeResult.raw.mission_number = data[2]; - } + ResultFormatter.tail(decodeResult, data[0]); + if (data.length > 1) { + ResultFormatter.flightNumber(decodeResult, data[1]); + } + if (data.length > 2) { + //TODO: figure out what this is + decodeResult.raw.mission_number = data[2]; + } } function processDT(decodeResult: DecodeResult, data: string[]) { - if (!decodeResult.raw.arrival_icao) { - ResultFormatter.arrivalAirport(decodeResult, data[0]); - } else if (decodeResult.raw.arrival_icao != data[0]) { - ResultFormatter.unknownArr(decodeResult, data); - } // else duplicate - don't do anything + if (!decodeResult.raw.arrival_icao) { + ResultFormatter.arrivalAirport(decodeResult, data[0]); + } else if (decodeResult.raw.arrival_icao != data[0]) { + ResultFormatter.unknownArr(decodeResult, data); + } // else duplicate - don't do anything + + if (data.length > 1) { + ResultFormatter.arrivalRunway(decodeResult, data[1]); + } + if (data.length > 2) { + ResultFormatter.currentFuel(decodeResult, Number(data[2])); + } + if (data.length > 3) { + ResultFormatter.eta( + decodeResult, + DateTimeUtils.convertHHMMSSToTod(data[3]), + ); + } + if (data.length > 4) { + ResultFormatter.remainingFuel(decodeResult, Number(data[4])); + } + if (data.length > 5) { + //TODO: figure out what this is + ResultFormatter.unknownArr(decodeResult, data); + } +} +function processLandingReport(decodeResult: DecodeResult, data: string[]) { + if (data.length === 19) { + ResultFormatter.unknown(decodeResult, data[1]); + ResultFormatter.flightNumber(decodeResult, data[2]); + ResultFormatter.departureAirport(decodeResult, data[3]); + ResultFormatter.arrivalAirport(decodeResult, data[4]); + ResultFormatter.arrivalRunway(decodeResult, data[5]); + ResultFormatter.unknown(decodeResult, data.slice(6, 19).join(',')); + } else { + ResultFormatter.unknown(decodeResult, data.join(',')); + } +} - if (data.length > 1) { - ResultFormatter.arrivalRunway(decodeResult, data[1]); - } - if (data.length > 2) { - ResultFormatter.currentFuel(decodeResult, Number(data[2])); - } - if (data.length > 3) { - ResultFormatter.eta(decodeResult, DateTimeUtils.convertHHMMSSToTod(data[3])); +function processCenterOfGravity(decodeResult: DecodeResult, data: string[]) { + if (data.length === 1) { + if (data[0] !== undefined && data[0] !== '') { + ResultFormatter.cg(decodeResult, parseInt(data[0], 10) / 10); } - if (data.length > 4) { - ResultFormatter.remainingFuel(decodeResult, Number(data[4])); + } else if (data.length === 3) { + if (data[0] !== undefined && data[0] !== '') { + ResultFormatter.cg(decodeResult, parseInt(data[0], 10) / 10, 'center'); } - if (data.length > 5) {//TODO: figure out what this is - ResultFormatter.unknownArr(decodeResult, data); + if (data[1] !== undefined && data[1] !== '') { + ResultFormatter.cg(decodeResult, parseInt(data[1], 10) / 10, 'lower'); } -}; - -function processLandingReport(decodeResult: DecodeResult, data: string[]) { - if (data.length === 19) { - ResultFormatter.unknown(decodeResult, data[1]); - ResultFormatter.flightNumber(decodeResult, data[2]); - ResultFormatter.departureAirport(decodeResult, data[3]); - ResultFormatter.arrivalAirport(decodeResult, data[4]); - ResultFormatter.arrivalRunway(decodeResult, data[5]); - ResultFormatter.unknown(decodeResult, data.slice(6, 19).join(',')); - } else { - ResultFormatter.unknown(decodeResult, data.join(',')); + if (data[2] !== undefined && data[2] !== '') { + ResultFormatter.cg(decodeResult, parseInt(data[2], 10) / 10, 'upper'); } -}; - -function processCenterOfGravity(decodeResult: DecodeResult, data: string[]) { - if(data.length === 1) { - if(data[0] !== undefined && data[0] !== '') { - ResultFormatter.cg(decodeResult, parseInt(data[0], 10) / 10); - } - } else if (data.length === 3) { - if(data[0] !== undefined && data[0] !== '') { - ResultFormatter.cg(decodeResult, parseInt(data[0], 10) / 10, 'center'); - } - if(data[1] !== undefined && data[1] !== '') { - ResultFormatter.cg(decodeResult, parseInt(data[1], 10) / 10, 'lower'); - } - if(data[2] !== undefined && data[2] !== '') { - ResultFormatter.cg(decodeResult, parseInt(data[2], 10) / 10, 'upper'); - } + } else { + ResultFormatter.unknown(decodeResult, data.join(',')); + } +} - } else { - ResultFormatter.unknown(decodeResult, data.join(',')); +function parseMessageType( + decodeResult: DecodeResult, + messageType: string, +): boolean { + if (messageType.startsWith('POS')) { + H1Helper.processPosition(decodeResult, messageType.substring(3).split(',')); + return processMessageType(decodeResult, 'POS'); + } else if (messageType.length === 13) { + if (processMessageType(decodeResult, messageType.substring(10))) { + ResultFormatter.unknown(decodeResult, messageType.substring(0, 4)); + ResultFormatter.flightNumber(decodeResult, messageType.slice(4, 10)); + return true; } -}; - - -function parseMessageType(decodeResult: DecodeResult, messageType: string): boolean { - - if (messageType.startsWith('POS')) { - H1Helper.processPosition(decodeResult, messageType.substring(3).split(',')); - return processMessageType(decodeResult, 'POS'); - } else if(messageType.length === 13) { - if(processMessageType(decodeResult, messageType.substring(10))) { - ResultFormatter.unknown(decodeResult, messageType.substring(0, 4)); - ResultFormatter.flightNumber(decodeResult, messageType.slice(4, 10)); - return true; - } - } - return processMessageType(decodeResult, messageType.substring(0,3)); + } + return processMessageType(decodeResult, messageType.substring(0, 3)); } function processMessageType(decodeResult: DecodeResult, type: string): boolean { - if (type === 'FPN') { - decodeResult.formatted.description = 'Flight Plan'; - } else if (type === 'FTX') { - decodeResult.formatted.description = 'Free Text'; - } else if (type === 'INI') { - decodeResult.formatted.description = 'Flight Plan Initial Report'; - } else if (type === 'PER') { - decodeResult.formatted.description = 'Performance Report'; - } else if (type === 'POS') { - decodeResult.formatted.description = 'Position Report'; - } else if (type === 'PRG') { - decodeResult.formatted.description = 'Progress Report'; - } else if (type === 'PWI') { - decodeResult.formatted.description = 'Pilot Weather Information'; - } else { - decodeResult.formatted.description = 'Unknown H1 Message'; - return false; - } - return true; + if (type === 'FPN') { + decodeResult.formatted.description = 'Flight Plan'; + } else if (type === 'FTX') { + decodeResult.formatted.description = 'Free Text'; + } else if (type === 'INI') { + decodeResult.formatted.description = 'Flight Plan Initial Report'; + } else if (type === 'PER') { + decodeResult.formatted.description = 'Performance Report'; + } else if (type === 'POS') { + decodeResult.formatted.description = 'Position Report'; + } else if (type === 'PRG') { + decodeResult.formatted.description = 'Progress Report'; + } else if (type === 'PWI') { + decodeResult.formatted.description = 'Pilot Weather Information'; + } else { + decodeResult.formatted.description = 'Unknown H1 Message'; + return false; + } + return true; } function processDateCode(decodeResult: DecodeResult, data: string[]) { - decodeResult.raw.message_date = data[0]; // DDMMYYYY; - - if (data.length === 1) { - // noop? - } else if (data.length === 2) { - // convert DDMMYY to MMDDYY - TODO figure out a better way to determine - const date = data[0].substring(2, 4) + data[0].substring(0, 2) + data[0].substring(4, 6); - const time = DateTimeUtils.convertDateTimeToEpoch(data[1], data[0]); // HHMMSS - - decodeResult.raw.message_timestamp = time; - } + decodeResult.raw.message_date = data[0]; // DDMMYYYY; + + if (data.length === 1) { + // noop? + } else if (data.length === 2) { + // convert DDMMYY to MMDDYY - TODO figure out a better way to determine + const date = + data[0].substring(2, 4) + + data[0].substring(0, 2) + + data[0].substring(4, 6); + const time = DateTimeUtils.convertDateTimeToEpoch(data[1], data[0]); // HHMMSS + + decodeResult.raw.message_timestamp = time; + } } -function processRoute(decodeResult: DecodeResult, last: string, time: string, next: string, eta: string, then?: string, date?: string) { - const lastTime = date ? DateTimeUtils.convertDateTimeToEpoch(time, date) : DateTimeUtils.convertHHMMSSToTod(time); - const nextTime = date ? DateTimeUtils.convertDateTimeToEpoch(eta, date) : DateTimeUtils.convertHHMMSSToTod(eta); - const timeFormat = date ? 'epoch' : 'tod'; - - const lastWaypoint = RouteUtils.getWaypoint(last); - lastWaypoint.time = lastTime; - lastWaypoint.timeFormat = timeFormat; - - const nextWaypoint = RouteUtils.getWaypoint(next); - nextWaypoint.time = nextTime; - nextWaypoint.timeFormat = timeFormat; - - const thenWaypoint = RouteUtils.getWaypoint(then || '?'); - - const waypoints: Waypoint[] = [lastWaypoint, nextWaypoint, thenWaypoint]; - decodeResult.raw.route = { waypoints: waypoints }; - decodeResult.formatted.items.push({ - type: 'aircraft_route', - code: 'ROUTE', - label: 'Aircraft Route', - value: RouteUtils.routeToString(decodeResult.raw.route), - }); +function processRoute( + decodeResult: DecodeResult, + last: string, + time: string, + next: string, + eta: string, + then?: string, + date?: string, +) { + const lastTime = date + ? DateTimeUtils.convertDateTimeToEpoch(time, date) + : DateTimeUtils.convertHHMMSSToTod(time); + const nextTime = date + ? DateTimeUtils.convertDateTimeToEpoch(eta, date) + : DateTimeUtils.convertHHMMSSToTod(eta); + const timeFormat = date ? 'epoch' : 'tod'; + + const lastWaypoint = RouteUtils.getWaypoint(last); + lastWaypoint.time = lastTime; + lastWaypoint.timeFormat = timeFormat; + + const nextWaypoint = RouteUtils.getWaypoint(next); + nextWaypoint.time = nextTime; + nextWaypoint.timeFormat = timeFormat; + + const thenWaypoint = RouteUtils.getWaypoint(then || '?'); + + const waypoints: Waypoint[] = [lastWaypoint, nextWaypoint, thenWaypoint]; + decodeResult.raw.route = { waypoints: waypoints }; + decodeResult.formatted.items.push({ + type: 'aircraft_route', + code: 'ROUTE', + label: 'Aircraft Route', + value: RouteUtils.routeToString(decodeResult.raw.route), + }); } - function processWindData(decodeResult: DecodeResult, message: string) { - const wind = [] as Wind[]; - - const flightLevel = Number(message.slice(0, 3)); - const fields = message.slice(4).split('.'); // strip off altitude and comma - fields.forEach((field) => { - const data = field.split(','); - const waypoint = {name: data[0]}; - const windData = data[1]; - const windDirection = Number(windData.slice(0, 3)); - const windSpeed = Number(windData.slice(3)); - - if (data.length === 3) { - const tempData = data[2]; - const tempFlightLevel = Number(tempData.slice(0, 3)); - const tempString = tempData.slice(3); - const tempDegrees = Number(tempString.substring(1)) * (tempString.charAt(0) === 'M' ? -1 : 1); - wind.push({ - waypoint: waypoint, - flightLevel: flightLevel, - windDirection: windDirection, - windSpeed: windSpeed, - temperature: { - flightLevel: tempFlightLevel, - degreesC: tempDegrees - }, - }); - - } else { - wind.push({ - waypoint: waypoint, - flightLevel: flightLevel, - windDirection: windDirection, - windSpeed: windSpeed, - }); - } - }); + const wind = [] as Wind[]; + + const flightLevel = Number(message.slice(0, 3)); + const fields = message.slice(4).split('.'); // strip off altitude and comma + fields.forEach((field) => { + const data = field.split(','); + const waypoint = { name: data[0] }; + const windData = data[1]; + const windDirection = Number(windData.slice(0, 3)); + const windSpeed = Number(windData.slice(3)); + + if (data.length === 3) { + const tempData = data[2]; + const tempFlightLevel = Number(tempData.slice(0, 3)); + const tempString = tempData.slice(3); + const tempDegrees = + Number(tempString.substring(1)) * + (tempString.charAt(0) === 'M' ? -1 : 1); + wind.push({ + waypoint: waypoint, + flightLevel: flightLevel, + windDirection: windDirection, + windSpeed: windSpeed, + temperature: { + flightLevel: tempFlightLevel, + degreesC: tempDegrees, + }, + }); + } else { + wind.push({ + waypoint: waypoint, + flightLevel: flightLevel, + windDirection: windDirection, + windSpeed: windSpeed, + }); + } + }); - ResultFormatter.windData(decodeResult, wind); + ResultFormatter.windData(decodeResult, wind); } // CRC-16/IBM-SDLC but nibbles are reversed function calculateChecksum(data: string): string { - let crc = 0xFFFF; - const bytes = Buffer.from(data, 'ascii'); - - for (const byte of bytes) { - crc ^= byte; - for (let i = 0; i < 8; i++) { - if ((crc & 0x0001) !== 0) { - crc = (crc >>> 1) ^ 0x8408; - } else { - crc = (crc >>> 1); - } - } + let crc = 0xffff; + const bytes = Buffer.from(data, 'ascii'); + + for (const byte of bytes) { + crc ^= byte; + for (let i = 0; i < 8; i++) { + if ((crc & 0x0001) !== 0) { + crc = (crc >>> 1) ^ 0x8408; + } else { + crc = crc >>> 1; + } } - crc = (crc ^ 0xFFFF) & 0xFFFF; + } + crc = (crc ^ 0xffff) & 0xffff; - const nibble1 = (crc >> 12) & 0xF; - const nibble2 = (crc >> 8) & 0xF; - const nibble3 = (crc >> 4) & 0xF; - const nibble4 = crc & 0xF; + const nibble1 = (crc >> 12) & 0xf; + const nibble2 = (crc >> 8) & 0xf; + const nibble3 = (crc >> 4) & 0xf; + const nibble4 = crc & 0xf; -return `${nibble4.toString(16)}${nibble3.toString(16)}${nibble2.toString(16)}${nibble1.toString(16)}`.toUpperCase(); + return `${nibble4.toString(16)}${nibble3.toString(16)}${nibble2.toString(16)}${nibble1.toString(16)}`.toUpperCase(); } - diff --git a/lib/utils/miam.test.ts b/lib/utils/miam.test.ts index 9c05974..c96181a 100644 --- a/lib/utils/miam.test.ts +++ b/lib/utils/miam.test.ts @@ -1,23 +1,30 @@ -import { MIAMCoreUtils, MIAMCoreV1Compression, MIAMCoreV2Compression } from "./miam"; +import { + MIAMCoreUtils, + MIAMCoreV1Compression, + MIAMCoreV2Compression, +} from './miam'; test('v1, compressed, acars', () => { - const msg = 'T32!<<,W/jVEJ5u(@\\!\'s.16q/)<:-JXX|Q&a)r_CuOS?R65eRb:E8bHPILl$J-V*::RL,d]?So_4M_VS;Ln"U,;8u?`,7j=ACsP@,t.rGoaFFQm1S7*\'>kRs>,:u>X?oe2->Z-5`_Ztu,Fr.R(jR7>J^s-94:^OqFYrF][5q?tMocL[p%^T#7).P:W;.$4Ym1#&8iu%ac;%S_9iqkLZ_rC^NA6gllCR_sE-?$k^?N:\'+'; + const msg = + "T32!<<,W/jVEJ5u(@\\!'s.16q/)<:-JXX|Q&a)r_CuOS?R65eRb:E8bHPILl$J-V*::RL,d]?So_4M_VS;Ln\"U,;8u?`,7j=ACsP@,t.rGoaFFQm1S7*'>kRs>,:u>X?oe2->Z-5`_Ztu,Fr.R(jR7>J^s-94:^OqFYrF][5q?tMocL[p%^T#7).P:W;.$4Ym1#&8iu%ac;%S_9iqkLZ_rC^NA6gllCR_sE-?$k^?N:'+"; const decodeResult = MIAMCoreUtils.parse(msg); - if(!decodeResult.decoded) { - expect(decodeResult.decoded).toBe(true); - return; + if (!decodeResult.decoded) { + expect(decodeResult.decoded).toBe(true); + return; } expect(decodeResult.message.data.version).toBe(1); expect(decodeResult.message.data.complete).toBe(true); expect(decodeResult.message.data.crcOk).toBe(true); expect(decodeResult.message.data.crc).toBe(0x1a764e3e); - expect(decodeResult.message.data.compression).toBe(MIAMCoreV1Compression.Deflate); + expect(decodeResult.message.data.compression).toBe( + MIAMCoreV1Compression.Deflate, + ); expect(decodeResult.message.data.msgNum).toBe(86); expect(decodeResult.message.data.ackOptions).toBe(1); - if(!decodeResult.message.data.acars) { - expect(decodeResult.message.data.acars).toBeDefined(); - return; + if (!decodeResult.message.data.acars) { + expect(decodeResult.message.data.acars).toBeDefined(); + return; } expect(decodeResult.message.data.acars.tail).toBe('.A7-ANS'); expect(decodeResult.message.data.acars.label).toBe('H1'); @@ -25,28 +32,30 @@ test('v1, compressed, acars', () => { expect(decodeResult.message.data.acars.mfi).toBe(undefined); expect(decodeResult.message.data.acars.text).toBe( 'A350,000163,1,1,TB000000/REP054,03,01;H01,054,03,01,4210,00836,.A7-ANS,5,0,11,07,22,03,21,00,080/H02,' + - 'KDFW OTHH,QTR22G ,S0285,S0485,T9400T0301/H03,Traffic Policing Failure/A10,XXXXXXXXXXXXXXXXXXXX,' + - 'XXXXXXXXXXXXXXXXXXXX,0000000001/A11,03,20,10,03,21,00/B10,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/' + - 'B11,0000000001,XXXXXXXXXX/C10,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/C11,0000000000,XXXXXXXXXX/D10,' + - 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/D11,0000012401,XXXXXXXXXX/E10,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/' + - 'E11,0000000000,XXXXXXXXXX/F10,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/F11,0000000000,XXXXXXXXXX/:' - ) -}) + 'KDFW OTHH,QTR22G ,S0285,S0485,T9400T0301/H03,Traffic Policing Failure/A10,XXXXXXXXXXXXXXXXXXXX,' + + 'XXXXXXXXXXXXXXXXXXXX,0000000001/A11,03,20,10,03,21,00/B10,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/' + + 'B11,0000000001,XXXXXXXXXX/C10,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/C11,0000000000,XXXXXXXXXX/D10,' + + 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/D11,0000012401,XXXXXXXXXX/E10,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/' + + 'E11,0000000000,XXXXXXXXXX/F10,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/F11,0000000000,XXXXXXXXXX/:', + ); +}); test('v2, uncompressed, non-acars', () => { const msg = 'T-3!YPJ?0L8c"VuQet|KJFK,KBOS,CYHZ,KPHL'; const decodeResult = MIAMCoreUtils.parse(msg); - if(!decodeResult.decoded) { - expect(decodeResult.decoded).toBe(true); - return; + if (!decodeResult.decoded) { + expect(decodeResult.decoded).toBe(true); + return; } expect(decodeResult.message.data.version).toBe(2); expect(decodeResult.message.data.msgNum).toBe(9); expect(decodeResult.message.data.ackOptions).toBe(0); - expect(decodeResult.message.data.compression).toBe(MIAMCoreV2Compression.None); + expect(decodeResult.message.data.compression).toBe( + MIAMCoreV2Compression.None, + ); expect(decodeResult.message.data.crcOk).toBe(true); expect(decodeResult.message.data.crc).toBe(0x38a8); - if(!decodeResult.message.data.non_acars) { + if (!decodeResult.message.data.non_acars) { expect(decodeResult.message.data.non_acars).toBeDefined(); return; } @@ -54,31 +63,35 @@ test('v2, uncompressed, non-acars', () => { expect(decodeResult.message.data.non_acars.text).toBe('KJFK,KBOS,CYHZ,KPHL'); expect(decodeResult.message.data.acars).toBe(undefined); -}) +}); test('v1, compressed, acars, incomplete', () => { - const msg = 'T12!<:Whn0\'KEp#_@kN+:Whn0'KEp#_@kN+ Object.values(MIAMVersion).includes(x as MIAMVersion); -const isMIAMFid= (x: string): x is MIAMFid => Object.values(MIAMFid).includes(x as MIAMFid); -const isMIAMCoreApp= (x: number): x is MIAMCoreApp => Object.values(MIAMCoreApp).includes(x as MIAMCoreApp); -const isMIAMCorePdu= (x: number): x is MIAMCorePdu => Object.values(MIAMCorePdu).includes(x as MIAMCorePdu); +const isMIAMVersion = (x: number): x is MIAMVersion => + Object.values(MIAMVersion).includes(x as MIAMVersion); +const isMIAMFid = (x: string): x is MIAMFid => + Object.values(MIAMFid).includes(x as MIAMFid); +const isMIAMCoreApp = (x: number): x is MIAMCoreApp => + Object.values(MIAMCoreApp).includes(x as MIAMCoreApp); +const isMIAMCorePdu = (x: number): x is MIAMCorePdu => + Object.values(MIAMCorePdu).includes(x as MIAMCorePdu); interface PduDecodingSuccess { decoded: true; @@ -89,7 +93,10 @@ interface PduDecodingFailure { type PduDecodingResult = PduDecodingSuccess | PduDecodingFailure; export class MIAMCoreUtils { - static AppTypeToAppIdLenTable: Record> = { + static AppTypeToAppIdLenTable: Record< + MIAMVersion, + Record + > = { [MIAMVersion.V1]: { [MIAMCoreApp.ACARS2Char]: 2, [MIAMCoreApp.ACARS4Char]: 4, @@ -102,138 +109,162 @@ export class MIAMCoreUtils { [MIAMCoreApp.ACARS6Char]: 6, [MIAMCoreApp.NonACARS6Char]: 6, }, - } + }; - static FidHandlerTable: Record PduDecodingResult> = { - [MIAMFid.SingleTransfer]: (txt: string) => { - if (txt.length < 3) { - return { - decoded: false, - error: 'Raw MIAM message too short (' + txt.length + ' < 3) ', - }; - } + static FidHandlerTable: Record PduDecodingResult> = + { + [MIAMFid.SingleTransfer]: (txt: string) => { + if (txt.length < 3) { + return { + decoded: false, + error: 'Raw MIAM message too short (' + txt.length + ' < 3) ', + }; + } - let bpad = txt[0] + let bpad = txt[0]; - if ('0123-.'.indexOf(bpad) === -1) { - return { - decoded: false, - error: 'Invalid body padding value: \'' + bpad + '\'', - }; - } + if ('0123-.'.indexOf(bpad) === -1) { + return { + decoded: false, + error: "Invalid body padding value: '" + bpad + "'", + }; + } - if ('0123'.indexOf(txt[1]) === -1) { - return { - decoded: false, - error: 'Invalid header padding value: \'' + txt[1] + '\'', - }; - } + if ('0123'.indexOf(txt[1]) === -1) { + return { + decoded: false, + error: "Invalid header padding value: '" + txt[1] + "'", + }; + } - const hpad = parseInt(txt[1]); + const hpad = parseInt(txt[1]); - const delimIdx = txt.indexOf('|') - if (delimIdx === -1) { - return { - decoded: false, - error: 'Raw MIAM message missing header-body delimiter', - }; - } + const delimIdx = txt.indexOf('|'); + if (delimIdx === -1) { + return { + decoded: false, + error: 'Raw MIAM message missing header-body delimiter', + }; + } - const rawHdr = txt.substring(2, delimIdx); - if (rawHdr.length === 0) { - return { - decoded: false, - error: 'Empty MIAM message header', - }; - } + const rawHdr = txt.substring(2, delimIdx); + if (rawHdr.length === 0) { + return { + decoded: false, + error: 'Empty MIAM message header', + }; + } - let hdr = Base85.decode('<~' + rawHdr + '~>', 'ascii85'); - if (!hdr || hdr.length < hpad) { - return { - decoded: false, - error: 'Ascii85 decode failed for MIAM message header', - }; - } + let hdr = Base85.decode('<~' + rawHdr + '~>', 'ascii85'); + if (!hdr || hdr.length < hpad) { + return { + decoded: false, + error: 'Ascii85 decode failed for MIAM message header', + }; + } - let body: Buffer | undefined = undefined; + let body: Buffer | undefined = undefined; - const rawBody = txt.substring(delimIdx + 1); - if (rawBody.length > 0) { - if ('0123'.indexOf(bpad) >= 0) { - const bpadValue = parseInt(bpad); + const rawBody = txt.substring(delimIdx + 1); + if (rawBody.length > 0) { + if ('0123'.indexOf(bpad) >= 0) { + const bpadValue = parseInt(bpad); - body = Base85.decode('<~' + rawBody + '~>', 'ascii85') || undefined; - if (body && body.length >= bpadValue) { - body = body.subarray(0, body.length - bpadValue); + body = Base85.decode('<~' + rawBody + '~>', 'ascii85') || undefined; + if (body && body.length >= bpadValue) { + body = body.subarray(0, body.length - bpadValue); + } + } else if (bpad === '-') { + body = Buffer.from(rawBody); } - } else if (bpad === '-') { - body = Buffer.from(rawBody); } - } - hdr = hdr.subarray(0, hdr.length - hpad); + hdr = hdr.subarray(0, hdr.length - hpad); - const version = hdr.readUInt8(0) & 0xf; - const pduType = (hdr.readUInt8(0) >> 4) & 0xf; + const version = hdr.readUInt8(0) & 0xf; + const pduType = (hdr.readUInt8(0) >> 4) & 0xf; - if(isMIAMVersion(version) && isMIAMCorePdu(pduType)) { - const versionPduHandler = this.VersionPduHandlerTable[version][pduType]; - return versionPduHandler(hdr, body); - } else { + if (isMIAMVersion(version) && isMIAMCorePdu(pduType)) { + const versionPduHandler = + this.VersionPduHandlerTable[version][pduType]; + return versionPduHandler(hdr, body); + } else { + return { + decoded: false, + error: + 'Invalid version and PDU type combination: v=' + + version + + ', pdu=' + + pduType, + }; + } + }, + [MIAMFid.FileTransferRequest]: () => { return { decoded: false, - error: 'Invalid version and PDU type combination: v=' + version + ', pdu=' + pduType, + error: 'File Transfer Request not implemented', }; - } - }, - [MIAMFid.FileTransferRequest]: () => {return {decoded: false, error: 'File Transfer Request not implemented'}}, - [MIAMFid.FileTransferAccept]: () => {return {decoded: false, error: 'File Transfer Accept not implemented'}}, - [MIAMFid.FileSegment]: () => {return {decoded: false, error: 'File Segment not implemented'}}, - [MIAMFid.FileTransferAbort]: () => {return {decoded: false, error: 'File Transfer Abort not implemented'}}, - [MIAMFid.XOFFIndication]: () => {return {decoded: false, error: 'XOFF Indication not implemented'}}, - [MIAMFid.XONIndication]: () => {return {decoded: false, error: 'XON Indication not implemented'}}, - } + }, + [MIAMFid.FileTransferAccept]: () => { + return { + decoded: false, + error: 'File Transfer Accept not implemented', + }; + }, + [MIAMFid.FileSegment]: () => { + return { decoded: false, error: 'File Segment not implemented' }; + }, + [MIAMFid.FileTransferAbort]: () => { + return { decoded: false, error: 'File Transfer Abort not implemented' }; + }, + [MIAMFid.XOFFIndication]: () => { + return { decoded: false, error: 'XOFF Indication not implemented' }; + }, + [MIAMFid.XONIndication]: () => { + return { decoded: false, error: 'XON Indication not implemented' }; + }, + }; private static arincCrc16(buf: Buffer, seed?: number) { const crctable = [ - 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, - 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, - 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, - 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, - 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, - 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, - 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, - 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, - 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, - 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, - 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, - 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, - 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, - 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, - 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, - 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, - 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, - 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, - 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, - 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, - 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, - 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, - 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, - 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, - 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, - 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, - 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, - 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, - 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, - 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, - 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, - 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, + 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210, + 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 0x9339, 0x8318, 0xb37b, + 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401, + 0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, + 0xf5cf, 0xc5ac, 0xd58d, 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, + 0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, + 0xc7bc, 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 0x5af5, + 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc, + 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 0x6ca6, 0x7c87, 0x4ce4, + 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd, + 0xad2a, 0xbd0b, 0x8d68, 0x9d49, 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, + 0x2e32, 0x1e51, 0x0e70, 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, + 0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, + 0xe16f, 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 0x02b1, + 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb, + 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, 0x34e2, 0x24c3, 0x14a0, + 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8, + 0xe75f, 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, + 0x7676, 0x4615, 0x5634, 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, + 0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, + 0x28a3, 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, 0xfd2e, + 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07, + 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, 0xef1f, 0xff3e, 0xcf5d, + 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74, + 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0, ]; let crc = (seed || 0) & 0xffff; for (let i = 0; i < buf.length; i++) { - crc = (((crc << 8) >>> 0) ^ crctable[(((crc >>> 8) ^ buf.readUInt8(i)) >>> 0) & 0xff]) >>> 0; + crc = + (((crc << 8) >>> 0) ^ + crctable[(((crc >>> 8) ^ buf.readUInt8(i)) >>> 0) & 0xff]) >>> + 0; } return crc & 0xffff; @@ -241,76 +272,58 @@ export class MIAMCoreUtils { private static arinc665Crc32(buf: Buffer, seed?: number) { const crctable = [ - 0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9, - 0x130476DC, 0x17C56B6B, 0x1A864DB2, 0x1E475005, - 0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6, 0x2B4BCB61, - 0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD, - 0x4C11DB70, 0x48D0C6C7, 0x4593E01E, 0x4152FDA9, - 0x5F15ADAC, 0x5BD4B01B, 0x569796C2, 0x52568B75, - 0x6A1936C8, 0x6ED82B7F, 0x639B0DA6, 0x675A1011, - 0x791D4014, 0x7DDC5DA3, 0x709F7B7A, 0x745E66CD, - 0x9823B6E0, 0x9CE2AB57, 0x91A18D8E, 0x95609039, - 0x8B27C03C, 0x8FE6DD8B, 0x82A5FB52, 0x8664E6E5, - 0xBE2B5B58, 0xBAEA46EF, 0xB7A96036, 0xB3687D81, - 0xAD2F2D84, 0xA9EE3033, 0xA4AD16EA, 0xA06C0B5D, - 0xD4326D90, 0xD0F37027, 0xDDB056FE, 0xD9714B49, - 0xC7361B4C, 0xC3F706FB, 0xCEB42022, 0xCA753D95, - 0xF23A8028, 0xF6FB9D9F, 0xFBB8BB46, 0xFF79A6F1, - 0xE13EF6F4, 0xE5FFEB43, 0xE8BCCD9A, 0xEC7DD02D, - 0x34867077, 0x30476DC0, 0x3D044B19, 0x39C556AE, - 0x278206AB, 0x23431B1C, 0x2E003DC5, 0x2AC12072, - 0x128E9DCF, 0x164F8078, 0x1B0CA6A1, 0x1FCDBB16, - 0x018AEB13, 0x054BF6A4, 0x0808D07D, 0x0CC9CDCA, - 0x7897AB07, 0x7C56B6B0, 0x71159069, 0x75D48DDE, - 0x6B93DDDB, 0x6F52C06C, 0x6211E6B5, 0x66D0FB02, - 0x5E9F46BF, 0x5A5E5B08, 0x571D7DD1, 0x53DC6066, - 0x4D9B3063, 0x495A2DD4, 0x44190B0D, 0x40D816BA, - 0xACA5C697, 0xA864DB20, 0xA527FDF9, 0xA1E6E04E, - 0xBFA1B04B, 0xBB60ADFC, 0xB6238B25, 0xB2E29692, - 0x8AAD2B2F, 0x8E6C3698, 0x832F1041, 0x87EE0DF6, - 0x99A95DF3, 0x9D684044, 0x902B669D, 0x94EA7B2A, - 0xE0B41DE7, 0xE4750050, 0xE9362689, 0xEDF73B3E, - 0xF3B06B3B, 0xF771768C, 0xFA325055, 0xFEF34DE2, - 0xC6BCF05F, 0xC27DEDE8, 0xCF3ECB31, 0xCBFFD686, - 0xD5B88683, 0xD1799B34, 0xDC3ABDED, 0xD8FBA05A, - 0x690CE0EE, 0x6DCDFD59, 0x608EDB80, 0x644FC637, - 0x7A089632, 0x7EC98B85, 0x738AAD5C, 0x774BB0EB, - 0x4F040D56, 0x4BC510E1, 0x46863638, 0x42472B8F, - 0x5C007B8A, 0x58C1663D, 0x558240E4, 0x51435D53, - 0x251D3B9E, 0x21DC2629, 0x2C9F00F0, 0x285E1D47, - 0x36194D42, 0x32D850F5, 0x3F9B762C, 0x3B5A6B9B, - 0x0315D626, 0x07D4CB91, 0x0A97ED48, 0x0E56F0FF, - 0x1011A0FA, 0x14D0BD4D, 0x19939B94, 0x1D528623, - 0xF12F560E, 0xF5EE4BB9, 0xF8AD6D60, 0xFC6C70D7, - 0xE22B20D2, 0xE6EA3D65, 0xEBA91BBC, 0xEF68060B, - 0xD727BBB6, 0xD3E6A601, 0xDEA580D8, 0xDA649D6F, - 0xC423CD6A, 0xC0E2D0DD, 0xCDA1F604, 0xC960EBB3, - 0xBD3E8D7E, 0xB9FF90C9, 0xB4BCB610, 0xB07DABA7, - 0xAE3AFBA2, 0xAAFBE615, 0xA7B8C0CC, 0xA379DD7B, - 0x9B3660C6, 0x9FF77D71, 0x92B45BA8, 0x9675461F, - 0x8832161A, 0x8CF30BAD, 0x81B02D74, 0x857130C3, - 0x5D8A9099, 0x594B8D2E, 0x5408ABF7, 0x50C9B640, - 0x4E8EE645, 0x4A4FFBF2, 0x470CDD2B, 0x43CDC09C, - 0x7B827D21, 0x7F436096, 0x7200464F, 0x76C15BF8, - 0x68860BFD, 0x6C47164A, 0x61043093, 0x65C52D24, - 0x119B4BE9, 0x155A565E, 0x18197087, 0x1CD86D30, - 0x029F3D35, 0x065E2082, 0x0B1D065B, 0x0FDC1BEC, - 0x3793A651, 0x3352BBE6, 0x3E119D3F, 0x3AD08088, - 0x2497D08D, 0x2056CD3A, 0x2D15EBE3, 0x29D4F654, - 0xC5A92679, 0xC1683BCE, 0xCC2B1D17, 0xC8EA00A0, - 0xD6AD50A5, 0xD26C4D12, 0xDF2F6BCB, 0xDBEE767C, - 0xE3A1CBC1, 0xE760D676, 0xEA23F0AF, 0xEEE2ED18, - 0xF0A5BD1D, 0xF464A0AA, 0xF9278673, 0xFDE69BC4, - 0x89B8FD09, 0x8D79E0BE, 0x803AC667, 0x84FBDBD0, - 0x9ABC8BD5, 0x9E7D9662, 0x933EB0BB, 0x97FFAD0C, - 0xAFB010B1, 0xAB710D06, 0xA6322BDF, 0xA2F33668, - 0xBCB4666D, 0xB8757BDA, 0xB5365D03, 0xB1F740B4 + 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, + 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, + 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7, + 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, + 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, + 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef, + 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, + 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, + 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, + 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, + 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, + 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, + 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, + 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, + 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, + 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, + 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050, + 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, + 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, + 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, + 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1, + 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, + 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, + 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, + 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9, + 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, + 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, + 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, + 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, + 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, + 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, + 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, + 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, + 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, + 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, + 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676, + 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, + 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, + 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, + 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4, ]; let crc = seed || 0; for (let i = 0; i < buf.length; i++) { - crc = (((crc << 8) >>> 0) ^ crctable[((crc >>> 24) ^ buf.readUInt8(i)) >>> 0]) >>> 0; + crc = + (((crc << 8) >>> 0) ^ + crctable[((crc >>> 24) ^ buf.readUInt8(i)) >>> 0]) >>> + 0; } return crc; @@ -319,23 +332,34 @@ export class MIAMCoreUtils { public static parse(txt: string): PduDecodingResult { const fidType = txt[0]; - if(isMIAMFid(fidType)) { + if (isMIAMFid(fidType)) { const handler = this.FidHandlerTable[fidType]; return handler(txt.substring(1)); - }else { + } else { return { decoded: false, error: 'Unsupported FID type: ' + fidType, }; } - } - private static corePduDataHandler(version: MIAMVersion, minHdrSize: number, crcLen: number, hdr: Buffer, body?: Buffer): PduDecodingResult { + private static corePduDataHandler( + version: MIAMVersion, + minHdrSize: number, + crcLen: number, + hdr: Buffer, + body?: Buffer, + ): PduDecodingResult { if (hdr.length < minHdrSize) { return { decoded: false, - error: 'v' + version + ' header size too short; expected >= ' + minHdrSize + ', got ' + hdr.length, + error: + 'v' + + version + + ' header size too short; expected >= ' + + minHdrSize + + ', got ' + + hdr.length, }; } @@ -356,12 +380,15 @@ export class MIAMCoreUtils { let ackOptions: number = 0; if (version === MIAMVersion.V1) { - pduSize = (hdr.readUInt8(1) << 16) | (hdr.readUInt8(2) << 8) | hdr.readUInt8(3); + pduSize = + (hdr.readUInt8(1) << 16) | (hdr.readUInt8(2) << 8) | hdr.readUInt8(3); const msgSize = hdr.length + (body === undefined ? 0 : body.length); if (pduSize > msgSize) { pduIsComplete = false; - pduErrors.push('v1 PDU truncated: expecting ' + pduSize + ', got ' + msgSize); + pduErrors.push( + 'v1 PDU truncated: expecting ' + pduSize + ', got ' + msgSize, + ); } hdr = hdr.subarray(4); @@ -375,16 +402,21 @@ export class MIAMCoreUtils { ackOptions = hdr.readUInt8(0) & 1; hdr = hdr.subarray(1); - pduCompression = ((hdr.readUInt8(0) << 2) | ((hdr.readUInt8(1) >> 6) & 0x3)) & 0x7; + pduCompression = + ((hdr.readUInt8(0) << 2) | ((hdr.readUInt8(1) >> 6) & 0x3)) & 0x7; pduEncoding = (hdr.readUInt8(1) >> 4) & 0x3; pduAppType = hdr.readUInt8(1) & 0xf; - hdr = hdr.subarray(2) + hdr = hdr.subarray(2); let appIdLen; - if(isMIAMCoreApp(pduAppType)) { + if (isMIAMCoreApp(pduAppType)) { appIdLen = this.AppTypeToAppIdLenTable[version][pduAppType]; } else { - if (version === MIAMVersion.V2 && (pduAppType & 0x8) !== 0 && pduAppType !== 0xd) { + if ( + version === MIAMVersion.V2 && + (pduAppType & 0x8) !== 0 && + pduAppType !== 0xd + ) { appIdLen = (pduAppType & 0x7) + 1; } else { return { @@ -394,9 +426,12 @@ export class MIAMCoreUtils { } } - const pduIsACARS = ([ - MIAMCoreApp.ACARS2Char, MIAMCoreApp.ACARS4Char, MIAMCoreApp.ACARS6Char - ].indexOf(pduAppType) >= 0); + const pduIsACARS = + [ + MIAMCoreApp.ACARS2Char, + MIAMCoreApp.ACARS4Char, + MIAMCoreApp.ACARS6Char, + ].indexOf(pduAppType) >= 0; if (hdr.length < appIdLen + crcLen) { return { @@ -405,18 +440,26 @@ export class MIAMCoreUtils { }; } - pduAppId = hdr.subarray(0, appIdLen).toString('ascii') + pduAppId = hdr.subarray(0, appIdLen).toString('ascii'); hdr = hdr.subarray(appIdLen); if (crcLen === 4) { - pduCrc = (hdr.readUInt8(0) << 24) | (hdr.readUInt8(1) << 16) | (hdr.readUInt8(2) << 8) | hdr.readUInt8(3); // crc + pduCrc = + (hdr.readUInt8(0) << 24) | + (hdr.readUInt8(1) << 16) | + (hdr.readUInt8(2) << 8) | + hdr.readUInt8(3); // crc } else if (crcLen === 2) { pduCrc = (hdr.readUInt8(0) << 8) | hdr.readUInt8(1); // crc } - hdr = hdr.subarray(crcLen) + hdr = hdr.subarray(crcLen); if (body !== undefined && body.length > 0) { - if ([MIAMCoreV1Compression.Deflate, MIAMCoreV2Compression.Deflate].indexOf(pduCompression) >= 0) { + if ( + [MIAMCoreV1Compression.Deflate, MIAMCoreV2Compression.Deflate].indexOf( + pduCompression, + ) >= 0 + ) { try { const decompress = new zlib.InflateRaw({}); decompress.write(body); @@ -425,15 +468,26 @@ export class MIAMCoreUtils { } catch (e) { pduErrors.push('Inflation failed for body: ' + e); } - } else if ([MIAMCoreV1Compression.None, MIAMCoreV2Compression.None].indexOf(pduCompression) >= 0) { + } else if ( + [MIAMCoreV1Compression.None, MIAMCoreV2Compression.None].indexOf( + pduCompression, + ) >= 0 + ) { pduData = body; } else { - pduErrors.push('Unsupported v' + version + ' compression type: ' + pduCompression) + pduErrors.push( + 'Unsupported v' + version + ' compression type: ' + pduCompression, + ); } if (pduData !== null) { - const crcAlgoHandlerByVersion: Record number> = { - [MIAMVersion.V1]: (buf: Buffer, seed?: number) => { return ~this.arinc665Crc32(buf, seed); }, + const crcAlgoHandlerByVersion: Record< + MIAMVersion, + (buf: Buffer, seed?: number) => number + > = { + [MIAMVersion.V1]: (buf: Buffer, seed?: number) => { + return ~this.arinc665Crc32(buf, seed); + }, [MIAMVersion.V2]: this.arincCrc16, }; @@ -449,7 +503,12 @@ export class MIAMCoreUtils { if (crcCheck === pduCrc) { pduCrcIsOk = true; } else { - pduErrors.push('Body failed CRC check: provided=' + pduCrc + ', generated=' + crcCheck); + pduErrors.push( + 'Body failed CRC check: provided=' + + pduCrc + + ', generated=' + + crcCheck, + ); } } } else { @@ -466,12 +525,12 @@ export class MIAMCoreUtils { encoding: pduEncoding, msgNum, ackOptions, - } + }; if (pduIsACARS) { const label = pduAppId.substring(0, 2); - const sublabel = (appIdLen >= 4) ? pduAppId.substring(2, 4) : undefined; - const mfi = (appIdLen >= 6) ? pduAppId.substring(4, 6) : undefined; + const sublabel = appIdLen >= 4 ? pduAppId.substring(2, 4) : undefined; + const mfi = appIdLen >= 6 ? pduAppId.substring(4, 6) : undefined; pdu.acars = { ...(tail ? { tail } : {}), @@ -479,12 +538,12 @@ export class MIAMCoreUtils { ...(sublabel ? { sublabel } : {}), ...(mfi ? { mfi } : {}), ...(pduData ? { text: pduData.toString('ascii') } : {}), - } + }; } else { pdu.non_acars = { appId: pduAppId, ...(pduData ? { text: pduData.toString('ascii') } : {}), - } + }; } return { @@ -495,18 +554,49 @@ export class MIAMCoreUtils { }; } - static VersionPduHandlerTable: Record PduDecodingResult)>> = { + static VersionPduHandlerTable: Record< + MIAMVersion, + Record PduDecodingResult> + > = { [MIAMVersion.V1]: { - [MIAMCorePdu.Data]: (hdr: Buffer, body?: Buffer) => { return this.corePduDataHandler(MIAMVersion.V1, 20, MIAMCoreV1CRCLength, hdr, body); }, - [MIAMCorePdu.Ack]: () => {return {decoded: false, error: 'v1 Ack PDU not implemented'}}, - [MIAMCorePdu.Aloha]: () => { return {decoded: false, error: 'v1 Aloha PDU not implemented'}}, - [MIAMCorePdu.AlohaReply]: () => {return {decoded: false, error: 'v1 AlohaReply PDU not implemented'}}, + [MIAMCorePdu.Data]: (hdr: Buffer, body?: Buffer) => { + return this.corePduDataHandler( + MIAMVersion.V1, + 20, + MIAMCoreV1CRCLength, + hdr, + body, + ); + }, + [MIAMCorePdu.Ack]: () => { + return { decoded: false, error: 'v1 Ack PDU not implemented' }; + }, + [MIAMCorePdu.Aloha]: () => { + return { decoded: false, error: 'v1 Aloha PDU not implemented' }; + }, + [MIAMCorePdu.AlohaReply]: () => { + return { decoded: false, error: 'v1 AlohaReply PDU not implemented' }; + }, }, [MIAMVersion.V2]: { - [MIAMCorePdu.Data]: (hdr: Buffer, body?: Buffer) => { return this.corePduDataHandler(MIAMVersion.V2, 7, MIAMCoreV2CRCLength, hdr, body); }, - [MIAMCorePdu.Ack]: () => {return {decoded: false, error: 'v2 Ack PDU not implemented'}}, - [MIAMCorePdu.Aloha]: () => {return {decoded: false, error: 'v2 Aloha PDU not implemented'}}, - [MIAMCorePdu.AlohaReply]: () => {return {decoded: false, error: 'v2 Aloha reply PDU not implemented'}}, - } - } + [MIAMCorePdu.Data]: (hdr: Buffer, body?: Buffer) => { + return this.corePduDataHandler( + MIAMVersion.V2, + 7, + MIAMCoreV2CRCLength, + hdr, + body, + ); + }, + [MIAMCorePdu.Ack]: () => { + return { decoded: false, error: 'v2 Ack PDU not implemented' }; + }, + [MIAMCorePdu.Aloha]: () => { + return { decoded: false, error: 'v2 Aloha PDU not implemented' }; + }, + [MIAMCorePdu.AlohaReply]: () => { + return { decoded: false, error: 'v2 Aloha reply PDU not implemented' }; + }, + }, + }; } diff --git a/lib/utils/result_formatter.ts b/lib/utils/result_formatter.ts index 33061c7..0425ccc 100644 --- a/lib/utils/result_formatter.ts +++ b/lib/utils/result_formatter.ts @@ -1,512 +1,534 @@ -import { DecodeResult } from "../DecoderPluginInterface"; -import { CoordinateUtils } from "./coordinate_utils"; -import { DateTimeUtils } from "../DateTimeUtils"; -import { RouteUtils } from "./route_utils"; -import { Route } from "../types/route"; -import { Wind } from "../types/wind"; +import { DecodeResult } from '../DecoderPluginInterface'; +import { CoordinateUtils } from './coordinate_utils'; +import { DateTimeUtils } from '../DateTimeUtils'; +import { RouteUtils } from './route_utils'; +import { Route } from '../types/route'; +import { Wind } from '../types/wind'; /** * Class to format the results of common fields */ export class ResultFormatter { + static route(decodeResult: DecodeResult, route: Route) { + decodeResult.raw.route = route; + decodeResult.formatted.items.push({ + type: 'aircraft_route', + code: 'ROUTE', + label: 'Aircraft Route', + value: RouteUtils.routeToString(route), + }); + } - static route(decodeResult: DecodeResult, route: Route) { - decodeResult.raw.route = route; - decodeResult.formatted.items.push({ - type: 'aircraft_route', - code: 'ROUTE', - label: 'Aircraft Route', - value: RouteUtils.routeToString(route), - }); + static state_change(decodeResult: DecodeResult, from: string, to: string) { + decodeResult.raw.state_change = { + from: from, + to: to, }; + from = RouteUtils.formatFlightState(from); + to = RouteUtils.formatFlightState(to); + decodeResult.formatted.items.push({ + type: 'state_change', + code: 'STATE_CHANGE', + label: 'State Change', + value: `${from} -> ${to}`, + }); + } - static state_change(decodeResult: DecodeResult, from: string, to: string) { - decodeResult.raw.state_change = { - from: from, - to: to, - }; - from = RouteUtils.formatFlightState(from); - to = RouteUtils.formatFlightState(to); - decodeResult.formatted.items.push({ - type: 'state_change', - code: 'STATE_CHANGE', - label: 'State Change', - value: `${from} -> ${to}`, - }); - } + static freetext(decodeResult: DecodeResult, value: string) { + decodeResult.raw.freetext = value; + decodeResult.formatted.items.push({ + type: 'freetext', + code: 'FREE_TEXT', + label: 'Free Text', + value: value, + }); + } - static freetext(decodeResult: DecodeResult, value: string) { - decodeResult.raw.freetext = value; - decodeResult.formatted.items.push({ - type: 'freetext', - code: 'FREE_TEXT', - label: 'Free Text', - value: value, - }); - } + static door_event(decodeResult: DecodeResult, name: string, state: string) { + decodeResult.raw.door_event = { + door: name, + state: state, + }; - static door_event(decodeResult: DecodeResult, name: string, state: string) { - decodeResult.raw.door_event = { - door: name, - state: state, - }; + decodeResult.formatted.items.push({ + type: 'door_event', + code: 'DOOR', + label: 'Door Event', + value: `${name} ${state}`, + }); + } - decodeResult.formatted.items.push({ - type: 'door_event', - code: 'DOOR', - label: 'Door Event', - value: `${name} ${state}`, - }); + static position( + decodeResult: DecodeResult, + value: { latitude: number; longitude: number } | undefined, + ) { + if (!value || isNaN(value.latitude) || isNaN(value.longitude)) { + return; } + decodeResult.raw.position = value; + decodeResult.formatted.items.push({ + type: 'aircraft_position', + code: 'POS', + label: 'Aircraft Position', + value: CoordinateUtils.coordinateString(value), + }); + } - static position(decodeResult: DecodeResult, value: { latitude: number, longitude: number } | undefined) { - if (!value || isNaN(value.latitude) || isNaN(value.longitude)) { - return; - } - decodeResult.raw.position = value; - decodeResult.formatted.items.push({ - type: 'aircraft_position', - code: 'POS', - label: 'Aircraft Position', - value: CoordinateUtils.coordinateString(value), - }); + static altitude(decodeResult: DecodeResult, value: number) { + if (isNaN(value)) { + return; } + decodeResult.raw.altitude = value; + decodeResult.formatted.items.push({ + type: 'altitude', + code: 'ALT', + label: 'Altitude', + value: `${decodeResult.raw.altitude} feet`, + }); + } - static altitude(decodeResult: DecodeResult, value: number) { - if(isNaN(value)) { - return; - } - decodeResult.raw.altitude = value; - decodeResult.formatted.items.push({ - type: 'altitude', - code: 'ALT', - label: 'Altitude', - value: `${decodeResult.raw.altitude} feet`, - }); + static flightNumber(decodeResult: DecodeResult, value: string) { + if (value.length === 0) { + return; } + decodeResult.raw.flight_number = value; + decodeResult.formatted.items.push({ + type: 'flight_number', + code: 'FLIGHT', + label: 'Flight Number', + value: decodeResult.raw.flight_number, + }); + } - static flightNumber(decodeResult: DecodeResult, value: string) { - if(value.length === 0 ) { - return; - } - decodeResult.raw.flight_number = value; - decodeResult.formatted.items.push({ - type: 'flight_number', - code: 'FLIGHT', - label: 'Flight Number', - value: decodeResult.raw.flight_number, - }); - }; + static callsign(decodeResult: DecodeResult, value: string) { + decodeResult.raw.callsign = value; + decodeResult.formatted.items.push({ + type: 'callsign', + code: 'CALLSIGN', + label: 'Callsign', + value: decodeResult.raw.callsign, + }); + } - static callsign(decodeResult: DecodeResult, value: string) { - decodeResult.raw.callsign = value; - decodeResult.formatted.items.push({ - type: 'callsign', - code: 'CALLSIGN', - label: 'Callsign', - value: decodeResult.raw.callsign, - }); - }; + static departureAirport( + decodeResult: DecodeResult, + value: string, + type: 'IATA' | 'ICAO' = 'ICAO', + ) { + if (type === 'ICAO') { + decodeResult.raw.departure_icao = value; + decodeResult.formatted.items.push({ + type: 'icao', + code: 'ORG', + label: 'Origin', + value: value, + }); + } else { + decodeResult.raw.departure_iata = value; + decodeResult.formatted.items.push({ + type: 'iata', + code: 'ORG', + label: 'Origin', + value: value, + }); + } + } - static departureAirport(decodeResult: DecodeResult, value: string, type: 'IATA' | 'ICAO' = 'ICAO') { - if (type === 'ICAO') { - decodeResult.raw.departure_icao = value; - decodeResult.formatted.items.push({ - type: 'icao', - code: 'ORG', - label: 'Origin', - value: value, - }); - } else { - decodeResult.raw.departure_iata = value; - decodeResult.formatted.items.push({ - type: 'iata', - code: 'ORG', - label: 'Origin', - value: value, - }); - } - }; + static departureRunway(decodeResult: DecodeResult, value: string) { + decodeResult.raw.departure_runway = value; + decodeResult.formatted.items.push({ + type: 'runway', + code: 'DEPRWY', + label: 'Departure Runway', + value: decodeResult.raw.departure_runway, + }); + } - static departureRunway(decodeResult: DecodeResult, value: string) { - decodeResult.raw.departure_runway = value; - decodeResult.formatted.items.push({ - type: 'runway', - code: 'DEPRWY', - label: 'Departure Runway', - value: decodeResult.raw.departure_runway, - }); + static arrivalAirport( + decodeResult: DecodeResult, + value: string, + type: 'IATA' | 'ICAO' = 'ICAO', + ) { + if (type === 'ICAO') { + decodeResult.raw.arrival_icao = value; + decodeResult.formatted.items.push({ + type: 'icao', + code: 'DST', + label: 'Destination', + value: value, + }); + } else { + decodeResult.raw.arrival_iata = value; + decodeResult.formatted.items.push({ + type: 'iata', + code: 'DST', + label: 'Destination', + value: value, + }); } + } - static arrivalAirport(decodeResult: DecodeResult, value: string, type: 'IATA' | 'ICAO' = 'ICAO') { - if (type === 'ICAO') { - decodeResult.raw.arrival_icao = value; - decodeResult.formatted.items.push({ - type: 'icao', - code: 'DST', - label: 'Destination', - value: value, - }); - } else { - decodeResult.raw.arrival_iata = value; - decodeResult.formatted.items.push({ - type: 'iata', - code: 'DST', - label: 'Destination', - value: value, - }); - } - }; - - static alternateAirport(decodeResult: DecodeResult, value: string) { - decodeResult.raw.alternate_icao = value; - decodeResult.formatted.items.push({ - type: 'icao', - code: 'ALT_DST', - label: 'Alternate Destination', - value: decodeResult.raw.alternate_icao, - }); - }; + static alternateAirport(decodeResult: DecodeResult, value: string) { + decodeResult.raw.alternate_icao = value; + decodeResult.formatted.items.push({ + type: 'icao', + code: 'ALT_DST', + label: 'Alternate Destination', + value: decodeResult.raw.alternate_icao, + }); + } - static eta(decodeResult: DecodeResult, time: number, type: 'tod' | 'epoch' = 'tod') { - if (type === 'tod') { - decodeResult.raw.eta_time = time; - decodeResult.formatted.items.push({ - type: 'time_of_day', - code: 'ETA', - label: 'Estimated Time of Arrival', - value: DateTimeUtils.timestampToString(time, 'tod'), - }); - } else { - decodeResult.raw.eta_date = time; - decodeResult.formatted.items.push({ - type: 'epoch', - code: 'ETA', - label: 'Estimated Time of Arrival', - value: DateTimeUtils.timestampToString(time, 'epoch'), - }); - } + static eta( + decodeResult: DecodeResult, + time: number, + type: 'tod' | 'epoch' = 'tod', + ) { + if (type === 'tod') { + decodeResult.raw.eta_time = time; + decodeResult.formatted.items.push({ + type: 'time_of_day', + code: 'ETA', + label: 'Estimated Time of Arrival', + value: DateTimeUtils.timestampToString(time, 'tod'), + }); + } else { + decodeResult.raw.eta_date = time; + decodeResult.formatted.items.push({ + type: 'epoch', + code: 'ETA', + label: 'Estimated Time of Arrival', + value: DateTimeUtils.timestampToString(time, 'epoch'), + }); } + } - static arrivalRunway(decodeResult: DecodeResult, value: string) { - decodeResult.raw.arrival_runway = value; - decodeResult.formatted.items.push({ - type: 'runway', - code: 'ARWY', - label: 'Arrival Runway', - value: decodeResult.raw.arrival_runway, - }); - }; - - static alternateRunway(decodeResult: DecodeResult, value: string) { - decodeResult.raw.alternate_runway = value; - decodeResult.formatted.items.push({ - type: 'runway', - code: 'ALT_ARWY', - label: 'Alternate Runway', - value: decodeResult.raw.alternate_runway, - }); - }; + static arrivalRunway(decodeResult: DecodeResult, value: string) { + decodeResult.raw.arrival_runway = value; + decodeResult.formatted.items.push({ + type: 'runway', + code: 'ARWY', + label: 'Arrival Runway', + value: decodeResult.raw.arrival_runway, + }); + } - static currentFuel(decodeResult: DecodeResult, value: number) { - decodeResult.raw.fuel_on_board = value; - decodeResult.formatted.items.push({ - type: 'fuel_on_board', - code: 'FOB', - label: 'Fuel On Board', - value: decodeResult.raw.fuel_on_board.toString(), - }); - }; + static alternateRunway(decodeResult: DecodeResult, value: string) { + decodeResult.raw.alternate_runway = value; + decodeResult.formatted.items.push({ + type: 'runway', + code: 'ALT_ARWY', + label: 'Alternate Runway', + value: decodeResult.raw.alternate_runway, + }); + } - static burnedFuel(decodeResult: DecodeResult, value: number) { - decodeResult.raw.fuel_burned = value; - decodeResult.formatted.items.push({ - type: 'fuel_burned', - code: 'FB', - label: 'Fuel Burned', - value: decodeResult.raw.fuel_burned.toString(), - }); - }; + static currentFuel(decodeResult: DecodeResult, value: number) { + decodeResult.raw.fuel_on_board = value; + decodeResult.formatted.items.push({ + type: 'fuel_on_board', + code: 'FOB', + label: 'Fuel On Board', + value: decodeResult.raw.fuel_on_board.toString(), + }); + } - static remainingFuel(decodeResult: DecodeResult, value: number) { - decodeResult.raw.fuel_remaining = value; - decodeResult.formatted.items.push({ - type: 'fuel_remaining', - code: 'FUEL', - label: 'Fuel Remaining', - value: decodeResult.raw.fuel_remaining.toString(), - }); - }; + static burnedFuel(decodeResult: DecodeResult, value: number) { + decodeResult.raw.fuel_burned = value; + decodeResult.formatted.items.push({ + type: 'fuel_burned', + code: 'FB', + label: 'Fuel Burned', + value: decodeResult.raw.fuel_burned.toString(), + }); + } + static remainingFuel(decodeResult: DecodeResult, value: number) { + decodeResult.raw.fuel_remaining = value; + decodeResult.formatted.items.push({ + type: 'fuel_remaining', + code: 'FUEL', + label: 'Fuel Remaining', + value: decodeResult.raw.fuel_remaining.toString(), + }); + } - static checksum(decodeResult: DecodeResult, value: string) { - decodeResult.raw.checksum = Number("0x" + value); - decodeResult.formatted.items.push({ - type: 'message_checksum', - code: 'CHECKSUM', - label: 'Message Checksum', - value: '0x' + ('0000' + decodeResult.raw.checksum.toString(16)).slice(-4), - }); - }; + static checksum(decodeResult: DecodeResult, value: string) { + decodeResult.raw.checksum = Number('0x' + value); + decodeResult.formatted.items.push({ + type: 'message_checksum', + code: 'CHECKSUM', + label: 'Message Checksum', + value: '0x' + ('0000' + decodeResult.raw.checksum.toString(16)).slice(-4), + }); + } - static groundspeed(decodeResult: DecodeResult, value: number) { - decodeResult.raw.groundspeed = value; - decodeResult.formatted.items.push({ - type: 'aircraft_groundspeed', - code: 'GSPD', - label: 'Aircraft Groundspeed', - value: `${decodeResult.raw.groundspeed} knots` - }); - } + static groundspeed(decodeResult: DecodeResult, value: number) { + decodeResult.raw.groundspeed = value; + decodeResult.formatted.items.push({ + type: 'aircraft_groundspeed', + code: 'GSPD', + label: 'Aircraft Groundspeed', + value: `${decodeResult.raw.groundspeed} knots`, + }); + } - static airspeed(decodeResult: DecodeResult, value: number) { - decodeResult.raw.airspeed = value; - decodeResult.formatted.items.push({ - type: 'airspeed', - code: 'ASPD', - label: 'True Airspeed', - value: `${decodeResult.raw.airspeed} knots` - }); - } + static airspeed(decodeResult: DecodeResult, value: number) { + decodeResult.raw.airspeed = value; + decodeResult.formatted.items.push({ + type: 'airspeed', + code: 'ASPD', + label: 'True Airspeed', + value: `${decodeResult.raw.airspeed} knots`, + }); + } - static mach(decodeResult: DecodeResult, value: number) { - decodeResult.raw.mach = value; - decodeResult.formatted.items.push({ - type: 'mach', - code: 'MACH', - label: 'Mach Number', - value: `${decodeResult.raw.mach} mach` - }); - } + static mach(decodeResult: DecodeResult, value: number) { + decodeResult.raw.mach = value; + decodeResult.formatted.items.push({ + type: 'mach', + code: 'MACH', + label: 'Mach Number', + value: `${decodeResult.raw.mach} mach`, + }); + } - static temperature(decodeResult: DecodeResult, value: string) { - if(value.length === 0 ) { - return; - } - decodeResult.raw.outside_air_temperature = Number(value.replace("M", "-").replace("P", "+")); - decodeResult.formatted.items.push({ - type: 'outside_air_temperature', - code: 'OATEMP', - label: 'Outside Air Temperature (C)', - value: `${decodeResult.raw.outside_air_temperature} degrees`, - }); + static temperature(decodeResult: DecodeResult, value: string) { + if (value.length === 0) { + return; } + decodeResult.raw.outside_air_temperature = Number( + value.replace('M', '-').replace('P', '+'), + ); + decodeResult.formatted.items.push({ + type: 'outside_air_temperature', + code: 'OATEMP', + label: 'Outside Air Temperature (C)', + value: `${decodeResult.raw.outside_air_temperature} degrees`, + }); + } - static heading(decodeResult: DecodeResult, value: number) { - decodeResult.raw.heading = value; - decodeResult.formatted.items.push({ - type: 'heading', - code: 'HDG', - label: 'Heading', - value: `${decodeResult.raw.heading}`, - }); - } + static heading(decodeResult: DecodeResult, value: number) { + decodeResult.raw.heading = value; + decodeResult.formatted.items.push({ + type: 'heading', + code: 'HDG', + label: 'Heading', + value: `${decodeResult.raw.heading}`, + }); + } - static tail(decodeResult: DecodeResult, value: string) { - decodeResult.raw.tail = value; - decodeResult.formatted.items.push({ - type: 'tail', - code: "TAIL", - label: 'Tail', - value: decodeResult.raw.tail, - }); - } + static tail(decodeResult: DecodeResult, value: string) { + decodeResult.raw.tail = value; + decodeResult.formatted.items.push({ + type: 'tail', + code: 'TAIL', + label: 'Tail', + value: decodeResult.raw.tail, + }); + } - static out(decodeResult: DecodeResult, time: number) { - decodeResult.raw.out_time = time; - decodeResult.formatted.items.push({ - type: 'time_of_day', - code: 'OUT', - label: 'Out of Gate Time', - value: DateTimeUtils.timestampToString(time, 'tod'), - }); - } + static out(decodeResult: DecodeResult, time: number) { + decodeResult.raw.out_time = time; + decodeResult.formatted.items.push({ + type: 'time_of_day', + code: 'OUT', + label: 'Out of Gate Time', + value: DateTimeUtils.timestampToString(time, 'tod'), + }); + } - static off(decodeResult: DecodeResult, time: number, type: 'tod' | 'epoch' = 'tod') { - if (type === 'tod') { - decodeResult.raw.off_time = time; - decodeResult.formatted.items.push({ - type: 'time_of_day', - code: 'OFF', - label: 'Takeoff Time', - value: DateTimeUtils.timestampToString(time, 'tod'), - }); - } else { - decodeResult.raw.off_date = time; - decodeResult.formatted.items.push({ - type: 'epoch', - code: 'OFF', - label: 'Takeoff Time', - value: DateTimeUtils.timestampToString(time, 'epoch'), - }); - } + static off( + decodeResult: DecodeResult, + time: number, + type: 'tod' | 'epoch' = 'tod', + ) { + if (type === 'tod') { + decodeResult.raw.off_time = time; + decodeResult.formatted.items.push({ + type: 'time_of_day', + code: 'OFF', + label: 'Takeoff Time', + value: DateTimeUtils.timestampToString(time, 'tod'), + }); + } else { + decodeResult.raw.off_date = time; + decodeResult.formatted.items.push({ + type: 'epoch', + code: 'OFF', + label: 'Takeoff Time', + value: DateTimeUtils.timestampToString(time, 'epoch'), + }); } + } - static on(decodeResult: DecodeResult, time: number) { - decodeResult.raw.on_time = time; - decodeResult.formatted.items.push({ - type: 'time_of_day', - code: 'ON', - label: 'Landing Time', - value: DateTimeUtils.timestampToString(time, 'tod'), - }); - } + static on(decodeResult: DecodeResult, time: number) { + decodeResult.raw.on_time = time; + decodeResult.formatted.items.push({ + type: 'time_of_day', + code: 'ON', + label: 'Landing Time', + value: DateTimeUtils.timestampToString(time, 'tod'), + }); + } - static in(decodeResult: DecodeResult, time: number) { - decodeResult.raw.in_time = time; - decodeResult.formatted.items.push({ - type: 'time_of_day', - code: 'IN', - label: 'In Gate Time', - value: DateTimeUtils.timestampToString(time, 'tod'), - }); - } + static in(decodeResult: DecodeResult, time: number) { + decodeResult.raw.in_time = time; + decodeResult.formatted.items.push({ + type: 'time_of_day', + code: 'IN', + label: 'In Gate Time', + value: DateTimeUtils.timestampToString(time, 'tod'), + }); + } - static time_of_day(decodeResult: DecodeResult, time: number) { - decodeResult.raw.time_of_day = time; - decodeResult.formatted.items.push({ - type: 'time_of_day', - code: 'MSG_TOD', - label: 'Message Timestamp', - value: DateTimeUtils.timestampToString(time, 'tod'), - }); - } + static time_of_day(decodeResult: DecodeResult, time: number) { + decodeResult.raw.time_of_day = time; + decodeResult.formatted.items.push({ + type: 'time_of_day', + code: 'MSG_TOD', + label: 'Message Timestamp', + value: DateTimeUtils.timestampToString(time, 'tod'), + }); + } - static day(decodeResult: DecodeResult, day: number) { - decodeResult.raw.day = day; - decodeResult.formatted.items.push({ - type: 'day', - code: 'MSG_DAY', - label: 'Day of Month', - value: `${day}`, - }); - } + static day(decodeResult: DecodeResult, day: number) { + decodeResult.raw.day = day; + decodeResult.formatted.items.push({ + type: 'day', + code: 'MSG_DAY', + label: 'Day of Month', + value: `${day}`, + }); + } - static month(decodeResult: DecodeResult, month: number) { - decodeResult.raw.month = month; - decodeResult.formatted.items.push({ - type: 'month', - code: 'MSG_MON', - label: 'Month of Year', - value: `${month}`, - }); - } + static month(decodeResult: DecodeResult, month: number) { + decodeResult.raw.month = month; + decodeResult.formatted.items.push({ + type: 'month', + code: 'MSG_MON', + label: 'Month of Year', + value: `${month}`, + }); + } - static departureDay(decodeResult: DecodeResult, day: number) { - decodeResult.raw.departure_day = day; - decodeResult.formatted.items.push({ - type: 'day', - code: 'DEP_DAY', - label: 'Departure Day', - value: `${day}`, - }); - } + static departureDay(decodeResult: DecodeResult, day: number) { + decodeResult.raw.departure_day = day; + decodeResult.formatted.items.push({ + type: 'day', + code: 'DEP_DAY', + label: 'Departure Day', + value: `${day}`, + }); + } - static arrivalDay(decodeResult: DecodeResult, day: number) { - decodeResult.raw.arrival_day = day; - decodeResult.formatted.items.push({ - type: 'day', - code: 'ARR_DAY', - label: 'Arrival Day', - value: `${day}`, - }); - } + static arrivalDay(decodeResult: DecodeResult, day: number) { + decodeResult.raw.arrival_day = day; + decodeResult.formatted.items.push({ + type: 'day', + code: 'ARR_DAY', + label: 'Arrival Day', + value: `${day}`, + }); + } - static text(decodeResult: DecodeResult, text: string) { - decodeResult.raw.text = text; - decodeResult.formatted.items.push({ - type: 'text', - code: 'TEXT', - label: 'Text Message', - value: text, - }); - } + static text(decodeResult: DecodeResult, text: string) { + decodeResult.raw.text = text; + decodeResult.formatted.items.push({ + type: 'text', + code: 'TEXT', + label: 'Text Message', + value: text, + }); + } - static mac(decodeResult: DecodeResult, mac: number) { - decodeResult.raw.mac = mac; - decodeResult.formatted.items.push({ - type: 'mac', - code: 'MAC', - label: 'Mean Aerodynamic Chord', - value: `${mac} %`, - }); - } + static mac(decodeResult: DecodeResult, mac: number) { + decodeResult.raw.mac = mac; + decodeResult.formatted.items.push({ + type: 'mac', + code: 'MAC', + label: 'Mean Aerodynamic Chord', + value: `${mac} %`, + }); + } - static trim(decodeResult: DecodeResult, trim: number) { - decodeResult.raw.trim = trim; - decodeResult.formatted.items.push({ - type: 'trim', - code: 'TRIM', - label: 'Trim', - value: `${trim} units`, - }); - } + static trim(decodeResult: DecodeResult, trim: number) { + decodeResult.raw.trim = trim; + decodeResult.formatted.items.push({ + type: 'trim', + code: 'TRIM', + label: 'Trim', + value: `${trim} units`, + }); + } - static windData(decodeResult: DecodeResult, windData: Wind[]) { - decodeResult.raw.wind_data = windData; - for(const wind of windData) { - let text = `${RouteUtils.waypointToString(wind.waypoint)} at FL${wind.flightLevel}: ${wind.windDirection}° at ${wind.windSpeed}kt`; - if (wind.temperature) { - text += `, ${wind.temperature.degreesC}°C at FL${wind.temperature.flightLevel}`; - - } - decodeResult.formatted.items.push({ - type: 'wind_data', - code: 'WIND', - label: 'Wind Data', - value: text, - }); - } + static windData(decodeResult: DecodeResult, windData: Wind[]) { + decodeResult.raw.wind_data = windData; + for (const wind of windData) { + let text = `${RouteUtils.waypointToString(wind.waypoint)} at FL${wind.flightLevel}: ${wind.windDirection}° at ${wind.windSpeed}kt`; + if (wind.temperature) { + text += `, ${wind.temperature.degreesC}°C at FL${wind.temperature.flightLevel}`; + } + decodeResult.formatted.items.push({ + type: 'wind_data', + code: 'WIND', + label: 'Wind Data', + value: text, + }); } + } - static cg(decodeResult: DecodeResult, value: number, type: "center" | "lower" | "upper" = "center") { - switch(type) { - case "center": + static cg( + decodeResult: DecodeResult, + value: number, + type: 'center' | 'lower' | 'upper' = 'center', + ) { + switch (type) { + case 'center': decodeResult.raw.center_of_gravity = value; decodeResult.formatted.items.push({ - type: "center_of_gravity", - code: "CG", - label: "Center of Gravity", + type: 'center_of_gravity', + code: 'CG', + label: 'Center of Gravity', value: `${decodeResult.raw.center_of_gravity} %`, }); break; - case "lower": - decodeResult.raw.cg_lower_limit = value; - decodeResult.formatted.items.push({ - type: "cg_lower_limit", - code: "CG_LOWER", - label: "Center of Gravity Lower Limit", - value: `${decodeResult.raw.cg_lower_limit} %`, - }); - break; - case "upper": - decodeResult.raw.cg_upper_limit = value; - decodeResult.formatted.items.push({ - type: "cg_upper_limit", - code: "CG_UPPER", - label: "Center of Gravity Upper Limit", - value: `${decodeResult.raw.cg_upper_limit} %`, - }); - break; - } + case 'lower': + decodeResult.raw.cg_lower_limit = value; + decodeResult.formatted.items.push({ + type: 'cg_lower_limit', + code: 'CG_LOWER', + label: 'Center of Gravity Lower Limit', + value: `${decodeResult.raw.cg_lower_limit} %`, + }); + break; + case 'upper': + decodeResult.raw.cg_upper_limit = value; + decodeResult.formatted.items.push({ + type: 'cg_upper_limit', + code: 'CG_UPPER', + label: 'Center of Gravity Upper Limit', + value: `${decodeResult.raw.cg_upper_limit} %`, + }); + break; } + } static version(decodeResult: DecodeResult, value: number) { decodeResult.raw.version = value; decodeResult.formatted.items.push({ - type: "version", - code: "VERSION", - label: "Message Version", + type: 'version', + code: 'VERSION', + label: 'Message Version', value: `v${decodeResult.raw.version.toFixed(1)}`, }); - } + } static label(decodeResult: DecodeResult, value: string) { decodeResult.raw.label = value; decodeResult.formatted.items.push({ - type: "label", - code: "LABEL", - label: "Message Label", + type: 'label', + code: 'LABEL', + label: 'Message Label', value: `${decodeResult.raw.label}`, }); } @@ -514,22 +536,23 @@ export class ResultFormatter { static sublabel(decodeResult: DecodeResult, value: string) { decodeResult.raw.sublabel = value; decodeResult.formatted.items.push({ - type: "sublabel", - code: "SUBLABEL", - label: "Message Sublabel", + type: 'sublabel', + code: 'SUBLABEL', + label: 'Message Sublabel', value: `${decodeResult.raw.sublabel}`, }); } - static unknown(decodeResult: DecodeResult, value: string, sep: string = ',') { - if (!decodeResult.remaining.text) - decodeResult.remaining.text = value; - else - decodeResult.remaining.text += sep + value; - }; - - static unknownArr(decodeResult: DecodeResult, value: string[], sep: string = ',') { - this.unknown(decodeResult, value.join(sep), sep); - }; + static unknown(decodeResult: DecodeResult, value: string, sep: string = ',') { + if (!decodeResult.remaining.text) decodeResult.remaining.text = value; + else decodeResult.remaining.text += sep + value; + } + static unknownArr( + decodeResult: DecodeResult, + value: string[], + sep: string = ',', + ) { + this.unknown(decodeResult, value.join(sep), sep); + } } diff --git a/lib/utils/route_utils.ts b/lib/utils/route_utils.ts index 9536906..557797d 100644 --- a/lib/utils/route_utils.ts +++ b/lib/utils/route_utils.ts @@ -1,86 +1,112 @@ -import { DateTimeUtils } from "../DateTimeUtils"; -import { Route } from "../types/route"; -import { Waypoint } from "../types/waypoint"; -import { CoordinateUtils } from "./coordinate_utils"; +import { DateTimeUtils } from '../DateTimeUtils'; +import { Route } from '../types/route'; +import { Waypoint } from '../types/waypoint'; +import { CoordinateUtils } from './coordinate_utils'; export class RouteUtils { + public static formatFlightState(state: string): string { + switch (state) { + case 'TO': + return 'Takeoff'; + case 'IC': + return 'Initial Climb'; + case 'CL': + return 'Climb'; + case 'ER': + return 'En Route'; + case 'DC': + return 'Descent'; + case 'AP': + return 'Approach'; + default: + return `Unknown ${state}`; + } + } - public static formatFlightState(state: string): string { - switch (state) { - case "TO": return "Takeoff"; - case "IC": return "Initial Climb"; - case "CL": return "Climb"; - case "ER": return "En Route"; - case "DC": return "Descent"; - case "AP": return "Approach"; - default: return `Unknown ${state}`; - } + public static routeToString(route: Route): string { + let str = ''; + if (route.name) { + str += route.name; + } + if (route.runway) { + str += `(${route.runway})`; + } + if (str.length !== 0 && route.waypoints && route.waypoints.length === 1) { + str += ' starting at '; + } else if (str.length !== 0 && route.waypoints) { + str += ': '; } - - public static routeToString(route: Route): string { - let str = ''; - if(route.name) { - str += route.name; - } - if(route.runway) { - str += `(${route.runway})`; - } - if(str.length!==0 && route.waypoints && route.waypoints.length === 1) { - str += ' starting at ' - } - else if(str.length!==0 && route.waypoints) { - str += ': '; - } - if(route.waypoints) { - str += RouteUtils.waypointsToString(route.waypoints); - } - - return str; + if (route.waypoints) { + str += RouteUtils.waypointsToString(route.waypoints); } - public static waypointToString(waypoint: Waypoint): string { - let s = waypoint.name; - if(waypoint.latitude && waypoint.longitude) { - s += `(${CoordinateUtils.coordinateString({latitude:waypoint.latitude, longitude: waypoint.longitude})})`; - } - if(waypoint.offset) { - s += `[${waypoint.offset.bearing}° ${waypoint.offset.distance}nm]`; - } - if(waypoint.time && waypoint.timeFormat) { - s +=`@${DateTimeUtils.timestampToString(waypoint.time, waypoint.timeFormat)}`; - } - return s; + return str; + } + + public static waypointToString(waypoint: Waypoint): string { + let s = waypoint.name; + if (waypoint.latitude && waypoint.longitude) { + s += `(${CoordinateUtils.coordinateString({ latitude: waypoint.latitude, longitude: waypoint.longitude })})`; + } + if (waypoint.offset) { + s += `[${waypoint.offset.bearing}° ${waypoint.offset.distance}nm]`; + } + if (waypoint.time && waypoint.timeFormat) { + s += `@${DateTimeUtils.timestampToString(waypoint.time, waypoint.timeFormat)}`; + } + return s; + } + + public static getWaypoint(leg: string): Waypoint { + const regex = leg.match(/^([A-Z]+)(\d{3})-(\d{4})$/); // {name}{bearing}-{distance} + if (regex?.length == 4) { + return { + name: regex[1], + offset: { + bearing: parseInt(regex[2]), + distance: parseInt(regex[3]) / 10, + }, + }; } - - public static getWaypoint(leg: string): Waypoint { - const regex = leg.match(/^([A-Z]+)(\d{3})-(\d{4})$/); // {name}{bearing}-{distance} - if(regex?.length == 4) { - return {name: regex[1], offset: {bearing: parseInt(regex[2]), distance: parseInt(regex[3])/10}}; - } - const waypoint = leg.split(','); - if(waypoint.length ==2) { - const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(waypoint[1]); - if(position) { - return {name: waypoint[0], latitude: position.latitude, longitude: position.longitude}; - } - } - if(leg.length == 13 || leg.length == 14) { //looks like coordinates - const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes(leg); - const name = waypoint.length == 2 ? waypoint[0] : ''; - if(position) { - return {name: name, latitude: position.latitude, longitude: position.longitude}; - } - } - return {name: leg}; + const waypoint = leg.split(','); + if (waypoint.length == 2) { + const position = CoordinateUtils.decodeStringCoordinatesDecimalMinutes( + waypoint[1], + ); + if (position) { + return { + name: waypoint[0], + latitude: position.latitude, + longitude: position.longitude, + }; + } + } + if (leg.length == 13 || leg.length == 14) { + //looks like coordinates + const position = + CoordinateUtils.decodeStringCoordinatesDecimalMinutes(leg); + const name = waypoint.length == 2 ? waypoint[0] : ''; + if (position) { + return { + name: name, + latitude: position.latitude, + longitude: position.longitude, + }; + } } + return { name: leg }; + } - private static waypointsToString(waypoints: Waypoint[]): string { - let str = waypoints.map((x) => RouteUtils.waypointToString(x)).join( ' > ').replaceAll('> >', '>>'); - if(str.startsWith(' > ')) { - str = '>>' + str.slice(2); - } - return str; + private static waypointsToString(waypoints: Waypoint[]): string { + let str = waypoints + .map((x) => RouteUtils.waypointToString(x)) + .join(' > ') + .replaceAll('> >', '>>'); + if (str.startsWith(' > ')) { + str = '>>' + str.slice(2); } + return str; + } } diff --git a/package-lock.json b/package-lock.json index 71ff60d..ddcc764 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,14 +20,26 @@ "@babel/core": "^7.26.9", "@babel/preset-env": "^7.26.9", "@babel/preset-typescript": "^7.26.0", + "@eslint/js": "^9.39.2", + "@stylistic/eslint-plugin": "^5.7.1", "@types/jest": "^30.0.0", "@types/node": "^25.0.9", + "@typescript-eslint/eslint-plugin": "^8.54.0", + "@typescript-eslint/parser": "^8.54.0", "babel-jest": "^30.2.0", + "eslint": "^9.39.2", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-import": "^2.32.0", + "eslint-plugin-prettier": "^5.5.5", + "globals": "^17.3.0", "jest": "^30.2.0", + "jiti": "^2.6.1", + "prettier": "^3.8.1", "ts-jest": "^29.2.6", "ts-node": "^10.9.2", "tsup": "^8.4.0", - "typescript": "^5.8.2" + "typescript": "^5.8.2", + "typescript-eslint": "^8.54.0" }, "engines": { "node": ">=18" @@ -2317,6 +2329,232 @@ "node": ">=18" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", + "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", + "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -3231,6 +3469,13 @@ "win32" ] }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true, + "license": "MIT" + }, "node_modules/@sinclair/typebox": { "version": "0.34.41", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", @@ -3258,6 +3503,53 @@ "@sinonjs/commons": "^3.0.1" } }, + "node_modules/@stylistic/eslint-plugin": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.7.1.tgz", + "integrity": "sha512-zjTUwIsEfT+k9BmXwq1QEFYsb4afBlsI1AXFyWQBgggMzwBFOuu92pGrE5OFx90IOjNl+lUbQoTG7f8S0PkOdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/types": "^8.53.1", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "estraverse": "^5.3.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": ">=9.0.0" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@tsconfig/node10": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", @@ -3387,6 +3679,20 @@ "pretty-format": "^30.0.0" } }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { "version": "25.2.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.0.tgz", @@ -3421,76 +3727,348 @@ "dev": true, "license": "MIT" }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.54.0.tgz", + "integrity": "sha512-hAAP5io/7csFStuOmR782YmTthKBJ9ND3WVL60hcOjvtGFb+HJxH4O5huAcmcZ9v9G8P+JETiZ/G1B8MALnWZQ==", "dev": true, - "license": "ISC" + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.54.0", + "@typescript-eslint/type-utils": "8.54.0", + "@typescript-eslint/utils": "8.54.0", + "@typescript-eslint/visitor-keys": "8.54.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.54.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } }, - "node_modules/@unrs/resolver-binding-android-arm-eabi": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", - "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", - "cpu": [ - "arm" - ], + "node_modules/@typescript-eslint/parser": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.54.0.tgz", + "integrity": "sha512-BtE0k6cjwjLZoZixN0t5AKP0kSzlGu7FctRXYuPAm//aaiZhmfq1JwdYpYr1brzEspYyFeF+8XF5j2VK6oalrA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ] + "dependencies": { + "@typescript-eslint/scope-manager": "8.54.0", + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/typescript-estree": "8.54.0", + "@typescript-eslint/visitor-keys": "8.54.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } }, - "node_modules/@unrs/resolver-binding-android-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", - "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", - "cpu": [ - "arm64" - ], + "node_modules/@typescript-eslint/project-service": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.54.0.tgz", + "integrity": "sha512-YPf+rvJ1s7MyiWM4uTRhE4DvBXrEV+d8oC3P9Y2eT7S+HBS0clybdMIPnhiATi9vZOYDc7OQ1L/i6ga6NFYK/g==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ] + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.54.0", + "@typescript-eslint/types": "^8.54.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } }, - "node_modules/@unrs/resolver-binding-darwin-arm64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", - "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", - "cpu": [ - "arm64" - ], + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.54.0.tgz", + "integrity": "sha512-27rYVQku26j/PbHYcVfRPonmOlVI6gihHtXFbTdB5sb6qA0wdAQAbyXFVarQ5t4HRojIz64IV90YtsjQSSGlQg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] + "dependencies": { + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/visitor-keys": "8.54.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } }, - "node_modules/@unrs/resolver-binding-darwin-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", - "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", - "cpu": [ - "x64" - ], + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.54.0.tgz", + "integrity": "sha512-dRgOyT2hPk/JwxNMZDsIXDgyl9axdJI3ogZ2XWhBPsnZUv+hPesa5iuhdYt2gzwA9t8RE5ytOJ6xB0moV0Ujvw==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } }, - "node_modules/@unrs/resolver-binding-freebsd-x64": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", - "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", - "cpu": [ - "x64" - ], + "node_modules/@typescript-eslint/type-utils": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.54.0.tgz", + "integrity": "sha512-hiLguxJWHjjwL6xMBwD903ciAwd7DmK30Y9Axs/etOkftC3ZNN9K44IuRD/EB08amu+Zw6W37x9RecLkOo3pMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/typescript-estree": "8.54.0", + "@typescript-eslint/utils": "8.54.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.54.0.tgz", + "integrity": "sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.54.0.tgz", + "integrity": "sha512-BUwcskRaPvTk6fzVWgDPdUndLjB87KYDrN5EYGetnktoeAvPtO4ONHlAZDnj5VFnUANg0Sjm7j4usBlnoVMHwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.54.0", + "@typescript-eslint/tsconfig-utils": "8.54.0", + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/visitor-keys": "8.54.0", + "debug": "^4.4.3", + "minimatch": "^9.0.5", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.54.0.tgz", + "integrity": "sha512-9Cnda8GS57AQakvRyG0PTejJNlA2xhvyNtEVIMlDWOOeEyBkYWhGPnfrIAnqxLMTSTo6q8g12XVjjev5l1NvMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.54.0", + "@typescript-eslint/types": "8.54.0", + "@typescript-eslint/typescript-estree": "8.54.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.54.0.tgz", + "integrity": "sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.54.0", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", + "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", + "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", + "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", + "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", + "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", "optional": true, @@ -3710,6 +4288,16 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/acorn-walk": { "version": "8.3.4", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", @@ -3723,6 +4311,23 @@ "node": ">=0.4.0" } }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -3810,93 +4415,241 @@ "dev": true, "license": "BSD-3-Clause" }, - "node_modules/babel-jest": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.2.0.tgz", - "integrity": "sha512-0YiBEOxWqKkSQWL9nNGGEgndoeL0ZpWrbLMNL5u/Kaxrli3Eaxlt3ZtIDktEvXt4L/R9r3ODr2zKwGM/2BjxVw==", + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/transform": "30.2.0", - "@types/babel__core": "^7.20.5", - "babel-plugin-istanbul": "^7.0.1", - "babel-preset-jest": "30.2.0", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "slash": "^3.0.0" + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" }, - "peerDependencies": { - "@babel/core": "^7.11.0 || ^8.0.0-0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/babel-plugin-istanbul": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.1.tgz", - "integrity": "sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==", + "node_modules/array-includes": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", "dev": true, - "license": "BSD-3-Clause", - "workspaces": [ - "test/babel-8" - ], + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-instrument": "^6.0.2", - "test-exclude": "^6.0.0" + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" }, "engines": { - "node": ">=12" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/babel-plugin-jest-hoist": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.2.0.tgz", - "integrity": "sha512-ftzhzSGMUnOzcCXd6WHdBGMyuwy15Wnn0iyyWGKgBDLxf9/s5ABuraCSpBX2uG0jUg4rqJnxsLc5+oYBqoxVaA==", + "node_modules/array.prototype.findlastindex": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", "dev": true, "license": "MIT", "dependencies": { - "@types/babel__core": "^7.20.5" + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.15", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.15.tgz", - "integrity": "sha512-hR3GwrRwHUfYwGfrisXPIDP3JcYfBrW7wKE7+Au6wDYl7fm/ka1NEII6kORzxNU556JjfidZeBsO10kYvtV1aw==", + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-define-polyfill-provider": "^0.6.6", - "semver": "^6.3.1" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.0.tgz", - "integrity": "sha512-AvDcMxJ34W4Wgy4KBIIePQTAOP1Ie2WFwkQp3dB7FQ/f0lI5+nM96zUnYEOE1P9sEg0es5VCP0HxiWu5fUHZAQ==", + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.6", - "core-js-compat": "^3.48.0" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.6", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.6.tgz", + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/babel-jest": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.2.0.tgz", + "integrity": "sha512-0YiBEOxWqKkSQWL9nNGGEgndoeL0ZpWrbLMNL5u/Kaxrli3Eaxlt3ZtIDktEvXt4L/R9r3ODr2zKwGM/2BjxVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "30.2.0", + "@types/babel__core": "^7.20.5", + "babel-plugin-istanbul": "^7.0.1", + "babel-preset-jest": "30.2.0", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0 || ^8.0.0-0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.1.tgz", + "integrity": "sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==", + "dev": true, + "license": "BSD-3-Clause", + "workspaces": [ + "test/babel-8" + ], + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-instrument": "^6.0.2", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.2.0.tgz", + "integrity": "sha512-ftzhzSGMUnOzcCXd6WHdBGMyuwy15Wnn0iyyWGKgBDLxf9/s5ABuraCSpBX2uG0jUg4rqJnxsLc5+oYBqoxVaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/babel__core": "^7.20.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.15", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.15.tgz", + "integrity": "sha512-hR3GwrRwHUfYwGfrisXPIDP3JcYfBrW7wKE7+Au6wDYl7fm/ka1NEII6kORzxNU556JjfidZeBsO10kYvtV1aw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-define-polyfill-provider": "^0.6.6", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.0.tgz", + "integrity": "sha512-AvDcMxJ34W4Wgy4KBIIePQTAOP1Ie2WFwkQp3dB7FQ/f0lI5+nM96zUnYEOE1P9sEg0es5VCP0HxiWu5fUHZAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.6", + "core-js-compat": "^3.48.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.6.tgz", "integrity": "sha512-hYm+XLYRMvupxiQzrvXUj7YyvFFVfv5gI0R71AJzudg1g2AI2vyCPPIFEBjk162/wFzti3inBHo7isWFuEVS/A==", "dev": true, "license": "MIT", @@ -4139,6 +4892,56 @@ "node": ">=8" } }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -4376,6 +5179,60 @@ "node": ">= 8" } }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -4409,6 +5266,13 @@ } } }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, "node_modules/deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", @@ -4419,6 +5283,42 @@ "node": ">=0.10.0" } }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -4439,6 +5339,34 @@ "node": ">=0.3.1" } }, + "node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -4483,30 +5411,179 @@ "is-arrayish": "^0.2.1" } }, - "node_modules/esbuild": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", - "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" + "node_modules/es-abstract": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz", + "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" }, "engines": { - "node": ">=18" + "node": ">= 0.4" }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.2", - "@esbuild/android-arm": "0.27.2", - "@esbuild/android-arm64": "0.27.2", - "@esbuild/android-x64": "0.27.2", - "@esbuild/darwin-arm64": "0.27.2", - "@esbuild/darwin-x64": "0.27.2", - "@esbuild/freebsd-arm64": "0.27.2", - "@esbuild/freebsd-x64": "0.27.2", - "@esbuild/linux-arm": "0.27.2", - "@esbuild/linux-arm64": "0.27.2", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/esbuild": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", + "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.2", + "@esbuild/android-arm": "0.27.2", + "@esbuild/android-arm64": "0.27.2", + "@esbuild/android-x64": "0.27.2", + "@esbuild/darwin-arm64": "0.27.2", + "@esbuild/darwin-x64": "0.27.2", + "@esbuild/freebsd-arm64": "0.27.2", + "@esbuild/freebsd-x64": "0.27.2", + "@esbuild/linux-arm": "0.27.2", + "@esbuild/linux-arm64": "0.27.2", "@esbuild/linux-ia32": "0.27.2", "@esbuild/linux-loong64": "0.27.2", "@esbuild/linux-mips64el": "0.27.2", @@ -4545,432 +5622,1510 @@ "node": ">=8" } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "node_modules/eslint": { + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", + "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.39.2", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" + "eslint": "bin/eslint.js" }, "engines": { - "node": ">=4" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "node_modules/eslint-config-prettier": { + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, + "peerDependencies": { + "eslint": ">=7.0.0" } }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", "dev": true, "license": "MIT", "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" } }, - "node_modules/exit-x": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/exit-x/-/exit-x-0.2.2.tgz", - "integrity": "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==", + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 0.8.0" + "dependencies": { + "ms": "^2.1.1" } }, - "node_modules/expect": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-30.2.0.tgz", - "integrity": "sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==", + "node_modules/eslint-module-utils": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", + "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/expect-utils": "30.2.0", - "@jest/get-type": "30.1.0", - "jest-matcher-utils": "30.2.0", - "jest-message-util": "30.2.0", - "jest-mock": "30.2.0", - "jest-util": "30.2.0" + "debug": "^3.2.7" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } } }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "bser": "2.1.1" + "ms": "^2.1.1" } }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "node_modules/eslint-plugin-import": { + "version": "2.32.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", + "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", "dependencies": { - "to-regex-range": "^5.0.1" + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.9", + "array.prototype.findlastindex": "^1.2.6", + "array.prototype.flat": "^1.3.3", + "array.prototype.flatmap": "^1.3.3", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.1", + "hasown": "^2.0.2", + "is-core-module": "^2.16.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.1", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.9", + "tsconfig-paths": "^3.15.0" }, "engines": { - "node": ">=8" + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" } }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "license": "MIT", "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" + "ms": "^2.1.1" } }, - "node_modules/fix-dts-default-cjs-exports": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fix-dts-default-cjs-exports/-/fix-dts-default-cjs-exports-1.0.1.tgz", - "integrity": "sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==", + "node_modules/eslint-plugin-prettier": { + "version": "5.5.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.5.tgz", + "integrity": "sha512-hscXkbqUZ2sPithAuLm5MXL+Wph+U7wHngPBv9OMWwlP8iaflyxpjTYZkmdgB4/vPIhemRlBEoLrH7UC1n7aUw==", "dev": true, "license": "MIT", "dependencies": { - "magic-string": "^0.30.17", - "mlly": "^1.7.4", - "rollup": "^4.34.8" + "prettier-linter-helpers": "^1.0.1", + "synckit": "^0.11.12" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } } }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, - "license": "ISC", + "license": "BSD-2-Clause", "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" }, "engines": { - "node": ">=14" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://opencollective.com/eslint" } }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, - "license": "ISC", + "license": "Apache-2.0", "engines": { - "node": ">=14" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://opencollective.com/eslint" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, - "hasInstallScript": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/function-bind": { + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/eslint/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit-x": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/exit-x/-/exit-x-0.2.2.tgz", + "integrity": "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-30.2.0.tgz", + "integrity": "sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "30.2.0", + "@jest/get-type": "30.1.0", + "jest-matcher-utils": "30.2.0", + "jest-message-util": "30.2.0", + "jest-mock": "30.2.0", + "jest-util": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fix-dts-default-cjs-exports": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fix-dts-default-cjs-exports/-/fix-dts-default-cjs-exports-1.0.1.tgz", + "integrity": "sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "magic-string": "^0.30.17", + "mlly": "^1.7.4", + "rollup": "^4.34.8" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true, "license": "MIT", "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "17.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.3.0.tgz", + "integrity": "sha512-yMqGUQVVCkD4tqjOJf3TnrvaaHDMYp4VlUSObbkIiuCPe/ofdMBFIAcBbCSRFWOnos6qRiTVStDwqPLUclaxIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, "license": "MIT", "engines": { - "node": ">=6.9.0" + "node": ">=4" } }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, "engines": { - "node": "6.* || 8.* || >= 10.*" + "node": ">= 0.4" } }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "node_modules/ip-address": { + "version": "5.9.4", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-5.9.4.tgz", + "integrity": "sha512-dHkI3/YNJq4b/qQaz+c8LuarD3pY24JqZWfjB8aZx1gtpc2MDILu9L9jpZe1sHpzo/yWFweQVn+U//FhazUxmw==", + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "lodash": "^4.17.15", + "sprintf-js": "1.1.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, "license": "MIT", "engines": { - "node": ">=8.0.0" + "node": ">=8" } }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6" } }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "node_modules/is-generator-function": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { - "node": "*" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "license": "MIT", "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" + "is-extglob": "^2.1.1" }, "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" + "node": ">=0.10.0" } }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "dev": true, "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, "engines": { - "node": ">=10.17.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/ieee754": { + "node_modules/is-regex": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/import-local": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", - "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", "dev": true, "license": "MIT", "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", "dev": true, "license": "MIT", "engines": { - "node": ">=0.8.19" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, - "license": "ISC" + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/ip-address": { - "version": "5.9.4", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-5.9.4.tgz", - "integrity": "sha512-dHkI3/YNJq4b/qQaz+c8LuarD3pY24JqZWfjB8aZx1gtpc2MDILu9L9jpZe1sHpzo/yWFweQVn+U//FhazUxmw==", + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, "license": "MIT", "dependencies": { - "jsbn": "1.1.0", - "lodash": "^4.17.15", - "sprintf-js": "1.1.2" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { - "node": ">= 0.10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", "dev": true, "license": "MIT", "dependencies": { - "hasown": "^2.0.2" + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -4979,49 +7134,75 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", "dev": true, "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "dev": true, "license": "MIT", "engines": { - "node": ">=6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", "dev": true, "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, "engines": { - "node": ">=0.12.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", "dev": true, "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -5829,6 +8010,16 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, "node_modules/joycon": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", @@ -5879,6 +8070,13 @@ "node": ">=6" } }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -5886,6 +8084,20 @@ "dev": true, "license": "MIT" }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -5899,6 +8111,16 @@ "node": ">=6" } }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -5909,6 +8131,20 @@ "node": ">=6" } }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/lilconfig": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", @@ -5972,6 +8208,13 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -6038,6 +8281,16 @@ "tmpl": "1.0.5" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -6222,6 +8475,103 @@ "node": ">=0.10.0" } }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -6248,6 +8598,42 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -6310,6 +8696,19 @@ "dev": true, "license": "BlueOak-1.0.0" }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -6452,6 +8851,16 @@ "pathe": "^2.0.1" } }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/postcss-load-config": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", @@ -6495,6 +8904,45 @@ } } }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", + "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.1.tgz", + "integrity": "sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/pretty-format": { "version": "30.2.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", @@ -6523,6 +8971,16 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/pure-rand": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz", @@ -6561,6 +9019,29 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -6581,6 +9062,27 @@ "node": ">=4" } }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/regexpu-core": { "version": "6.4.0", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz", @@ -6715,6 +9217,61 @@ "fsevents": "~2.3.2" } }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -6725,6 +9282,55 @@ "semver": "bin/semver.js" } }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -6748,6 +9354,82 @@ "node": ">=8" } }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -6805,6 +9487,20 @@ "node": ">=10" } }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -6850,6 +9546,65 @@ "node": ">=8" } }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -7007,9 +9762,9 @@ } }, "node_modules/synckit": { - "version": "0.11.11", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz", - "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==", + "version": "0.11.12", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.12.tgz", + "integrity": "sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==", "dev": true, "license": "MIT", "dependencies": { @@ -7145,6 +9900,19 @@ "tree-kill": "cli.js" } }, + "node_modules/ts-api-utils": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", + "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", @@ -7275,6 +10043,42 @@ } } }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -7346,6 +10150,19 @@ "node": ">= 12" } }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -7369,6 +10186,84 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", @@ -7383,6 +10278,30 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.54.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.54.0.tgz", + "integrity": "sha512-CKsJ+g53QpsNPqbzUsfKVgd3Lny4yKZ1pP4qN3jdMOg/sisIDLGyDMezycquXLE5JsEU0wp3dGNdzig0/fmSVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.54.0", + "@typescript-eslint/parser": "8.54.0", + "@typescript-eslint/typescript-estree": "8.54.0", + "@typescript-eslint/utils": "8.54.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, "node_modules/ufo": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", @@ -7404,6 +10323,25 @@ "node": ">=0.8.0" } }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/undici-types": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", @@ -7521,6 +10459,16 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", @@ -7569,6 +10517,105 @@ "node": ">= 8" } }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", + "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", diff --git a/package.json b/package.json index f73a39d..74b108f 100644 --- a/package.json +++ b/package.json @@ -37,14 +37,26 @@ "@babel/core": "^7.26.9", "@babel/preset-env": "^7.26.9", "@babel/preset-typescript": "^7.26.0", + "@eslint/js": "^9.39.2", + "@stylistic/eslint-plugin": "^5.7.1", "@types/jest": "^30.0.0", "@types/node": "^25.0.9", + "@typescript-eslint/eslint-plugin": "^8.54.0", + "@typescript-eslint/parser": "^8.54.0", "babel-jest": "^30.2.0", + "eslint": "^9.39.2", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-import": "^2.32.0", + "eslint-plugin-prettier": "^5.5.5", + "globals": "^17.3.0", "jest": "^30.2.0", + "jiti": "^2.6.1", + "prettier": "^3.8.1", "ts-jest": "^29.2.6", "ts-node": "^10.9.2", "tsup": "^8.4.0", - "typescript": "^5.8.2" + "typescript": "^5.8.2", + "typescript-eslint": "^8.54.0" }, "publishConfig": { "access": "public" diff --git a/tsconfig.json b/tsconfig.json index 64d594f..3ba4e98 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,11 +6,6 @@ "outDir": "./dist", "strict": true }, - "include": [ - "lib/*.ts", - "lib/**/*.ts", - ], - "exclude": [ - "node_modules" - ] + "include": ["lib/*.ts", "lib/**/*.ts"], + "exclude": ["node_modules"] } diff --git a/tsup.config.ts b/tsup.config.ts index 36bd7ad..548340e 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -1,11 +1,11 @@ -import { defineConfig } from "tsup"; +import { defineConfig } from 'tsup'; export default defineConfig({ - entry: ["index.ts"], - format: ["cjs", "esm"], + entry: ['index.ts'], + format: ['cjs', 'esm'], dts: true, splitting: false, sourcemap: true, clean: true, - target: "node18", + target: 'node18', });