From 11ff349333f80582f5acd636fea13a3eae9310a9 Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Thu, 24 Sep 2026 00:27:01 +0200 Subject: [PATCH] Lead the changelog with the read-side break, and show each repeatable TLV's shape --- CHANGELOG.md | 12 +++++++++--- README.md | 5 ++++- src/defs/tlvs.ts | 2 +- todo.md | 7 +++++++ 4 files changed, 21 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 847371f..77f326f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,9 +38,15 @@ - `server()` refuses a `maxOctets` below 1 or not a whole number, `Infinity` included, like its other limits. `server({ maxOctets: 0 })` used to start and then refuse every multipart message. - `callback_num`, `callback_num_atag`, `callback_num_pres_ind`, `broadcast_area_identifier` and - `broadcast_error_status`, the TLVs SMPP allows more than once in a PDU, read as an array of every - occurrence in wire order, even where only one arrived, and `objToPdu()` takes an array for them and - refuses a lone value. A PDU carrying two of one used to keep only the last. + `broadcast_error_status`, the TLVs SMPP allows more than once in a PDU, keep every occurrence in + wire order. A PDU carrying two of one used to keep only the last. + + **Reading one of these now needs an index.** `pduObj.tlvs.callback_num?.tagValue` is a `Buffer[]` + even where one arrived (a `number[]` for `callback_num_pres_ind` and `broadcast_error_status`), so a + `Buffer.isBuffer()` or `typeof` check written for 0.5.0 now reads it as absent. Read `tagValue[0]` + for the first occurrence. `objToPdu()`, `session.send()` and `session.sendReturn()` take + `{ tagValue: [value] }` for them and refuse a lone value before anything goes out. +- `cmds.broadcast_sm_resp.tlvMap` is removed; nothing read it. ## 0.5.0 diff --git a/README.md b/README.md index 9eb1fb1..4395005 100644 --- a/README.md +++ b/README.md @@ -610,7 +610,8 @@ if (isCommand(pduObj, 'submit_sm')) { carried them, `'udh'` or `'sar'`, or `undefined` for a whole message. - `callback_num`, `callback_num_atag`, `callback_num_pres_ind`, `broadcast_area_identifier` and `broadcast_error_status` may repeat in one PDU, so each reads as an array of every occurrence in wire - order, and `objToPdu()` writes one TLV per element of the array it takes for them. + order: `number[]` for `callback_num_pres_ind` and `broadcast_error_status`, `Buffer[]` for the rest. + A `broadcast_sm_resp`'s `failed_broadcast_area_identifier` reads as `broadcast_area_identifier`. - `messageClassOf(dataCoding)`: `0` for the flash class, `1`, `2` and `3` for the ME-, SIM- and TE-specific ones, `undefined` where that `data_coding`'s coding group carries no class. @@ -621,6 +622,8 @@ if (isCommand(pduObj, 'submit_sm')) { naming the character, its code point and where it is. - Every text field is latin1: addresses, `system_id`, `message_id`, `service_type` and the C-Octet String TLVs. A character past `U+00FF` is refused, as is a `U+0000` in a C-Octet String. +- The five repeatable TLVs take an array, written as one TLV per element; a lone value or an empty + array is refused. - A `Buffer` goes out exactly as given under any `data_coding`: binary payloads, hand-built user data headers, deliberately malformed bodies. - `session.send()` and `session.sendReturn()` build through the same codec and refuse the same bodies. diff --git a/src/defs/tlvs.ts b/src/defs/tlvs.ts index a585936..7959625 100644 --- a/src/defs/tlvs.ts +++ b/src/defs/tlvs.ts @@ -154,7 +154,7 @@ function occurrences(value: TlvValue, multiple: boolean): Result<{ values: Param return Array.isArray(value) ? { err: new Error('takes one value, not an array') } : { values: [value] }; } - if (!Array.isArray(value)) return { err: new Error('is repeatable, give an array of its values') }; + if (!Array.isArray(value)) return { err: new Error('is repeatable, wrap it in an array: [value]') }; if (value.length === 0) return { err: new Error('holds no values, omit it instead') }; diff --git a/todo.md b/todo.md index b1a4781..ea80ae6 100644 --- a/todo.md +++ b/todo.md @@ -197,6 +197,13 @@ and is also what the panel ranked hardest — two methods, one answer. ### Correctness, ahead of everything below +- [ ] **Type each known TLV's value by its tag, on read and on write, before 0.6.0 is cut.** Every + `tagValue` is `TlvValue`, so `{ callback_num: { tagValue: buf } }` compiles and is refused + only at runtime, and reading `receipted_message_id` has to narrow out arrays it can never + hold. Derive the types from the specs' own wire types and `multiple` flag. It has to land in + the same minor as the arrays, or narrowing the types is a second break. From the + product-owner review of #25. + - [ ] **Settle what a repeated tag not marked `multiple` reads as, and pin it in a test.** A vendor tag or a known single-value tag a peer sends twice keeps the last occurrence and drops the rest silently, which goal 3 argues against; listing it would change every such tag's shape.