Answer every segment of an inbound concatenated message as it arrives (#83)

* Regression tests for answering every inbound segment as it arrives

* Answer every segment of an inbound concatenated message as it arrives

* Record the segment-by-segment answer in AGENTS.md and README

* Regression tests for an empty message_id on deliver_sm_resp

* Answer a deliver_sm with the empty message_id SMPP 3.4 makes it

* Record Jasmin's refusal of a deliver_sm_resp message_id

* Mark the Jasmin multipart deadlock fixed

* Regression tests for the architecture review's findings

* Give the segment id notation an owner, and every segment a status

* Correct what the ids reach and what a lost group tells the application

* Regression tests for a group lost to its own octet overrun

* Report a group lost to its own overrun, and refuse by the command it arrived on

* Keep the docs true about what a segment is answered with

* Count only the answered segments of a group lost to an overrun

* Report only what a lost group cost, and say which cap bit
This commit is contained in:
2026-09-06 04:16:11 +02:00
committed by GitHub
parent 66b49ebfb3
commit 5b7b563dc2
14 changed files with 824 additions and 160 deletions
+26
View File
@@ -1,3 +1,6 @@
import type { CommandName } from './defs/commands.ts';
import type { ParamValue } from './defs/types.ts';
const notations = {
decimal: { digits: /^[0-9]+$/, prefix: '' },
hex: { digits: /^[0-9a-f]+$/i, prefix: '0x' },
@@ -33,3 +36,26 @@ export function normaliseSmsId(id: string, notation: SmsIdNotation | undefined):
return digits.test(id) ? BigInt(`${prefix}${id}`).toString(10) : id;
}
const numbered = /^(.*)-(\d+)$/;
/** Each segment of a multipart message gets its own message_id, as a separate submit_sm must. */
export function segmentId(smsId: string, index: number, total: number): string {
return total === 1 ? smsId : `${smsId}-${String(index + 1)}`;
}
/** The message and the part an id names, or nothing where `segmentId()` did not write it. */
export function parseSegmentId(smsId: string): { base: string; part: number } | undefined {
const match = numbered.exec(smsId);
const base = match?.[1];
const part = match?.[2];
if (base === undefined || part === undefined) return undefined;
return { base, part: Number(part) };
}
/** SMPP 3.4 4.6.2 makes `deliver_sm_resp`'s `message_id` unused, and Jasmin FINs the link over one. */
export function respIdParams(cmdName: CommandName, smsId: string): Record<string, ParamValue> {
return cmdName === 'deliver_sm' ? {} : { message_id: smsId };
}