Custom commands & TLVs
Vendor extensions through an instance-scoped registry — no global mutation.
SMSC vendors extend SMPP with proprietary commands and optional parameters. better-smpp
supports both through SmppRegistry — an instance you build and pass around, unlike
node-smpp's addCommand/addTLV which mutated global tables.
Registering
import { SmppRegistry } from "@better-smpp/core";
const registry = new SmppRegistry()
.registerCommand("vendor_ping", {
id: 0x00010001,
params: [
{ name: "token", type: "cstring" },
{ name: "weight", type: "int16" },
],
})
.registerCommand("vendor_ping_resp", {
id: 0x80010001, // response id = request id | 0x80000000
params: [{ name: "token", type: "cstring" }],
})
.registerTlv("vendorTag", { tag: 0x1600, type: "cstring" });Field types: int8, int16, int32, cstring (null-terminated), string
(length-prefixed), buffer, plus the composite dest_address_array /
unsuccess_sme_array. TLVs additionally accept multiple: true (repeatable tags) and
a filter (value transform, e.g. "time").
Name or id collisions with existing registrations throw immediately.
Using it
Pass the registry to whichever layer speaks the dialect:
// Client side:
const client = new SmppClient({ url: "…", registry });
const pong = await client.request({ command: "vendor_ping", token: "abc", weight: 5 });
// Server side:
const server = new SmppServer({ registry });
server.on("session", (session) => {
session.connection.on("pdu", (pdu) => {
if (pdu.command === "vendor_ping") {
void session.respond(pdu, { token: `pong:${pdu.token}` });
}
});
});
// Or the pure codec:
const bytes = encodePdu({ command: "vendor_ping", token: "abc" }, { registry });
const decoded = decodePdu(bytes, { registry });Custom TLVs work on standard commands too — attach vendorTag: "value" to any PDU
and it encodes/decodes wherever the registry is in effect.
Unknown extensions without a registry
Even without registrations, nothing breaks: unknown inbound commands decode to
{ command: "unknown", commandId, body } (and are nacked automatically by the
connection layer), and unknown TLV tags round-trip through the
unknownTlvs: Map<number, Buffer> field — enough for pass-through proxying.