Files
smpp-js/src/protocol/bind.ts
T
lilleman 980e9d4569
Mirror / push (push) Has been cancelled
Test / lint (pull_request) Successful in 22s
Test / test (18) (pull_request) Successful in 32s
Test / test (20) (pull_request) Successful in 29s
Test / test (22) (pull_request) Successful in 32s
Test / test (24) (pull_request) Successful in 29s
Test / test (26) (pull_request) Successful in 36s
Move src into codec, protocol, messages, session, client and server
2026-09-30 20:15:04 +02:00

74 lines
2.9 KiB
TypeScript

import type { Result } from '../result.ts';
import { quoted } from '../result.ts';
export const bindCommands: readonly string[] = [
'bind_receiver',
'bind_transceiver',
'bind_transmitter',
];
export type BindType = 'receiver' | 'transceiver' | 'transmitter';
/** Which end of the link a session is. Only `server()` is the SMSC; everything else is the ESME. */
export type LinkEnd = 'esme' | 'smsc';
export function bindTypeFromCommand(cmdName: string): BindType | undefined {
if (cmdName === 'bind_receiver') return 'receiver';
if (cmdName === 'bind_transceiver') return 'transceiver';
if (cmdName === 'bind_transmitter') return 'transmitter';
return undefined;
}
/**
* Which message-carrying command an inbound one stands in for. Every command but `data_sm` names
* its own direction; that one travels either way, so the end it arrived at is what says.
*/
export function standsInFor(cmdName: string, linkEnd: LinkEnd): string {
if (cmdName !== 'data_sm') return cmdName;
return linkEnd === 'smsc' ? 'submit_sm' : 'deliver_sm';
}
/**
* Whether a bind direction carries a command at all. A receiver-bound ESME submits nothing and a
* transmitter-bound one is delivered nothing, whichever end of the link is looking. A session that
* has not bound carries everything, since nothing has declared a direction yet.
*/
export function bindCarries(
bindType: BindType | undefined,
cmdName: string,
linkEnd: LinkEnd,
): boolean {
const carried = standsInFor(cmdName, linkEnd);
if (bindType === 'receiver') return carried !== 'submit_sm';
if (bindType === 'transmitter') return carried !== 'deliver_sm';
return true;
}
/** SMPP 3.4: a peer that declares no version at all is one from before optional parameters. */
export const undeclaredInterfaceVersion = 0x00;
export type SessionBind = { as: BindType; peerVersion: number };
function isBindType(value: unknown): value is BindType {
return typeof value === 'string' && bindTypeFromCommand(`bind_${value}`) !== undefined;
}
/** A bind as `Session.bound()` records it: undefined declares no version, which is pre-3.4. */
export function checkedBind(bindType: unknown, declaredVersion: unknown): Result<{ bind: SessionBind }> {
if (!isBindType(bindType)) {
return { err: new Error(`bindType must be receiver, transceiver or transmitter, the bind command's name without "bind_", got ${quoted(bindType)}`) };
}
if (declaredVersion === undefined) return { bind: { as: bindType, peerVersion: undeclaredInterfaceVersion } };
if (typeof declaredVersion !== 'number' || !Number.isInteger(declaredVersion) || declaredVersion < 0 || declaredVersion > 0xFF) {
return { err: new Error(`declaredVersion must be an integer 0-255, the interface_version param or the sc_interface_version TLV's tagValue, or undefined where the peer declared none, got ${quoted(declaredVersion)}`) };
}
return { bind: { as: bindType, peerVersion: declaredVersion } };
}