Commit Graph

109 Commits

Author SHA1 Message Date
lilleman 9fb8f348c8 Drop the alternate TLV names from tlvs, correct the false doc claims the prose pass found, and file the moves
Mirror / push (push) Has been cancelled
Test / lint (pull_request) Successful in 22s
Test / test (20) (pull_request) Successful in 30s
Test / test (22) (pull_request) Successful in 36s
Test / test (24) (pull_request) Successful in 31s
Test / test (26) (pull_request) Successful in 30s
Test / test (18) (pull_request) Successful in 30s
2026-09-27 15:36:56 +02:00
lilleman 01891bc764 Word a disagreeing tagId as a mismatch, and record how a TLV input is keyed
Mirror / push (push) Has been cancelled
Test / test (18) (pull_request) Successful in 31s
Test / lint (pull_request) Successful in 22s
Test / test (20) (pull_request) Successful in 30s
Test / test (24) (pull_request) Successful in 30s
Test / test (26) (pull_request) Successful in 30s
Test / test (22) (pull_request) Successful in 37s
2026-09-27 15:33:35 +02:00
lilleman 42b1239f3d Name the retry status by direction, and correct what the held-message decision claims
Test / lint (pull_request) Successful in 28s
Test / test (18) (pull_request) Successful in 31s
Test / test (20) (pull_request) Successful in 30s
Test / test (22) (pull_request) Successful in 31s
Test / test (24) (pull_request) Successful in 30s
Test / test (26) (pull_request) Successful in 37s
Mirror / push (push) Successful in 4s
2026-09-26 14:53:09 +02:00
lilleman 855ccbe75f Refuse a message past the held-message bound with ESME_RTHROTTLED, and answer a full reassembly buffer the same way
Test / lint (pull_request) Successful in 23s
Test / test (18) (pull_request) Successful in 31s
Test / test (20) (pull_request) Successful in 31s
Test / test (22) (pull_request) Successful in 31s
Test / test (24) (pull_request) Successful in 31s
Test / test (26) (pull_request) Successful in 31s
Mirror / push (push) Successful in 4s
2026-09-26 13:18:56 +02:00
lilleman d8232bcd67 Split the held-message bounds and the shutdownTimeout fallback into decisions of their own, and drop what code and README say
Test / test (20) (pull_request) Successful in 32s
Test / test (24) (pull_request) Successful in 38s
Mirror / push (push) Successful in 7s
Test / lint (pull_request) Successful in 29s
Test / test (18) (pull_request) Successful in 33s
Test / test (22) (pull_request) Successful in 32s
Test / test (26) (pull_request) Successful in 32s
2026-09-25 19:32:59 +02:00
lilleman c3ebd6d6bc Cap the heap unanswered messages hold, and detach them from the chunk they arrived in
Test / lint (pull_request) Successful in 25s
Test / test (22) (pull_request) Successful in 32s
Test / test (18) (pull_request) Successful in 33s
Test / test (20) (pull_request) Successful in 32s
Test / test (24) (pull_request) Successful in 31s
Test / test (26) (pull_request) Successful in 31s
Mirror / push (push) Successful in 6s
2026-09-25 19:22:31 +02:00
lilleman 01210e734a Correct the doc claims the prose pass found, and move two decisions into the log
Test / lint (pull_request) Successful in 22s
Test / test (18) (pull_request) Successful in 28s
Test / test (20) (pull_request) Successful in 34s
Test / test (22) (pull_request) Successful in 34s
Test / test (24) (pull_request) Successful in 30s
Test / test (26) (pull_request) Successful in 28s
Mirror / push (push) Successful in 7s
2026-09-24 00:31:49 +02:00
lilleman cbfd6e0fbe Count ten goals, and point at where the hard rules live
Mirror / push (push) Has been cancelled
Test / lint (pull_request) Successful in 20s
Test / test (18) (pull_request) Successful in 34s
Test / test (20) (pull_request) Successful in 29s
Test / test (22) (pull_request) Successful in 30s
Test / test (24) (pull_request) Successful in 30s
Test / test (26) (pull_request) Successful in 33s
2026-09-24 00:19:19 +02:00
lilleman 5cdd3ccdae Say tlvs.ts reads a TLV stream too
Mirror / push (push) Successful in 6s
Test / lint (pull_request) Successful in 31s
Test / test (18) (pull_request) Successful in 37s
Test / test (20) (pull_request) Successful in 29s
Test / test (22) (pull_request) Successful in 29s
Test / test (24) (pull_request) Successful in 37s
Test / test (26) (pull_request) Successful in 29s
2026-09-24 00:18:13 +02:00
lilleman c1e0407942 Refuse a NULL inside a C-Octet String, which ends the field on the peer
Mirror / push (push) Successful in 4s
Test / lint (pull_request) Successful in 21s
Test / test (18) (pull_request) Successful in 29s
Test / test (20) (pull_request) Successful in 29s
Test / test (22) (pull_request) Successful in 31s
Test / test (24) (pull_request) Successful in 35s
Test / test (26) (pull_request) Successful in 30s
2026-09-21 07:59:08 +02:00
lilleman 7db242e375 Read and write every text field on the wire as latin1
Mirror / push (push) Successful in 5s
Test / lint (pull_request) Successful in 22s
Test / test (18) (pull_request) Successful in 29s
Test / test (20) (pull_request) Successful in 30s
Test / test (22) (pull_request) Successful in 30s
Test / test (24) (pull_request) Successful in 29s
Test / test (26) (pull_request) Successful in 30s
2026-09-21 07:54:25 +02:00
lilleman 1416615ca3 Bound every connect attempt at 10 s by default, with false the way to opt out
Mirror / push (push) Successful in 5s
Test / lint (pull_request) Successful in 21s
Test / test (18) (pull_request) Successful in 19s
Test / test (20) (pull_request) Successful in 19s
Test / test (22) (pull_request) Successful in 19s
Test / test (24) (pull_request) Successful in 20s
Test / test (26) (pull_request) Successful in 19s
2026-09-20 20:34:53 +02:00
lilleman 8f416c2bc4 Name the peer and the stalled phase on a connect timeout, and quote an untyped value
Mirror / push (push) Successful in 5s
Test / lint (pull_request) Successful in 21s
Test / test (18) (pull_request) Successful in 19s
Test / test (20) (pull_request) Successful in 19s
Test / test (22) (pull_request) Successful in 24s
Test / test (24) (pull_request) Successful in 18s
Test / test (26) (pull_request) Successful in 19s
2026-09-20 18:17:39 +02:00
lilleman 84174b4a5b Refuse a connectTimeout Node's timers cannot hold, and pin the refusals the tests missed
Mirror / push (push) Successful in 5s
Test / lint (pull_request) Successful in 20s
Test / test (18) (pull_request) Successful in 26s
Test / test (20) (pull_request) Successful in 19s
Test / test (22) (pull_request) Successful in 20s
Test / test (24) (pull_request) Successful in 19s
Test / test (26) (pull_request) Successful in 20s
2026-09-20 18:12:39 +02:00
lilleman 17447bbe3a Give up a connect after connectTimeout instead of waiting the OS out
Mirror / push (push) Successful in 4s
Test / lint (pull_request) Successful in 21s
Test / test (18) (pull_request) Successful in 22s
Test / test (20) (pull_request) Successful in 18s
Test / test (22) (pull_request) Successful in 18s
Test / test (24) (pull_request) Successful in 19s
Test / test (26) (pull_request) Successful in 27s
2026-09-20 00:42:09 +02:00
lilleman 656bbae500 Move the decisions to docs/decisions.md and index them in AGENTS.md
Test / lint (pull_request) Successful in 23s
Test / test (18) (pull_request) Successful in 20s
Test / test (20) (pull_request) Successful in 21s
Test / test (22) (pull_request) Successful in 20s
Test / test (24) (pull_request) Successful in 20s
Test / test (26) (pull_request) Successful in 21s
Mirror / push (push) Successful in 6s
2026-09-19 01:04:08 +02:00
lilleman 0f13951e8c Move the goals to README.md and state the audience
Mirror / push (push) Successful in 5s
Test / lint (pull_request) Successful in 21s
Test / test (18) (pull_request) Successful in 21s
Test / test (20) (pull_request) Successful in 19s
Test / test (22) (pull_request) Successful in 20s
Test / test (24) (pull_request) Successful in 20s
Test / test (26) (pull_request) Successful in 20s
2026-09-19 00:48:21 +02:00
lilleman c5adfd8e39 Say what the mirror's concurrency group does, and name every way a stale ref survives
Test / lint (pull_request) Successful in 20s
Test / test (18) (pull_request) Successful in 18s
Test / test (20) (pull_request) Successful in 18s
Test / test (22) (pull_request) Successful in 18s
Test / test (24) (pull_request) Successful in 17s
Test / test (26) (pull_request) Successful in 17s
Mirror / push (push) Successful in 3s
2026-09-14 20:54:04 +02:00
lilleman 14b114afda Split the GitHub mirror so Gitea creates its runs and no delete is dropped
Mirror / push (push) Successful in 5s
Test / lint (pull_request) Successful in 20s
Test / test (18) (pull_request) Successful in 18s
Test / test (20) (pull_request) Successful in 18s
Test / test (22) (pull_request) Successful in 22s
Test / test (24) (pull_request) Successful in 17s
Test / test (26) (pull_request) Successful in 17s
2026-09-14 20:40:38 +02:00
lilleman baa0ad553a Set failover's options, and decide to test on Linux only while keeping macOS and Windows unbroken
Test / lint (pull_request) Successful in 19s
Test / test (18) (pull_request) Successful in 17s
Test / test (20) (pull_request) Successful in 17s
Test / test (22) (pull_request) Successful in 17s
Test / test (24) (pull_request) Successful in 17s
Test / test (26) (pull_request) Successful in 17s
2026-09-14 17:26:46 +02:00
lilleman e32f9ccf46 Plan failover, an optional store for pooling and restarts, shipping src, and Gitea runners for macOS and Windows
Test / lint (pull_request) Successful in 19s
Test / test (18) (pull_request) Successful in 18s
Test / test (20) (pull_request) Successful in 18s
Test / test (22) (pull_request) Successful in 19s
Test / test (24) (pull_request) Successful in 17s
Test / test (26) (pull_request) Successful in 18s
2026-09-14 17:08:54 +02:00
lilleman b3cbbba615 Record the gaps found against other SMPP libraries, and make configurable and extendable a goal
Test / lint (pull_request) Successful in 1m4s
Test / test (18) (pull_request) Successful in 17s
Test / test (20) (pull_request) Successful in 17s
Test / test (22) (pull_request) Successful in 18s
Test / test (24) (pull_request) Successful in 25s
Test / test (26) (pull_request) Successful in 56s
2026-09-14 16:32:23 +02:00
lilleman e8558aa471 Release as 0.5.0, and file issues on the GitHub mirror 2026-09-14 14:09:58 +02:00
lilleman 8315f1882a Keep todo.md, and plan the backlog close-out and the move to Gitea in it
Test / lint (push) Successful in 20s
Test / test (18) (push) Successful in 19s
Test / test (20) (push) Successful in 19s
Test / test (22) (push) Successful in 19s
Test / test (24) (push) Successful in 18s
Test / test (26) (push) Successful in 29s
2026-09-13 16:31:07 +02:00
lilleman 311ee7c677 Name the branches as the rename leaves them 2026-09-13 16:31:07 +02:00
lilleman 7310634d7d Rewrite the README task-first for 1.0.0 and move the 0.4.0 guide to MIGRATION.md (#102)
Test / lint (push) Successful in 20s
Test / test (18) (push) Successful in 18s
Test / test (20) (push) Successful in 18s
Test / test (22) (push) Successful in 18s
Test / test (24) (push) Successful in 24s
Test / test (26) (push) Successful in 18s
2026-09-13 12:05:13 +02:00
lilleman 549b3260c9 Record the smsIds decision under the surface it changes 2026-09-12 22:41:02 +02:00
lilleman 13e3ff8f1f Say undefined where the SMSC named no id for a segment it took (#101)
* Cover the ids a send gets back where the SMSC named only some of them

* Say undefined where the SMSC named no id for a segment it took

* Record why the merger takes the wider type, and de-duplicate the interop narrowing

* Guard the numbering hole the merger must refuse, and correct the phase record
2026-09-12 22:37:55 +02:00
lilleman ca1a7473ed Declare the alphabet a GSM message is actually written in (#100)
* Regression tests for the alphabet a GSM message declares

* Declare the alphabet a GSM message is actually written in

* Reflow the composing-by-hand paragraph

* Drop the ASCII alias SMPP's flat table shares with the option's own

* Bound the receipt test's wait and tighten the notes around it
2026-09-09 21:30:55 +02:00
lilleman 989eb01763 Name the refusal rule, and refuse a period the encoder cannot spell (#99)
* Refuse a relative validity period the SMPP time format cannot hold

* Export unencodableText(), so a caller can word a refusal as the library does

* Name the refusal rule in the README, and correct what it claims

* Word the period refusal by what the encoder spells, and keep unencodableText() internal

* Correct the decision bullet premise about what the relative format holds

* Close the stability nits on the time guard and its tests
2026-09-09 20:36:13 +02:00
lilleman ef13290230 Refuse a body the PDU's own data_coding cannot carry (#98)
* Regression tests for a body the PDU's own data_coding cannot carry

* Refuse a body the PDU's own data_coding cannot carry, in the codec both send paths build through

* Record the codec's body refusal, and drop two todo entries it and the interop run close

* Regression tests for the aliased body TLV and the data_coding short_message settles

* Find the body TLV by tag id, leave data_coding to the field that will be read, and correct the record

* Record the two architecture findings this change does not close

* Regression tests for a duplicated body tag and a short_message the wire never carries

* Resolve every body TLV against the field that will be read, and move TLV writing to its table

* Regression tests for a body only the TLV carries and an empty short_message

* Let only octets the command writes settle the alphabet, and stop shadowing the TLV table
2026-09-09 19:33:36 +02:00
lilleman c06cc0648b Refuse what we cannot send: an alphabet that cannot carry the message, and a time nobody can read (#97)
* Regression tests for a named alphabet and a time that cannot carry the send

* Refuse a message the named alphabet cannot carry, and a time nobody can read

* Close the architecture review: a builder that cannot drop a time, and docs that hold

* Refuse a time of no known kind, and clamp a relative period to what the format holds

* Reflow the decision record and name the clamp in its test
2026-09-09 18:16:08 +02:00
lilleman 7d6039796d Fit every concatenated segment in an SMS, and close the encoding helpers' throw (#96)
* Regression tests for the concatenated segment budget and the encoding-name door

* Budget unpacked segments at 134 octets, and export isEncodingName

* Name the length a Latin-1 message starts costing more segments at

* Fold the totality clause into hard rule 1, and log the unrepresentable-alphabet finding

* Make the maxSegments regression discriminate, and drop the narrating comments

* State the per-alphabet capacities accurately, and log the invalid-date path
2026-09-09 17:21:51 +02:00
lilleman e80b07167c Read the GSM 03.38 message class where the spec puts it (#95)
* Regression tests for the GSM 03.38 message class behind sms.flash

* Read the GSM 03.38 message class where the spec puts it, and refuse a flash message no alphabet can carry

* Keep the code span in the message-class decision on one line

* Stop offering the flash message class as an alphabet, and check the encoding option by name

* Settle every refusable send option in one check, and record what the review left open

* Say what the flash refusal and the encoding option actually do

* Keep the README off the alphabet whose long messages do not fit
2026-09-09 16:40:32 +02:00
lilleman 5a0cf56754 Fixtures for the receipt bodies operator documentation publishes (#93)
* Fixtures for the receipt bodies operator documentation publishes

* Read the stat:FAILED several operators write as UNDELIVERABLE

* Cover the 16-bit UDH concatenation element with a fixture

* Record phase 9 of the interoperability plan

* Share the dummy SMSC and tighten the operator fixtures

* Correct what the test conventions claim about the doubles and the tree

* Read a receipt date that carries its century

* Read the stat spelling CM.com publishes

* Say what the receipt status table actually holds
2026-09-08 18:56:57 +02:00
lilleman 83176f53b0 Let sendSms() name the esm_class messaging mode (#92)
* Regression tests for the messaging mode on sendSms()

* Let sendSms() name the esm_class messaging mode

* Document the messaging mode option and the decision behind it

* Name the constants move for migrating consumers and share the refusal's value naming

* Cover the messaging-mode refusals through submitSms()

* Refuse transaction mode and a report under datagram mode on sendSms()

* Narrow the mode checker and name the default mode once
2026-09-08 17:30:20 +02:00
lilleman 18de565aad Reassemble a concatenated message segmented with the sar_* TLVs (#91)
* Regression tests for inbound sar_* concatenation

* Reassemble a concatenated message segmented with the sar_* TLVs

* Record the sar_* fix in the jasmin and Java-client findings

* Name the sar_* TLVs in the refusal, and separate group keys the addresses cannot forge

* Name the field a refused segment got wrong, and key groups on a separator no address holds

* Bound the new session tests, and derive Concat from the UDH fields
2026-09-06 21:03:14 +02:00
lilleman 30eedc81ee Let a signal abort a send queued behind a full send window (#90)
* Regression tests for aborting a send queued behind a full window

* Let a signal abort a send queued behind a full send window

* Log a queued send, tighten its regression tests and settle the decision record

* Take the send window's queue off an array so an abort cannot stall the loop

---------

Co-authored-by: Mikael Göransson <mikael.goransson@timewave.se>
2026-09-06 18:17:01 +02:00
lilleman 039951e69b Refuse a PDU whose optional parameters do not end on command_length (#87)
* Regression tests for a truncated TLV tail refused rather than accepted

* Refuse a PDU whose optional parameters do not end on command_length

* Assert the bare TLV header refusal against jsmpp instead of recording it as a defect

* Note the truncated TLV tail defect as fixed in the java-client findings

* Derive the padding position, share the bare TLV fixture and trim the decision record

* Regression tests for a PDU whose trailing C-Octet String a peer left out

* An absent trailing C-Octet String consumes no octet, so a bodyless PDU still parses

* Bound the TLV loop by the buffer it was given rather than a second spelling of its length

* Answer the stability review's questions in the record and pin the array contract
2026-09-06 18:01:35 +02:00
lilleman bcbb042c1b Give server() consumers the onRequest hook the docs promise (#86)
* Regression tests for an onRequest hook on server()

* Compose the application's onRequest hook into server() past bind

* Document the server's onRequest hook and record the composition decision

* Pin that a re-bind and a pre-bind unbind never reach the hook, and that a non-function hook is refused at startup

* Keep every bind and all pre-bind traffic out of the application's hook

* Make the hook's published promises exact, and record the src layout deferral

* Trim the record to what the tests do not already forbid

* Pin that nothing answers a request whose hook failed

* Answer nothing for a request whose hook failed, on both surfaces alike

* State the hook's failure policy as it now behaves

* Name the request a failed handler left unanswered, and say what a wedged hook reports
2026-09-06 17:20:45 +02:00
lilleman 184d1dc7af Read a body from message_payload and accept data_sm (#84)
* Regression tests for a body in message_payload and for data_sm

* Read a body from message_payload and accept data_sm

* Assert the fixed message_payload and data_sm behaviour against Jasmin

* Record the message_payload and data_sm fixes in the Jasmin findings

* Read an inbound data_sm as the direction it travelled, and export messageOctets

* Refuse a segment with the code its stand-in command defines

* Name the stand-in the refusal status is read from
2026-09-06 05:59:27 +02:00
lilleman 5b7b563dc2 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
2026-09-06 04:16:11 +02:00
lilleman db02f0058d Tell a refused PDU apart from a session that failed (#82)
* Regression tests for telling a refused PDU from a failed session

* Export PduRefusedError so a refusal can be told from a session failure

* Document telling a refused PDU from a session failure

* Share the malformed-PDU fixtures and pin that a dead stream is no refusal

* Say once what a refusal is, and why the header type is published

* Fold the refused-response test into the one that already staged it

* Drop the refusal-rate claim the interop finding does not support

* Build a raw PDU one way, sequence number included

* Follow PduRefusedError into pdu-refusal.ts
2026-09-05 23:16:59 +02:00
lilleman afe188eecd Retry the first connect and bind on reconnect fromStart (#80)
* Regression tests for a client that retries its first connect and bind

* Retry the first connect and bind on reconnect: { fromStart: true }

* Pin that the signal bounding the first bind also closes the session

* Name a failed connect once, default the backoff where it lives, and fix the deadline advice

* Carry the failure that started the retries on the abort that ends them

* Pin one retry announcement per attempt when a link drops mid-bind

* Announce a retry when its wait is over, not when it is scheduled

* Say what the process-hold assertion expects, not what a failure would mean
2026-09-05 22:47:06 +02:00
lilleman 45d2f5548e Read a delivery receipt's body as the octets that arrived (#81)
* Regression tests for a receipt body read under a data_coding it is not written in

* Read a delivery receipt's body as the octets that arrived, not by its data_coding

* Assert SMPPSim's UCS2 receipt body parses like every other segment's

* Record the receipt-body defect as fixed

* Retain a multipart segment's octets once, not twice

* Sort the new type import into its file's order
2026-09-05 22:37:05 +02:00
lilleman ea42bc6d20 Refuse a single unreadable PDU instead of the whole link (#79)
* Regression tests: a PDU the codec cannot read costs that PDU, not the link

* Refuse a single unreadable PDU instead of the whole link

* smscsim interop asserts the first attempt gets every DLR

* Record the smscsim sequence number defect as fixed

* One framing rule and one response-command lookup, per the architecture review

* Apply the stability review's nits: honest sessionError docs and one link-survives assertion
2026-09-05 21:00:52 +02:00
lilleman 144567c294 Claim an error only where the state failed, and freeze the two state lists 2026-09-04 08:51:13 +02:00
lilleman 54493c12b2 Write a transient state as an intermediate delivery notification 2026-09-04 08:38:29 +02:00
lilleman 2936c7060d Justify the state test by what peers write, not by what this library happens to write 2026-09-03 21:17:39 +02:00
lilleman e28b219a4f Record that message_state 0 is read as the transient state 5.0 names it 2026-09-03 21:05:40 +02:00