Files
smpp-js/todo.md
T

8.1 KiB

todo.md

Remaining work for the @larvit/smpp 1.0.0 rewrite. Read AGENTS.md first — the hard rules there constrain every item below.

Status

The rewrite is feature complete and green: 223 tests, lint and typecheck clean, verified on Node 18, 20, 22 and 24. What is left is release work and a few things worth adding before or after 1.0.0.

docker compose run --rm node npm install
docker compose run --rm node npm test

The agreed API

Settled with the maintainer before implementation. Do not change any of it without asking. The public surface is documented in README.md; this is the short form.

import { client, server } from '@larvit/smpp';

const { err, session } = await client({ host, password, port, username });
const { err: sendErr, pduObjs, smsIds } = await session.sendSms({ dlr, from, message, to });
await session.unbind();

const { err: serverErr, server: smpp } = await server({ authenticate, port });
smpp.on('session', session => {
	session.on('sms', async sms => {
		await sms.sendResp();
		if (sms.dlr) await sms.sendDlr('DELIVERED');
	});
});
await smpp.close();

Rules the API follows:

  • Never throws. Everything fallible resolves to { err?, … }. See AGENTS.md rule 1.
  • Named exports only, no default export. defs is exported as a group alongside the individual tables.
  • The PDU codec is synchronous and returns { err?, pduObj? } / { err?, buffer? }.
  • Low-level surface stays public, including session.sock, session.send() and session.sendReturn().

Done

Covered by
Definition tables: constants, errors, encodings, wire types, TLVs, commands test/encodings.test.ts, test/types.test.ts, test/commands.test.ts
Message helpers: splitting, bit counting, SMPP dates and times test/message.test.ts
PDU codec: parse, build, respond, per-command typing, bounds checks test/pdu.test.ts
Stream framing test/pdu-framer.test.ts
Delivery receipt parsing, TLV and text test/dlr.test.ts
Session, client, server: bind, auth, send, reassembly, DLRs, timeouts, abort, send window test/session.test.ts
Merged multipart DLRs, reconnect, reassembly bounds, per-send abort, the segment cap test/session-extras.test.ts
Every runnable README example test/readme.test.ts
Cross-checked against node-smpp both ways and over a live session test/interop.test.ts
CI on Node 18/20/22/24, Renovate, tag-triggered publish .github/workflows/

Every defect listed in the AGENTS.md table has a regression test naming the behaviour.

The GitHub backlog, once this branch is master

Nothing below is closed while master is still 0.4.0 — declining a security bump on a live default branch is worse than leaving it open. Work through this immediately after the merge.

Close as fixed by 1.0.0, naming the replacement in the comment:

Fixed by
#4 DLR errors with message_state missing dlrFromPdu() parses the stat: receipt text when the TLVs are absent
#33 Large inbound text arrives as raw Buffer segments IncomingRequests reassembles a UDH-carrying deliver_sm into one sms event
#3 Tests for flash messages test/session.test.ts
#20 Tests fail on current dependency versions The mocha suite is gone; node:test on Node 18/20/22/24
#2 Tests for the README examples test/readme.test.ts
#17 addr_ton/addr_npi should be settable sendSms() takes all four, documented and tested
#16 Support all three bind types Bound and enforced in both directions
#13 Limit a long SMS to fewer segments The maxSegments send option
#68 message_id in submit_sm_resp, spec DLR codes All four hold: sendResp() always answers a message_id, per segment; stat:UNDELIV is the 7-character code. Credit the reporter — the fork found real defects.

Close as superseded, all against 0.4.0 dependencies the rewrite does not have — async, coveralls, eslint, iconv-lite, larvitutils, mocha, mocha-eslint, portfinder, uuid: #40, #41, #42, #45, #46, #47, #59, #63, #64, #65, #67, #70. #70 is the open uuid advisory GitHub reports on the default branch; it disappears with the runtime dependencies rather than being fixed.

#60 is Renovate's dashboard — leave it, it re-baselines itself against the new package.json.

Leave open: #8, the socket's remote host and port on log messages. Only server - incoming connection carries them today; putting them on every session message is a change to every call site.

Before publishing 1.0.0

  • Create the @larvit/smpp package on npm and add NPM_TOKEN to the repository secrets, which .github/workflows/release.yaml needs.
  • Tag v1.0.0 to publish.
  • npm deprecate larvitsmpp pointing at @larvit/smpp. Maintainer's call to run it; not something CI should do.
  • Decide what happens to master: this branch is an orphan, so merging it is a deliberate act.

Worth doing, not blocking

  • An async event listener that rejects escapes the guard. Session.emit() wraps super.emit() in try/catch, which catches a listener that throws synchronously but not one that returns a rejected promise — that surfaces as an unhandled rejection and takes the process down, which hard rule 1 says must not happen. Every README example uses session.on('sms', async sms => …), so the shape is the one applications will write. The library's own calls inside such a listener never reject, so the examples themselves are safe. Fixing it means dispatching rawListeners() by hand in emit() and routing a rejection to sessionError — a change to the hottest path, so it needs a decision before 1.0.0.

  • In-flight sends across a reconnect. They currently fail with "Session closed before a response arrived" and the caller retries. Re-queueing them automatically would be friendlier but risks duplicate delivery, so it needs a decision before it is built.

  • session.ts is 386 lines. The one seam left in it is a socket-to-PDU transport, which would move the deliberately public sock field out of Session or turn it into a getter — a public-surface change, so it waits for a decision.

  • Group the session's collaborators under src/session/. Only session.ts imports reassembly, dlr-merger, send-window, link-timers, reconnect-loop, pending-requests and send-sms, so the directory would make that boundary visible. Do it on the next extraction out of session.ts, not as a move of its own.

  • submit_multi and the broadcast commands encode and decode, but nothing exercises them end to end. The interop suite is the natural place.

  • Move to TypeScript 7 once typescript-eslint supports it; renovate.json pins TypeScript below 6.1 for exactly that reason.

  • Coverage reporting. node --test --experimental-test-coverage works today; nothing publishes the numbers.