Conversation
* inmarsat prefix and checksum * LDI messages * enabled skipped tests
WalkthroughThis PR updates ACARS message decoding tests and refactors core H1 label decoding logic. Changes include converting skipped test cases to active inmarsat tests, modifying checksum parsing to use hexadecimal interpretation, enhancing H1Helper with runway processing and CRC checksum algorithm detection, and updating ResultFormatter to track checksum algorithm types. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Tip Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
lib/utils/result_formatter.ts (1)
255-263: Commented-out formatted item push — intentional?The
checksumAlgorithmmethod stores the value inrawbut the formatted item push is commented out. If this is intentional (e.g., raw-only for now, to be exposed later), consider adding a brief// TODOor explanation comment so future contributors know the intent.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/utils/result_formatter.ts` around lines 255 - 263, In checksumAlgorithm (function checksumAlgorithm and the DecodeResult handling), either restore the commented formatted item push so decodeResult.formatted.items receives a message_checksum_algorithm entry (matching the raw.checksum_algorithm value), or add a brief explanatory comment/TODO above the commented block stating why the formatted push is intentionally omitted (e.g., "TODO: keep raw-only until UI support for checksum algorithm is added"); update the comment to reference the decoded field name (checksum_algorithm) so future contributors can locate and understand the intent.lib/utils/h1_helper.ts (1)
560-584: Minor JSDoc inaccuracy:@param datasays "byte array" but it's astring.The function signature takes
data: string, not aUint8Arrayas the JSDoc implies.Proposed fix
/** * Calculates the CRC-16/GENIBUS checksum for a given Uint8Array. - * `@param` data The input data as a byte array. + * `@param` data The input data as a string. * `@returns` The 16-bit checksum as a number. */🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/utils/h1_helper.ts` around lines 560 - 584, The JSDoc for crc16Genibus incorrectly describes the parameter as a "byte array" while the function signature accepts a string; update the `@param` data description and type to reflect that it's a string (e.g., "input string to be encoded as ASCII bytes") and optionally note the encoding used (ascii) so callers know how the string is converted; ensure the function-level description matches the actual parameter type and behavior of crc16Genibus.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@lib/plugins/Label_H1.ts`:
- Around line 39-40: The ternary that computes offset in Label_H1.ts currently
includes a redundant parts[0] === '- ' check which can force an incorrect offset
for messages like '- `#M1B`...' — replace the expression const offset = parts[0]
=== '- ' || isNaN(parseInt(parts[1][1])) ? 3 : 4; with a simpler check that only
uses the numeric test (e.g., const offset = isNaN(parseInt(parts[1][1])) ? 3 :
4;) so offset is 3 when the second char is non-numeric and 4 when numeric; keep
or update the inline comment to note fragility around slicing logic in the
function that uses parts/offset.
In `@lib/utils/h1_helper.ts`:
- Around line 522-533: processRunway can throw when the incoming data array has
only one element (no ":" in the RW field); add a guard at the start of
processRunway to check whether data[1] exists before accessing it, and
return/handle gracefully if missing; also avoid duplicate splitting by assigning
data[1].split(',') to a local variable (use that variable for
ResultFormatter.departureAirport and the subsequent ResultFormatter.unknownArr
call) and keep the existing handling of data[0]/parts/more as-is.
---
Nitpick comments:
In `@lib/utils/h1_helper.ts`:
- Around line 560-584: The JSDoc for crc16Genibus incorrectly describes the
parameter as a "byte array" while the function signature accepts a string;
update the `@param` data description and type to reflect that it's a string (e.g.,
"input string to be encoded as ASCII bytes") and optionally note the encoding
used (ascii) so callers know how the string is converted; ensure the
function-level description matches the actual parameter type and behavior of
crc16Genibus.
In `@lib/utils/result_formatter.ts`:
- Around line 255-263: In checksumAlgorithm (function checksumAlgorithm and the
DecodeResult handling), either restore the commented formatted item push so
decodeResult.formatted.items receives a message_checksum_algorithm entry
(matching the raw.checksum_algorithm value), or add a brief explanatory
comment/TODO above the commented block stating why the formatted push is
intentionally omitted (e.g., "TODO: keep raw-only until UI support for checksum
algorithm is added"); update the comment to reference the decoded field name
(checksum_algorithm) so future contributors can locate and understand the
intent.
| // need a better way to figure this out | ||
| const offset = parts[0] === '- ' || isNaN(parseInt(parts[1][1])) ? 3 : 4; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's look at the actual code in Label_H1.ts around lines 39-40
find . -type f -name "Label_H1.ts" | head -5Repository: airframesio/acars-decoder-typescript
Length of output: 105
🏁 Script executed:
# Search for test files related to H1 or Label_H1
find . -type f -name "*test*" -o -name "*spec*" | grep -i h1Repository: airframesio/acars-decoder-typescript
Length of output: 723
🏁 Script executed:
# Let's also look for fixture or example data files
find . -type f \( -name "*.json" -o -name "*.txt" -o -name "*.fixture*" \) | xargs grep -l "^-\s*#M" 2>/dev/null | head -10Repository: airframesio/acars-decoder-typescript
Length of output: 62
🏁 Script executed:
# Let's examine the actual code in Label_H1.ts around lines 39-40
cat -n ./lib/plugins/Label_H1.ts | sed -n '30,50p'Repository: airframesio/acars-decoder-typescript
Length of output: 1329
🏁 Script executed:
# Let's look at the test file to see what message formats are tested
cat -n ./lib/plugins/Label_H1.test.ts | head -100Repository: airframesio/acars-decoder-typescript
Length of output: 744
🏁 Script executed:
# Let's look at H1_FTX test file for actual message examples
cat -n ./lib/plugins/Label_H1_FTX.test.ts | head -150Repository: airframesio/acars-decoder-typescript
Length of output: 4285
🏁 Script executed:
# Let's also check H1_FPN test file
cat -n ./lib/plugins/Label_H1_FPN.test.ts | head -150Repository: airframesio/acars-decoder-typescript
Length of output: 9786
🏁 Script executed:
# Search for all message text patterns in test files to identify actual message formats
rg "message\.text\s*=" ./lib/plugins/Label_H1*.test.ts -A 1 | grep -oE "'[^']*'" | head -30Repository: airframesio/acars-decoder-typescript
Length of output: 2431
🏁 Script executed:
# Specifically search for any '- `#M`' patterns with digits
rg "'\s*-\s*#M[0-9]" ./lib/plugins/ -r '$0' 2>/dev/nullRepository: airframesio/acars-decoder-typescript
Length of output: 62
🏁 Script executed:
# Let's check if there are any patterns like '- `#T1B`' or similar
rg "'\s*-\s*#[MT][0-9]" ./lib/plugins/ -r '$0' 2>/dev/nullRepository: airframesio/acars-decoder-typescript
Length of output: 62
🏁 Script executed:
# Let's check the H1_helper to understand the message structure better
cat -n ./lib/utils/h1_helper.ts | head -100Repository: airframesio/acars-decoder-typescript
Length of output: 4289
🏁 Script executed:
# Look for any comments or documentation about H1 message formats
rg -i "message.*format|format.*message" ./lib/ -B 2 -A 2 | head -80Repository: airframesio/acars-decoder-typescript
Length of output: 4625
🏁 Script executed:
# Check if there are any other references to parts[1][1] or offset logic
rg "parts\[1\]\[1\]" ./lib/ -B 3 -A 3Repository: airframesio/acars-decoder-typescript
Length of output: 593
🏁 Script executed:
# Check if there's any git history showing when this condition was added
git log --oneline --all -S "parts[0] === '- '" -- ./lib/plugins/Label_H1.ts | head -5Repository: airframesio/acars-decoder-typescript
Length of output: 110
🏁 Script executed:
# Let's also verify the complete split logic by looking at what the msg variable contains
cat -n ./lib/plugins/Label_H1.ts | sed -n '25,50p'Repository: airframesio/acars-decoder-typescript
Length of output: 1532
🏁 Script executed:
# Let's check the git commit to see what was changed
git show 3c8ed5b --stat | head -20Repository: airframesio/acars-decoder-typescript
Length of output: 879
🏁 Script executed:
# Let's verify the complete logic by examining what `msg` is before the split
cat -n ./lib/plugins/Label_H1.ts | sed -n '1,25p'Repository: airframesio/acars-decoder-typescript
Length of output: 1064
🏁 Script executed:
# Let's check the exact change that introduced the parts[0] === '- ' condition
git show 3c8ed5b -- lib/plugins/Label_H1.ts | grep -A 10 -B 10 "parts\[0\]"Repository: airframesio/acars-decoder-typescript
Length of output: 1502
🏁 Script executed:
# Let's also check if there are any other tests that might use '- `#M1`' or similar patterns
rg "- #" ./lib/plugins/*.test.ts -n | head -20Repository: airframesio/acars-decoder-typescript
Length of output: 484
🏁 Script executed:
# Search for test patterns with escaped characters
rg '\- #' ./lib/plugins/*.test.ts -n | head -20Repository: airframesio/acars-decoder-typescript
Length of output: 841
🏁 Script executed:
# Let's check all test cases in the H1 tests that use the "- #" prefix
grep -rn "- #" ./lib/plugins/Label_H1*.test.ts | head -30Repository: airframesio/acars-decoder-typescript
Length of output: 190
🏁 Script executed:
# Let's trace through the logic with concrete examples
# For '- `#MDFTX/ID77170A`,...':
# - msg = '- `#MDFTX/ID77170A`,...'
# - parts = msg.split('#') = ['- ', 'MDFTX/ID77170A,...']
# - parts[0] = '- ' (length 2)
# - parts[1][1] = 'D'
# Let's verify the exact message format in the test
cat -n ./lib/plugins/Label_H1_FTX.test.ts | sed -n '37,65p'Repository: airframesio/acars-decoder-typescript
Length of output: 1853
🏁 Script executed:
# Let's verify what the offset calculation should be for '- `#MDFTX`' format
python3 << 'EOF'
msg = '- `#MDFTX/ID77170A`,RCH836,ABZ01G6XH273/MR2,/FXIRAN IS LAUNCHING MISSILES'
parts = msg.split('#')
print(f"msg = '{msg}'")
print(f"parts[0] = '{parts[0]}' (length {len(parts[0])})")
print(f"parts[1] = '{parts[1]}'")
print(f"parts[1][1] = '{parts[1][1]}'")
# Current logic
offset_current = 3 if (parts[0] == '- ' or int(parts[1][1]) != int(parts[1][1])) else 4
# Simulating: parts[0] === '- ' || isNaN(parseInt(parts[1][1])) ? 3 : 4
import math
is_nan = math.isnan(float(parts[1][1])) if parts[1][1] not in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' else True
offset_current = 3 if (parts[0] == '- ' or is_nan) else 4
print(f"\nFor '- `#MDFTX`' (D is non-numeric):")
print(f" isNaN(parseInt('D')) = True")
print(f" offset = 3")
print(f" msg.slice({len(parts[0])} + 3) = msg.slice({len(parts[0]) + 3})")
print(f" Result: '{msg[len(parts[0]) + 3:]}'")
# Hypothetical '- `#M1B`' message
msg2 = '- `#M1BFTX/ID77170A`,RCH836'
parts2 = msg2.split('#')
print(f"\nFor hypothetical '- `#M1BFTX`' (1 is numeric):")
print(f" parts[1][1] = '{parts2[1][1]}'")
print(f" isNaN(parseInt('1')) = False")
print(f" WITH parts[0] === '- ': offset = 3, msg.slice({len(parts2[0]) + 3}) = '{msg2[len(parts2[0]) + 3:]}'")
print(f" WITHOUT parts[0] === '- ': offset = 4, msg.slice({len(parts2[0]) + 4}) = '{msg2[len(parts2[0]) + 4:]}'")
EOFRepository: airframesio/acars-decoder-typescript
Length of output: 729
Remove the redundant parts[0] === '- ' condition that introduces a latent offset bug.
For all currently tested '- #MD...' messages, the second character is 'D' (non-numeric), so isNaN(parseInt('D')) already returns true and produces the correct offset = 3. The new parts[0] === '- ' clause adds no value for known cases.
However, it creates a latent bug: if a '- #M1B...' or '- #T1B...' message is ever encountered, the condition forces offset = 3 (incorrect) instead of the proper 4. For example, with '- #M1BFTX/...', slicing at 2 + 3 = 5 yields 'BFTX/...' (includes trailing B), while correct offset of 4 would yield 'FTX/...'.
The inline comment already acknowledges this approach is fragile. Remove the redundant clause:
♻️ Suggested simplification
- // need a better way to figure this out
- const offset = parts[0] === '- ' || isNaN(parseInt(parts[1][1])) ? 3 : 4;
+ // offset: 3 for '#MD...' style (non-numeric 2nd char), 4 for '#M1B...' style
+ const offset = isNaN(parseInt(parts[1][1])) ? 3 : 4;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // need a better way to figure this out | |
| const offset = parts[0] === '- ' || isNaN(parseInt(parts[1][1])) ? 3 : 4; | |
| // offset: 3 for '#MD...' style (non-numeric 2nd char), 4 for '#M1B...' style | |
| const offset = isNaN(parseInt(parts[1][1])) ? 3 : 4; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/plugins/Label_H1.ts` around lines 39 - 40, The ternary that computes
offset in Label_H1.ts currently includes a redundant parts[0] === '- ' check
which can force an incorrect offset for messages like '- `#M1B`...' — replace the
expression const offset = parts[0] === '- ' || isNaN(parseInt(parts[1][1])) ? 3
: 4; with a simpler check that only uses the numeric test (e.g., const offset =
isNaN(parseInt(parts[1][1])) ? 3 : 4;) so offset is 3 when the second char is
non-numeric and 4 when numeric; keep or update the inline comment to note
fragility around slicing logic in the function that uses parts/offset.
There was a problem hiding this comment.
the suggestion does not work for the example that is - #M1FPN...
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for test messages that contain the '- `#M1`' pattern
rg "- `#M1`" lib/plugins/Label_H1*.test.ts -n -C3
Length of output: 404
🏁 Script executed:
#!/bin/bash
# Search for test messages that contain the '- `#M1`' pattern (properly escaped)
rg '- `#M1`' lib/plugins/Label_H1*.test.ts -n -C3
Length of output: 404
🏁 Script executed:
#!/bin/bash
# Search for test messages containing '- `#M1`' pattern using -e flag
rg -e '- `#M1`' lib/plugins/Label_H1*.test.ts -n -C3
# Also search for messages starting with '- `#M`' more broadly
rg -e '- `#M`' lib/plugins/Label_H1*.test.ts -n -C2
# Look at the actual H1_FPN test file to see the inmarsat test
cat lib/plugins/Label_H1_FPN.test.ts | grep -A5 -B5 "inmarsat"
Length of output: 3158
🏁 Script executed:
#!/bin/bash
# Search for messages containing M1 in test files
rg 'M1' lib/plugins/Label_H1*.test.ts -n -B2 -A2 | head -80
# Also look for the specific pattern in the FPN test file since user mentioned M1FPN
cat lib/plugins/Label_H1_FPN.test.ts
Length of output: 27333
@makrsmark, you're absolutely right—thank you for the correction! My analysis was incorrect.
For a message like '- #M1FPN/...':
parts[0] = '- 'andparts[1][1] = '1'(numeric)- Current code:
parts[0] === '- 'returnstrue→offset = 3→msg.slice(2+3)skips'-#M1'→ correctly decodes'FPN/...' - My suggestion:
isNaN(parseInt('1'))returnsfalse→offset = 4→msg.slice(2+4)skips'-#M1F'→ incorrectly decodes'PN/...'
The current logic is correct: when the prefix is '- ', the offset should always be 3 (to skip the 3 characters after #), regardless of whether parts[1][1] is numeric. My suggestion would break this case.
I apologize for the confusion! The parts[0] === '- ' condition is necessary, not redundant.
/\_/\
( o.o )
> ^ <
(Sorry!)
🧬 Code Graph Analysis Results
-
File: lib/utils/result_formatter.ts
- Snippet:
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,
});
} - This snippet is relevant because Label_H1 uses ResultFormatter.flightNumber(...) when parsing an H1 message that starts with a flight number prefix (as seen in the code path handling parts[0]). Understanding this function helps interpret how flight numbers get recorded in the decode result.
- Snippet:
-
File: lib/utils/h1_helper.ts
- Summary (docstring-style description):
Class: H1Helper- Method: static decodeH1Message(decodeResult: DecodeResult, message: string)
- Parameters:
- decodeResult: the DecodeResult object being populated.
- message: the H1 data payload (excluding the trailing checksum).
- Returns: boolean indicating whether decoding succeeded.
- Behavior:
- Computes checksum from the last 4 hex characters of the message; validates using CRC16 IBM SDLC reversed or GENIBUS algorithms on the data portion.
- If checksum is valid, splits data into fields (by '/'), validates message type (via parseMessageType), and processes each field key/data pair (AF, AK, CG, DC, DI, DT, DQ, ET, etc.) by invoking corresponding processing/formatter routines.
- For each known key, it delegates to specialized handlers (e.g., processPS, processPosition, processDateCode, processETA, etc.) or records unknowns via ResultFormatter.unknown.
- After processing all fields, it records the checksum algorithm and checksum itself.
- Exceptions/Error handling:
- If checksum validation fails or message type parsing fails, sets decodeResult.decoded = false and decodeResult.decoder.decodeLevel = 'none', and returns false.
- Important implementation notes:
- Handles many H1 field types by dispatching to helper functions (e.g., processPS, processCenterOfGravity, processDateCode, etc.).
- On success, returns true; on failure, returns false and adjusts decode state accordingly.
- This function is central to interpreting H1 payloads and is directly invoked by Label_H1 for various message formats.
- Parameters:
- Method: static decodeH1Message(decodeResult: DecodeResult, message: string)
- Summary (docstring-style description):
-
File: lib/DecoderPluginInterface.ts
- Snippet: Message interface
export interface Message {
label: string;
sublabel?: string;
text: string;
} - Snippet: Options interface
export interface Options {
debug?: boolean;
} - Snippet: DecodeResult interface
export interface DecodeResult {
decoded: boolean;
decoder: {
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; // eslint-disable-line@typescript-eslint/no-explicit-any
remaining: {
text?: string;
};
} - These interfaces define the core structures used by Label_H1.ts (Message, DecodeResult, and Options), and help explain how text messages are represented, how decoding state is tracked, and how results are formatted.
- Snippet: Message interface
-
File: lib/DecoderPluginInterface.ts
- Rationale: The Label_H1.ts decoder interacts with the DecodeResult, Message, and Options types defined here. Understanding the fields of DecodeResult (especially remaining.text, decoded, decoder.decodeLevel, and formatted.items) is important for diagnosing why certain inputs like "-
#M1FPN..." might not be parsed as expected.
- Rationale: The Label_H1.ts decoder interacts with the DecodeResult, Message, and Options types defined here. Understanding the fields of DecodeResult (especially remaining.text, decoded, decoder.decodeLevel, and formatted.items) is important for diagnosing why certain inputs like "-
✏️ Learnings added
Learnt from: makrsmark
Repo: airframesio/acars-decoder-typescript PR: 332
File: lib/plugins/Label_H1.ts:39-40
Timestamp: 2026-02-21T01:32:00.829Z
Learning: In lib/plugins/Label_H1.ts, when parsing two-part H1 messages (split by '#'), the offset logic `parts[0] === '- ' || isNaN(parseInt(parts[1][1])) ? 3 : 4` is correct. The `parts[0] === '- '` condition ensures that messages like '- `#M1FPN/`...' use offset=3 to skip 3 characters after '#' ('M1' + next char), whereas messages like 'F37AKL0767#M1BFPN/...' use offset=4 to skip 4 characters ('M1B' + '/'). Removing the `parts[0] === '- '` check would break '- `#M1`...' messages where parts[1][1] is numeric.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
🧠 Learnings used
Learnt from: makrsmark
Repo: airframesio/acars-decoder-typescript PR: 309
File: lib/utils/h1_helper.ts:12-19
Timestamp: 2026-01-30T02:52:39.855Z
Learning: In H1 message decoding (lib/utils/h1_helper.ts), the checksum in incoming messages is always uppercase, so case normalization is not required when comparing against calculateChecksum() output.
| function processRunway(decodeResult: DecodeResult, data: string[]) { | ||
| //FIXME - figure out names | ||
| const parts = data[0].split('.'); | ||
| const more = parts[0].split(','); | ||
|
|
||
| ResultFormatter.departureRunway(decodeResult, more[0]); | ||
| ResultFormatter.unknownArr(decodeResult, more.slice(1), ','); | ||
| ResultFormatter.unknownArr(decodeResult, parts.slice(1), '.'); | ||
| ResultFormatter.departureAirport(decodeResult, data[1].split(',')[0]); | ||
| ResultFormatter.unknownArr(decodeResult, data[1].split(',').slice(1), ','); | ||
| //ResultFormatter.unknownArr(decodeResult, data.slice(2), ':'); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
rg -n "processRunway|case 'RW'" --type=ts -C3Repository: airframesio/acars-decoder-typescript
Length of output: 963
🏁 Script executed:
sed -n '522,533p' lib/utils/h1_helper.tsRepository: airframesio/acars-decoder-typescript
Length of output: 656
processRunway will throw if the RW field has no : separator.
At line 104, the function is called with data.split(':'). If the RW field contains no :, this returns a single-element array, making data[1] undefined. Accessing undefined.split(',') on line 530 throws TypeError. Add a bounds check before accessing data[1].
Also, data[1].split(',') is evaluated twice (lines 530–531); extract it to a local variable to avoid redundant splitting.
Proposed fix
function processRunway(decodeResult: DecodeResult, data: string[]) {
//FIXME - figure out names
const parts = data[0].split('.');
const more = parts[0].split(',');
ResultFormatter.departureRunway(decodeResult, more[0]);
ResultFormatter.unknownArr(decodeResult, more.slice(1), ',');
ResultFormatter.unknownArr(decodeResult, parts.slice(1), '.');
- ResultFormatter.departureAirport(decodeResult, data[1].split(',')[0]);
- ResultFormatter.unknownArr(decodeResult, data[1].split(',').slice(1), ',');
+ if (data.length > 1) {
+ const airportParts = data[1].split(',');
+ ResultFormatter.departureAirport(decodeResult, airportParts[0]);
+ ResultFormatter.unknownArr(decodeResult, airportParts.slice(1), ',');
+ }
//ResultFormatter.unknownArr(decodeResult, data.slice(2), ':');
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/utils/h1_helper.ts` around lines 522 - 533, processRunway can throw when
the incoming data array has only one element (no ":" in the RW field); add a
guard at the start of processRunway to check whether data[1] exists before
accessing it, and return/handle gracefully if missing; also avoid duplicate
splitting by assigning data[1].split(',') to a local variable (use that variable
for ResultFormatter.departureAirport and the subsequent
ResultFormatter.unknownArr call) and keep the existing handling of
data[0]/parts/more as-is.
Summary by CodeRabbit
Release Notes
New Features
Bug Fixes
Tests